fix(finance): default the journal currency when settings are absent
A fresh install dies creating the first airline:
App\Models\Airline::initJournal(): Argument #1 ($currency_code)
must be of type string, null given
setting() returns its $default for a key it cannot read -- retrieve()
throws SettingNotFound and the helper swallows it -- and the created
hook passed no default, so it handed null to a string parameter. The
'USD' default on initJournal() cannot help: an explicit argument always
beats a declared default, even when that argument is null.
The installer creates the airline and user in the same step, and the
settings seed is gated on a separate wizard step, so units.currency is
not guaranteed to exist by then. Every other reader of this setting
already names the fallback -- Money.php:59,74 and
PirepFinanceService.php:57,61 -- so this is the odd one out.
Airline and User are the only models using the trait; both are covered.
The suite could not have caught this because tests/Pest.php seeds
SettingsSeeder before every test, making an empty settings table
unreachable, so the new tests clear it explicitly.
This commit is contained in:
parent
ab7d3d004c
commit
290544cdd8
@ -14,7 +14,13 @@ trait JournalTrait
|
||||
public static function bootJournalTrait(): void
|
||||
{
|
||||
static::created(function ($model): void {
|
||||
$model->initJournal(setting('units.currency'));
|
||||
// The default on initJournal() cannot cover this: an explicit
|
||||
// argument always beats a declared default, and setting() returns
|
||||
// null for a key it cannot read. During install the first airline
|
||||
// is created before units.currency necessarily exists, so passing
|
||||
// the bare lookup hands a null to a string parameter. Every other
|
||||
// caller of this setting already names the fallback.
|
||||
$model->initJournal(setting('units.currency', 'USD'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\Airline;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
|
||||
/*
|
||||
* Regression coverage for JournalTrait::initJournal().
|
||||
@ -27,3 +29,44 @@ test('initJournal populates the journal relation on the same instance', function
|
||||
// same journal a fresh reload from the database resolves to.
|
||||
expect($airline->journal->id)->toBe($airline->fresh()->journal->id);
|
||||
});
|
||||
|
||||
/*
|
||||
* The `created` hook reads units.currency, and setting() returns null for a key
|
||||
* it cannot read -- the settings table is empty during install, and the first
|
||||
* airline is created there. initJournal() declares a 'USD' default, but a
|
||||
* default only applies when the argument is omitted; passing the bare lookup
|
||||
* hands an explicit null to a string parameter and the install dies with
|
||||
* "Argument #1 ($currency_code) must be of type string, null given".
|
||||
*
|
||||
* tests/Pest.php seeds SettingsSeeder before every test, so the empty-settings
|
||||
* state these two cover is otherwise unreachable from the suite.
|
||||
*/
|
||||
test('a model with a journal can be created before units.currency is seeded', function (): void {
|
||||
Setting::query()->delete();
|
||||
|
||||
$airline = Airline::factory()->create();
|
||||
|
||||
expect($airline->journal)->not->toBeNull()
|
||||
->and($airline->journal->currency)->toBe('USD');
|
||||
});
|
||||
|
||||
test('users are also covered, not just airlines', function (): void {
|
||||
Setting::query()->delete();
|
||||
|
||||
// Airline and User are the only two models using the trait, and the
|
||||
// installer creates both back to back.
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect($user->journal)->not->toBeNull()
|
||||
->and($user->journal->currency)->toBe('USD');
|
||||
});
|
||||
|
||||
test('a seeded units.currency still wins over the fallback', function (): void {
|
||||
// Settings are already seeded by tests/Pest.php, so this is the normal
|
||||
// path: the fallback must not shadow a value that is actually present.
|
||||
// Matched on `key` -- the primary key stores dots as underscores
|
||||
// (`units_currency`), which SettingService normalises on lookup.
|
||||
Setting::where('key', 'units.currency')->update(['value' => 'EUR']);
|
||||
|
||||
expect(Airline::factory()->create()->journal->currency)->toBe('EUR');
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user