| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | <?php | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * NetWorth.php | 
					
						
							| 
									
										
										
										
											2020-01-28 08:46:01 +01:00
										 |  |  |  * Copyright (c) 2019 james@firefly-iii.org | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * This file is part of Firefly III (https://github.com/firefly-iii). | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * 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. | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +02:00
										 |  |  |  * GNU Affero General Public License for more details. | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-10-02 06:37:26 +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/>. | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | declare(strict_types=1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FireflyIII\Helpers\Report; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use Carbon\Carbon; | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  | use FireflyIII\Exceptions\FireflyException; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | use FireflyIII\Models\Account; | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  | use FireflyIII\Models\AccountType; | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  | use FireflyIII\Models\UserGroup; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | use FireflyIII\Repositories\Account\AccountRepositoryInterface; | 
					
						
							| 
									
										
										
										
											2023-09-23 07:15:41 +02:00
										 |  |  | use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface; | 
					
						
							| 
									
										
										
										
											2023-10-28 15:03:33 +02:00
										 |  |  | use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | use FireflyIII\Support\CacheProperties; | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  | use FireflyIII\Support\Http\Api\ExchangeRateConverter; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | use FireflyIII\User; | 
					
						
							| 
									
										
										
										
											2023-02-22 19:54:19 +01:00
										 |  |  | use Illuminate\Contracts\Auth\Authenticatable; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | use Illuminate\Support\Collection; | 
					
						
							| 
									
										
										
										
											2023-12-29 08:21:14 +01:00
										 |  |  | use Illuminate\Support\Facades\Log; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2023-09-23 07:15:41 +02:00
										 |  |  |  * This class can handle both request with and without a user group and will return the appropriate repository when | 
					
						
							|  |  |  |  * necessary. | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Class NetWorth | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class NetWorth implements NetWorthInterface | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |     private AccountRepositoryInterface      $accountRepository; | 
					
						
							|  |  |  |     private AdminAccountRepositoryInterface $adminAccountRepository; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-25 14:23:52 +02:00
										 |  |  |     private CurrencyRepositoryInterface $currencyRepos; | 
					
						
							| 
									
										
										
										
											2022-12-29 19:41:57 +01:00
										 |  |  |     private User                        $user; | 
					
						
							| 
									
										
										
										
											2024-03-06 07:16:01 +01:00
										 |  |  |     private ?UserGroup                  $userGroup; | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-10-29 05:54:01 +01:00
										 |  |  |      * This method collects the user's net worth in ALL the user's currencies | 
					
						
							|  |  |  |      * (1, 4 and 8) and also in the 'native' currency for ease of use. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The set of accounts has to be fed to it. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |      * @throws FireflyException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function byAccounts(Collection $accounts, Carbon $date): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // start in the past, end in the future? use $date
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $ids       = implode(',', $accounts->pluck('id')->toArray()); | 
					
						
							|  |  |  |         $cache     = new CacheProperties(); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |         $cache->addProperty($date); | 
					
						
							|  |  |  |         $cache->addProperty('net-worth-by-accounts'); | 
					
						
							|  |  |  |         $cache->addProperty($ids); | 
					
						
							|  |  |  |         if ($cache->has()) { | 
					
						
							| 
									
										
										
										
											2023-09-23 07:15:41 +02:00
										 |  |  |             return $cache->get(); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         app('log')->debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d'))); | 
					
						
							| 
									
										
										
										
											2023-12-29 12:00:15 +01:00
										 |  |  |         Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |         $default   = app('amount')->getDefaultCurrency(); | 
					
						
							|  |  |  |         $converter = new ExchangeRateConverter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // default "native" currency has everything twice, for consistency.
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $netWorth  = [ | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |             'native' => [ | 
					
						
							| 
									
										
										
										
											2023-12-29 19:59:19 +01:00
										 |  |  |                 'balance'                        => '0', | 
					
						
							|  |  |  |                 'native_balance'                 => '0', | 
					
						
							|  |  |  |                 'currency_id'                    => $default->id, | 
					
						
							|  |  |  |                 'currency_code'                  => $default->code, | 
					
						
							|  |  |  |                 'currency_name'                  => $default->name, | 
					
						
							|  |  |  |                 'currency_symbol'                => $default->symbol, | 
					
						
							|  |  |  |                 'currency_decimal_places'        => $default->decimal_places, | 
					
						
							|  |  |  |                 'native_currency_id'             => $default->id, | 
					
						
							|  |  |  |                 'native_currency_code'           => $default->code, | 
					
						
							|  |  |  |                 'native_currency_name'           => $default->name, | 
					
						
							|  |  |  |                 'native_currency_symbol'         => $default->symbol, | 
					
						
							|  |  |  |                 'native_currency_decimal_places' => $default->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |             ], | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $balances  = app('steam')->balancesByAccountsConverted($accounts, $date); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** @var Account $account */ | 
					
						
							|  |  |  |         foreach ($accounts as $account) { | 
					
						
							|  |  |  |             app('log')->debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name)); | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $currency                                  = $this->getRepository()->getAccountCurrency($account); | 
					
						
							| 
									
										
										
										
											2023-12-10 06:51:59 +01:00
										 |  |  |             if (null === $currency) { | 
					
						
							| 
									
										
										
										
											2023-12-09 20:12:34 +01:00
										 |  |  |                 $currency = app('amount')->getDefaultCurrency(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $currencyCode                              = $currency->code; | 
					
						
							|  |  |  |             $balance                                   = '0'; | 
					
						
							|  |  |  |             $nativeBalance                             = '0'; | 
					
						
							| 
									
										
										
										
											2023-11-05 19:41:37 +01:00
										 |  |  |             if (array_key_exists($account->id, $balances)) { | 
					
						
							|  |  |  |                 $balance       = $balances[$account->id]['balance'] ?? '0'; | 
					
						
							|  |  |  |                 $nativeBalance = $balances[$account->id]['native_balance'] ?? '0'; | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             app('log')->debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance)); | 
					
						
							|  |  |  |             // always subtract virtual balance
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $virtualBalance                            = $account->virtual_balance; | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |             if ('' !== $virtualBalance) { | 
					
						
							|  |  |  |                 $balance              = bcsub($balance, $virtualBalance); | 
					
						
							|  |  |  |                 $nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance); | 
					
						
							|  |  |  |                 $nativeBalance        = bcsub($nativeBalance, $nativeVirtualBalance); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-12-29 19:59:19 +01:00
										 |  |  |             $netWorth[$currencyCode] ??= [ | 
					
						
							|  |  |  |                 'balance'                        => '0', | 
					
						
							|  |  |  |                 'native_balance'                 => '0', | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |                 'currency_id'                    => (string)$currency->id, | 
					
						
							| 
									
										
										
										
											2023-12-29 19:59:19 +01:00
										 |  |  |                 'currency_code'                  => $currency->code, | 
					
						
							|  |  |  |                 'currency_name'                  => $currency->name, | 
					
						
							|  |  |  |                 'currency_symbol'                => $currency->symbol, | 
					
						
							|  |  |  |                 'currency_decimal_places'        => $currency->decimal_places, | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |                 'native_currency_id'             => (string)$default->id, | 
					
						
							| 
									
										
										
										
											2023-12-29 19:59:19 +01:00
										 |  |  |                 'native_currency_code'           => $default->code, | 
					
						
							|  |  |  |                 'native_currency_name'           => $default->name, | 
					
						
							|  |  |  |                 'native_currency_symbol'         => $default->symbol, | 
					
						
							|  |  |  |                 'native_currency_decimal_places' => $default->decimal_places, | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |             ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-29 19:59:19 +01:00
										 |  |  |             $netWorth[$currencyCode]['balance']        = bcadd($balance, $netWorth[$currencyCode]['balance']); | 
					
						
							|  |  |  |             $netWorth[$currencyCode]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyCode]['native_balance']); | 
					
						
							|  |  |  |             $netWorth['native']['balance']             = bcadd($nativeBalance, $netWorth['native']['balance']); | 
					
						
							|  |  |  |             $netWorth['native']['native_balance']      = bcadd($nativeBalance, $netWorth['native']['native_balance']); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         $cache->store($netWorth); | 
					
						
							| 
									
										
										
										
											2023-12-22 06:14:14 +01:00
										 |  |  |         $converter->summarize(); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return $netWorth; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |     private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (null === $this->userGroup) { | 
					
						
							|  |  |  |             return $this->accountRepository; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->adminAccountRepository; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-29 20:25:32 +01:00
										 |  |  |     public function setUser(null|Authenticatable|User $user): void | 
					
						
							| 
									
										
										
										
											2023-09-23 07:15:41 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  |         if (!$user instanceof User) { | 
					
						
							| 
									
										
										
										
											2023-02-22 19:54:19 +01:00
										 |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $this->user              = $user; | 
					
						
							|  |  |  |         $this->userGroup         = null; | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // make repository:
 | 
					
						
							|  |  |  |         $this->accountRepository = app(AccountRepositoryInterface::class); | 
					
						
							|  |  |  |         $this->accountRepository->setUser($this->user); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |         $this->currencyRepos     = app(CurrencyRepositoryInterface::class); | 
					
						
							| 
									
										
										
										
											2018-08-26 18:40:38 +02:00
										 |  |  |         $this->currencyRepos->setUser($this->user); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |     public function setUserGroup(UserGroup $userGroup): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->userGroup              = $userGroup; | 
					
						
							|  |  |  |         $this->adminAccountRepository = app(AdminAccountRepositoryInterface::class); | 
					
						
							| 
									
										
										
										
											2023-09-21 16:26:07 +02:00
										 |  |  |         $this->adminAccountRepository->setUserGroup($userGroup); | 
					
						
							| 
									
										
										
										
											2023-08-06 11:22:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-10-29 06:09:21 +01:00
										 |  |  |      * @deprecated | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function sumNetWorthByCurrency(Carbon $date): array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Collect accounts | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         $accounts = $this->getAccounts(); | 
					
						
							|  |  |  |         $return   = []; | 
					
						
							|  |  |  |         $balances = app('steam')->balancesByAccounts($accounts, $date); | 
					
						
							|  |  |  |         foreach ($accounts as $account) { | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $currency                     = $this->getRepository()->getAccountCurrency($account); | 
					
						
							|  |  |  |             $balance                      = $balances[$account->id] ?? '0'; | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // always subtract virtual balance.
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $virtualBalance               = $account->virtual_balance; | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |             if ('' !== $virtualBalance) { | 
					
						
							|  |  |  |                 $balance = bcsub($balance, $virtualBalance); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 14:43:56 +01:00
										 |  |  |             $return[$currency->id] ??= [ | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |                 'id'             => (string)$currency->id, | 
					
						
							| 
									
										
										
										
											2022-12-29 19:41:57 +01:00
										 |  |  |                 'name'           => $currency->name, | 
					
						
							|  |  |  |                 'symbol'         => $currency->symbol, | 
					
						
							|  |  |  |                 'code'           => $currency->code, | 
					
						
							|  |  |  |                 'decimal_places' => $currency->decimal_places, | 
					
						
							|  |  |  |                 'sum'            => '0', | 
					
						
							|  |  |  |             ]; | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |             $return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $balance); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private function getAccounts(): Collection | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-09-23 07:15:41 +02:00
										 |  |  |         $accounts = $this->getRepository()->getAccountsByType( | 
					
						
							| 
									
										
										
										
											2023-02-22 18:14:14 +01:00
										 |  |  |             [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE] | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2022-10-30 14:24:19 +01:00
										 |  |  |         $filtered = new Collection(); | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |         /** @var Account $account */ | 
					
						
							|  |  |  |         foreach ($accounts as $account) { | 
					
						
							| 
									
										
										
										
											2024-02-22 20:11:09 +01:00
										 |  |  |             if (1 === (int)$this->getRepository()->getMetaValue($account, 'include_net_worth')) { | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |                 $filtered->push($account); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-12-20 19:35:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-06 17:39:50 +02:00
										 |  |  |         return $filtered; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-31 07:48:23 +01:00
										 |  |  | } |