mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Fix charts and balances.
This commit is contained in:
		| @@ -116,7 +116,7 @@ class AccountController extends Controller | ||||
|             ]; | ||||
|             // TODO this code is also present in the V2 chart account controller so this method is due to be deprecated.
 | ||||
|             $currentStart = clone $start; | ||||
|             $range        = app('steam')->finalAccountBalanceInRange($account, $start, clone $end); | ||||
|             $range        = app('steam')->finalAccountBalanceInRange($account, $start, clone $end, $this->convertToNative); | ||||
|             // 2022-10-11 this method no longer converts to float.
 | ||||
|             $previous     = array_values($range)[0]; | ||||
|             while ($currentStart <= $end) { | ||||
|   | ||||
| @@ -55,6 +55,7 @@ abstract class Controller extends BaseController | ||||
|     /** @var array<int, string> */ | ||||
|     protected array        $allowedSort; | ||||
|     protected ParameterBag $parameters; | ||||
|     protected bool        $convertToNative = false; | ||||
| 
 | ||||
|     /** | ||||
|      * Controller constructor. | ||||
| @@ -68,7 +69,9 @@ abstract class Controller extends BaseController | ||||
|                 $this->parameters = $this->getParameters(); | ||||
|                 if (auth()->check()) { | ||||
|                     $language = app('steam')->getLanguage(); | ||||
|                     $this->convertToNative = app('preferences')->get('convert_to_native', false)->data; | ||||
|                     app()->setLocale($language); | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|                 return $next($request); | ||||
|   | ||||
| @@ -118,7 +118,7 @@ class AccountController extends Controller | ||||
|             'native_entries'                 => [], | ||||
|         ]; | ||||
|         $currentStart   = clone $params['start']; | ||||
|         $range          = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $currency); | ||||
|         $range          = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative); | ||||
| 
 | ||||
|         $previous       = array_values($range)[0]['balance']; | ||||
|         $previousNative = array_values($range)[0]['native_balance']; | ||||
|   | ||||
| @@ -54,9 +54,10 @@ class Controller extends BaseController | ||||
| { | ||||
|     use ValidatesUserGroupTrait; | ||||
| 
 | ||||
|     protected const string CONTENT_TYPE   = 'application/vnd.api+json'; | ||||
|     protected const string CONTENT_TYPE = 'application/vnd.api+json'; | ||||
|     protected array        $acceptedRoles = [UserRoleEnum::READ_ONLY]; | ||||
|     protected ParameterBag $parameters; | ||||
|     protected bool         $convertToNative = false; | ||||
| 
 | ||||
|     public function __construct() | ||||
|     { | ||||
| @@ -77,12 +78,12 @@ class Controller extends BaseController | ||||
|      */ | ||||
|     private function getParameters(): ParameterBag | ||||
|     { | ||||
|         $bag      = new ParameterBag(); | ||||
|         $bag = new ParameterBag(); | ||||
|         $bag->set('limit', 50); | ||||
| 
 | ||||
|         try { | ||||
|             $page = (int) request()->get('page'); | ||||
|         } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { | ||||
|         } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { | ||||
|             $page = 1; | ||||
|         } | ||||
| 
 | ||||
| @@ -112,7 +113,7 @@ class Controller extends BaseController | ||||
|             if (null !== $date) { | ||||
|                 try { | ||||
|                     $obj = Carbon::parse((string) $date, config('app.timezone')); | ||||
|                 } catch (InvalidDateException|InvalidFormatException $e) { | ||||
|                 } catch (InvalidDateException | InvalidFormatException $e) { | ||||
|                     // don't care
 | ||||
|                     app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr((string) $date, 0, 20), $e->getMessage())); | ||||
|                 } | ||||
| @@ -155,18 +156,18 @@ class Controller extends BaseController | ||||
| 
 | ||||
|     final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array | ||||
|     { | ||||
|         $manager  = new Manager(); | ||||
|         $baseUrl  = request()->getSchemeAndHttpHost().'/api/v2'; | ||||
|         $manager = new Manager(); | ||||
|         $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2'; | ||||
| 
 | ||||
|         // TODO add stuff to path?
 | ||||
| 
 | ||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||
| 
 | ||||
|         $objects  = $paginator->getCollection(); | ||||
|         $objects = $paginator->getCollection(); | ||||
| 
 | ||||
|         // the transformer, at this point, needs to collect information that ALL items in the collection
 | ||||
|         // require, like meta-data and stuff like that, and save it for later.
 | ||||
|         $objects  = $transformer->collectMetaData($objects); | ||||
|         $objects = $transformer->collectMetaData($objects); | ||||
|         $paginator->setCollection($objects); | ||||
| 
 | ||||
|         $resource = new FractalCollection($objects, $transformer, $key); | ||||
| @@ -180,11 +181,11 @@ class Controller extends BaseController | ||||
|      * | ||||
|      * @param array<int, mixed>|Model $object | ||||
|      */ | ||||
|     final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array | ||||
|     final protected function jsonApiObject(string $key, array | Model $object, AbstractTransformer $transformer): array | ||||
|     { | ||||
|         // create some objects:
 | ||||
|         $manager  = new Manager(); | ||||
|         $baseUrl  = request()->getSchemeAndHttpHost().'/api/v2'; | ||||
|         $manager = new Manager(); | ||||
|         $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2'; | ||||
|         $manager->setSerializer(new JsonApiSerializer($baseUrl)); | ||||
| 
 | ||||
|         $transformer->collectMetaData(new Collection([$object])); | ||||
|   | ||||
| @@ -30,7 +30,6 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface; | ||||
| use FireflyIII\Helpers\Collector\GroupCollectorInterface; | ||||
| use FireflyIII\Http\Controllers\Controller; | ||||
| use FireflyIII\Models\Account; | ||||
| use FireflyIII\Models\AccountType; | ||||
| use FireflyIII\Models\TransactionCurrency; | ||||
| use FireflyIII\Models\TransactionType; | ||||
| use FireflyIII\Repositories\Account\AccountRepositoryInterface; | ||||
| @@ -86,11 +85,11 @@ class AccountController extends Controller | ||||
|         Log::debug('RevenueAccounts'); | ||||
| 
 | ||||
|         /** @var Carbon $start */ | ||||
|         $start         = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $start = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end           = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $cache         = new CacheProperties(); | ||||
|         $end   = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($this->convertToNative); | ||||
| @@ -101,14 +100,14 @@ class AccountController extends Controller | ||||
|         $start->subDay(); | ||||
| 
 | ||||
|         // prep some vars:
 | ||||
|         $currencies    = []; | ||||
|         $chartData     = []; | ||||
|         $tempData      = []; | ||||
|         $default       = Amount::getDefaultCurrency(); | ||||
|         $currencies = []; | ||||
|         $chartData  = []; | ||||
|         $tempData   = []; | ||||
|         $default    = Amount::getDefaultCurrency(); | ||||
| 
 | ||||
|         // grab all accounts and names
 | ||||
|         $accounts      = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]); | ||||
|         $accountNames  = $this->extractNames($accounts); | ||||
|         $accounts     = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]); | ||||
|         $accountNames = $this->extractNames($accounts); | ||||
| 
 | ||||
