Merge pull request #41 from okorpheus/auditionadmin-38
Admin can waive late fees
This commit is contained in:
commit
1373ea8012
|
|
@ -16,9 +16,9 @@ class CreateEntry
|
||||||
/**
|
/**
|
||||||
* @throws ManageEntryException
|
* @throws ManageEntryException
|
||||||
*/
|
*/
|
||||||
public function __invoke(Student|int $student, Audition|int $audition, string|array|null $entry_for = null): void
|
public function __invoke(Student|int $student, Audition|int $audition, string|array|null $entry_for = null)
|
||||||
{
|
{
|
||||||
$this->createEntry($student, $audition, $entry_for);
|
return $this->createEntry($student, $audition, $entry_for);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,9 @@ class UpdateEntry
|
||||||
if ($this->entry->scoreSheets()->count() > 0) {
|
if ($this->entry->scoreSheets()->count() > 0) {
|
||||||
throw new ManageEntryException('Cannot change the audition for an entry with scores');
|
throw new ManageEntryException('Cannot change the audition for an entry with scores');
|
||||||
}
|
}
|
||||||
if (Entry::where('student_id', $this->entry->student_id)->where('audition_id', $audition->id)->exists()) {
|
if ($audition->id !== $this->entry->audition_id &&
|
||||||
|
Entry::where('student_id', $this->entry->student_id)
|
||||||
|
->where('audition_id', $audition->id)->exists()) {
|
||||||
throw new ManageEntryException('That student is already entered in that audition');
|
throw new ManageEntryException('That student is already entered in that audition');
|
||||||
}
|
}
|
||||||
// OK we're allowed to change the audition
|
// OK we're allowed to change the audition
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,5 @@ enum EntryFlags: string
|
||||||
case DECLINED = 'declined';
|
case DECLINED = 'declined';
|
||||||
case NO_SHOW = 'no_show';
|
case NO_SHOW = 'no_show';
|
||||||
case FAILED_PRELIM = 'failed_prelim';
|
case FAILED_PRELIM = 'failed_prelim';
|
||||||
|
case LATE_FEE_WAIVED = 'late_fee_waived';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ class EntryController extends Controller
|
||||||
|
|
||||||
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
$validData['late_fee_waived'] = $request->get('late_fee_waived') ? 1 : 0;
|
||||||
$enter_for = [];
|
$enter_for = [];
|
||||||
if ($validData['for_seating']) {
|
if ($validData['for_seating']) {
|
||||||
$enter_for[] = 'seating';
|
$enter_for[] = 'seating';
|
||||||
|
|
@ -111,10 +112,13 @@ class EntryController extends Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$creator($validData['student_id'], $validData['audition_id'], $enter_for);
|
$entry = $creator($validData['student_id'], $validData['audition_id'], $enter_for);
|
||||||
} catch (ManageEntryException $ex) {
|
} catch (ManageEntryException $ex) {
|
||||||
return redirect()->route('admin.entries.index')->with('error', $ex->getMessage());
|
return redirect()->route('admin.entries.index')->with('error', $ex->getMessage());
|
||||||
}
|
}
|
||||||
|
if ($validData['late_fee_waived']) {
|
||||||
|
$entry->addFlag('late_fee_waived');
|
||||||
|
}
|
||||||
|
|
||||||
return redirect(route('admin.entries.index'))->with('success', 'The entry has been added.');
|
return redirect(route('admin.entries.index'))->with('success', 'The entry has been added.');
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +161,7 @@ class EntryController extends Controller
|
||||||
|
|
||||||
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
$validData['for_seating'] = $request->get('for_seating') ? 1 : 0;
|
||||||
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
$validData['for_advancement'] = $request->get('for_advancement') ? 1 : 0;
|
||||||
|
$validData['late_fee_waived'] = $request->get('late_fee_waived') ? 1 : 0;
|
||||||
|
|
||||||
// If the audition is not set to advance to the next round, then the entry must be for seating
|
// If the audition is not set to advance to the next round, then the entry must be for seating
|
||||||
if (! auditionSetting('advanceTo')) {
|
if (! auditionSetting('advanceTo')) {
|
||||||
|
|
@ -167,12 +172,11 @@ class EntryController extends Controller
|
||||||
} catch (ManageEntryException $e) {
|
} catch (ManageEntryException $e) {
|
||||||
return redirect()->route('admin.entries.index')->with('error', $e->getMessage());
|
return redirect()->route('admin.entries.index')->with('error', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
if ($validData['late_fee_waived']) {
|
||||||
// $entry->update([
|
$entry->addFlag('late_fee_waived');
|
||||||
// 'audition_id' => $validData['audition_id'],
|
} else {
|
||||||
// 'for_seating' => $validData['for_seating'],
|
$entry->removeFlag('late_fee_waived');
|
||||||
// 'for_advancement' => $validData['for_advancement'],
|
}
|
||||||
// ]);
|
|
||||||
|
|
||||||
return to_route('admin.entries.index')->with('success', 'Entry updated successfully');
|
return to_route('admin.entries.index')->with('success', 'Entry updated successfully');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ class Entry extends Model
|
||||||
'declined' => EntryFlags::DECLINED,
|
'declined' => EntryFlags::DECLINED,
|
||||||
'no_show' => EntryFlags::NO_SHOW,
|
'no_show' => EntryFlags::NO_SHOW,
|
||||||
'failed_prelim' => EntryFlags::FAILED_PRELIM,
|
'failed_prelim' => EntryFlags::FAILED_PRELIM,
|
||||||
|
'late_fee_waived' => EntryFlags::LATE_FEE_WAIVED,
|
||||||
};
|
};
|
||||||
$this->flags()->create(['flag_name' => $enum]);
|
$this->flags()->create(['flag_name' => $enum]);
|
||||||
$this->load('flags');
|
$this->load('flags');
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,8 @@ class InvoiceOneFeePerEntry implements InvoiceDataService
|
||||||
foreach ($school->students as $student) {
|
foreach ($school->students as $student) {
|
||||||
foreach ($entries[$student->id] ?? [] as $entry) {
|
foreach ($entries[$student->id] ?? [] as $entry) {
|
||||||
$entryFee = $entry->audition->entry_fee / 100;
|
$entryFee = $entry->audition->entry_fee / 100;
|
||||||
$lateFee = $this->entryService->isEntryLate($entry) ? auditionSetting('late_fee') / 100 : 0;
|
$lateFee = ($this->entryService->isEntryLate($entry) && ! $entry->hasFlag('late_fee_waived'))
|
||||||
|
? auditionSetting('late_fee') / 100 : 0;
|
||||||
|
|
||||||
$invoiceData['lines'][] = [
|
$invoiceData['lines'][] = [
|
||||||
'student_name' => $student->full_name(true),
|
'student_name' => $student->full_name(true),
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ class InvoiceOneFeePerStudent implements InvoiceDataService
|
||||||
foreach ($entries[$student->id] ?? [] as $entry) {
|
foreach ($entries[$student->id] ?? [] as $entry) {
|
||||||
if ($firstEntryForStudent) {
|
if ($firstEntryForStudent) {
|
||||||
$entryFee = $entry->audition->entry_fee / 100;
|
$entryFee = $entry->audition->entry_fee / 100;
|
||||||
$lateFee = $this->entryService->entryIsLate($entry) ? auditionSetting('late_fee') / 100 : 0;
|
$lateFee = ($this->entryService->isEntryLate($entry) && ! $entry->hasFlag('late_fee_waived'))
|
||||||
|
? auditionSetting('late_fee') / 100 : 0;
|
||||||
} else {
|
} else {
|
||||||
$entryFee = 0;
|
$entryFee = 0;
|
||||||
$lateFee = 0;
|
$lateFee = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!--suppress JSUnresolvedReference -->
|
||||||
<x-layout.app>
|
<x-layout.app>
|
||||||
<x-card.card class="mx-auto max-w-2xl">
|
<x-card.card class="mx-auto max-w-2xl">
|
||||||
<x-card.heading>Create Entry</x-card.heading>
|
<x-card.heading>Create Entry</x-card.heading>
|
||||||
|
|
@ -8,7 +9,8 @@
|
||||||
<x-slot:label>Student</x-slot:label>
|
<x-slot:label>Student</x-slot:label>
|
||||||
<option value="" disabled selected>Select a student</option>
|
<option value="" disabled selected>Select a student</option>
|
||||||
<template x-for="student in students" :key="student.id">
|
<template x-for="student in students" :key="student.id">
|
||||||
<option :value="student.id" x-text="`${student.name} - ${student.school} - Grade ${student.grade}`"></option>
|
<option :value="student.id"
|
||||||
|
x-text="`${student.name} - ${student.school} - Grade ${student.grade}`"></option>
|
||||||
</template>
|
</template>
|
||||||
</x-form.select>
|
</x-form.select>
|
||||||
|
|
||||||
|
|
@ -24,16 +26,20 @@
|
||||||
<div class="col-span-3 align-top">
|
<div class="col-span-3 align-top">
|
||||||
<x-form.checkbox name="for_seating"
|
<x-form.checkbox name="for_seating"
|
||||||
label="Enter for {{ auditionSetting('auditionAbbreviation') }}"
|
label="Enter for {{ auditionSetting('auditionAbbreviation') }}"
|
||||||
checked />
|
checked/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3 align-top">
|
<div class="col-span-3 align-top">
|
||||||
<x-form.checkbox name="for_advancement"
|
<x-form.checkbox name="for_advancement"
|
||||||
label="Enter for {{ auditionSetting('advanceTo') }}"
|
label="Enter for {{ auditionSetting('advanceTo') }}"
|
||||||
checked />
|
checked/>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<input type="hidden" name="for_seating" value="on">
|
<input type="hidden" name="for_seating" value="on">
|
||||||
@endif
|
@endif
|
||||||
|
<div class="col-span-3 align-top">
|
||||||
|
<x-form.checkbox name="late_fee_waived"
|
||||||
|
label="Wave late fee if applicable"/>
|
||||||
|
</div>
|
||||||
</x-form.body-grid>
|
</x-form.body-grid>
|
||||||
<x-form.footer class="mb-5">
|
<x-form.footer class="mb-5">
|
||||||
<x-form.button>Create Entry</x-form.button>
|
<x-form.button>Create Entry</x-form.button>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@
|
||||||
<x-slot:label>Audition</x-slot:label>
|
<x-slot:label>Audition</x-slot:label>
|
||||||
@foreach ($auditions as $audition)
|
@foreach ($auditions as $audition)
|
||||||
@continue($entry->student->grade < $audition->minimum_grade || $entry->student->grade > $audition->maximum_grade)
|
@continue($entry->student->grade < $audition->minimum_grade || $entry->student->grade > $audition->maximum_grade)
|
||||||
<option value="{{ $audition->id }}" {{ ($audition->id == $entry->audition_id ? 'selected':'') }}>
|
<option
|
||||||
|
value="{{ $audition->id }}" {{ ($audition->id == $entry->audition_id ? 'selected':'') }}>
|
||||||
{{ $audition->name }}
|
{{ $audition->name }}
|
||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
@ -58,6 +59,12 @@
|
||||||
@else
|
@else
|
||||||
<input type="hidden" name="for_seating" value="on">
|
<input type="hidden" name="for_seating" value="on">
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
|
<div class="col-span-3 align-top">
|
||||||
|
<x-form.checkbox name="late_fee_waived"
|
||||||
|
label="Wave late fee if applicable"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
</x-form.body-grid>
|
</x-form.body-grid>
|
||||||
<x-form.footer class="!py-5">
|
<x-form.footer class="!py-5">
|
||||||
<x-form.button>Edit Entry</x-form.button>
|
<x-form.button>Edit Entry</x-form.button>
|
||||||
|
|
@ -70,7 +77,8 @@
|
||||||
<x-card.heading>
|
<x-card.heading>
|
||||||
Scores
|
Scores
|
||||||
<x-slot:right_side>
|
<x-slot:right_side>
|
||||||
<x-form.button href="{{route('scores.entryScoreSheet', ['entry_id'=>$entry->id])}}">Edit Scores</x-form.button>
|
<x-form.button href="{{route('scores.entryScoreSheet', ['entry_id'=>$entry->id])}}">Edit Scores
|
||||||
|
</x-form.button>
|
||||||
</x-slot:right_side>
|
</x-slot:right_side>
|
||||||
</x-card.heading>
|
</x-card.heading>
|
||||||
<x-card.list.body>
|
<x-card.list.body>
|
||||||
|
|
@ -96,7 +104,8 @@
|
||||||
</p>
|
</p>
|
||||||
@endforeach
|
@endforeach
|
||||||
<p class="grid grid-cols-2 border-b">
|
<p class="grid grid-cols-2 border-b">
|
||||||
<span class="font-semibold text-sm">{{ auditionSetting('auditionAbbreviation') }} Total</span>
|
<span
|
||||||
|
class="font-semibold text-sm">{{ auditionSetting('auditionAbbreviation') }} Total</span>
|
||||||
<span class="text-right font-semibold">{{ $score->totalScore('seating')[0] }}</span>
|
<span class="text-right font-semibold">{{ $score->totalScore('seating')[0] }}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
|
use App\Models\Entry;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
|
@ -87,3 +88,31 @@ it('can create a late entry', function () {
|
||||||
'for_advancement' => 0,
|
'for_advancement' => 0,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
it('can mark a late fee waived for an entry', function () {
|
||||||
|
$audition = Audition::factory()->closed()->create(['maximum_grade' => 12, 'minimum_grade' => 7]);
|
||||||
|
$student = Student::factory()->create(['grade' => 9]);
|
||||||
|
actAsAdmin();
|
||||||
|
$response = $this->post(route('admin.entries.store'), [
|
||||||
|
'student_id' => $student->id,
|
||||||
|
'audition_id' => $audition->id,
|
||||||
|
'for_seating' => 'on',
|
||||||
|
'late_fee_waived' => 'on',
|
||||||
|
]);
|
||||||
|
$response->assertRedirect(route('admin.entries.index'))
|
||||||
|
->assertSessionDoesntHaveErrors()
|
||||||
|
->assertSessionMissing('error')
|
||||||
|
->assertSessionHas('success', 'The entry has been added.');
|
||||||
|
assertDatabaseHas('entries', [
|
||||||
|
'student_id' => $student->id,
|
||||||
|
'audition_id' => $audition->id,
|
||||||
|
'for_seating' => 1,
|
||||||
|
'for_advancement' => 0,
|
||||||
|
]);
|
||||||
|
$entry = Entry::where('student_id', $student->id)->where('audition_id', $audition->id)->first();
|
||||||
|
|
||||||
|
assertDatabaseHas('entry_flags', [
|
||||||
|
'entry_id' => 1,
|
||||||
|
'flag_name' => 'late_fee_waived',
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ it('has a link to delete scores', function () {
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
$scoreSheet = ScoreSheet::where('entry_id', $entry->id)->first();
|
$scoreSheet = ScoreSheet::where('entry_id', $entry->id)->first();
|
||||||
actAsAdmin();
|
actAsAdmin();
|
||||||
$response = get(route('admin.entries.edit', $entry))
|
get(route('admin.entries.edit', $entry))
|
||||||
->assertSee(route('scores.destroy', ['score' => $scoreSheet]));
|
->assertSee(route('scores.destroy', ['score' => $scoreSheet]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -286,3 +286,20 @@ it('does not allow an admin to delete an entry if that entries advancement is pu
|
||||||
->assertRedirect(route('admin.entries.index'));
|
->assertRedirect(route('admin.entries.index'));
|
||||||
expect(Entry::find($this->entry->id))->not->toBeNull();
|
expect(Entry::find($this->entry->id))->not->toBeNull();
|
||||||
});
|
});
|
||||||
|
it('allows an admin to waive a late fee on an entry', function () {
|
||||||
|
// Arrange
|
||||||
|
$newAudition = Audition::factory()->create(['minimum_grade' => 1, 'maximum_grade' => 20]);
|
||||||
|
actAsAdmin();
|
||||||
|
// Act & Assert
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
patch(route('admin.entries.update', $this->entry), ['audition_id' => $newAudition->id, 'late_fee_waived' => 1])
|
||||||
|
->assertSessionHasNoErrors()
|
||||||
|
->assertSessionMissing('error')
|
||||||
|
->assertSessionHas('success', 'Entry updated successfully')
|
||||||
|
->assertRedirect(route('admin.entries.index'));
|
||||||
|
$this->entry->refresh();
|
||||||
|
expect($this->entry->audition_id)->toBe($newAudition->id)
|
||||||
|
->and($this->entry->for_seating)->toBe(0)
|
||||||
|
->and($this->entry->for_advancement)->toBe(0);
|
||||||
|
$this->assertDatabaseHas('entry_flags', ['entry_id' => $this->entry->id, 'flag_name' => 'late_fee_waived']);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue