diff --git a/README.md b/README.md index 77af4773c..aa2a4db85 100644 --- a/README.md +++ b/README.md @@ -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) -# Invoice Ninja version 5.1 RC2! - -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! +# Invoice Ninja version 5! ## Quick Start diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php index 7ac6dcee3..1a98f29dd 100644 --- a/app/Http/Controllers/Auth/LoginController.php +++ b/app/Http/Controllers/Auth/LoginController.php @@ -171,7 +171,7 @@ class LoginController extends BaseController //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(); @@ -194,6 +194,7 @@ class LoginController extends BaseController $user->setCompany($user->account->default_company); $timeout = auth()->user()->company()->default_password_timeout; + Cache::put(auth()->user()->hashed_id.'_logged_in', Str::random(64), $timeout); $cu = CompanyUser::query() @@ -322,33 +323,34 @@ class LoginController extends BaseController if ($user) { - $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')); + // we are no longer accessing the permissions for gmail - email permissions here - $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 = $client->authenticate(request()->input('server_auth_code')); - } - catch(\Exception $e) { + // $token = false; - 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')); + // try{ + // $token = $client->authenticate(request()->input('server_auth_code')); + // } + // 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 = $token['refresh_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)); $new_account = [ diff --git a/app/Http/Middleware/QueryLogging.php b/app/Http/Middleware/QueryLogging.php index 92aee1a20..b898814f6 100644 --- a/app/Http/Middleware/QueryLogging.php +++ b/app/Http/Middleware/QueryLogging.php @@ -54,7 +54,7 @@ class QueryLogging nlog($request->method().' - '.$request->url().": $count queries - ".$time); // if($count > 50) - // Log::nlog($queries); + //nlog($queries); } } diff --git a/app/Http/Requests/Client/StoreClientRequest.php b/app/Http/Requests/Client/StoreClientRequest.php index b9cb4f1a1..313ee11eb 100644 --- a/app/Http/Requests/Client/StoreClientRequest.php +++ b/app/Http/Requests/Client/StoreClientRequest.php @@ -19,6 +19,7 @@ use App\Models\Client; use App\Models\GroupSetting; use App\Utils\Traits\MakesHash; use Illuminate\Support\Facades\Cache; +use Illuminate\Validation\Rule; class StoreClientRequest extends Request { @@ -48,7 +49,6 @@ class StoreClientRequest extends Request /* Ensure we have a client name, and that all emails are unique*/ //$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['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email'; $rules['contacts.*.password'] = [ @@ -66,6 +66,10 @@ class StoreClientRequest extends Request $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; } @@ -122,7 +126,7 @@ class StoreClientRequest extends Request public function messages() { return [ - 'unique' => ctrans('validation.unique', ['attribute' => 'email']), + // 'unique' => ctrans('validation.unique', ['attribute' => ['email','number']), //'required' => trans('validation.required', ['attribute' => 'email']), 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']), ]; diff --git a/app/Http/Requests/Client/UpdateClientRequest.php b/app/Http/Requests/Client/UpdateClientRequest.php index e322ad0a2..c23059796 100644 --- a/app/Http/Requests/Client/UpdateClientRequest.php +++ b/app/Http/Requests/Client/UpdateClientRequest.php @@ -16,6 +16,7 @@ use App\Http\Requests\Request; use App\Http\ValidationRules\ValidClientGroupSettingsRule; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; class UpdateClientRequest extends Request { @@ -52,7 +53,14 @@ class UpdateClientRequest extends Request $rules['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,'.$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['contacts.*.email'] = 'bail|nullable|distinct|sometimes|email'; $rules['contacts.*.password'] = [ @@ -72,7 +80,6 @@ class UpdateClientRequest extends Request public function messages() { return [ - 'unique' => ctrans('validation.unique', ['attribute' => 'email']), 'email' => ctrans('validation.email', ['attribute' => 'email']), 'name.required' => ctrans('validation.required', ['attribute' => 'name']), 'required' => ctrans('validation.required', ['attribute' => 'email']), diff --git a/app/Http/Requests/Credit/UpdateCreditRequest.php b/app/Http/Requests/Credit/UpdateCreditRequest.php index 426301ef3..2c7c43d39 100644 --- a/app/Http/Requests/Credit/UpdateCreditRequest.php +++ b/app/Http/Requests/Credit/UpdateCreditRequest.php @@ -16,6 +16,7 @@ use App\Http\Requests\Request; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; 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'; } - if ($this->input('number')) { - $rules['number'] = 'unique:credits,number,'.$this->id.',id,company_id,'.$this->credit->company_id; - } + if($this->number) + $rules['number'] = Rule::unique('credits')->where('company_id', auth()->user()->company()->id)->ignore($this->credit->id); $rules['line_items'] = 'array'; diff --git a/app/Http/Requests/Expense/StoreExpenseRequest.php b/app/Http/Requests/Expense/StoreExpenseRequest.php index 828217b4e..3a4726867 100644 --- a/app/Http/Requests/Expense/StoreExpenseRequest.php +++ b/app/Http/Requests/Expense/StoreExpenseRequest.php @@ -35,10 +35,9 @@ class StoreExpenseRequest extends Request { $rules = []; - if (isset($this->number)) { + if ($this->number) $rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id); - } - + if(!empty($this->client_id)) $rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id; diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 7234211d1..0fb69a64e 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -16,6 +16,7 @@ use App\Http\ValidationRules\Invoice\LockedInvoiceRule; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; class UpdateInvoiceRequest extends Request { @@ -49,10 +50,8 @@ class UpdateInvoiceRequest extends Request $rules['id'] = new LockedInvoiceRule($this->invoice); - // if ($this->input('number') && strlen($this->input('number')) >= 1) { - if ($this->input('number')) { - $rules['number'] = 'unique:invoices,number,'.$this->id.',id,company_id,'.$this->invoice->company_id; - } + if($this->number) + $rules['number'] = Rule::unique('invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->invoice->id); $rules['line_items'] = 'array'; diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php index e5436b55b..9f921821d 100644 --- a/app/Http/Requests/Payment/UpdatePaymentRequest.php +++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php @@ -16,6 +16,7 @@ use App\Http\ValidationRules\PaymentAppliedValidAmount; use App\Http\ValidationRules\ValidCreditsPresentRule; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; class UpdatePaymentRequest extends Request { @@ -35,12 +36,14 @@ class UpdatePaymentRequest extends Request public function rules() { $rules = [ - 'number' => 'nullable|unique:payments,number,'.$this->id.',id,company_id,'.$this->payment->company_id, 'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule], 'invoices.*.invoice_id' => 'distinct', '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'))) { $documents = count($this->input('documents')); diff --git a/app/Http/Requests/Quote/UpdateQuoteRequest.php b/app/Http/Requests/Quote/UpdateQuoteRequest.php index 019c630aa..6ce240c6b 100644 --- a/app/Http/Requests/Quote/UpdateQuoteRequest.php +++ b/app/Http/Requests/Quote/UpdateQuoteRequest.php @@ -15,6 +15,7 @@ use App\Http\Requests\Request; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; 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'; } - if ($this->input('number')) { - $rules['number'] = 'unique:quotes,number,'.$this->id.',id,company_id,'.$this->quote->company_id; - } + if($this->number) + $rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id); $rules['line_items'] = 'array'; diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php index 6d6609534..7156974c2 100644 --- a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php @@ -16,6 +16,7 @@ use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\CleanLineItems; use App\Utils\Traits\MakesHash; use Illuminate\Http\UploadedFile; +use Illuminate\Validation\Rule; 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'; } - if ($this->input('number')) { - $rules['number'] = 'unique:recurring_invoices,number,'.$this->recurring_invoice->id.',id,company_id,'.$this->recurring_invoice->company_id; - } + if($this->number) + $rules['number'] = Rule::unique('recurring_invoices')->where('company_id', auth()->user()->company()->id)->ignore($this->recurring_invoice->id); + return $rules; } diff --git a/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php b/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php index 7a05426a1..8527cba80 100644 --- a/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php +++ b/app/Http/Requests/RecurringQuote/UpdateRecurringQuoteRequest.php @@ -14,6 +14,7 @@ namespace App\Http\Requests\RecurringQuote; use App\Http\Requests\Request; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\CleanLineItems; +use Illuminate\Validation\Rule; class UpdateRecurringQuoteRequest extends Request { @@ -47,6 +48,9 @@ class UpdateRecurringQuoteRequest extends Request $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); } } diff --git a/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php b/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php index 949630e58..ea11b59f3 100644 --- a/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php +++ b/app/Http/Requests/TaxRate/UpdateTaxRateRequest.php @@ -12,6 +12,7 @@ namespace App\Http\Requests\TaxRate; use App\Http\Requests\Request; +use Illuminate\Validation\Rule; class UpdateTaxRateRequest extends Request { @@ -27,10 +28,14 @@ class UpdateTaxRateRequest extends Request public function rules() { - return [ - // '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, - 'rate' => 'numeric', - ]; + $rules = []; + + $rules['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; + } } diff --git a/app/Http/Requests/Vendor/UpdateVendorRequest.php b/app/Http/Requests/Vendor/UpdateVendorRequest.php index 2c7038492..14f334e27 100644 --- a/app/Http/Requests/Vendor/UpdateVendorRequest.php +++ b/app/Http/Requests/Vendor/UpdateVendorRequest.php @@ -14,6 +14,7 @@ namespace App\Http\Requests\Vendor; use App\Http\Requests\Request; use App\Utils\Traits\ChecksEntityStatus; use App\Utils\Traits\MakesHash; +use Illuminate\Validation\Rule; class UpdateVendorRequest extends Request { @@ -35,18 +36,15 @@ class UpdateVendorRequest extends Request /* Ensure we have a client name, and that all emails are unique*/ $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'; - $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; } @@ -54,7 +52,6 @@ class UpdateVendorRequest extends Request public function messages() { return [ - 'unique' => ctrans('validation.unique', ['attribute' => 'email']), 'email' => ctrans('validation.email', ['attribute' => 'email']), 'name.required' => ctrans('validation.required', ['attribute' => 'name']), 'required' => ctrans('validation.required', ['attribute' => 'email']), diff --git a/app/Models/Project.php b/app/Models/Project.php index 42b84cbba..daf67ad2d 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -37,6 +37,7 @@ class Project extends BaseModel 'custom_value4', 'assigned_user_id', 'color', + 'number', ]; public function getEntityType() diff --git a/database/migrations/2021_03_19_221024_add_unique_constraints_on_all_entities.php b/database/migrations/2021_03_19_221024_add_unique_constraints_on_all_entities.php new file mode 100644 index 000000000..a1a1d2d01 --- /dev/null +++ b/database/migrations/2021_03_19_221024_add_unique_constraints_on_all_entities.php @@ -0,0 +1,65 @@ +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() + { + // + } +} diff --git a/tests/Feature/ClientApiTest.php b/tests/Feature/ClientApiTest.php index 3c87a5e07..affd7461d 100644 --- a/tests/Feature/ClientApiTest.php +++ b/tests/Feature/ClientApiTest.php @@ -54,6 +54,28 @@ class ClientApiTest extends TestCase $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() { $data = [ @@ -67,6 +89,20 @@ class ClientApiTest extends TestCase ])->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, + ])->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() diff --git a/tests/Feature/CreditTest.php b/tests/Feature/CreditTest.php index 936a10b69..0562f2fd1 100644 --- a/tests/Feature/CreditTest.php +++ b/tests/Feature/CreditTest.php @@ -131,4 +131,80 @@ class CreditTest extends TestCase $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); + } + + + } diff --git a/tests/Feature/ExpenseApiTest.php b/tests/Feature/ExpenseApiTest.php index f17aeb536..70254f13b 100644 --- a/tests/Feature/ExpenseApiTest.php +++ b/tests/Feature/ExpenseApiTest.php @@ -57,11 +57,34 @@ class ExpenseApiTest extends TestCase $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() { $data = [ 'public_notes' => $this->faker->firstName, - 'id_number' => 'Coolio', + 'number' => 'Coolio', ]; $response = $this->withHeaders([ @@ -70,6 +93,22 @@ class ExpenseApiTest extends TestCase ])->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, + ])->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() diff --git a/tests/Feature/InvoiceTest.php b/tests/Feature/InvoiceTest.php index 8ad15adfc..68341e10b 100644 --- a/tests/Feature/InvoiceTest.php +++ b/tests/Feature/InvoiceTest.php @@ -125,8 +125,15 @@ class InvoiceTest extends TestCase ])->post('/api/v1/invoices/', $invoice) ->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([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php index 74e89dbdc..11a138725 100644 --- a/tests/Feature/PaymentTest.php +++ b/tests/Feature/PaymentTest.php @@ -104,6 +104,15 @@ class PaymentTest extends TestCase $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([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, diff --git a/tests/Feature/ProjectApiTest.php b/tests/Feature/ProjectApiTest.php index d84332948..a9062e742 100644 --- a/tests/Feature/ProjectApiTest.php +++ b/tests/Feature/ProjectApiTest.php @@ -16,6 +16,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; use Tests\MockAccountData; use Tests\TestCase; +use Illuminate\Validation\ValidationException; /** * @test @@ -56,6 +57,7 @@ class ProjectApiTest extends TestCase $data = [ 'name' => $this->faker->firstName, 'client_id' => $this->client->hashed_id, + 'number' => 'duplicate', ]; $response = $this->withHeaders([ @@ -64,6 +66,26 @@ class ProjectApiTest extends TestCase ])->post('/api/v1/projects', $data); $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() diff --git a/tests/Feature/QuoteTest.php b/tests/Feature/QuoteTest.php index a01e66bdc..c452c57b1 100644 --- a/tests/Feature/QuoteTest.php +++ b/tests/Feature/QuoteTest.php @@ -86,7 +86,8 @@ class QuoteTest extends TestCase $quote_update = [ '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); @@ -98,6 +99,22 @@ class QuoteTest extends TestCase $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([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php index 47cbd5415..ca2e84d43 100644 --- a/tests/Feature/RecurringInvoiceTest.php +++ b/tests/Feature/RecurringInvoiceTest.php @@ -117,6 +117,7 @@ class RecurringInvoiceTest extends TestCase $RecurringInvoice_update = [ 'status_id' => RecurringInvoice::STATUS_DRAFT, 'client_id' => $this->encodePrimaryKey($RecurringInvoice->client_id), + 'number' => 'customnumber' ]; $this->assertNotNull($RecurringInvoice); @@ -127,6 +128,26 @@ class RecurringInvoiceTest extends TestCase ])->put('/api/v1/recurring_invoices/'.$this->encodePrimaryKey($RecurringInvoice->id), $RecurringInvoice_update) ->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([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, diff --git a/tests/Feature/TaskApiTest.php b/tests/Feature/TaskApiTest.php index e9d07ae47..e401ed05e 100644 --- a/tests/Feature/TaskApiTest.php +++ b/tests/Feature/TaskApiTest.php @@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; +use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; @@ -44,6 +45,7 @@ class TaskApiTest extends TestCase { $data = [ 'description' => $this->faker->firstName, + 'number' => 'taskynumber' ]; $response = $this->withHeaders([ @@ -54,6 +56,27 @@ class TaskApiTest extends TestCase $arr = $response->json(); $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']); } diff --git a/tests/Feature/VendorApiTest.php b/tests/Feature/VendorApiTest.php index cd8983413..d4288c8b5 100644 --- a/tests/Feature/VendorApiTest.php +++ b/tests/Feature/VendorApiTest.php @@ -14,6 +14,7 @@ use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; +use Illuminate\Validation\ValidationException; use Tests\MockAccountData; use Tests\TestCase; @@ -59,6 +60,7 @@ class VendorApiTest extends TestCase $data = [ 'name' => $this->faker->firstName, 'id_number' => 'Coolio', + 'number' => 'wiggles' ]; $response = $this->withHeaders([ @@ -67,6 +69,33 @@ class VendorApiTest extends TestCase ])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data); $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()