Laravel 11 + Livewire 3/Volt + Alpine.js + Tailwind app for household budget planning, transaction tracking, and financial dashboards. - Multi-tenant households with member invites - Plan: monthly/yearly budget per category with overrides - Suivi: transaction log with running balance (desktop + mobile quick-entry) - Dashboard: budget vs tracked breakdown + Chart.js donuts - Flux: Sankey diagram of income allocation - WCAG 2.1 AA color tokens, dark mode, accessible tables/forms - 50 Pest tests Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
67 lines
2.9 KiB
PHP
67 lines
2.9 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Plan\Index;
|
|
use App\Models\User;
|
|
use Livewire\Livewire;
|
|
|
|
it('sets a default amount that applies to all months without override', function (): void {
|
|
$user = User::factory()->create();
|
|
$category = $user->currentHousehold->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(Index::class)
|
|
->call('setYear', 2026)
|
|
->call('updateAmount', $category->id, null, '1200');
|
|
|
|
$line = $category->budgetLines()->where('year', 2026)->whereNull('month')->first();
|
|
expect((float) $line->amount)->toBe(1200.0);
|
|
});
|
|
|
|
it('overrides a single month while others keep the default', function (): void {
|
|
$user = User::factory()->create();
|
|
$category = $user->currentHousehold->categories()->create(['name' => 'Assurances', 'type' => 'depense', 'position' => 1]);
|
|
|
|
Livewire::actingAs($user)
|
|
->test(Index::class)
|
|
->call('setYear', 2026)
|
|
->call('updateAmount', $category->id, null, '189.89')
|
|
->call('updateAmount', $category->id, 8, '270');
|
|
|
|
expect((float) $category->budgetLines()->where('year', 2026)->whereNull('month')->first()->amount)->toBe(189.89);
|
|
expect((float) $category->budgetLines()->where('year', 2026)->where('month', 8)->first()->amount)->toBe(270.0);
|
|
expect($category->budgetLines()->where('year', 2026)->where('month', 9)->exists())->toBeFalse();
|
|
});
|
|
|
|
it('rejects updating a category from another household', function (): void {
|
|
$owner = User::factory()->create();
|
|
$intruder = User::factory()->create();
|
|
|
|
$category = $owner->currentHousehold->categories()->create(['name' => 'Courses', 'type' => 'depense', 'position' => 1]);
|
|
|
|
Livewire::actingAs($intruder)
|
|
->test(Index::class)
|
|
->call('updateAmount', $category->id, null, '500')
|
|
->assertForbidden();
|
|
});
|
|
|
|
it('computes remaining revenue to allocate per month after depenses and epargne', function (): void {
|
|
$user = User::factory()->create();
|
|
$household = $user->currentHousehold;
|
|
|
|
$revenu = $household->categories()->create(['name' => 'Salaire', 'type' => 'revenu', 'position' => 1]);
|
|
$depense = $household->categories()->create(['name' => 'Loyer', 'type' => 'depense', 'position' => 1]);
|
|
$epargne = $household->categories()->create(['name' => 'Vacances', 'type' => 'epargne', 'position' => 1]);
|
|
|
|
$component = Livewire::actingAs($user)->test(Index::class)->call('setYear', 2026);
|
|
|
|
$component->call('updateAmount', $revenu->id, null, '2900');
|
|
$component->call('updateAmount', $depense->id, null, '1200');
|
|
$component->call('updateAmount', $epargne->id, null, '200');
|
|
$component->call('updateAmount', $depense->id, 8, '1500');
|
|
|
|
$sections = $component->viewData('sections');
|
|
|
|
expect($sections['revenu']['remaining'][1])->toBe(1500.0); // 2900 - 1200 - 200
|
|
expect($sections['revenu']['remaining'][8])->toBe(1200.0); // 2900 - 1500 - 200
|
|
});
|