|         // grab all balances
 | ||||
|         $startBalances = app('steam')->finalAccountsBalance($accounts, $start); | ||||
| @@ -140,13 +139,13 @@ class AccountController extends Controller | ||||
|                     continue; | ||||
|                 } | ||||
|                 Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); | ||||
|                 $searchCode   = $this->convertToNative ? $default->code : $key; | ||||
|                 $searchCode = $this->convertToNative ? $default->code : $key; | ||||
|                 Log::debug(sprintf('Search code is %s', $searchCode)); | ||||
|                 // see if there is an accompanying start amount.
 | ||||
|                 // grab the difference and find the currency.
 | ||||
|                 $startBalance = ($startBalances[$account->id][$key] ?? '0'); | ||||
|                 Log::debug(sprintf('Start balance is %s', $startBalance)); | ||||
|                 $diff         = bcsub($endBalance, $startBalance); | ||||
|                 $diff                    = bcsub($endBalance, $startBalance); | ||||
|                 $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); | ||||
|                 if (0 !== bccomp($diff, '0')) { | ||||
|                     // store the values in a temporary array.
 | ||||
| @@ -164,10 +163,10 @@ class AccountController extends Controller | ||||
|         foreach ($currencies as $currency) { | ||||
|             $newCurrencies[$currency->id] = $currency; | ||||
|         } | ||||
|         $currencies    = $newCurrencies; | ||||
|         $currencies = $newCurrencies; | ||||
| 
 | ||||
|         // sort temp array by amount.
 | ||||
|         $amounts       = array_column($tempData, 'diff_float'); | ||||
|         $amounts = array_column($tempData, 'diff_float'); | ||||
|         array_multisort($amounts, SORT_DESC, $tempData); | ||||
| 
 | ||||
|         // loop all found currencies and build the data array for the chart.
 | ||||
| @@ -178,12 +177,12 @@ class AccountController extends Controller | ||||
|         foreach ($currencies as $currencyId => $currency) { | ||||
|             $dataSet | ||||
|                                     = [ | ||||
|                                         'label'           => (string) trans('firefly.spent'), | ||||
|                                         'type'            => 'bar', | ||||
|                                         'currency_symbol' => $currency->symbol, | ||||
|                                         'currency_code'   => $currency->code, | ||||
|                                         'entries'         => $this->expandNames($tempData), | ||||
|                                     ]; | ||||
|                 'label'           => (string) trans('firefly.spent'), | ||||
|                 'type'            => 'bar', | ||||
|                 'currency_symbol' => $currency->symbol, | ||||
|                 'currency_code'   => $currency->code, | ||||
|                 'entries'         => $this->expandNames($tempData), | ||||
|             ]; | ||||
|             $chartData[$currencyId] = $dataSet; | ||||
|         } | ||||
| 
 | ||||
| @@ -194,7 +193,7 @@ class AccountController extends Controller | ||||
|             $chartData[$currencyId]['entries'][$name] = (float) $entry['difference']; | ||||
|         } | ||||
| 
 | ||||
|         $data          = $this->generator->multiSet($chartData); | ||||
|         $data = $this->generator->multiSet($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
| @@ -216,7 +215,7 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $cache     = new CacheProperties(); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($account->id); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
| @@ -235,9 +234,9 @@ class AccountController extends Controller | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             $budgetId              = (int) $journal['budget_id']; | ||||
|             $key                   = sprintf('%d-%d', $budgetId, $journal['currency_id']); | ||||
|             $budgetIds[]           = $budgetId; | ||||
|             $budgetId    = (int) $journal['budget_id']; | ||||
|             $key         = sprintf('%d-%d', $budgetId, $journal['currency_id']); | ||||
|             $budgetIds[] = $budgetId; | ||||
|             if (!array_key_exists($key, $result)) { | ||||
|                 $result[$key] = [ | ||||
|                     'total'           => '0', | ||||
| @@ -250,7 +249,7 @@ class AccountController extends Controller | ||||
|             $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); | ||||
|         } | ||||
| 
 | ||||
|         $names     = $this->getBudgetNames($budgetIds); | ||||
|         $names = $this->getBudgetNames($budgetIds); | ||||
| 
 | ||||
|         foreach ($result as $row) { | ||||
|             $budgetId          = $row['budget_id']; | ||||
| @@ -259,7 +258,7 @@ class AccountController extends Controller | ||||
|             $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; | ||||
|         } | ||||
| 
 | ||||
|         $data      = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $data = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
| @@ -281,7 +280,7 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $cache     = new CacheProperties(); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($account->id); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
| @@ -299,7 +298,7 @@ class AccountController extends Controller | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             $key                   = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); | ||||
|             $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); | ||||
|             if (!array_key_exists($key, $result)) { | ||||
|                 $result[$key] = [ | ||||
|                     'total'           => '0', | ||||
| @@ -311,7 +310,7 @@ class AccountController extends Controller | ||||
|             } | ||||
|             $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); | ||||
|         } | ||||
|         $names     = $this->getCategoryNames(array_keys($result)); | ||||
|         $names = $this->getCategoryNames(array_keys($result)); | ||||
| 
 | ||||
