* test(setting): characterization tests for setting() helper + API shape
* refactor(setting,expense): add #[Scope] + #[ObservedBy] + casts() + drop dead $rules
* feat(services): add SettingService with cache-aware store/retrieve
* feat(finance): add FinanceService::getExpensesForType
* refactor(setting): migrate setting()/setting_save() helpers to SettingService
* refactor(setting): migrate Api controller, Filament page, importer, test helper to SettingService
* refactor(expense): migrate PirepFinanceService, ExportAction, FinanceTest to FinanceService::getExpensesForType
* refactor(setting,expense): delete SettingRepository and ExpenseRepository
* test(setting): use Setting::byKey scope in characterization test
* refactor(setting): drop redundant Cache::forget in Maintenance page
SettingService::store() now invalidates the per-key cache slot internally
(introduced in d9c94195). Manual Cache::forget calls after setting_save()
became dead code in this branch, mirroring the same cleanup applied to
Filament/Pages/Settings.php in 31aa55da. Drops the now-unused Cache import
as well — caught by deep review.
* fix(setting): increment count in SettingsImporter run
Pre-existing bug. $count was declared but never incremented, so the
import log always read 'Imported 0 settings' regardless of how many
settings the legacy importer actually wrote.
Found during Phase 5 deep review.
* refactor(filament): wrap settings save in DB::transaction
The Filament Settings page writes N setting rows in a foreach loop,
then calls FinanceService::changeJournalCurrencies() which rewrites
journal + journal_transaction rows to a (potentially) new currency.
A throw mid-loop or mid-currency-migration left the DB with half-saved
settings or a half-migrated journal.
Wrap both halves in a single DB::transaction() so a partial failure
rolls back all writes atomically.
Found during Phase 5 deep review.
* refactor(services): declare strict_types=1 across app/Services/
Phase 5 introduced declare(strict_types=1); in SettingService.php only.
This commit applies the same declaration to the remaining 65 service
files for consistency.
65 files migrated. 6 files required minimal signature/cast adjustments
to handle existing caller patterns where strict mode exposed implicit
type coercion at runtime or in phpstan:
- FareService::recalculateFares cast $pivot->capacity to (float)
before floor() (DB returns string).
- Finance/RecurringFinanceService::processExpenses cast
$expense->ref_model to (string) before explode() (nullable column).
- AirportLookup/VaCentralLookup, GeoService: pass $e->getMessage()
to Log::error() instead of the Exception object.
- DatabaseService::time() cast Carbon to (string) (return type contract).
- ModuleService::installModule pass $file->getRealPath() to PharData
and Madzipper::make() (UploadedFile -> string path).
No files reverted. No tests modified.
Found during Phase 5 deep review (convention drift).
* style(setting): align match arms for pint 1.29.1
Pint 1.29.1 aligns => arrows in match expressions under
binary_operator_spaces (with align_single_space_minimal). 1.29.0 did
not. CI installs latest globally and fails on the un-aligned form.
Bumped composer.lock so local matches CI.
* Update app/Services/Finance/RecurringFinanceService.php
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* refactor(installer): add strict types to LoggerTrait
LoggerTrait declared strict_types=1 but methods had no parameter or
return types. Added: comment(string $text): void, info(string $text):
void, error(string $text): void.
FlightImporter:59 was passing a Throwable to error() — switched to
$e->getMessage() to match the new contract. Same pattern applied in
5b2133a8 to VaCentralLookup and GeoService.
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>