Files
firefly-iii/app/Support/Navigation.php

886 lines
30 KiB
PHP
Raw Normal View History

2015-02-06 21:23:14 +01:00
<?php
2022-12-29 19:42:26 +01:00
/**
* Navigation.php
2020-02-16 13:56:52 +01:00
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
2017-10-21 08:40:00 +02:00
*
* This program is distributed in the hope that it will be useful,
2017-10-21 08:40:00 +02:00
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
2017-10-21 08:40:00 +02:00
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
2015-02-06 21:23:14 +01:00
namespace FireflyIII\Support;
use Carbon\Carbon;
2015-02-22 09:46:21 +01:00
use FireflyIII\Exceptions\FireflyException;
2023-10-29 17:41:14 +01:00
use FireflyIII\Exceptions\IntervalException;
2019-06-21 19:10:02 +02:00
use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Support\Calendar\Calculator;
use FireflyIII\Support\Calendar\Periodicity;
2023-11-03 05:52:35 +01:00
use Illuminate\Support\Facades\Log;
2015-02-06 21:23:14 +01:00
/**
2017-11-15 12:25:49 +01:00
* Class Navigation.
2015-02-06 21:23:14 +01:00
*/
class Navigation
{
private Calculator $calculator;
public function __construct(?Calculator $calculator = null)
{
2023-11-05 08:15:17 +01:00
$this->calculator = $calculator instanceof Calculator ? $calculator : new Calculator();
}
public function addPeriod(Carbon $theDate, string $repeatFreq, int $skip = 0): Carbon
2015-02-22 15:40:13 +01:00
{
2023-10-28 15:03:33 +02:00
$date = clone $theDate;
2015-02-25 15:19:14 +01:00
$functionMap = [
'1D' => Periodicity::Daily,
'daily' => Periodicity::Daily,
'1W' => Periodicity::Weekly,
'weekly' => Periodicity::Weekly,
'week' => Periodicity::Weekly,
'1M' => Periodicity::Monthly,
'month' => Periodicity::Monthly,
'monthly' => Periodicity::Monthly,
'3M' => Periodicity::Quarterly,
'quarter' => Periodicity::Quarterly,
'quarterly' => Periodicity::Quarterly,
'6M' => Periodicity::HalfYearly,
'half-year' => Periodicity::HalfYearly,
'year' => Periodicity::Yearly,
'yearly' => Periodicity::Yearly,
'1Y' => Periodicity::Yearly,
'custom' => Periodicity::Monthly, // custom? just add one month.
2023-05-19 05:43:50 +02:00
// last X periods? Jump the relevant month / quarter / year
'last7' => Periodicity::Weekly,
'last30' => Periodicity::Monthly,
'last90' => Periodicity::Quarterly,
'last365' => Periodicity::Yearly,
'MTD' => Periodicity::Monthly,
'QTD' => Periodicity::Quarterly,
'YTD' => Periodicity::Yearly,
2015-02-25 15:19:14 +01:00
];
2015-06-06 23:09:12 +02:00
2021-04-06 17:00:16 +02:00
if (!array_key_exists($repeatFreq, $functionMap)) {
2023-10-30 07:27:43 +01:00
Log::error(sprintf(
'The periodicity %s is unknown. Choose one of available periodicity: %s',
$repeatFreq,
implode(', ', array_keys($functionMap))
));
2023-12-20 19:35:52 +01:00
return $theDate;
2015-02-22 15:40:13 +01:00
}
2017-09-25 07:32:29 +02:00
2023-10-27 17:47:12 +02:00
return $this->nextDateByInterval($date, $functionMap[$repeatFreq], $skip);
2015-02-22 15:40:13 +01:00
}
2023-07-15 16:02:42 +02:00
public function nextDateByInterval(Carbon $epoch, Periodicity $periodicity, int $skipInterval = 0): Carbon
{
try {
return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval);
} catch (IntervalException $exception) {
2023-10-30 07:27:43 +01:00
Log::warning($exception->getMessage(), ['exception' => $exception]);
2025-01-04 19:43:58 +01:00
} catch (\Throwable $exception) {
2023-10-30 07:27:43 +01:00
Log::error($exception->getMessage(), ['exception' => $exception]);
2023-07-15 16:02:42 +02:00
}
2023-10-30 07:27:43 +01:00
Log::debug(
2023-07-15 16:02:42 +02:00
'Any error occurred to calculate the next date.',
['date' => $epoch, 'periodicity' => $periodicity->name, 'skipInterval' => $skipInterval]
);
return $epoch;
}
2021-05-24 08:57:02 +02:00
public function blockPeriods(Carbon $start, Carbon $end, string $range): array
2018-01-14 10:48:17 +01:00
{
2018-02-09 16:47:01 +01:00
if ($end < $start) {
2018-04-02 14:50:17 +02:00
[$start, $end] = [$end, $start];
2018-02-09 16:47:01 +01:00
}
$periods = [];
2020-01-26 07:15:47 +01:00
// first, 13 periods of [range]
$loopCount = 0;
$loopDate = clone $end;
$workStart = clone $loopDate;
$workEnd = clone $loopDate;
while ($loopCount < 13) {
// make range:
2023-10-29 17:41:14 +01:00
$workStart = $this->startOfPeriod($workStart, $range);
$workEnd = $this->endOfPeriod($workStart, $range);
2020-01-26 07:15:47 +01:00
// make sure we don't go overboard
if ($workEnd->gt($start)) {
2018-01-14 10:48:17 +01:00
$periods[] = [
2020-01-26 07:15:47 +01:00
'start' => clone $workStart,
'end' => clone $workEnd,
2018-01-14 10:48:17 +01:00
'period' => $range,
];
}
2020-01-26 07:15:47 +01:00
// skip to the next period:
$workStart->subDay()->startOfDay();
2023-12-20 19:35:52 +01:00
++$loopCount;
2018-01-14 10:48:17 +01:00
}
2020-01-26 07:15:47 +01:00
// if $workEnd is still before $start, continue on a yearly basis:
$loopCount = 0;
if ($workEnd->gt($start)) {
while ($workEnd->gt($start) && $loopCount < 20) {
// make range:
$workStart = app('navigation')->startOfPeriod($workStart, '1Y');
$workEnd = app('navigation')->endOfPeriod($workStart, '1Y');
2018-01-14 10:48:17 +01:00
2020-01-26 07:15:47 +01:00
// make sure we don't go overboard
if ($workEnd->gt($start)) {
$periods[] = [
'start' => clone $workStart,
'end' => clone $workEnd,
'period' => '1Y',
];
}
// skip to the next period:
$workStart->subDay()->startOfDay();
2023-12-20 19:35:52 +01:00
++$loopCount;
2018-01-14 10:48:17 +01:00
}
}
2021-04-06 17:00:16 +02:00
2018-01-14 10:48:17 +01:00
return $periods;
}
2023-06-21 12:34:58 +02:00
public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon
{
$date = clone $theDate;
// Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq));
$functionMap = [
2023-06-21 12:34:58 +02:00
'1D' => 'startOfDay',
'daily' => 'startOfDay',
'1W' => 'startOfWeek',
'week' => 'startOfWeek',
'weekly' => 'startOfWeek',
'month' => 'startOfMonth',
'1M' => 'startOfMonth',
'monthly' => 'startOfMonth',
'3M' => 'firstOfQuarter',
'quarter' => 'firstOfQuarter',
'quarterly' => 'firstOfQuarter',
'year' => 'startOfYear',
'yearly' => 'startOfYear',
'1Y' => 'startOfYear',
'MTD' => 'startOfMonth',
2023-06-21 12:34:58 +02:00
];
2024-03-17 12:26:56 +01:00
$parameterMap = [
'startOfWeek' => [Carbon::MONDAY],
];
2023-06-21 12:34:58 +02:00
if (array_key_exists($repeatFreq, $functionMap)) {
$function = $functionMap[$repeatFreq];
// Log::debug(sprintf('Function is ->%s()', $function));
if (array_key_exists($function, $parameterMap)) {
// Log::debug(sprintf('Parameter map, function becomes ->%s(%s)', $function, implode(', ', $parameterMap[$function])));
$date->{$function}($parameterMap[$function][0]); // @phpstan-ignore-line
// Log::debug(sprintf('Result is "%s"', $date->toIso8601String()));
2024-03-17 12:26:56 +01:00
return $date;
}
2023-12-20 19:35:52 +01:00
$date->{$function}(); // @phpstan-ignore-line
// Log::debug(sprintf('Result is "%s"', $date->toIso8601String()));
2023-06-21 12:34:58 +02:00
return $date;
}
if ('half-year' === $repeatFreq || '6M' === $repeatFreq) {
$skipTo = $date->month > 7 ? 6 : 0;
$date->startOfYear()->addMonths($skipTo);
// Log::debug(sprintf('Custom call for "%s": addMonths(%d)', $repeatFreq, $skipTo));
// Log::debug(sprintf('Result is "%s"', $date->toIso8601String()));
2023-06-21 12:34:58 +02:00
return $date;
}
$result = match ($repeatFreq) {
2023-07-15 16:02:42 +02:00
'last7' => $date->subDays(7)->startOfDay(),
'last30' => $date->subDays(30)->startOfDay(),
'last90' => $date->subDays(90)->startOfDay(),
2023-06-21 12:34:58 +02:00
'last365' => $date->subDays(365)->startOfDay(),
2023-07-15 16:02:42 +02:00
'MTD' => $date->startOfMonth()->startOfDay(),
'QTD' => $date->firstOfQuarter()->startOfDay(),
'YTD' => $date->startOfYear()->startOfDay(),
default => null,
2023-06-21 12:34:58 +02:00
};
if (null !== $result) {
// Log::debug(sprintf('Result is "%s"', $date->toIso8601String()));
2023-06-21 12:34:58 +02:00
return $result;
}
if ('custom' === $repeatFreq) {
// Log::debug(sprintf('Custom, result is "%s"', $date->toIso8601String()));
2023-06-21 12:34:58 +02:00
return $date; // the date is already at the start.
}
2023-10-30 07:27:43 +01:00
Log::error(sprintf('Cannot do startOfPeriod for $repeat_freq "%s"', $repeatFreq));
2023-06-21 12:34:58 +02:00
return $theDate;
}
2021-05-24 08:57:02 +02:00
public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon
2015-02-25 15:19:14 +01:00
{
$currentEnd = clone $end;
// Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq));
2015-02-25 15:19:14 +01:00
$functionMap = [
2019-03-02 08:05:15 +01:00
'1D' => 'endOfDay',
'daily' => 'endOfDay',
'1W' => 'addWeek',
'week' => 'addWeek',
'weekly' => 'addWeek',
'1M' => 'addMonth',
'month' => 'addMonth',
'monthly' => 'addMonth',
2024-03-31 16:51:53 +02:00
'3M' => 'addQuarter',
'quarter' => 'addQuarter',
'quarterly' => 'addQuarter',
2019-03-02 08:05:15 +01:00
'6M' => 'addMonths',
'half-year' => 'addMonths',
2020-03-14 10:25:12 +01:00
'half_year' => 'addMonths',
2019-03-02 08:05:15 +01:00
'year' => 'addYear',
'yearly' => 'addYear',
'1Y' => 'addYear',
2015-02-25 15:19:14 +01:00
];
2024-03-31 16:51:53 +02:00
$modifierMap = ['half-year' => 6, 'half_year' => 6, '6M' => 6];
$subDay = ['week', 'weekly', '1W', 'month', 'monthly', '1M', '3M', 'quarter', 'quarterly', '6M', 'half-year', 'half_year', '1Y', 'year', 'yearly'];
2015-02-25 15:19:14 +01:00
2017-11-15 12:25:49 +01:00
if ('custom' === $repeatFreq) {
2024-02-03 10:08:34 +01:00
// if the repeat frequency is "custom", use the current session start/end to see how large the range is,
// and use that to "add" another period.
// if there is no session data available use "30 days" as a default.
$diffInDays = 30;
if (null !== session('start') && null !== session('end')) {
Log::debug('Session data available.');
2023-12-20 19:35:52 +01:00
2024-02-03 10:08:34 +01:00
/** @var Carbon $tStart */
$tStart = session('start', today(config('app.timezone'))->startOfMonth());
2024-02-03 10:08:34 +01:00
/** @var Carbon $tEnd */
$tEnd = session('end', today(config('app.timezone'))->endOfMonth());
$diffInDays = (int) $tStart->diffInDays($tEnd, true);
2024-02-03 10:08:34 +01:00
}
Log::debug(sprintf('Diff in days is %d', $diffInDays));
2016-02-05 07:16:55 +01:00
$currentEnd->addDays($diffInDays);
return $currentEnd;
}
if ('MTD' === $repeatFreq) {
$today = today();
if ($today->isSameMonth($end)) {
return $today->endOfDay();
}
return $end->endOfMonth();
}
2023-05-06 15:29:29 +02:00
$result = match ($repeatFreq) {
2023-07-15 16:02:42 +02:00
'last7' => $currentEnd->addDays(7)->startOfDay(),
'last30' => $currentEnd->addDays(30)->startOfDay(),
'last90' => $currentEnd->addDays(90)->startOfDay(),
2023-05-06 15:29:29 +02:00
'last365' => $currentEnd->addDays(365)->startOfDay(),
2023-07-15 16:02:42 +02:00
'MTD' => $currentEnd->startOfMonth()->startOfDay(),
'QTD' => $currentEnd->firstOfQuarter()->startOfDay(),
'YTD' => $currentEnd->startOfYear()->startOfDay(),
default => null,
2023-05-06 15:29:29 +02:00
};
if (null !== $result) {
return $result;
}
unset($result);
2021-04-06 17:00:16 +02:00
if (!array_key_exists($repeatFreq, $functionMap)) {
2023-10-30 07:27:43 +01:00
Log::error(sprintf('Cannot do endOfPeriod for $repeat_freq "%s"', $repeatFreq));
return $end;
2015-02-25 15:19:14 +01:00
}
$function = $functionMap[$repeatFreq];
2018-06-02 18:19:35 +02:00
2021-04-06 17:00:16 +02:00
if (array_key_exists($repeatFreq, $modifierMap)) {
2023-12-20 19:35:52 +01:00
$currentEnd->{$function}($modifierMap[$repeatFreq]); // @phpstan-ignore-line
2019-06-21 19:10:02 +02:00
if (in_array($repeatFreq, $subDay, true)) {
$currentEnd->subDay();
}
2019-03-02 08:05:15 +01:00
$currentEnd->endOfDay();
return $currentEnd;
2015-02-25 15:19:14 +01:00
}
2023-12-20 19:35:52 +01:00
$currentEnd->{$function}(); // @phpstan-ignore-line
2019-03-02 08:05:15 +01:00
$currentEnd->endOfDay();
2019-06-21 19:10:02 +02:00
if (in_array($repeatFreq, $subDay, true)) {
2015-02-25 15:19:14 +01:00
$currentEnd->subDay();
}
// Log::debug(sprintf('Final result: %s', $currentEnd->toIso8601String()));
2015-02-25 15:19:14 +01:00
return $currentEnd;
}
2015-02-22 15:40:13 +01:00
public function daysUntilEndOfMonth(Carbon $date): int
{
$endOfMonth = $date->copy()->endOfMonth();
return (int) $date->diffInDays($endOfMonth, true);
}
2023-10-27 17:47:12 +02:00
public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int
2023-10-06 18:21:49 +02:00
{
2023-10-30 07:27:43 +01:00
Log::debug(sprintf(
'diffInPeriods: %s (skip: %d), between %s and %s.',
$period,
$skip,
$beginning->format('Y-m-d'),
$end->format('Y-m-d')
));
$map = [
2024-03-17 12:00:28 +01:00
'daily' => 'diffInDays',
'weekly' => 'diffInWeeks',
'monthly' => 'diffInMonths',
'quarterly' => 'diffInMonths',
'half-year' => 'diffInMonths',
'yearly' => 'diffInYears',
2023-10-06 18:21:49 +02:00
];
if (!array_key_exists($period, $map)) {
2023-10-30 07:27:43 +01:00
Log::warning(sprintf('No diffInPeriods for period "%s"', $period));
2023-12-20 19:35:52 +01:00
2023-10-06 18:21:49 +02:00
return 1;
}
$func = $map[$period];
2023-10-27 18:07:56 +02:00
// first do the diff
2024-03-17 12:00:28 +01:00
$floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line
2023-10-27 18:07:56 +02:00
// then correct for quarterly or half-year
2023-10-28 15:03:33 +02:00
if ('quarterly' === $period) {
2023-11-03 05:52:35 +01:00
Log::debug(sprintf('Q: Corrected %f to %f', $floatDiff, $floatDiff / 3));
$floatDiff /= 3;
2023-10-27 18:07:56 +02:00
}
2023-10-28 15:03:33 +02:00
if ('half-year' === $period) {
2023-11-03 05:52:35 +01:00
Log::debug(sprintf('H: Corrected %f to %f', $floatDiff, $floatDiff / 6));
$floatDiff /= 6;
2023-10-06 18:21:49 +02:00
}
2023-10-27 17:47:12 +02:00
2023-10-27 18:07:56 +02:00
// then do ceil()
$diff = ceil($floatDiff);
2023-10-27 18:07:56 +02:00
2023-11-03 05:52:35 +01:00
Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff));
2023-10-27 18:07:56 +02:00
2023-10-28 15:03:33 +02:00
if ($skip > 0) {
2023-10-27 17:47:12 +02:00
$parameter = $skip + 1;
2023-10-28 15:03:33 +02:00
$diff = ceil($diff / $parameter) * $parameter;
2023-10-30 07:27:43 +01:00
Log::debug(sprintf(
'diffInPeriods: skip is %d, so param is %d, and diff becomes %d',
$skip,
$parameter,
$diff
));
2023-10-27 17:47:12 +02:00
}
return (int) $diff;
2023-10-06 18:21:49 +02:00
}
2017-07-23 19:06:24 +02:00
public function endOfX(Carbon $theCurrentEnd, string $repeatFreq, ?Carbon $maxDate): Carbon
2015-02-27 11:02:08 +01:00
{
$functionMap = [
2015-09-25 17:28:42 +02:00
'1D' => 'endOfDay',
2015-02-27 11:02:08 +01:00
'daily' => 'endOfDay',
2015-09-25 17:28:42 +02:00
'1W' => 'endOfWeek',
2015-02-27 11:02:08 +01:00
'week' => 'endOfWeek',
'weekly' => 'endOfWeek',
'month' => 'endOfMonth',
2015-09-25 16:00:14 +02:00
'1M' => 'endOfMonth',
2015-02-27 11:02:08 +01:00
'monthly' => 'endOfMonth',
2015-09-25 17:28:42 +02:00
'3M' => 'lastOfQuarter',
2015-02-27 11:02:08 +01:00
'quarter' => 'lastOfQuarter',
'quarterly' => 'lastOfQuarter',
2015-09-25 17:28:42 +02:00
'1Y' => 'endOfYear',
2015-02-27 11:02:08 +01:00
'year' => 'endOfYear',
'yearly' => 'endOfYear',
];
$currentEnd = clone $theCurrentEnd;
2015-02-27 11:02:08 +01:00
2021-04-06 17:00:16 +02:00
if (array_key_exists($repeatFreq, $functionMap)) {
2015-02-27 11:02:08 +01:00
$function = $functionMap[$repeatFreq];
2023-12-20 19:35:52 +01:00
$currentEnd->{$function}(); // @phpstan-ignore-line
2015-02-27 11:02:08 +01:00
}
2015-06-29 09:14:39 +02:00
2017-11-15 12:25:49 +01:00
if (null !== $maxDate && $currentEnd > $maxDate) {
2015-02-27 11:02:08 +01:00
return clone $maxDate;
}
return $currentEnd;
}
2023-02-22 18:14:14 +01:00
/**
* Returns the user's view range and if necessary, corrects the dynamic view
* range to a normal range.
*/
public function getViewRange(bool $correct): string
{
2025-01-04 08:42:06 +01:00
$range = app('preferences')->get('viewRange', '1M')->data ?? '1M';
2023-11-28 05:31:26 +01:00
if (is_array($range)) {
$range = '1M';
}
$range = (string) $range;
2023-02-22 18:14:14 +01:00
if (!$correct) {
return $range;
}
2023-12-20 19:35:52 +01:00
2023-02-22 18:14:14 +01:00
switch ($range) {
default:
return $range;
2023-12-20 19:35:52 +01:00
2023-02-22 18:14:14 +01:00
case 'last7':
return '1W';
2023-12-20 19:35:52 +01:00
2023-02-22 18:14:14 +01:00
case 'last30':
case 'MTD':
return '1M';
2023-12-20 19:35:52 +01:00
2023-02-22 18:14:14 +01:00
case 'last90':
case 'QTD':
return '3M';
2023-12-20 19:35:52 +01:00
2023-02-22 18:14:14 +01:00
case 'last365':
case 'YTD':
return '1Y';
}
}
2016-11-19 13:37:44 +01:00
/**
2022-03-29 15:10:05 +02:00
* @throws FireflyException
2016-11-19 13:37:44 +01:00
*/
public function listOfPeriods(Carbon $start, Carbon $end): array
{
$locale = app('steam')->getLocale();
2016-11-19 13:37:44 +01:00
// define period to increment
$increment = 'addDay';
2018-01-25 18:41:27 +01:00
$format = $this->preferredCarbonFormat($start, $end);
$displayFormat = (string) trans('config.month_and_day_js', [], $locale);
$diff = $start->diffInMonths($end, true);
2016-11-19 13:37:44 +01:00
// increment by month (for year)
2024-04-21 07:07:06 +02:00
if ($diff >= 1.0001) {
2016-11-19 13:37:44 +01:00
$increment = 'addMonth';
$displayFormat = (string) trans('config.month_js');
2016-11-19 13:37:44 +01:00
}
2024-03-17 12:00:28 +01:00
// increment by year (for multi-year)
2024-04-21 07:07:06 +02:00
if ($diff >= 12.0001) {
2016-11-19 13:37:44 +01:00
$increment = 'addYear';
$displayFormat = (string) trans('config.year_js');
2016-11-19 13:37:44 +01:00
}
$begin = clone $start;
$entries = [];
2016-11-19 13:37:44 +01:00
while ($begin < $end) {
$formatted = $begin->format($format);
2022-03-27 20:24:13 +02:00
$displayed = $begin->isoFormat($displayFormat);
2016-11-19 13:37:44 +01:00
$entries[$formatted] = $displayed;
2023-12-20 19:35:52 +01:00
$begin->{$increment}(); // @phpstan-ignore-line
2016-11-19 13:37:44 +01:00
}
2020-01-25 23:29:26 +01:00
2016-11-19 13:37:44 +01:00
return $entries;
}
2015-02-25 15:19:14 +01:00
/**
2023-06-21 12:34:58 +02:00
* If the date difference between start and end is less than a month, method returns "Y-m-d". If the difference is
* less than a year, method returns "Y-m". If the date difference is larger, method returns "Y".
*/
public function preferredCarbonFormat(Carbon $start, Carbon $end): string
{
$format = 'Y-m-d';
$diff = $start->diffInMonths($end, true);
// Log::debug(sprintf('preferredCarbonFormat(%s, %s) = %f', $start->format('Y-m-d'), $end->format('Y-m-d'), $diff));
2024-04-21 07:07:06 +02:00
if ($diff >= 1.001) {
// Log::debug(sprintf('Return Y-m because %s', $diff));
2023-06-21 12:34:58 +02:00
$format = 'Y-m';
}
2024-04-21 07:07:06 +02:00
if ($diff >= 12.001) {
// Log::debug(sprintf('Return Y because %s', $diff));
2023-06-21 12:34:58 +02:00
$format = 'Y';
}
return $format;
}
2021-05-24 08:57:02 +02:00
public function periodShow(Carbon $theDate, string $repeatFrequency): string
2015-02-25 15:19:14 +01:00
{
2017-11-01 15:34:08 +01:00
$date = clone $theDate;
2015-02-25 15:19:14 +01:00
$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'),
'month' => (string) trans('config.month_js'),
'monthly' => (string) trans('config.month_js'),
'1Y' => (string) trans('config.year_js'),
'year' => (string) trans('config.year_js'),
'yearly' => (string) trans('config.year_js'),
'6M' => (string) trans('config.half_year_js'),
2015-02-25 15:19:14 +01:00
];
2015-05-25 21:17:36 +02:00
2021-04-06 17:00:16 +02:00
if (array_key_exists($repeatFrequency, $formatMap)) {
2023-11-05 08:15:17 +01:00
return $date->isoFormat($formatMap[$repeatFrequency]);
2015-02-25 15:19:14 +01:00
}
2017-11-15 12:25:49 +01:00
if ('3M' === $repeatFrequency || 'quarter' === $repeatFrequency) {
2017-11-01 15:34:08 +01:00
$quarter = ceil($theDate->month / 3);
return sprintf('Q%d %d', $quarter, $theDate->year);
}
// special formatter for quarter of year
2023-10-30 07:27:43 +01:00
Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency));
return $date->format('Y-m-d');
2015-02-25 15:19:14 +01:00
}
2023-08-01 09:27:39 +02:00
/**
* Same as preferredCarbonFormat but by string
*/
public function preferredCarbonFormatByPeriod(string $period): string
{
return match ($period) {
default => 'Y-m-d',
2023-12-20 19:35:52 +01:00
// '1D' => 'Y-m-d',
2023-08-01 09:27:39 +02:00
'1W' => '\WW,Y',
'1M' => 'Y-m',
'3M', '6M' => '\QQ,Y',
'1Y' => 'Y',
};
}
2023-05-29 13:56:55 +02:00
/**
2023-06-21 12:34:58 +02:00
* If the date difference between start and end is less than a month, method returns trans(config.month_and_day).
* If the difference is less than a year, method returns "config.month". If the date difference is larger, method
* returns "config.year".
2016-11-26 08:41:15 +01:00
*/
2016-12-30 13:47:23 +01:00
public function preferredCarbonLocalizedFormat(Carbon $start, Carbon $end): string
2016-11-26 08:41:15 +01:00
{
$locale = app('steam')->getLocale();
$format = (string) trans('config.month_and_day_js', [], $locale);
2024-03-17 12:00:28 +01:00
if ($start->diffInMonths($end, true) > 1) {
$format = (string) trans('config.month_js', [], $locale);
2016-11-26 08:41:15 +01:00
}
2024-03-17 12:00:28 +01:00
if ($start->diffInMonths($end, true) > 12) {
$format = (string) trans('config.year_js', [], $locale);
2016-11-26 08:41:15 +01:00
}
2016-12-30 13:47:23 +01:00
2016-12-15 13:47:28 +01:00
return $format;
}
/**
2023-06-21 12:34:58 +02:00
* If the date difference between start and end is less than a month, method returns "endOfDay". If the difference
* is less than a year, method returns "endOfMonth". If the date difference is larger, method returns "endOfYear".
2016-12-15 13:47:28 +01:00
*/
2016-12-30 13:47:23 +01:00
public function preferredEndOfPeriod(Carbon $start, Carbon $end): string
2016-12-15 13:47:28 +01:00
{
2016-12-30 13:47:23 +01:00
$format = 'endOfDay';
if ((int) $start->diffInMonths($end, true) > 1) {
2016-12-30 13:47:23 +01:00
$format = 'endOfMonth';
2016-12-15 13:47:28 +01:00
}
if ((int) $start->diffInMonths($end, true) > 12) {
2016-12-30 13:47:23 +01:00
$format = 'endOfYear';
2016-12-15 13:47:28 +01:00
}
2016-11-26 08:41:15 +01:00
return $format;
}
/**
2023-06-21 12:34:58 +02:00
* If the date difference between start and end is less than a month, method returns "1D". If the difference is
* less than a year, method returns "1M". If the date difference is larger, method returns "1Y".
2016-11-26 08:41:15 +01:00
*/
public function preferredRangeFormat(Carbon $start, Carbon $end): string
{
$format = '1D';
if ((int) $start->diffInMonths($end, true) > 1) {
2016-11-26 08:41:15 +01:00
$format = '1M';
}
if ((int) $start->diffInMonths($end, true) > 12) {
2016-11-26 08:41:15 +01:00
$format = '1Y';
}
return $format;
}
/**
2023-06-21 12:34:58 +02:00
* If the date difference between start and end is less than a month, method returns "%Y-%m-%d". If the difference
* is less than a year, method returns "%Y-%m". If the date difference is larger, method returns "%Y".
2016-11-26 08:41:15 +01:00
*/
public function preferredSqlFormat(Carbon $start, Carbon $end): string
{
$format = '%Y-%m-%d';
if ((int) $start->diffInMonths($end, true) > 1) {
2016-11-26 08:41:15 +01:00
$format = '%Y-%m';
}
if ((int) $start->diffInMonths($end, true) > 12) {
2016-11-26 08:41:15 +01:00
$format = '%Y';
}
return $format;
}
2023-05-29 13:56:55 +02:00
/**
2021-05-24 08:57:02 +02:00
* @throws FireflyException
2015-03-29 21:27:51 +02:00
*/
public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon
2015-03-29 21:27:51 +02:00
{
2023-12-10 06:45:59 +01:00
$subtract ??= 1;
$date = clone $theDate;
2015-09-25 17:28:42 +02:00
// 1D 1W 1M 3M 6M 1Y
2015-03-29 21:27:51 +02:00
$functionMap = [
2015-09-25 17:28:42 +02:00
'1D' => 'subDays',
2015-03-29 21:27:51 +02:00
'daily' => 'subDays',
'week' => 'subWeeks',
2015-09-25 17:28:42 +02:00
'1W' => 'subWeeks',
2015-03-29 21:27:51 +02:00
'weekly' => 'subWeeks',
'month' => 'subMonths',
2015-09-25 16:00:14 +02:00
'1M' => 'subMonths',
2015-03-29 21:27:51 +02:00
'monthly' => 'subMonths',
'year' => 'subYears',
'1Y' => 'subYears',
2015-03-29 21:27:51 +02:00
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'3M' => 3,
2015-03-29 21:27:51 +02:00
'quarterly' => 3,
'half-year' => 6,
'6M' => 6,
2015-03-29 21:27:51 +02:00
];
2021-04-06 17:00:16 +02:00
if (array_key_exists($repeatFreq, $functionMap)) {
2015-03-29 21:27:51 +02:00
$function = $functionMap[$repeatFreq];
2023-12-20 19:35:52 +01:00
$date->{$function}($subtract); // @phpstan-ignore-line
2015-03-29 21:27:51 +02:00
return $date;
}
2021-04-06 17:00:16 +02:00
if (array_key_exists($repeatFreq, $modifierMap)) {
2018-03-29 19:01:47 +02:00
$subtract *= $modifierMap[$repeatFreq];
2015-03-29 21:27:51 +02:00
$date->subMonths($subtract);
2017-12-29 09:05:35 +01:00
2015-03-29 21:27:51 +02:00
return $date;
}
// a custom range requires the session start
// and session end to calculate the difference in days.
// this is then subtracted from $theDate (* $subtract).
2017-11-15 12:25:49 +01:00
if ('custom' === $repeatFreq) {
/** @var Carbon $tStart */
$tStart = session('start', today(config('app.timezone'))->startOfMonth());
2023-12-20 19:35:52 +01:00
/** @var Carbon $tEnd */
2023-02-11 07:36:45 +01:00
$tEnd = session('end', today(config('app.timezone'))->endOfMonth());
$diffInDays = (int) $tStart->diffInDays($tEnd, true);
$date->subDays($diffInDays * $subtract);
2021-04-06 17:00:16 +02:00
return $date;
}
2023-12-20 19:35:52 +01:00
switch ($repeatFreq) {
default:
break;
2023-12-20 19:35:52 +01:00
2022-10-30 14:24:37 +01:00
case 'last7':
2022-11-02 05:02:09 +01:00
$date->subDays(7);
2023-12-20 19:35:52 +01:00
2022-11-02 05:02:09 +01:00
return $date;
2023-12-20 19:35:52 +01:00
2022-10-30 14:24:37 +01:00
case 'last30':
2022-11-02 05:02:09 +01:00
$date->subDays(30);
2023-12-20 19:35:52 +01:00
2022-11-02 05:02:09 +01:00
return $date;
2023-12-20 19:35:52 +01:00
case 'last90':
$date->subDays(90);
2023-12-20 19:35:52 +01:00
return $date;
2023-12-20 19:35:52 +01:00
case 'last365':
$date->subDays(365);
2023-12-20 19:35:52 +01:00
return $date;
2023-12-20 19:35:52 +01:00
case 'YTD':
$date->subYear();
2023-12-20 19:35:52 +01:00
return $date;
2023-12-20 19:35:52 +01:00
case 'QTD':
$date->subQuarter();
2023-12-20 19:35:52 +01:00
return $date;
2023-12-20 19:35:52 +01:00
case 'MTD':
$date->subMonth();
2023-12-20 19:35:52 +01:00
return $date;
}
throw new FireflyException(sprintf('Cannot do subtractPeriod for $repeat_freq "%s"', $repeatFreq));
2015-03-29 21:27:51 +02:00
}
2015-02-11 07:35:10 +01:00
/**
2021-05-24 08:57:02 +02:00
* @throws FireflyException
2015-02-11 07:35:10 +01:00
*/
2016-04-05 22:00:03 +02:00
public function updateEndDate(string $range, Carbon $start): Carbon
2015-02-06 21:23:14 +01:00
{
2023-10-30 07:27:43 +01:00
Log::debug(sprintf('updateEndDate("%s", "%s")', $range, $start->format('Y-m-d')));
2015-02-06 21:23:14 +01:00
$functionMap = [
2016-11-20 07:24:18 +01:00
'1D' => 'endOfDay',
'1W' => 'endOfWeek',
'1M' => 'endOfMonth',
'3M' => 'lastOfQuarter',
'custom' => 'startOfMonth', // this only happens in test situations.
2015-02-06 21:23:14 +01:00
];
2015-06-05 16:49:16 +02:00
$end = clone $start;
2015-02-06 21:23:14 +01:00
2021-04-06 17:00:16 +02:00
if (array_key_exists($range, $functionMap)) {
2015-02-06 21:23:14 +01:00
$function = $functionMap[$range];
2023-12-20 19:35:52 +01:00
$end->{$function}(); // @phpstan-ignore-line
2015-02-06 21:23:14 +01:00
2024-07-10 11:55:02 +02:00
Log::debug(sprintf('updateEndDate returns "%s"', $end->format('Y-m-d')));
2015-02-06 21:23:14 +01:00
return $end;
}
2017-11-15 12:25:49 +01:00
if ('6M' === $range) {
2015-05-05 10:30:39 +02:00
if ($start->month >= 7) {
2015-02-06 21:23:14 +01:00
$end->endOfYear();
2016-05-20 17:58:10 +02:00
return $end;
2015-02-06 21:23:14 +01:00
}
2016-05-20 17:58:10 +02:00
$end->startOfYear()->addMonths(6);
2015-02-06 21:23:14 +01:00
return $end;
}
// make sure 1Y takes the fiscal year into account.
if ('1Y' === $range) {
/** @var FiscalHelperInterface $fiscalHelper */
$fiscalHelper = app(FiscalHelperInterface::class);
2019-01-04 17:10:16 +01:00
return $fiscalHelper->endOfFiscalYear($end);
}
$list = [
2022-12-30 20:25:04 +01:00
'last7',
'last30',
'last90',
'last365',
'YTD',
'QTD',
'MTD',
];
if (in_array($range, $list, true)) {
2023-02-11 07:37:05 +01:00
$end = today(config('app.timezone'));
2023-02-11 07:36:45 +01:00
$end->endOfDay();
2023-10-30 07:27:43 +01:00
Log::debug(sprintf('updateEndDate returns "%s"', $end->format('Y-m-d')));
2023-12-20 19:35:52 +01:00
2022-12-30 20:25:04 +01:00
return $end;
2022-04-03 14:48:22 +02:00
}
throw new FireflyException(sprintf('updateEndDate cannot handle range "%s"', $range));
2015-02-06 21:23:14 +01:00
}
2015-02-11 07:35:10 +01:00
/**
2021-05-24 08:57:02 +02:00
* @throws FireflyException
2015-02-11 07:35:10 +01:00
*/
2016-04-05 22:00:03 +02:00
public function updateStartDate(string $range, Carbon $start): Carbon
2015-02-06 21:23:14 +01:00
{
2023-10-30 07:27:43 +01:00
Log::debug(sprintf('updateStartDate("%s", "%s")', $range, $start->format('Y-m-d')));
2015-02-06 21:23:14 +01:00
$functionMap = [
2016-11-20 07:24:18 +01:00
'1D' => 'startOfDay',
'1W' => 'startOfWeek',
'1M' => 'startOfMonth',
'3M' => 'firstOfQuarter',
'custom' => 'startOfMonth', // this only happens in test situations.
2015-02-06 21:23:14 +01:00
];
2021-04-06 17:00:16 +02:00
if (array_key_exists($range, $functionMap)) {
2015-02-06 21:23:14 +01:00
$function = $functionMap[$range];
2023-12-20 19:35:52 +01:00
$start->{$function}(); // @phpstan-ignore-line
2024-07-10 11:55:02 +02:00
Log::debug(sprintf('updateStartDate returns "%s"', $start->format('Y-m-d')));
2015-02-06 21:23:14 +01:00
return $start;
}
2017-11-15 12:25:49 +01:00
if ('6M' === $range) {
2015-05-05 10:30:39 +02:00
if ($start->month >= 7) {
2015-02-06 21:23:14 +01:00
$start->startOfYear()->addMonths(6);
return $start;
2015-02-06 21:23:14 +01:00
}
$start->startOfYear();
2015-02-06 21:23:14 +01:00
return $start;
}
// make sure 1Y takes the fiscal year into account.
if ('1Y' === $range) {
/** @var FiscalHelperInterface $fiscalHelper */
$fiscalHelper = app(FiscalHelperInterface::class);
2019-01-04 17:10:16 +01:00
return $fiscalHelper->startOfFiscalYear($start);
}
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
switch ($range) {
default:
break;
2023-12-20 19:35:52 +01:00
2022-10-30 14:24:37 +01:00
case 'last7':
2022-11-02 05:02:09 +01:00
$start->subDays(7);
2023-12-20 19:35:52 +01:00
2022-11-02 05:02:09 +01:00
return $start;
2023-12-20 19:35:52 +01:00
2022-10-30 14:24:37 +01:00
case 'last30':
2022-11-02 05:02:09 +01:00
$start->subDays(30);
2023-12-20 19:35:52 +01:00
2022-11-02 05:02:09 +01:00
return $start;
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
case 'last90':
$start->subDays(90);
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
return $start;
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
case 'last365':
$start->subDays(365);
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
return $start;
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
case 'YTD':
$start->startOfYear();
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
return $start;
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
case 'QTD':
$start->startOfQuarter();
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
return $start;
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
case 'MTD':
$start->startOfMonth();
2023-12-20 19:35:52 +01:00
2022-04-03 14:48:22 +02:00
return $start;
}
2023-12-20 19:35:52 +01:00
throw new FireflyException(sprintf('updateStartDate cannot handle range "%s"', $range));
2015-02-06 21:23:14 +01:00
}
2015-03-29 08:14:32 +02:00
}