|         foreach ($result as $row) { | ||||
|             $categoryId        = $row['category_id']; | ||||
| @@ -320,7 +319,7 @@ class AccountController extends Controller | ||||
|             $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; | ||||
|         } | ||||
| 
 | ||||
|         $data      = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $data = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
| @@ -333,9 +332,9 @@ class AccountController extends Controller | ||||
|      *                                              */ | ||||
|     public function frontpage(AccountRepositoryInterface $repository): JsonResponse | ||||
|     { | ||||
|         $start          = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $end            = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $defaultSet     = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); | ||||
|         $start      = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $end        = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); | ||||
|         Log::debug('Default set is ', $defaultSet); | ||||
|         $frontpage      = app('preferences')->get('frontpageAccounts', $defaultSet); | ||||
|         $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data; | ||||
| @@ -344,7 +343,7 @@ class AccountController extends Controller | ||||
|             app('preferences')->set('frontpageAccounts', $defaultSet); | ||||
|             Log::debug('frontpage set is empty!'); | ||||
|         } | ||||
|         $accounts       = $repository->getAccountsById($frontpageArray); | ||||
|         $accounts = $repository->getAccountsById($frontpageArray); | ||||
| 
 | ||||
|         return response()->json($this->accountBalanceChart($accounts, $start, $end)); | ||||
|     } | ||||
| @@ -365,7 +364,7 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $cache     = new CacheProperties(); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($account->id); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
| @@ -385,7 +384,7 @@ class AccountController extends Controller | ||||
| 
 | ||||
|         /** @var array $journal */ | ||||
|         foreach ($journals as $journal) { | ||||
|             $key                   = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); | ||||
|             $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); | ||||
|             if (!array_key_exists($key, $result)) { | ||||
|                 $result[$key] = [ | ||||
|                     'total'           => '0', | ||||
| @@ -398,14 +397,14 @@ class AccountController extends Controller | ||||
|             $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); | ||||
|         } | ||||
| 
 | ||||
