Invoices honor late_fee_waived flag

#38 Add ability for admin to forgive late fee on an entry
This commit is contained in:
Matt Young 2024-07-17 19:31:02 -05:00
parent 51436bda40
commit d522148cb9
6 changed files with 43 additions and 13 deletions

View File

@ -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

View File

@ -161,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')) {
@ -171,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');
} }

View File

@ -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),

View File

@ -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;

View File

@ -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>

View File

@ -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']);
});