reviewed epic 3

This commit is contained in:
Naser Mansour
2025-12-20 21:25:58 +02:00
parent b93b9363a6
commit 8b8d9735b9
8 changed files with 2116 additions and 24 deletions
@@ -52,6 +52,32 @@ So that **I don't forget my appointment and can prepare accordingly**.
- [ ] Logging for debugging
- [ ] Tests for reminder logic
## Prerequisites & Assumptions
### Consultation Model (from Story 3.4/3.5)
The `Consultation` model must exist with the following structure:
- **Fields:**
- `status` - enum: `pending`, `approved`, `completed`, `cancelled`, `no_show`
- `scheduled_date` - date (cast to Carbon)
- `scheduled_time` - time string (H:i:s)
- `type` - enum: `free`, `paid`
- `payment_status` - enum: `pending`, `received`, `not_applicable`
- `payment_amount` - decimal, nullable
- `user_id` - foreign key to users table
- `reminder_24h_sent_at` - timestamp, nullable (added by this story)
- `reminder_2h_sent_at` - timestamp, nullable (added by this story)
- **Relationships:**
- `user()` - BelongsTo User
- **Factory States:**
- `approved()` - sets status to 'approved'
### User Model Requirements
- `preferred_language` field must exist (values: `ar`, `en`)
- This field should be added in Epic 1 or early user stories
### Route Requirements
- `client.consultations.calendar` route from Story 3.6 must exist for calendar file download link
## Technical Notes
### Reminder Commands
@@ -344,6 +370,96 @@ it('does not send reminder for cancelled consultation', function () {
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
});
it('does not send reminder for no-show consultation', function () {
Notification::fake();
$consultation = Consultation::factory()->create([
'status' => 'no_show',
'scheduled_date' => now()->addHours(24)->toDateString(),
'scheduled_time' => now()->addHours(24)->format('H:i:s'),
]);
$this->artisan('reminders:send-24h')
->assertSuccessful();
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
});
it('does not send duplicate 24h reminders', function () {
Notification::fake();
$consultation = Consultation::factory()->approved()->create([
'scheduled_date' => now()->addHours(24)->toDateString(),
'scheduled_time' => now()->addHours(24)->format('H:i:s'),
'reminder_24h_sent_at' => now()->subHour(), // Already sent
]);
$this->artisan('reminders:send-24h')
->assertSuccessful();
Notification::assertNotSentTo($consultation->user, ConsultationReminder24h::class);
});
it('sends 2h reminder for upcoming consultation', function () {
Notification::fake();
$consultation = Consultation::factory()->approved()->create([
'scheduled_date' => now()->addHours(2)->toDateString(),
'scheduled_time' => now()->addHours(2)->format('H:i:s'),
'reminder_2h_sent_at' => null,
]);
$this->artisan('reminders:send-2h')
->assertSuccessful();
Notification::assertSentTo(
$consultation->user,
ConsultationReminder2h::class
);
expect($consultation->fresh()->reminder_2h_sent_at)->not->toBeNull();
});
it('includes payment reminder for unpaid consultations', function () {
Notification::fake();
$consultation = Consultation::factory()->approved()->create([
'scheduled_date' => now()->addHours(24)->toDateString(),
'scheduled_time' => now()->addHours(24)->format('H:i:s'),
'type' => 'paid',
'payment_status' => 'pending',
'payment_amount' => 200.00,
]);
$this->artisan('reminders:send-24h')
->assertSuccessful();
Notification::assertSentTo(
$consultation->user,
ConsultationReminder24h::class,
function ($notification) {
return $notification->consultation->type === 'paid'
&& $notification->consultation->payment_status === 'pending';
}
);
});
it('respects user language preference for reminders', function () {
Notification::fake();
$user = User::factory()->create(['preferred_language' => 'en']);
$consultation = Consultation::factory()->approved()->create([
'user_id' => $user->id,
'scheduled_date' => now()->addHours(24)->toDateString(),
'scheduled_time' => now()->addHours(24)->format('H:i:s'),
]);
$this->artisan('reminders:send-24h')
->assertSuccessful();
Notification::assertSentTo($user, ConsultationReminder24h::class);
});
```
## Definition of Done
@@ -363,8 +479,11 @@ it('does not send reminder for cancelled consultation', function () {
## Dependencies
- **Story 3.5:** Booking approval (creates approved consultations)
- **Epic 8:** Email infrastructure
- **Story 3.4:** Consultation model and booking submission (`app/Models/Consultation.php`, `database/factories/ConsultationFactory.php`)
- **Story 3.5:** Booking approval workflow (creates approved consultations)
- **Story 3.6:** Calendar file generation (provides `client.consultations.calendar` route)
- **Epic 1:** User model with `preferred_language` field (`app/Models/User.php`)
- **Epic 8:** Email infrastructure (mail configuration, queue setup)
## Risk Assessment