|         $names     = $this->getCategoryNames(array_keys($result)); | ||||
|         $names = $this->getCategoryNames(array_keys($result)); | ||||
|         foreach ($result as $row) { | ||||
|             $categoryId        = $row['category_id']; | ||||
|             $name              = $names[$categoryId] ?? '(unknown)'; | ||||
|             $label             = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]); | ||||
|             $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; | ||||
|         } | ||||
|         $data      = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $data = $this->generator->multiCurrencyPieChart($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
| @@ -418,59 +417,103 @@ class AccountController extends Controller | ||||
|      */ | ||||
|     public function period(Account $account, Carbon $start, Carbon $end): JsonResponse | ||||
|     { | ||||
|         $chartData  = []; | ||||
|         $cache      = new CacheProperties(); | ||||
|         Log::debug('Now in period()'); | ||||
|         $chartData = []; | ||||
|         $cache     = new CacheProperties(); | ||||
|         $cache->addProperty('chart.account.period'); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($this->convertToNative); | ||||
|         $cache->addProperty($account->id); | ||||
|         if ($cache->has()) { | ||||
|             return response()->json($cache->get()); | ||||
|         } | ||||
|         $currencies = $this->accountRepository->getUsedCurrencies($account); | ||||
| 
 | ||||
|         // if the account is not expense or revenue, just use the account's default currency.
 | ||||
|         if (!in_array($account->accountType->type, [AccountType::REVENUE, AccountType::EXPENSE], true)) { | ||||
|             $currencies = [$this->accountRepository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency()]; | ||||
|             // return response()->json($cache->get());
 | ||||
|         } | ||||
| 
 | ||||
|         /** @var TransactionCurrency $currency */ | ||||
|         foreach ($currencies as $currency) { | ||||
|             $chartData[] = $this->periodByCurrency($start, $end, $account, $currency); | ||||
|         // collect and filter balances for the entire period.
 | ||||
|         $step    = $this->calculateStep($start, $end); | ||||
|         Log::debug(sprintf('Step is %s', $step)); | ||||
|         $locale  = app('steam')->getLocale(); | ||||
|         $return = []; | ||||
|         // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
 | ||||
|         // have to make sure this chart is always based on the balance at the END of the period.
 | ||||
|         // This period depends on the size of the chart
 | ||||
|         $current         = clone $start; | ||||
|         $current         = app('navigation')->endOfX($current, $step, null); | ||||
|         $format          = (string) trans('config.month_and_day_js', [], $locale); | ||||
|         $accountCurrency = $this->accountRepository->getAccountCurrency($account); | ||||
| 
 | ||||
|         Log::debug('One'); | ||||
|         $range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToNative); | ||||
|         Log::debug('Two'); | ||||
|         $range    = Steam::filterAccountBalances($range, $account, $this->convertToNative, $accountCurrency); | ||||
|         Log::debug('Three'); | ||||
|         $previous = array_values($range)[0]; | ||||
|         $accountCurrency = $accountCurrency ?? $this->defaultCurrency; // do this AFTER getting the balances.
 | ||||
|         while ($end >= $current) { | ||||
|             $theDate = $current->format('Y-m-d'); | ||||
|             // each day contains multiple balances, and this may even be different over time.
 | ||||
|             $momentBalance = $range[$theDate] ?? $previous; | ||||
|             $return       = $this->updateChartKeys($return, $momentBalance); | ||||
| 
 | ||||
|             // process each balance thing.
 | ||||
|             foreach($momentBalance as $key => $amount) { | ||||
|                 $label = $current->isoFormat($format); | ||||
|                 $return[$key]['entries'][$label] = $amount; | ||||
|             } | ||||
|             $current         = app('navigation')->addPeriod($current, $step, 0); | ||||
|             // here too, to fix #8041, the data is corrected to the end of the period.
 | ||||
|             $current = app('navigation')->endOfX($current, $step, null); | ||||
|             $previous = $momentBalance; | ||||
|         } | ||||
|         // second loop (yes) to create nice array with info! Yay!
 | ||||
|         $chartData = []; | ||||
|         foreach($return as $key => $info) { | ||||
|             if(3 === strlen($key)) { | ||||
|                 // assume it's a currency:
 | ||||
|                 $setCurrency = $this->currencyRepository->findByCode($key); | ||||
|                 $info['currency_symbol'] = $setCurrency->symbol; | ||||
|                 $info['currency_code']   = $setCurrency->code; | ||||
|                 $info['label']           = sprintf('%s (%s)', $account->name, $setCurrency->symbol); | ||||
|             } | ||||
|             if('balance' === $key) { | ||||
|                 $info['currency_symbol'] = $accountCurrency->symbol; | ||||
|                 $info['currency_code']   = $accountCurrency->code; | ||||
|                 $info['label']           = sprintf('%s (%s)', $account->name, $accountCurrency->symbol); | ||||
|             } | ||||
|             if('native_balance' === $key) { | ||||
|                 $info['currency_symbol'] = $this->defaultCurrency->symbol; | ||||
|                 $info['currency_code']   = $this->defaultCurrency->code; | ||||
|                 $info['label']           = sprintf('%s (%s) (%s)', $account->name, (string)trans('firefly.sum'), $this->defaultCurrency->symbol); | ||||
|             } | ||||
|             $chartData[] = $info; | ||||
|         } | ||||
| 
 | ||||
|         $data       = $this->generator->multiSet($chartData); | ||||
|         $data = $this->generator->multiSet($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array | ||||
|     { | ||||
|         Log::debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code)); | ||||
|         $locale            = app('steam')->getLocale(); | ||||
|         $step              = $this->calculateStep($start, $end); | ||||
|         $result            = [ | ||||
| 
 | ||||
| 
 | ||||
|         var_dump($chartData);exit; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         $result  = [ | ||||
|             'label'           => sprintf('%s (%s)', $account->name, $currency->symbol), | ||||
|             'currency_symbol' => $currency->symbol, | ||||
|             'currency_code'   => $currency->code, | ||||
|         ]; | ||||
|         $entries           = []; | ||||
|         $current           = clone $start; | ||||
|         Log::debug(sprintf('Step is %s', $step)); | ||||
|         $entries = []; | ||||
|         $current = clone $start; | ||||
| 
 | ||||
| 
 | ||||
|         // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
 | ||||
|         // have to make sure this chart is always based on the balance at the END of the period.
 | ||||
|         // This period depends on the size of the chart
 | ||||
|         $current           = app('navigation')->endOfX($current, $step, null); | ||||
|         Log::debug(sprintf('$current date is %s', $current->format('Y-m-d'))); | ||||
|         if ('1D' === $step) { | ||||
|             // per day the entire period, balance for every day.
 | ||||
|             $format   = (string) trans('config.month_and_day_js', [], $locale); | ||||
|             $range    = app('steam')->finalAccountBalanceInRange($account, $start, $end); | ||||
|             $range    = app('steam')->finalAccountBalanceInRange($account, $start, $end, $this->convertToNative); | ||||
|             $previous = array_values($range)[0]; | ||||
|             while ($end >= $current) { | ||||
|                 $theDate         = $current->format('Y-m-d'); | ||||
| @@ -489,7 +532,70 @@ class AccountController extends Controller | ||||
|                 $entries[$label] = $balance; | ||||
|                 $current         = app('navigation')->addPeriod($current, $step, 0); | ||||
|                 // here too, to fix #8041, the data is corrected to the end of the period.
 | ||||
|                 $current         = app('navigation')->endOfX($current, $step, null); | ||||
|                 $current = app('navigation')->endOfX($current, $step, null); | ||||
|             } | ||||
|         } | ||||
|         $result['entries'] = $entries; | ||||
| 
 | ||||
|         return $result; | ||||
| 
 | ||||
| 
 | ||||
|         /** @var TransactionCurrency $currency */ | ||||
|         foreach ($currencies as $currency) { | ||||
|             $chartData[] = $this->periodByCurrency($start, $end, $account, $currency); | ||||
|         } | ||||
| 
 | ||||
|         $data = $this->generator->multiSet($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @throws FireflyException | ||||
|      */ | ||||
|     private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array | ||||
|     { | ||||
|         Log::debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code)); | ||||
|         $locale  = app('steam')->getLocale(); | ||||
|         $step    = $this->calculateStep($start, $end); | ||||
|         $result  = [ | ||||
|             'label'           => sprintf('%s (%s)', $account->name, $currency->symbol), | ||||
|             'currency_symbol' => $currency->symbol, | ||||
|             'currency_code'   => $currency->code, | ||||
|         ]; | ||||
|         $entries = []; | ||||
|         $current = clone $start; | ||||
|         Log::debug(sprintf('Step is %s', $step)); | ||||
| 
 | ||||
|         // fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
 | ||||
|         // have to make sure this chart is always based on the balance at the END of the period.
 | ||||
|         // This period depends on the size of the chart
 | ||||
|         $current = app('navigation')->endOfX($current, $step, null); | ||||
|         Log::debug(sprintf('$current date is %s', $current->format('Y-m-d'))); | ||||
|         if ('1D' === $step) { | ||||
|             // per day the entire period, balance for every day.
 | ||||
|             $format   = (string) trans('config.month_and_day_js', [], $locale); | ||||
|             $range    = app('steam')->finalAccountBalanceInRange($account, $start, $end, $this->convertToNative); | ||||
|             $previous = array_values($range)[0]; | ||||
|             while ($end >= $current) { | ||||
|                 $theDate         = $current->format('Y-m-d'); | ||||
|                 $balance         = $range[$theDate]['balance'] ?? $previous; | ||||
|                 $label           = $current->isoFormat($format); | ||||
|                 $entries[$label] = (float) $balance; | ||||
|                 $previous        = $balance; | ||||
|                 $current->addDay(); | ||||
|             } | ||||
|         } | ||||
|         if ('1W' === $step || '1M' === $step || '1Y' === $step) { | ||||
|             while ($end >= $current) { | ||||
|                 Log::debug(sprintf('Current is: %s', $current->format('Y-m-d'))); | ||||
|                 $balance         = Steam::finalAccountBalance($account, $current)[$currency->code] ?? '0'; | ||||
|                 $label           = app('navigation')->periodShow($current, $step); | ||||
|                 $entries[$label] = $balance; | ||||
|                 $current         = app('navigation')->addPeriod($current, $step, 0); | ||||
|                 // here too, to fix #8041, the data is corrected to the end of the period.
 | ||||
|                 $current = app('navigation')->endOfX($current, $step, null); | ||||
|             } | ||||
|         } | ||||
|         $result['entries'] = $entries; | ||||
| @@ -517,11 +623,11 @@ class AccountController extends Controller | ||||
|     public function revenueAccounts(): JsonResponse | ||||
|     { | ||||
|         /** @var Carbon $start */ | ||||
|         $start         = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
|         $start = clone session('start', today(config('app.timezone'))->startOfMonth()); | ||||
| 
 | ||||
|         /** @var Carbon $end */ | ||||
|         $end           = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $cache         = new CacheProperties(); | ||||
|         $end   = clone session('end', today(config('app.timezone'))->endOfMonth()); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($start); | ||||
|         $cache->addProperty($end); | ||||
|         $cache->addProperty($this->convertToNative); | ||||
| @@ -532,14 +638,14 @@ class AccountController extends Controller | ||||
|         $start->subDay(); | ||||
| 
 | ||||
|         // prep some vars:
 | ||||
|         $currencies    = []; | ||||
|         $chartData     = []; | ||||
|         $tempData      = []; | ||||
|         $default       = Amount::getDefaultCurrency(); | ||||
|         $currencies = []; | ||||
|         $chartData  = []; | ||||
|         $tempData   = []; | ||||
|         $default    = Amount::getDefaultCurrency(); | ||||
| 
 | ||||
|         // grab all accounts and names
 | ||||
|         $accounts      = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]); | ||||
|         $accountNames  = $this->extractNames($accounts); | ||||
|         $accounts     = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]); | ||||
|         $accountNames = $this->extractNames($accounts); | ||||
| 
 | ||||
