From 7abd30f4dd5daa5dfcec8f478dc8af3f88153b2b Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 26 Nov 2025 18:35:56 +0100 Subject: [PATCH] Attempted fix with sanity check for #11284 --- app/Support/Navigation.php | 142 +++++++++++------- .../RecalculatesAvailableBudgetsTrait.php | 14 +- 2 files changed, 90 insertions(+), 66 deletions(-) diff --git a/app/Support/Navigation.php b/app/Support/Navigation.php index b2d2c76f5d..52eaf56fb0 100644 --- a/app/Support/Navigation.php +++ b/app/Support/Navigation.php @@ -77,10 +77,10 @@ class Navigation if (!array_key_exists($repeatFreq, $functionMap)) { Log::error(sprintf( - 'The periodicity %s is unknown. Choose one of available periodicity: %s', - $repeatFreq, - implode(', ', array_keys($functionMap)) - )); + 'The periodicity %s is unknown. Choose one of available periodicity: %s', + $repeatFreq, + implode(', ', array_keys($functionMap)) + )); return $theDate; } @@ -93,7 +93,7 @@ class Navigation if ($end < $start) { [$start, $end] = [$end, $start]; } - $periods = []; + $periods = []; // first, 13 periods of [range] $loopCount = 0; $loopDate = clone $end; @@ -151,13 +151,13 @@ class Navigation public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int { Log::debug(sprintf( - 'diffInPeriods: %s (skip: %d), between %s and %s.', - $period, - $skip, - $beginning->format('Y-m-d'), - $end->format('Y-m-d') - )); - $map = [ + 'diffInPeriods: %s (skip: %d), between %s and %s.', + $period, + $skip, + $beginning->format('Y-m-d'), + $end->format('Y-m-d') + )); + $map = [ 'daily' => 'diffInDays', 'weekly' => 'diffInWeeks', 'monthly' => 'diffInMonths', @@ -170,7 +170,7 @@ class Navigation return 1; } - $func = $map[$period]; + $func = $map[$period]; // first do the diff $floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line @@ -185,7 +185,7 @@ class Navigation } // then do ceil() - $diff = ceil($floatDiff); + $diff = ceil($floatDiff); Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); @@ -193,11 +193,11 @@ class Navigation $parameter = $skip + 1; $diff = ceil($diff / $parameter) * $parameter; Log::debug(sprintf( - 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', - $skip, - $parameter, - $diff - )); + 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', + $skip, + $parameter, + $diff + )); } return (int)$diff; @@ -205,7 +205,7 @@ class Navigation public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon { - $currentEnd = clone $end; + $currentEnd = clone $end; // Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq)); $functionMap = [ @@ -239,7 +239,7 @@ class Navigation Log::debug('Session data available.'); /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); @@ -248,18 +248,32 @@ class Navigation Log::debug(sprintf('Diff in days is %d', $diffInDays)); $currentEnd->addDays($diffInDays); + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[a] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } + return $currentEnd; } if ('MTD' === $repeatFreq) { $today = today(); if ($today->isSameMonth($end)) { - return $today->endOfDay()->milli(0); + $res = $today->endOfDay()->milli(0); + // add sanity check. + if($res->lt($end)) { + throw new FireflyException(sprintf('[b] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $res->toW3cString())); + } + return $res; } + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[c] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } return $end->endOfMonth(); } - $result = match ($repeatFreq) { + $result = match ($repeatFreq) { 'last7' => $currentEnd->addDays(7)->startOfDay(), 'last30' => $currentEnd->addDays(30)->startOfDay(), 'last90' => $currentEnd->addDays(90)->startOfDay(), @@ -270,6 +284,11 @@ class Navigation default => null, }; if (null !== $result) { + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[d] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } + return $result; } unset($result); @@ -277,9 +296,14 @@ class Navigation if (!array_key_exists($repeatFreq, $functionMap)) { Log::error(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq)); + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[e] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } + return $end; } - $function = $functionMap[$repeatFreq]; + $function = $functionMap[$repeatFreq]; if (array_key_exists($repeatFreq, $modifierMap)) { $currentEnd->{$function}($modifierMap[$repeatFreq])->milli(0); // @phpstan-ignore-line @@ -288,6 +312,11 @@ class Navigation } $currentEnd->endOfDay()->milli(0); + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[f] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } + return $currentEnd; } $currentEnd->{$function}(); // @phpstan-ignore-line @@ -297,6 +326,11 @@ class Navigation } // Log::debug(sprintf('Final result: %s', $currentEnd->toIso8601String())); + // add sanity check. + if ($currentEnd->lt($end)) { + throw new FireflyException(sprintf('[g] endOfPeriod(%s, %s) failed, because it resulted in %s.', $end->toW3cString(), $repeatFreq, $currentEnd->toW3cString())); + } + return $currentEnd; } @@ -319,7 +353,7 @@ class Navigation 'yearly' => 'endOfYear', ]; - $currentEnd = clone $theCurrentEnd; + $currentEnd = clone $theCurrentEnd; if (array_key_exists($repeatFreq, $functionMap)) { $function = $functionMap[$repeatFreq]; @@ -362,7 +396,7 @@ class Navigation */ public function listOfPeriods(Carbon $start, Carbon $end): array { - $locale = app('steam')->getLocale(); + $locale = app('steam')->getLocale(); // define period to increment $increment = 'addDay'; $format = $this->preferredCarbonFormat($start, $end); @@ -379,8 +413,8 @@ class Navigation $increment = 'addYear'; $displayFormat = (string)trans('config.year_js'); } - $begin = clone $start; - $entries = []; + $begin = clone $start; + $entries = []; while ($begin < $end) { $formatted = $begin->format($format); $displayed = $begin->isoFormat($displayFormat); @@ -413,26 +447,26 @@ class Navigation { $date = clone $theDate; $formatMap = [ - '1D' => (string)trans('config.specific_day_js'), - 'daily' => (string)trans('config.specific_day_js'), - 'custom' => (string)trans('config.specific_day_js'), - '1W' => (string)trans('config.week_in_year_js'), - 'week' => (string)trans('config.week_in_year_js'), - 'weekly' => (string)trans('config.week_in_year_js'), - '1M' => (string)trans('config.month_js'), - 'MTD' => (string)trans('config.month_js'), - 'month' => (string)trans('config.month_js'), - 'monthly' => (string)trans('config.month_js'), - '1Y' => (string)trans('config.year_js'), - 'YTD' => (string)trans('config.year_js'), - 'year' => (string)trans('config.year_js'), - 'yearly' => (string)trans('config.year_js'), - '6M' => (string)trans('config.half_year_js'), - 'last7' => (string)trans('config.specific_day_js'), - 'last30' => (string)trans('config.month_js'), - 'last90' => (string)trans('config.month_js'), - 'last365' => (string)trans('config.year_js'), - 'QTD' => (string)trans('config.month_js'), + '1D' => (string)trans('config.specific_day_js'), + 'daily' => (string)trans('config.specific_day_js'), + 'custom' => (string)trans('config.specific_day_js'), + '1W' => (string)trans('config.week_in_year_js'), + 'week' => (string)trans('config.week_in_year_js'), + 'weekly' => (string)trans('config.week_in_year_js'), + '1M' => (string)trans('config.month_js'), + 'MTD' => (string)trans('config.month_js'), + 'month' => (string)trans('config.month_js'), + 'monthly' => (string)trans('config.month_js'), + '1Y' => (string)trans('config.year_js'), + 'YTD' => (string)trans('config.year_js'), + 'year' => (string)trans('config.year_js'), + 'yearly' => (string)trans('config.year_js'), + '6M' => (string)trans('config.half_year_js'), + 'last7' => (string)trans('config.specific_day_js'), + 'last30' => (string)trans('config.month_js'), + 'last90' => (string)trans('config.month_js'), + 'last365' => (string)trans('config.year_js'), + 'QTD' => (string)trans('config.month_js'), ]; if (array_key_exists($repeatFrequency, $formatMap)) { @@ -563,9 +597,9 @@ class Navigation public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon { - $date = clone $theDate; + $date = clone $theDate; // Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq)); - $functionMap = [ + $functionMap = [ '1D' => 'startOfDay', 'daily' => 'startOfDay', '1W' => 'startOfWeek', @@ -612,7 +646,7 @@ class Navigation return $date; } - $result = match ($repeatFreq) { + $result = match ($repeatFreq) { 'last7' => $date->subDays(7)->startOfDay(), 'last30' => $date->subDays(30)->startOfDay(), 'last90' => $date->subDays(90)->startOfDay(), @@ -644,7 +678,7 @@ class Navigation public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon { $subtract ??= 1; - $date = clone $theDate; + $date = clone $theDate; // 1D 1W 1M 3M 6M 1Y $functionMap = [ '1D' => 'subDays', @@ -683,7 +717,7 @@ class Navigation // this is then subtracted from $theDate (* $subtract). if ('custom' === $repeatFreq) { /** @var Carbon $tStart */ - $tStart = session('start', today(config('app.timezone'))->startOfMonth()); + $tStart = session('start', today(config('app.timezone'))->startOfMonth()); /** @var Carbon $tEnd */ $tEnd = session('end', today(config('app.timezone'))->endOfMonth()); @@ -777,7 +811,7 @@ class Navigation return $fiscalHelper->endOfFiscalYear($end); } - $list = [ + $list = [ 'last7', 'last30', 'last90', diff --git a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php index 2d4ee01702..58f40b85a4 100644 --- a/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php +++ b/app/Support/Observers/RecalculatesAvailableBudgetsTrait.php @@ -201,18 +201,8 @@ trait RecalculatesAvailableBudgetsTrait Log::debug('Found 1 AB, will update.'); $this->calculateAmount($availableBudget); } - if (null === $availableBudget) { - Log::debug('No AB found, will create.'); - - // if not exists: - try { - $currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); - } catch (InvalidPeriod $e) { - Log::error('Tried to make invalid period.'); - Log::error($e->getMessage()); - - continue; - } + if (null === $availableBudget && $currentEnd->gte($current)) { + $currentPeriod = Period::make($current, $currentEnd, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); $daily = $this->getDailyAmount($budgetLimit); $amount = bcmul((string)$daily, (string)$currentPeriod->length(), 12);