Files
suivicpt/tests/Feature/RecurringTransactionTest.php
T
jeremy bayseandClaude Sonnet 5 b78c314569 Add recurring transactions with monthly confirmation flow
New "Récurrences" section lets users define recurring depense/epargne
models (name, category, default amount, day of month). Nothing is
created automatically: from the day a recurrence is due, it appears
as a pending proposal in Suivi (desktop + mobile) with an editable
amount, so variable bills (electricity, etc.) can be adjusted before
confirming. Dismissing a proposal skips it for the current month
without creating a transaction, and it reappears the following month.

- recurring_transactions table (household/category scoped, active flag,
  last_generated_year/month to prevent duplicate generation)
- transactions.recurring_transaction_id nullable FK to trace origin
- RecurringTransactionPolicy mirrors CategoryPolicy tenant scoping
- 10 new Pest tests covering CRUD, tenant isolation, and the
  pending/confirm/dismiss lifecycle across month boundaries (travelTo)

58/58 tests pass, Pint clean, verified end-to-end against production
data (test recurrence created and cleaned up).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 09:10:16 +02:00

68 lines
2.6 KiB
PHP

<?php
use App\Livewire\Recurring\Index;
use App\Models\RecurringTransaction;
use App\Models\User;
use Livewire\Livewire;
it('creates a recurring transaction for the current household', function (): void {
$user = User::factory()->create();
$category = $user->currentHousehold->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
Livewire::actingAs($user)
->test(Index::class)
->set('name', 'Loyer')
->set('categoryId', $category->id)
->set('amount', '1200')
->set('dayOfMonth', '5')
->call('add')
->assertHasNoErrors();
$recurring = $user->currentHousehold->recurringTransactions()->first();
expect($recurring)->not->toBeNull();
expect($recurring->name)->toBe('Loyer');
expect((float) $recurring->amount)->toBe(1200.0);
expect($recurring->day_of_month)->toBe(5);
expect($recurring->active)->toBeTrue();
});
it('prevents editing a recurring transaction from another household', function (): void {
$owner = User::factory()->create();
$intruder = User::factory()->create();
$category = $owner->currentHousehold->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
$recurring = $owner->currentHousehold->recurringTransactions()->create([
'category_id' => $category->id, 'name' => 'Loyer', 'amount' => 1200, 'day_of_month' => 5,
]);
Livewire::actingAs($intruder)
->test(Index::class)
->call('startEdit', $recurring->id)
->assertForbidden();
});
it('toggles active state', function (): void {
$user = User::factory()->create();
$category = $user->currentHousehold->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
$recurring = $user->currentHousehold->recurringTransactions()->create([
'category_id' => $category->id, 'name' => 'Loyer', 'amount' => 1200, 'day_of_month' => 5,
]);
Livewire::actingAs($user)->test(Index::class)->call('toggleActive', $recurring->id);
expect($recurring->fresh()->active)->toBeFalse();
});
it('deletes a recurring transaction', function (): void {
$user = User::factory()->create();
$category = $user->currentHousehold->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
$recurring = $user->currentHousehold->recurringTransactions()->create([
'category_id' => $category->id, 'name' => 'Loyer', 'amount' => 1200, 'day_of_month' => 5,
]);
Livewire::actingAs($user)->test(Index::class)->call('delete', $recurring->id);
expect(RecurringTransaction::find($recurring->id))->toBeNull();
});