|         // grab all balances
 | ||||
|         $startBalances = app('steam')->finalAccountsBalance($accounts, $start); | ||||
| @@ -572,13 +678,13 @@ class AccountController extends Controller | ||||
|                     continue; | ||||
|                 } | ||||
|                 Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); | ||||
|                 $searchCode   = $this->convertToNative ? $default->code : $key; | ||||
|                 $searchCode = $this->convertToNative ? $default->code : $key; | ||||
|                 Log::debug(sprintf('Search code is %s', $searchCode)); | ||||
|                 // see if there is an accompanying start amount.
 | ||||
|                 // grab the difference and find the currency.
 | ||||
|                 $startBalance = ($startBalances[$account->id][$key] ?? '0'); | ||||
|                 Log::debug(sprintf('Start balance is %s', $startBalance)); | ||||
|                 $diff         = bcsub($endBalance, $startBalance); | ||||
|                 $diff                    = bcsub($endBalance, $startBalance); | ||||
|                 $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); | ||||
|                 if (0 !== bccomp($diff, '0')) { | ||||
|                     // store the values in a temporary array.
 | ||||
| @@ -598,10 +704,10 @@ class AccountController extends Controller | ||||
|         foreach ($currencies as $currency) { | ||||
|             $newCurrencies[$currency->id] = $currency; | ||||
|         } | ||||
|         $currencies    = $newCurrencies; | ||||
|         $currencies = $newCurrencies; | ||||
| 
 | ||||
|         // sort temp array by amount.
 | ||||
|         $amounts       = array_column($tempData, 'diff_float'); | ||||
|         $amounts = array_column($tempData, 'diff_float'); | ||||
|         array_multisort($amounts, SORT_ASC, $tempData); | ||||
| 
 | ||||
|         // loop all found currencies and build the data array for the chart.
 | ||||
| @@ -612,12 +718,12 @@ class AccountController extends Controller | ||||
|         foreach ($currencies as $currencyId => $currency) { | ||||
|             $dataSet | ||||
|                                     = [ | ||||
|                                         'label'           => (string) trans('firefly.earned'), | ||||
|                                         'type'            => 'bar', | ||||
|                                         'currency_symbol' => $currency->symbol, | ||||
|                                         'currency_code'   => $currency->code, | ||||
|                                         'entries'         => $this->expandNames($tempData), | ||||
|                                     ]; | ||||
|                 'label'           => (string) trans('firefly.earned'), | ||||
|                 'type'            => 'bar', | ||||
|                 'currency_symbol' => $currency->symbol, | ||||
|                 'currency_code'   => $currency->code, | ||||
|                 'entries'         => $this->expandNames($tempData), | ||||
|             ]; | ||||
|             $chartData[$currencyId] = $dataSet; | ||||
|         } | ||||
| 
 | ||||
| @@ -628,9 +734,19 @@ class AccountController extends Controller | ||||
|             $chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1'); | ||||
|         } | ||||
| 
 | ||||
|         $data          = $this->generator->multiSet($chartData); | ||||
|         $data = $this->generator->multiSet($chartData); | ||||
|         $cache->store($data); | ||||
| 
 | ||||
|         return response()->json($data); | ||||
|     } | ||||
| 
 | ||||
|     private function updateChartKeys(array $array, array $balances): array | ||||
|     { | ||||
|         foreach (array_keys($balances) as $key) { | ||||
|             $array[$key] ??= [ | ||||
|                 'key' => $key, | ||||
|             ]; | ||||
|         } | ||||
|         return $array; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -38,8 +38,8 @@ class Steam | ||||
| { | ||||
|     public function getAccountCurrency(Account $account): ?TransactionCurrency | ||||
|     { | ||||
|         $type   = $account->accountType->type; | ||||
|         $list   = config('firefly.valid_currency_account_types'); | ||||
|         $type = $account->accountType->type; | ||||
|         $list = config('firefly.valid_currency_account_types'); | ||||
| 
 | ||||
|         // return null if not in this list.
 | ||||
|         if (!in_array($type, $list, true)) { | ||||
| @@ -69,14 +69,15 @@ class Steam | ||||
|         return $sum; | ||||
|     } | ||||
| 
 | ||||
|     public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end): array | ||||
|     public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end, bool $convertToNative): array | ||||
|     { | ||||
|         // expand period.
 | ||||
|         $start->subDay()->startOfDay(); | ||||
|         $end->addDay()->endOfDay(); | ||||
|         Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); | ||||
| 
 | ||||
|         // set up cache
 | ||||
|         $cache                = new CacheProperties(); | ||||
|         $cache = new CacheProperties(); | ||||
|         $cache->addProperty($account->id); | ||||
|         $cache->addProperty('final-balance-in-range'); | ||||
|         $cache->addProperty($start); | ||||
| @@ -85,71 +86,108 @@ class Steam | ||||
|             // return $cache->get();
 | ||||
|         } | ||||
| 
 | ||||
