From d54d54cbd023ed9c2afb896635a26005965b3be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Wed, 8 Jul 2026 23:26:34 +0200 Subject: [PATCH] fix(accounts): floor purchase/loan start dates at 1900 to stop OOM at source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up addressing the root cause of PHP-LARAVEL-49/4A. The batched upsert stops the crash, but purchase_date (`before_or_equal:today`) and loan_start_date (no constraint) had no LOWER bound, so a mistyped or absurd year (e.g. 0201, 1080) would still make the historical balance generator produce a multi-century monthly series — thousands of fabricated interpolated rows per account (storage bloat and a misleading 100-year net-worth ramp) even once it no longer OOMs. Add `after_or_equal:1900-01-01` to both dates. 1900 is generous enough to accept any legitimately old asset in a personal-finance app while rejecting the data-entry typos that drive the runaway series — capping both the in-memory date list and the generated row count at the input boundary, complementing the batching defense-in-depth. Not adding `before_or_equal:today` to loan_start_date here (real estate has it, loan doesn't): a future start date does not cause the OOM (the generator no-ops on it), so aligning that is a separate, non-urgent consistency change. Refs PHP-LARAVEL-49, PHP-LARAVEL-4A --- .../Concerns/ValidatesAccountDetailRules.php | 9 +++++++-- tests/Feature/RealEstateTest.php | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/Concerns/ValidatesAccountDetailRules.php b/app/Http/Requests/Concerns/ValidatesAccountDetailRules.php index ff753b30..cc0325d2 100644 --- a/app/Http/Requests/Concerns/ValidatesAccountDetailRules.php +++ b/app/Http/Requests/Concerns/ValidatesAccountDetailRules.php @@ -24,7 +24,10 @@ trait ValidatesAccountDetailRules ], 'address' => ['nullable', 'string', 'max:500'], 'purchase_price' => ['nullable', 'integer', 'min:0'], - 'purchase_date' => ['nullable', 'date', 'before_or_equal:today'], + // Floor the date: a mistyped/ancient year would make the historical + // balance generator build a multi-century monthly series and OOM the + // queue worker (PHP-LARAVEL-49). 1900 rejects typos, not real assets. + 'purchase_date' => ['nullable', 'date', 'after_or_equal:1900-01-01', 'before_or_equal:today'], 'area_value' => ['nullable', 'numeric', 'min:0', 'max:99999999.99'], 'area_unit' => ['nullable', 'string', Rule::in(['sqm', 'sqft', 'acres', 'hectares'])], 'linked_loan_account_id' => [ @@ -52,7 +55,9 @@ trait ValidatesAccountDetailRules return [ 'annual_interest_rate' => ['nullable', 'numeric', 'min:0', 'max:100'], 'loan_term_months' => ['nullable', 'integer', 'min:1', 'max:600'], - 'loan_start_date' => ['nullable', 'date'], + // Floor the date for the same reason as purchase_date above: an + // ancient loan start date OOMs the balance generator (PHP-LARAVEL-49). + 'loan_start_date' => ['nullable', 'date', 'after_or_equal:1900-01-01'], 'original_amount' => ['nullable', 'integer', 'min:0'], ]; } diff --git a/tests/Feature/RealEstateTest.php b/tests/Feature/RealEstateTest.php index 963ad0ac..65bc3f94 100644 --- a/tests/Feature/RealEstateTest.php +++ b/tests/Feature/RealEstateTest.php @@ -63,6 +63,26 @@ it('can create a real estate account with property details', function () { ]); }); +it('rejects a purchase date before the 1900 floor', function () { + actingAs($this->user); + + // A pre-1900 (typically mistyped) date would make the historical balance + // generator build a multi-century monthly series and OOM the queue worker + // (PHP-LARAVEL-49). It must be rejected at validation. + $data = [ + 'name' => 'My Apartment', + 'currency_code' => 'EUR', + 'type' => AccountType::RealEstate->value, + 'property_type' => PropertyType::Residential->value, + 'purchase_price' => 25000000, + 'purchase_date' => '1899-12-31', + ]; + + $response = $this->post(route('accounts.store'), $data); + + $response->assertSessionHasErrors(['purchase_date']); +}); + it('can create a real estate account with only required fields', function () { actingAs($this->user);