diff --git a/app/Http/Controllers/Tabulation/BonusScoreController.php b/app/Http/Controllers/Tabulation/BonusScoreController.php index bc8ed1a..cb2e8a8 100644 --- a/app/Http/Controllers/Tabulation/BonusScoreController.php +++ b/app/Http/Controllers/Tabulation/BonusScoreController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers\Tabulation; use App\Actions\Tabulation\EnterBonusScore; use App\Actions\Tabulation\GetBonusScoreRelatedEntries; -use App\Exceptions\ScoreEntryException; +use App\Exceptions\AuditionAdminException; use App\Http\Controllers\Controller; use App\Models\BonusScore; use App\Models\Entry; @@ -73,7 +73,7 @@ class BonusScoreController extends Controller // Set the new score try { $saveBonusScore($judge, $entry, $validData['score']); - } catch (ScoreEntryException $ex) { + } catch (AuditionAdminException $ex) { DB::rollBack(); return redirect()->route('bonus-scores.entryBonusScoreSheet', diff --git a/app/Observers/BonusScoreObserver.php b/app/Observers/BonusScoreObserver.php index 10cc620..cfb1d80 100644 --- a/app/Observers/BonusScoreObserver.php +++ b/app/Observers/BonusScoreObserver.php @@ -14,6 +14,12 @@ class BonusScoreObserver { $calculator = app(TotalEntryScores::class); $calculator($bonusScore->entry, true); + $message = 'Bonus Score Entered.'; + $message .= '
Judge: '.$bonusScore->judge->full_name().' <'.$bonusScore->judge->email.'>'; + $message .= '
Score: '.$bonusScore->score; + $message .= '
Scored Audition: '.$bonusScore->originallyScoredEntry->audition->name; + $affected = ['auditions' => [$bonusScore->entry->audition_id]]; + auditionLog($message, $affected); } /** diff --git a/tests/Feature/app/Actions/Tabulation/TotalEntryScoresTest.php b/tests/Feature/app/Actions/Tabulation/TotalEntryScoresTest.php index 5ff9c9f..1a1225e 100644 --- a/tests/Feature/app/Actions/Tabulation/TotalEntryScoresTest.php +++ b/tests/Feature/app/Actions/Tabulation/TotalEntryScoresTest.php @@ -190,7 +190,7 @@ test('it correctly brings in bonus scores', function () { } $bonusScore = BonusScore::create([ 'entry_id' => $this->entry->id, - 'user_id' => $this->judge1, + 'user_id' => $this->judge1->id, 'originally_scored_entry' => $this->entry->id, 'score' => 6, ]); diff --git a/tests/Feature/app/Http/Controllers/Tabulation/BonusScoreControllerTest.php b/tests/Feature/app/Http/Controllers/Tabulation/BonusScoreControllerTest.php new file mode 100644 index 0000000..abf481d --- /dev/null +++ b/tests/Feature/app/Http/Controllers/Tabulation/BonusScoreControllerTest.php @@ -0,0 +1,204 @@ +get(route('bonus-scores.chooseEntry'))->assertRedirect(route('home')); + actAsNormal(); + $this->get(route('bonus-scores.chooseEntry'))->assertRedirect(route('dashboard')); + }); + it('provides a form to enter an entry id to select an entry to score', function () { + actAsTab(); + $this->get(route('bonus-scores.chooseEntry'))->assertOk(); + actAsAdmin(); + $this->get(route('bonus-scores.chooseEntry'))->assertOk()->assertViewIs('tabulation.choose_entry'); + }); +}); + +describe('BonusScoreController::entryBonusScoreSheet', function () { + beforeEach(function () { + $this->event = Event::factory()->create(); + $this->ASaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Alto Sax', + ]); + $this->TSaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Tenor Sax', + ]); + $this->BSaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Bari Sax', + ]); + $this->student = Student::factory()->create(); + $this->ASentry = Entry::create([ + 'audition_id' => $this->ASaudition->id, 'student_id' => $this->student->id, + ]); + $this->TSentry = Entry::create([ + 'audition_id' => $this->TSaudition->id, 'student_id' => $this->student->id, + ]); + $this->BSentry = Entry::create([ + 'audition_id' => $this->BSaudition->id, 'student_id' => $this->student->id, + ]); + $this->bonusScoreDefinition = BonusScoreDefinition::create([ + 'name' => 'Sax Improv', + 'max_score' => 10, + 'weight' => 1, + 'for_seating' => 1, + 'for_attendance' => 0, + ]); + $this->bonusScoreDefinition->auditions()->attach($this->ASaudition); + $this->bonusScoreDefinition->auditions()->attach($this->TSaudition); + $this->bonusScoreDefinition->auditions()->attach($this->BSaudition); + $this->judge1 = User::factory()->create(); + $this->judge2 = User::factory()->create(); + $this->bonusScoreDefinition->judges()->attach($this->judge1); + $this->bonusScoreDefinition->judges()->attach($this->judge2); + }); + + it('does not allow access to guests or normal users', function () { + $this->get(route('bonus-scores.entryBonusScoreSheet'))->assertRedirect(route('home')); + actAsNormal(); + $this->get(route('bonus-scores.entryBonusScoreSheet'))->assertRedirect(route('dashboard')); + }); + + it('shows a form to enter bonus scores for a judge by an administrator', function () { + actAsAdmin(); + $response = $this->get(route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $this->TSentry->id])); + $response->assertOk()->assertViewIs('tabulation.bonus-score-sheet'); + }); + it('identifies the student', function () { + actAsAdmin(); + $response = $this->get(route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $this->TSentry->id])); + $response->assertSee($this->student->full_name()) + ->assertSee($this->student->school->name); + }); + it('makes judges available', function () { + actAsAdmin(); + $response = $this->get(route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $this->TSentry->id])); + $response->assertSee($this->judge1->full_name()) + ->assertSee($this->judge2->full_name()); + }); +}); + +describe('BonusScoreController::saveEntryBonusScoreSheet', function () { + beforeEach(function () { + $this->event = Event::factory()->create(); + $sg = ScoringGuide::factory()->create(); + $this->ASaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Alto Sax', 'scoring_guide_id' => $sg->id, + ]); + $this->TSaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Tenor Sax', 'scoring_guide_id' => $sg->id, + ]); + $this->BSaudition = Audition::factory()->create([ + 'event_id' => $this->event->id, 'name' => 'Bari Sax', 'scoring_guide_id' => $sg->id, + ]); + $this->student = Student::factory()->create(); + $this->ASentry = Entry::create([ + 'audition_id' => $this->ASaudition->id, 'student_id' => $this->student->id, + ]); + $this->TSentry = Entry::create([ + 'audition_id' => $this->TSaudition->id, 'student_id' => $this->student->id, + ]); + $this->BSentry = Entry::create([ + 'audition_id' => $this->BSaudition->id, 'student_id' => $this->student->id, + ]); + $this->bonusScoreDefinition = BonusScoreDefinition::create([ + 'name' => 'Sax Improv', + 'max_score' => 10, + 'weight' => 1, + 'for_seating' => 1, + 'for_attendance' => 0, + ]); + $this->bonusScoreDefinition->auditions()->attach($this->ASaudition); + $this->bonusScoreDefinition->auditions()->attach($this->TSaudition); + $this->bonusScoreDefinition->auditions()->attach($this->BSaudition); + $this->judge1 = User::factory()->create(); + $this->judge2 = User::factory()->create(); + $this->bonusScoreDefinition->judges()->attach($this->judge1); + $this->bonusScoreDefinition->judges()->attach($this->judge2); + + }); + it('does not allow access to guests or normal users', function () { + $this->post(route('bonus-scores.saveEntryBonusScoreSheet', $this->TSentry))->assertRedirect(route('home')); + actAsNormal(); + $this->post(route('bonus-scores.saveEntryBonusScoreSheet', $this->TSentry))->assertRedirect(route('dashboard')); + }); + it('will not post a bonus score to a published audition', function () { + actAsAdmin(); + $this->TSaudition->addFlag('seats_published'); + $response = $this->post(route('bonus-scores.saveEntryBonusScoreSheet', $this->TSentry)); + $response->assertRedirect(route('bonus-scores.entryBonusScoreSheet', ['entry_id' => $this->TSentry->id])) + ->assertSessionHas('error'); + }); + it('applies the bonus score to all related entries, even if a bonus score exists', function () { + $ASbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->ASentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + $TSbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->TSentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + $BSbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->BSentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + actAsAdmin(); + $response = $this->post(route('bonus-scores.saveEntryBonusScoreSheet', $this->TSentry), [ + 'judge_id' => $this->judge1->id, + 'entry_id' => $this->TSentry->id, + 'score' => 3, + ]); + foreach (Entry::all() as $entry) { + $bs = $entry->bonusScores()->first(); + expect($bs->score)->toEqual(3); + expect($bs->originallyScoredEntry->id)->toEqual($this->TSentry->id); + } + }); + + it('deletes all related entries when a null score is submitted', function () { + $ASbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->ASentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + $TSbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->TSentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + $BSbonusScoreRecorded = BonusScore::create([ + 'entry_id' => $this->BSentry->id, + 'user_id' => $this->judge1->id, + 'originally_scored_entry' => $this->ASentry->id, + 'score' => 2, + ]); + $adminUser = User::factory()->admin()->create(); + $this->actingAs($adminUser); + $response = $this->post(route('bonus-scores.saveEntryBonusScoreSheet', $this->TSentry), [ + 'judge_id' => $this->judge1->id, + 'entry_id' => $this->TSentry->id, + 'score' => null, + ]); + expect(BonusScore::all())->toHaveCount(0); + }); + +});