|         $balances             = []; | ||||
|         $formatted            = $start->format('Y-m-d'); | ||||
|         $startBalance         = $this->finalAccountBalance($account, $start); | ||||
|         $defaultCurrency      = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); | ||||
|         $currency             = $this->getAccountCurrency($account) ?? $defaultCurrency; | ||||
|         $balances        = []; | ||||
|         $formatted       = $start->format('Y-m-d'); | ||||
|         $startBalance    = $this->finalAccountBalance($account, $start); | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); | ||||
|         $accountCurrency = $this->getAccountCurrency($account); | ||||
|         $hasCurrency     = null !== $accountCurrency; | ||||
|         $currency        = $accountCurrency ?? $defaultCurrency; | ||||
|         Log::debug(sprintf('Currency is %s', $currency->code)); | ||||
|         if (!$hasCurrency) { | ||||
|             Log::debug(sprintf('Also set start balance in %s', $defaultCurrency->code)); | ||||
|             $startBalance[$defaultCurrency->code] ??= '0'; | ||||
|         } | ||||
|         $currencies           = [ | ||||
|             $currency->id        => $currency, | ||||
|             $defaultCurrency->id => $defaultCurrency, | ||||
|         ]; | ||||
|         $startBalance[$defaultCurrency->code] ??= '0'; | ||||
|         $startBalance[$currency->code]        ??= '0'; | ||||
|         $balances[$formatted] = $startBalance; | ||||
| 
 | ||||
| 
 | ||||
|         $startBalance[$currency->code] ??= '0'; | ||||
|         $balances[$formatted]          = $startBalance; | ||||
|         Log::debug('Final start balance: ', $startBalance); | ||||
| 
 | ||||
| 
 | ||||
|         // sums up the balance changes per day, for foreign, native and normal amounts.
 | ||||
|         $set                  = $account->transactions() | ||||
|             ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|             ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) | ||||
|             ->where('transaction_journals.date', '<=', $end->format('Y-m-d  H:i:s')) | ||||
|             ->groupBy('transaction_journals.date') | ||||
|             ->groupBy('transactions.transaction_currency_id') | ||||
|             ->groupBy('transactions.foreign_currency_id') | ||||
|             ->orderBy('transaction_journals.date', 'ASC') | ||||
|             ->whereNull('transaction_journals.deleted_at') | ||||
|             ->get( | ||||
|                 [ // @phpstan-ignore-line
 | ||||
|                     'transaction_journals.date', | ||||
|                     'transactions.transaction_currency_id', | ||||
|                     \DB::raw('SUM(transactions.amount) AS modified'), | ||||
|                     'transactions.foreign_currency_id', | ||||
|                     \DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), | ||||
|                     \DB::raw('SUM(transactions.native_amount) AS modified_native'), | ||||
|                 ] | ||||
|             ) | ||||
|         ; | ||||
|         $set = $account->transactions() | ||||
|                        ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') | ||||
|                        ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) | ||||
|                        ->where('transaction_journals.date', '<=', $end->format('Y-m-d  H:i:s')) | ||||
|                        ->groupBy('transaction_journals.date') | ||||
|                        ->groupBy('transactions.transaction_currency_id') | ||||
|                        ->groupBy('transactions.foreign_currency_id') | ||||
|                        ->orderBy('transaction_journals.date', 'ASC') | ||||
|                        ->whereNull('transaction_journals.deleted_at') | ||||
|                        ->get( | ||||
|                            [ // @phpstan-ignore-line
 | ||||
|                              'transaction_journals.date', | ||||
|                              'transactions.transaction_currency_id', | ||||
|                              \DB::raw('SUM(transactions.amount) AS modified'), | ||||
|                              'transactions.foreign_currency_id', | ||||
|                              \DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), | ||||
|                              \DB::raw('SUM(transactions.native_amount) AS modified_native'), | ||||
|                            ] | ||||
|                        ); | ||||
| 
 | ||||
|         $currentBalance       = $startBalance; | ||||
|         $currentBalance = $startBalance; | ||||
| 
 | ||||
|         /** @var Transaction $entry */ | ||||
|         foreach ($set as $entry) { | ||||
| 
 | ||||
|             // normal, native and foreign amount
 | ||||
|             $carbon                             = new Carbon($entry->date, $entry->date_tz); | ||||
|             $modified                           = (string) (null === $entry->modified ? '0' : $entry->modified); | ||||
|             $foreignModified                    = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); | ||||
|             $nativeModified                     = (string) (null === $entry->modified_native ? '0' : $entry->modified_native); | ||||
|             $carbon          = new Carbon($entry->date, $entry->date_tz); | ||||
|             $modified        = (string) (null === $entry->modified ? '0' : $entry->modified); | ||||
|             $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); | ||||
|             $nativeModified  = (string) (null === $entry->modified_native ? '0' : $entry->modified_native); | ||||
| 
 | ||||
|             // add "modified" to amount if the currency id matches the account currency id.
 | ||||
