diff --git a/app/Filters/InvoiceFilters.php b/app/Filters/InvoiceFilters.php index 0343402a37b2..a9fa3c165dda 100644 --- a/app/Filters/InvoiceFilters.php +++ b/app/Filters/InvoiceFilters.php @@ -152,22 +152,22 @@ class InvoiceFilters extends QueryFilters { return $this->builder->where(function ($query) { - $query->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT]) - ->where('is_deleted', 0) - ->where('balance', '>', 0) - ->where(function ($query) { + $query->whereIn('invoices.status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT]) + ->where('invoices.is_deleted', 0) + ->where('invoices.balance', '>', 0) + ->orWhere(function ($query) { - $query->whereNull('due_date') + $query->whereNull('invoices.due_date') ->orWhere(function ($q) { - $q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0); + $q->where('invoices.due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', 0); }) ->orWhere(function ($q) { - $q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0); + $q->where('invoices.partial_due_date', '>=', now()->startOfDay()->subSecond())->where('invoices.partial', '>', 0); }); }) - ->orderByRaw('ISNULL(due_date), due_date ' . 'desc') - ->orderByRaw('ISNULL(partial_due_date), partial_due_date ' . 'desc'); + ->orderByRaw('ISNULL(invoices.due_date), invoices.due_date ' . 'desc') + ->orderByRaw('ISNULL(invoices.partial_due_date), invoices.partial_due_date ' . 'desc'); }); } @@ -337,10 +337,10 @@ class InvoiceFilters extends QueryFilters // return $this->builder->orderByRaw('CAST(number AS UNSIGNED), number ' . $dir); // return $this->builder->orderByRaw("number REGEXP '^[A-Za-z]+$',CAST(number as SIGNED INTEGER),CAST(REPLACE(number,'-','')AS SIGNED INTEGER) ,number"); // return $this->builder->orderByRaw('ABS(number) ' . $dir); - return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir); + return $this->builder->orderByRaw("REGEXP_REPLACE(invoices.number,'[^0-9]+','')+0 " . $dir); } - return $this->builder->orderBy($sort_col[0], $dir); + return $this->builder->orderBy("{$this->builder->getQuery()->from}.".$sort_col[0], $dir); } /** diff --git a/app/Filters/QueryFilters.php b/app/Filters/QueryFilters.php index e5380da3eed6..7f98a1d14e93 100644 --- a/app/Filters/QueryFilters.php +++ b/app/Filters/QueryFilters.php @@ -273,20 +273,11 @@ abstract class QueryFilters public function filter_deleted_clients($value) { - // if ($value == 'true') { - // return $this->builder->whereHas('client', function (Builder $query) { - // $query->where('is_deleted', 0); - // }); - // } - - if($value == 'true') - { - return $this->builder->leftJoin('clients', function($join) { - $join->on("{$this->builder->getQuery()->from}.client_id", '=', 'clients.id') - ->where('clients.is_deleted', 0); + if ($value == 'true') { + return $this->builder->whereHas('client', function (Builder $query) { + $query->where('is_deleted', 0); }); - - } + } return $this->builder; } @@ -307,7 +298,7 @@ abstract class QueryFilters { return $this->builder->where(function ($query) { $query->whereHas('client', function ($sub_query) { - $sub_query->where('is_deleted', 0); + $sub_query->where('is_deleted', 0)->where('deleted_at', null); })->orWhere('client_id', null); }); } diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index dbd7545e9dc1..74934925da28 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -937,7 +937,9 @@ class BaseController extends Controller } elseif (in_array($this->entity_type, [Design::class, GroupSetting::class, PaymentTerm::class, TaskStatus::class])) { // nlog($this->entity_type); } else { - $query->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id); + $query->where(function ($q) use ($user){ //grouping these together improves query performance significantly) + $q->where('user_id', '=', $user->id)->orWhere('assigned_user_id', $user->id); + }); } } diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 38b2009c4b2f..330b29c301ac 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -71,7 +71,7 @@ class ReminderJob implements ShouldQueue ->whereHas('company', function ($query) { $query->where('is_disabled', 0); }) - ->with('invitations')->chunk(50, function ($invoices) { + ->with('invitations')->chunk(200, function ($invoices) { foreach ($invoices as $invoice) { $this->sendReminderForInvoice($invoice); } diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 065e3c577cb2..8da00b9043da 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -129,7 +129,7 @@ class BaseModel extends Model /** @var \App\Models\User $user */ $user = auth()->user(); - $query->where('company_id', $user->companyId()); + $query->where("{$query->getQuery()->from}.company_id", $user->companyId()); return $query; } diff --git a/app/Models/Quote.php b/app/Models/Quote.php index f751590cc42e..4e3209843944 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -107,7 +107,6 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @method static \Illuminate\Database\Eloquent\Builder|BaseModel company() * @mixin \Eloquent * @mixin \Illuminate\Database\Eloquent\Builder */ diff --git a/tests/Feature/ClientModelTest.php b/tests/Feature/ClientModelTest.php index d82db842aa33..60421d6afa1b 100644 --- a/tests/Feature/ClientModelTest.php +++ b/tests/Feature/ClientModelTest.php @@ -11,7 +11,9 @@ namespace Tests\Feature; +use App\Models\Client; use App\Models\CompanyGateway; +use App\Models\Invoice; use Illuminate\Foundation\Testing\DatabaseTransactions; use Tests\MockAccountData; use Tests\TestCase; @@ -44,6 +46,122 @@ class ClientModelTest extends TestCase } + public function testNewWithoutAndDeletedClientFilters() + { + + $this->invoice->amount = 10; + $this->invoice->balance = 10; + $this->invoice->status_id=2; + $this->invoice->date = now()->subDays(2); + $this->invoice->due_date = now()->addDays(2); + $this->invoice->save(); + + $cd = Client::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + + $cd2 = Client::factory()->create([ + 'company_id' => $this->company->id, + 'user_id' => $this->user->id, + ]); + + $invoice_count = Invoice::where('company_id', $this->company->id)->count(); + + $this->assertGreaterThan(0, $invoice_count); + + $i = Invoice::factory()->create([ + 'client_id' => $cd->id, + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'status_id' => 2, + 'amount' => 10, + 'balance' => 10, + 'date' => now()->subDays(2)->format('Y-m-d'), + 'due_date' => now()->addDays(5)->format('Y-m-d'), + ]); + + + $i2 = Invoice::factory()->create([ + 'client_id' => $cd2->id, + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'status_id' => 2, + 'amount' => 10, + 'balance' => 10, + 'date' => now()->subDays(2)->format('Y-m-d'), + 'due_date' => now()->addDays(5)->format('Y-m-d'), + ]); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?status=active'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count+2, count($arr['data'])); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?upcoming=true&status=active&include=client'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count + 2, count($arr['data'])); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?upcoming=true&status=active&without_deleted_clients=true'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count + 2, count($arr['data'])); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?upcoming=true&status=active&filter_deleted_clients=true'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count + 2, count($arr['data'])); + + $cd2->is_deleted = true; + $cd2->save(); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?upcoming=true&status=active&without_deleted_clients=true'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count + 1, count($arr['data'])); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->get('/api/v1/invoices?upcoming=true&status=active&filter_deleted_clients=true'); + + $response->assertStatus(200); + $arr = $response->json(); + + $this->assertEquals($invoice_count + 1, count($arr['data'])); + + + } + public function testPaymentMethodsWithCreditsEnforced() { @@ -51,6 +169,6 @@ class ClientModelTest extends TestCase $this->assertGreaterThan(0, CompanyGateway::count()); - $this->assertEquals(1, count($payment_methods)); + $this->assertEquals(2, count($payment_methods)); } }