From 3ad5de1d2cbe53445c47966d1a3c9a413fb1e28d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 28 Feb 2024 21:15:43 +1100 Subject: [PATCH] Add filters for documents --- app/Filters/DocumentFilters.php | 39 ++++++- app/Models/Document.php | 20 ++++ app/Transformers/DocumentTransformer.php | 1 + app/Utils/SystemHealth.php | 1 + lang/en/texts.php | 1 + tests/Feature/DocumentsApiTest.php | 136 ++++++++++++++++++++++- 6 files changed, 193 insertions(+), 5 deletions(-) diff --git a/app/Filters/DocumentFilters.php b/app/Filters/DocumentFilters.php index 73b9b97a5b59..15856a2e762d 100644 --- a/app/Filters/DocumentFilters.php +++ b/app/Filters/DocumentFilters.php @@ -29,11 +29,13 @@ class DocumentFilters extends QueryFilters */ public function filter(string $filter = ''): Builder { + if (strlen($filter) == 0) { return $this->builder; } - return $this->builder; + return $this->builder->where('name', 'like', '%'.$filter.'%'); + } /** @@ -47,9 +49,42 @@ class DocumentFilters extends QueryFilters */ public function client_id(string $client_id = ''): Builder { - return $this->builder; + + return $this->builder->where(function ($query) use ($client_id) { + $query->whereHasMorph('documentable', [ + \App\Models\Invoice::class, + \App\Models\Quote::class, + \App\Models\Credit::class, + \App\Models\Expense::class, + \App\Models\Payment::class, + \App\Models\Task::class], function ($q2) use ($client_id) { + $q2->where('client_id', $this->decodePrimaryKey($client_id)); + })->orWhereHasMorph('documentable', [\App\Models\Client::class], function ($q3) use ($client_id) { + $q3->where('id', $this->decodePrimaryKey($client_id)); + }); + }); + } + public function type(string $types = '') + { + $types = explode(',', $types); + + foreach ($types as $type) + { + match($type) { + 'private' => $this->builder->where('is_public', 0), + 'public' => $this->builder->where('is_public', 1), + 'pdf' => $this->builder->where('type', 'pdf'), + 'image' => $this->builder->whereIn('type', ['png','jpeg','jpg','gif','svg']), + 'other' => $this->builder->whereNotIn('type', ['pdf','png','jpeg','jpg','gif','svg']), + default => $this->builder, + }; + } + + return $this->builder; + } + /** * Sorts the list based on $sort. * diff --git a/app/Models/Document.php b/app/Models/Document.php index 061210041ff8..8b2a45f7a508 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -208,6 +208,26 @@ class Document extends BaseModel return ctrans('texts.document'); } + public function link() + { + $entity_id = $this->encodePrimaryKey($this->documentable_id); + + match($this->documentable_type) { + 'App\Models\Vendor' => $link = "vendors/{$entity_id}", + 'App\Models\Project' => $link = "projects/{$entity_id}", + 'invoices' => $link = "invoices/{$entity_id}/edit", + 'App\Models\Quote' => $link = "quotes/{$entity_id}/edit", + 'App\Models\Credit' => $link = "credits/{$entity_id}/edit", + 'App\Models\Expense' => $link = "expenses/{$entity_id}/edit", + 'App\Models\Payment' => $link = "payments/{$entity_id}/edit", + 'App\Models\Task' => $link = "tasks/{$entity_id}/edit", + 'App\Models\Client' => $link = "clients/{$entity_id}", + default => $link = '' + }; + + return $link; + } + public function compress(): mixed { diff --git a/app/Transformers/DocumentTransformer.php b/app/Transformers/DocumentTransformer.php index c916efe9d440..baab0e4271fd 100644 --- a/app/Transformers/DocumentTransformer.php +++ b/app/Transformers/DocumentTransformer.php @@ -52,6 +52,7 @@ class DocumentTransformer extends EntityTransformer 'created_at' => (int) $document->created_at, 'is_deleted' => (bool) false, 'is_public' => (bool) $document->is_public, + 'link' => (string) $document->link(), ]; } } diff --git a/app/Utils/SystemHealth.php b/app/Utils/SystemHealth.php index 6b60a7d845e7..c862091c162e 100644 --- a/app/Utils/SystemHealth.php +++ b/app/Utils/SystemHealth.php @@ -84,6 +84,7 @@ class SystemHealth 'trailing_slash' => (bool) self::checkUrlState(), 'file_permissions' => (string) self::checkFileSystem(), 'exchange_rate_api_not_configured' => (bool)self::checkCurrencySanity(), + 'api_version' => (string) config('ninja.app_version'), ]; } diff --git a/lang/en/texts.php b/lang/en/texts.php index 11811703ee24..217ce04ed9f1 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5240,6 +5240,7 @@ $lang = array( 'use_available_payments' => 'Use Available Payments', 'test_email_sent' => 'Successfully sent email', 'gateway_type' => 'Gateway Type', + 'save_template_body' => 'Would you like to save this import mapping as a template for future use?', ); return $lang; diff --git a/tests/Feature/DocumentsApiTest.php b/tests/Feature/DocumentsApiTest.php index 2c46c6b2effb..4537019e37ae 100644 --- a/tests/Feature/DocumentsApiTest.php +++ b/tests/Feature/DocumentsApiTest.php @@ -11,13 +11,14 @@ namespace Tests\Feature; +use Tests\TestCase; +use App\Models\Task; use App\Models\Document; +use Tests\MockAccountData; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; -use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\Session; -use Tests\MockAccountData; -use Tests\TestCase; +use Illuminate\Foundation\Testing\DatabaseTransactions; /** * @test @@ -44,6 +45,135 @@ class DocumentsApiTest extends TestCase Model::reguard(); } + public function testDocumentFilters() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $this->client->documents()->save($d); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()); + } + + + public function testDocumentFilters2() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $this->task->documents()->save($d); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents/{$d->hashed_id}?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()); + } + + public function testDocumentFilters3() + { + Document::query()->withTrashed()->cursor()->each(function ($d){ + $d->forceDelete(); + }); + + $d = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable.jpg', + 'type' => 'jpg', + ]); + + $t = Task::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + ]); + + $t->documents()->save($d); + + $dd = Document::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + 'name' => 'searchable2.jpg', + 'type' => 'jpg', + ]); + + $this->client->documents()->save($dd); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=craycray"); + + $response->assertStatus(200); + + $this->assertCount(0, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=s"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable"); + + $response->assertStatus(200); + + $this->assertCount(2, $response->json()['data']); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get("/api/v1/documents?client_id={$this->client->hashed_id}&filter=searchable2"); + + $response->assertStatus(200); + + $this->assertCount(1, $response->json()['data']); + + } + + public function testIsPublicTypesForDocumentRequest() { $d = Document::factory()->create([