|             if ($entry->transaction_currency_id === $currency->id) { | ||||
|                 $currentBalance['balance']       = bcadd($currentBalance['balance'], $modified); | ||||
|                 $currentBalance[$currency->code] = bcadd($currentBalance[$currency->code], $modified); | ||||
|             // find currency of this entry.
 | ||||
|             $currencies[$entry->transaction_currency_id] = $currencies[$entry->transaction_currency_id] ?? TransactionCurrency::find($entry->transaction_currency_id); | ||||
|             $entryCurrency = $currencies[$entry->transaction_currency_id]; | ||||
| 
 | ||||
|             Log::debug(sprintf('Processing transaction(s) on date %s', $carbon->format('Y-m-d H:i:s'))); | ||||
| 
 | ||||
|             // if convert to native, if NOT convert to native.
 | ||||
|             if($convertToNative) { | ||||
|                 Log::debug(sprintf('Amount is %s %s, foreign amount is %s, native amount is %s', $entryCurrency->code, $modified, $foreignModified, $nativeModified)); | ||||
|                 // add to native balance.
 | ||||
|                 $currentBalance['native_balance']       = bcadd($currentBalance['native_balance'], $nativeModified); | ||||
|                 if($entry->foreign_currency_id === $defaultCurrency->id) { | ||||
|                     $currentBalance['native_balance']       = bcadd($currentBalance['native_balance'], $foreignModified); | ||||
|                 } | ||||
|                 // add to balance if is the same.
 | ||||
|                 if($entry->transaction_currency_id === $accountCurrency?->id) { | ||||
|                     $currentBalance['balance']       = bcadd($currentBalance['balance'], $modified); | ||||
|                 } | ||||
|                 // add currency balance
 | ||||
|                 $currentBalance[$entryCurrency->code] = bcadd($currentBalance[$entryCurrency->code], $modified); | ||||
|             } | ||||
|             if(!$convertToNative) { | ||||
|                 Log::debug(sprintf('Amount is %s %s, foreign amount is %s, native amount is %s', $entryCurrency->code, $modified, $foreignModified, $nativeModified)); | ||||
|                 // add to balance, as expected.
 | ||||
|                 $currentBalance['balance']       = bcadd($currentBalance['balance'] ?? '0', $modified); | ||||
|                 // add to GBP, as expected.
 | ||||
|                 $currentBalance[$entryCurrency->code] = bcadd($currentBalance[$entryCurrency->code], $modified); | ||||
|             } | ||||
| 
 | ||||
|             // always add the native balance, even if it ends up at zero.
 | ||||
|             $currentBalance['native_balance']   = bcadd($currentBalance['native_balance'], $nativeModified); | ||||
| //            // add "modified" to amount if the currency id matches the account currency id.
 | ||||
| //            if ($entry->transaction_currency_id === $currency->id) {
 | ||||
| //                $currentBalance['balance']       = bcadd($currentBalance['balance'], $modified);
 | ||||
| //                $currentBalance[$currency->code] = bcadd($currentBalance[$currency->code], $modified);
 | ||||
| //            }
 | ||||
| //
 | ||||
| //            // always add the native balance, even if it ends up at zero.
 | ||||
| //            $currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified);
 | ||||
| 
 | ||||
|             // add modified foreign to the array
 | ||||
|             if (null !== $entry->foreign_currency_id) { | ||||
|                 $foreignId                              = $entry->foreign_currency_id; | ||||
|                 $currencies[$foreignId]                 ??= TransactionCurrency::find($foreignId); | ||||
|                 $foreignCurrency                        = $currencies[$foreignId]; | ||||
|                 $currentBalance[$foreignCurrency->code] ??= '0'; | ||||
|                 $currentBalance[$foreignCurrency->code] = bcadd($currentBalance[$foreignCurrency->code], $foreignModified); | ||||
|             } | ||||
|             // DO NOT add modified foreign to the array
 | ||||
| //            if (null !== $entry->foreign_currency_id) {
 | ||||
| //                $foreignId                              = $entry->foreign_currency_id;
 | ||||
| //                $currencies[$foreignId]                 ??= TransactionCurrency::find($foreignId);
 | ||||
| //                $foreignCurrency                        = $currencies[$foreignId];
 | ||||
| //                $currentBalance[$foreignCurrency->code] ??= '0';
 | ||||
| //                $currentBalance[$foreignCurrency->code] = bcadd($currentBalance[$foreignCurrency->code], $foreignModified);
 | ||||
| //            }
 | ||||
|             $balances[$carbon->format('Y-m-d')] = $currentBalance; | ||||
|             Log::debug('Updated entry',$currentBalance); | ||||
|         } | ||||
|         $cache->store($balances); | ||||
| 
 | ||||
| @@ -185,10 +223,10 @@ class Steam | ||||
|         // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
 | ||||
|         if (str_contains($number, '.')) { | ||||
|             if ('-' !== $number[0]) { | ||||
|                 return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); | ||||
|                 return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); | ||||
|             } | ||||
| 
 | ||||
|             return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); | ||||
|             return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); | ||||
|         } | ||||
| 
 | ||||
|         return $number; | ||||
| @@ -287,12 +325,11 @@ class Steam | ||||
|         // first, the "balance", as described earlier.
 | ||||
|         if ($convertToNative) { | ||||
|             // normal balance
 | ||||
|             $return['balance']        = (string) $account->transactions() | ||||
|                 ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                 ->where('transactions.transaction_currency_id', $native->id) | ||||
|                 ->sum('transactions.amount') | ||||
|             ; | ||||
|             $return['balance'] = (string) $account->transactions() | ||||
|                                                   ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                                                   ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                                                   ->where('transactions.transaction_currency_id', $native->id) | ||||
|                                                   ->sum('transactions.amount'); | ||||
|             // plus virtual balance, if the account has a virtual_balance in the native currency
 | ||||
|             if ($native->id === $accountCurrency?->id) { | ||||
|                 $return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']); | ||||
| @@ -301,36 +338,33 @@ class Steam | ||||
| 
 | ||||
|             // native balance
 | ||||
|             $return['native_balance'] = (string) $account->transactions() | ||||
|                 ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                 ->whereNot('transactions.transaction_currency_id', $native->id) | ||||
|                 ->sum('transactions.native_amount') | ||||
|             ; | ||||
|                                                          ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                                                          ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                                                          ->whereNot('transactions.transaction_currency_id', $native->id) | ||||
|                                                          ->sum('transactions.native_amount'); | ||||
|             // plus native virtual balance.
 | ||||
|             $return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']); | ||||
|             Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $return['native_balance'])); | ||||
| 
 | ||||
|             // plus foreign transactions in THIS currency.
 | ||||
|             $sum                      = (string) $account->transactions() | ||||
|                 ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                 ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                 ->whereNot('transactions.transaction_currency_id', $native->id) | ||||
|                 ->where('transactions.foreign_currency_id', $native->id) | ||||
|                 ->sum('transactions.foreign_amount') | ||||
|             ; | ||||
|                                                          ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                                                          ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                                                          ->whereNot('transactions.transaction_currency_id', $native->id) | ||||
|                                                          ->where('transactions.foreign_currency_id', $native->id) | ||||
|                                                          ->sum('transactions.foreign_amount'); | ||||
|             $return['native_balance'] = bcadd($return['native_balance'], $sum); | ||||
| 
 | ||||
