diff --git a/app/Http/Controllers/V2/Nurseries/AdminIndexNurseriesController.php b/app/Http/Controllers/V2/Nurseries/AdminIndexNurseriesController.php index 05a6167b9..bdd340248 100644 --- a/app/Http/Controllers/V2/Nurseries/AdminIndexNurseriesController.php +++ b/app/Http/Controllers/V2/Nurseries/AdminIndexNurseriesController.php @@ -29,7 +29,7 @@ public function __invoke(Request $request): NurseriesCollection AllowedFilter::scope('country'), AllowedFilter::scope('organisation_uuid', 'organisationUuid'), AllowedFilter::scope('project_uuid', 'projectUuid'), - AllowedFilter::exact('framework', 'framework_key'), + AllowedFilter::exact('framework_key'), AllowedFilter::exact('status'), AllowedFilter::exact('update_request_status'), ]); diff --git a/app/Models/V2/Projects/Project.php b/app/Models/V2/Projects/Project.php index 393e2aa7a..c1d76cb03 100644 --- a/app/Models/V2/Projects/Project.php +++ b/app/Models/V2/Projects/Project.php @@ -25,6 +25,8 @@ use App\Models\V2\Sites\SiteReport; use App\Models\V2\Tasks\Task; use App\Models\V2\TreeSpecies\TreeSpecies; +use App\Models\V2\Workdays\Workday; +use App\Models\V2\Workdays\WorkdayDemographic; use App\StateMachines\EntityStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -34,7 +36,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Facades\DB; use Laravel\Scout\Searchable; use OwenIt\Auditing\Auditable; use OwenIt\Auditing\Contracts\Auditable as AuditableContract; @@ -358,16 +359,17 @@ public function getRegeneratedTreesCountAttribute(): int public function getWorkdayCountAttribute(): int { - $sumQueries = [ - DB::raw('sum(`workdays_paid`) as paid'), - DB::raw('sum(`workdays_volunteer`) as volunteer'), - ]; - $projectTotals = $this->reports()->hasBeenSubmitted()->get($sumQueries)->first(); - // The groupBy is superfluous, but required because Laravel adds "v2_sites.project_id as laravel_through_key" to - // the SQL select. - $siteTotals = $this->submittedSiteReports()->groupBy('v2_sites.project_id')->get($sumQueries)->first(); - - return $projectTotals?->paid + $projectTotals?->volunteer + $siteTotals?->paid + $siteTotals?->volunteer; + return WorkdayDemographic::whereIn( + 'workday_id', + Workday::where('workdayable_type', SiteReport::class) + ->whereIn('workdayable_id', $this->submittedSiteReports()->select('v2_site_reports.id')) + ->select('id') + )->orWhereIn( + 'workday_id', + Workday::where('workdayable_type', ProjectReport::class) + ->whereIn('workdayable_id', $this->reports()->hasBeenSubmitted()->select('id')) + ->select('id') + )->gender()->sum('amount') ?? 0; } public function getTotalJobsCreatedAttribute(): int diff --git a/app/Models/V2/Sites/Site.php b/app/Models/V2/Sites/Site.php index a98a8b84e..d3dda28d5 100644 --- a/app/Models/V2/Sites/Site.php +++ b/app/Models/V2/Sites/Site.php @@ -20,6 +20,8 @@ use App\Models\V2\Seeding; use App\Models\V2\Stratas\Strata; use App\Models\V2\TreeSpecies\TreeSpecies; +use App\Models\V2\Workdays\Workday; +use App\Models\V2\Workdays\WorkdayDemographic; use App\StateMachines\ReportStatusStateMachine; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -29,7 +31,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; -use Illuminate\Support\Facades\DB; use Laravel\Scout\Searchable; use OwenIt\Auditing\Auditable; use OwenIt\Auditing\Contracts\Auditable as AuditableContract; @@ -302,12 +303,12 @@ public function getRegeneratedTreesCountAttribute(): int public function getWorkdayCountAttribute(): int { - $totals = $this->reports()->hasBeenSubmitted()->get([ - DB::raw('sum(`workdays_volunteer`) as volunteer'), - DB::raw('sum(`workdays_paid`) as paid'), - ])->first(); - - return $totals?->paid + $totals?->volunteer; + return WorkdayDemographic::whereIn( + 'workday_id', + Workday::where('workdayable_type', SiteReport::class) + ->whereIn('workdayable_id', $this->reports()->hasBeenSubmitted()->select('id')) + ->select('id') + )->gender()->sum('amount') ?? 0; } public function getFrameworkUuidAttribute(): ?string diff --git a/database/factories/V2/Workdays/WorkdayFactory.php b/database/factories/V2/Workdays/WorkdayFactory.php index e6a19f119..ce4669559 100644 --- a/database/factories/V2/Workdays/WorkdayFactory.php +++ b/database/factories/V2/Workdays/WorkdayFactory.php @@ -2,6 +2,7 @@ namespace Database\Factories\V2\Workdays; +use App\Models\V2\Projects\ProjectReport; use App\Models\V2\Sites\SiteReport; use App\Models\V2\Workdays\Workday; use Illuminate\Database\Eloquent\Factories\Factory; @@ -22,4 +23,15 @@ public function definition() 'collection' => $this->faker->randomElement(array_keys(Workday::SITE_COLLECTIONS)), ]; } + + public function projectReport(): Factory + { + return $this->state(function (array $attributes) { + return [ + 'workdayable_type' => ProjectReport::class, + 'workdayable_id' => ProjectReport::factory()->create(), + 'collection' => $this->faker->randomElement(array_keys(Workday::PROJECT_COLLECTION)), + ]; + }); + } } diff --git a/tests/Unit/Models/V2/Projects/ProjectTest.php b/tests/Unit/Models/V2/Projects/ProjectTest.php index 27a3a1455..249157afc 100644 --- a/tests/Unit/Models/V2/Projects/ProjectTest.php +++ b/tests/Unit/Models/V2/Projects/ProjectTest.php @@ -7,6 +7,10 @@ use App\Models\V2\Projects\ProjectMonitoring; use App\Models\V2\Projects\ProjectReport; use App\Models\V2\Sites\Site; +use App\Models\V2\Sites\SiteReport; +use App\Models\V2\Workdays\Workday; +use App\Models\V2\Workdays\WorkdayDemographic; +use App\StateMachines\EntityStatusStateMachine; use Tests\TestCase; class ProjectTest extends TestCase @@ -107,6 +111,46 @@ public function test_it_deletes_its_own_project_monitorings(string $permission, } } + public function test_workday_count() + { + $project = Project::factory()->ppc()->create(); + + // The amounts are all prime so that it's easy to tell what got counted and what didn't when there's an error + + // Unapproved site (doesn't count toward workday count) + $site = Site::factory()->ppc()->create(['project_id' => $project->id, 'status' => EntityStatusStateMachine::AWAITING_APPROVAL]); + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::APPROVED]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 3]); + + // Approved site + $site = Site::factory()->ppc()->create(['project_id' => $project->id, 'status' => EntityStatusStateMachine::APPROVED]); + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::APPROVED]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 5]); + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::AWAITING_APPROVAL]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 7]); + // Unsubmitted report (doesn't count toward workday count) + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::STARTED]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 11]); + + $report = ProjectReport::factory()->ppc()->create(['project_id' => $project->id, 'status' => EntityStatusStateMachine::APPROVED]); + $workday = Workday::factory()->projectReport()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 13]); + $report = ProjectReport::factory()->ppc()->create(['project_id' => $project->id, 'status' => EntityStatusStateMachine::AWAITING_APPROVAL]); + $workday = Workday::factory()->projectReport()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 17]); + // Unsubmitted report (doesn't count toward workday count) + $report = ProjectReport::factory()->ppc()->create(['project_id' => $project->id, 'status' => EntityStatusStateMachine::STARTED]); + $workday = Workday::factory()->projectReport()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 19]); + + // 42 = 5 and 7 from the approved site's reports and 13 and 17 from the project reports + $this->assertEquals(42, $project->workday_count); + } + public static function permissionsDataProvider() { return [ diff --git a/tests/Unit/Models/V2/Sites/SiteTest.php b/tests/Unit/Models/V2/Sites/SiteTest.php index afd58959a..ca9d25c90 100644 --- a/tests/Unit/Models/V2/Sites/SiteTest.php +++ b/tests/Unit/Models/V2/Sites/SiteTest.php @@ -5,6 +5,9 @@ use App\Models\V2\Sites\Site; use App\Models\V2\Sites\SiteMonitoring; use App\Models\V2\Sites\SiteReport; +use App\Models\V2\Workdays\Workday; +use App\Models\V2\Workdays\WorkdayDemographic; +use App\StateMachines\EntityStatusStateMachine; use Tests\TestCase; class SiteTest extends TestCase @@ -57,6 +60,26 @@ public function test_it_deletes_its_own_site_monitorings(string $permission, str } } + public function test_workday_count() + { + $site = Site::factory()->ppc()->create(); + + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::APPROVED]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 3]); + + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::AWAITING_APPROVAL]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 5]); + + // Unsubmitted report (doesn't count toward workday count) + $report = SiteReport::factory()->ppc()->create(['site_id' => $site->id, 'status' => EntityStatusStateMachine::STARTED]); + $workday = Workday::factory()->create(['workdayable_id' => $report->id]); + WorkdayDemographic::factory()->create(['workday_id' => $workday->id, 'amount' => 7]); + + $this->assertEquals(8, $site->workday_count); + } + public static function permissionsDataProvider() { return [ diff --git a/tests/V2/Nurseries/AdminIndexNurseriesControllerTest.php b/tests/V2/Nurseries/AdminIndexNurseriesControllerTest.php index b24bf9f5d..86f76aef0 100644 --- a/tests/V2/Nurseries/AdminIndexNurseriesControllerTest.php +++ b/tests/V2/Nurseries/AdminIndexNurseriesControllerTest.php @@ -61,7 +61,7 @@ public function test_nursery_index_has_required_filters() 'country', 'organisation_uuid', 'project_uuid', - 'framework', + 'framework_key', ]; foreach ($filters as $filter) {