|             Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $sum, $return['native_balance'])); | ||||
|         } | ||||
| 
 | ||||
|         // balance(s) in other (all) currencies.
 | ||||
|         $array           = $account->transactions() | ||||
|             ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|             ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') | ||||
|             ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|             ->get(['transaction_currencies.code', 'transactions.amount'])->toArray() | ||||
|         ; | ||||
|         $others          = $this->groupAndSumTransactions($array, 'code', 'amount'); | ||||
|         $array  = $account->transactions() | ||||
|                           ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||
|                           ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') | ||||
|                           ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) | ||||
|                           ->get(['transaction_currencies.code', 'transactions.amount'])->toArray(); | ||||
|         $others = $this->groupAndSumTransactions($array, 'code', 'amount'); | ||||
|         Log::debug('All balances are (joined)', $others); | ||||
|         // if the account has no own currency preference, drop balance in favor of native balance
 | ||||
|         if ($hasCurrency && !$convertToNative) { | ||||
| @@ -346,30 +380,36 @@ class Steam | ||||
| 
 | ||||
|         if (!$hasCurrency && array_key_exists('balance', $return) && array_key_exists('native_balance', $return)) { | ||||
|             Log::debug('Account has no currency preference, dropping balance in favor of native balance.'); | ||||
|             $sum                      = bcadd($return['balance'], $return['native_balance']); | ||||
|             $sum = bcadd($return['balance'], $return['native_balance']); | ||||
|             Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum)); | ||||
|             $return['native_balance'] = $sum; | ||||
|             unset($return['balance']); | ||||
|         } | ||||
| 
 | ||||
|         return array_merge($return, $others); | ||||
|         $final = array_merge($return, $others); | ||||
|         Log::debug('Return is', $final); | ||||
|         return $final; | ||||
|     } | ||||
| 
 | ||||
|     public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array { | ||||
|     public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array | ||||
|     { | ||||
|         Log::debug(sprintf('filterAccountBalances(#%d)', $account->id)); | ||||
|         $return = []; | ||||
|         foreach($total as $key => $value) { | ||||
|         foreach ($total as $key => $value) { | ||||
|             $return[$key] = $this->filterAccountBalance($value, $account, $convertToNative, $currency); | ||||
|         } | ||||
|         Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id)); | ||||
|         return $return; | ||||
|     } | ||||
| 
 | ||||
|     public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array { | ||||
|         if(0 === count($set)) { | ||||
|     public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array | ||||
|     { | ||||
|         Log::debug(sprintf('filterAccountBalance(#%d)', $account->id), $set); | ||||
|         if (0 === count($set)) { | ||||
|             Log::debug(sprintf('Return empty array for account #%d', $account->id)); | ||||
|             return []; | ||||
|         } | ||||
|         $defaultCurrency = app('amount')->getDefaultCurrency(); | ||||
|         if($convertToNative) { | ||||
|         if ($convertToNative) { | ||||
|             if ($defaultCurrency->id === $currency?->id) { | ||||
|                 Log::debug(sprintf('Unset "native_balance" and "%s" for account #%d', $defaultCurrency->code, $account->id)); | ||||
|                 unset($set['native_balance'], $set[$defaultCurrency->code]); | ||||
| @@ -385,10 +425,10 @@ class Steam | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(!$convertToNative) { | ||||
|         if (!$convertToNative) { | ||||
|             if (null === $currency) { | ||||
|                 Log::debug(sprintf('Unset native_balance and make defaultCurrency balance the balance for account #%d', $account->id)); | ||||
|                 $set['balance'] = $set[$this->defaultCurrency->code] ?? '0'; | ||||
|                 $set['balance'] = $set[$defaultCurrency->code] ?? '0'; | ||||
|                 unset($set['native_balance'], $set[$defaultCurrency->code]); | ||||
|             } | ||||
| 
 | ||||
| @@ -448,15 +488,15 @@ class Steam | ||||
|     { | ||||
|         $list = []; | ||||
| 
 | ||||
|         $set  = auth()->user()->transactions() | ||||
|             ->whereIn('transactions.account_id', $accounts) | ||||
|             ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) | ||||
|             ->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
 | ||||
|         $set = auth()->user()->transactions() | ||||
|                      ->whereIn('transactions.account_id', $accounts) | ||||
|                      ->groupBy(['transactions.account_id', 'transaction_journals.user_id']) | ||||
|                      ->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
 | ||||
|         ; | ||||
| 
 | ||||
|         /** @var Transaction $entry */ | ||||
|         foreach ($set as $entry) { | ||||
|             $date                           = new Carbon($entry->max_date, config('app.timezone')); | ||||
|             $date = new Carbon($entry->max_date, config('app.timezone')); | ||||
|             $date->setTimezone(config('app.timezone')); | ||||
|             $list[(int) $entry->account_id] = $date; | ||||
|         } | ||||
| @@ -531,9 +571,9 @@ class Steam | ||||
|     public function getSafeUrl(string $unknownUrl, string $safeUrl): string | ||||
|     { | ||||
|         // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
 | ||||
|         $returnUrl      = $safeUrl; | ||||
|         $unknownHost    = parse_url($unknownUrl, PHP_URL_HOST); | ||||
|         $safeHost       = parse_url($safeUrl, PHP_URL_HOST); | ||||
|         $returnUrl   = $safeUrl; | ||||
|         $unknownHost = parse_url($unknownUrl, PHP_URL_HOST); | ||||
|         $safeHost    = parse_url($safeUrl, PHP_URL_HOST); | ||||
| 
 | ||||
|         if (null !== $unknownHost && $unknownHost === $safeHost) { | ||||
|             $returnUrl = $unknownUrl; | ||||
| @@ -570,7 +610,7 @@ class Steam | ||||
|      */ | ||||
|     public function floatalize(string $value): string | ||||
|     { | ||||
|         $value  = strtoupper($value); | ||||
|         $value = strtoupper($value); | ||||
|         if (!str_contains($value, 'E')) { | ||||
|             return $value; | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user