mirror of
				https://github.com/firefly-iii/firefly-iii.git
				synced 2025-10-31 02:36:28 +00:00 
			
		
		
		
	Clean up autocomplete controller (accounts)
This commit is contained in:
		| @@ -68,8 +68,8 @@ class AccountController extends Controller | |||||||
|      */ |      */ | ||||||
|     public function accounts(AutocompleteRequest $request): JsonResponse |     public function accounts(AutocompleteRequest $request): JsonResponse | ||||||
|     { |     { | ||||||
|         $queryParameters = $request->getParameters(); |         $params = $request->getParameters(); | ||||||
|         $result          = $this->repository->searchAccount($queryParameters['query'], $queryParameters['account_types'], $queryParameters['size']); |         $result          = $this->repository->searchAccount($params['query'], $params['account_types'], $params['page'], $params['size']); | ||||||
|         $return          = []; |         $return          = []; | ||||||
| 
 | 
 | ||||||
|         /** @var Account $account */ |         /** @var Account $account */ | ||||||
| @@ -89,6 +89,7 @@ class AccountController extends Controller | |||||||
|             'title' => $account->name, |             'title' => $account->name, | ||||||
|             'meta'  => [ |             'meta'  => [ | ||||||
|                 'type'                    => $account->accountType->type, |                 'type'                    => $account->accountType->type, | ||||||
|  |                 // TODO is multi currency property.
 | ||||||
|                 'currency_id'             => null === $currency ? null : (string) $currency->id, |                 'currency_id'             => null === $currency ? null : (string) $currency->id, | ||||||
|                 'currency_code'           => $currency?->code, |                 'currency_code'           => $currency?->code, | ||||||
|                 'currency_symbol'         => $currency?->symbol, |                 'currency_symbol'         => $currency?->symbol, | ||||||
|   | |||||||
| @@ -23,15 +23,12 @@ declare(strict_types=1); | |||||||
| 
 | 
 | ||||||
| namespace FireflyIII\Api\V2\Request\Autocomplete; | namespace FireflyIII\Api\V2\Request\Autocomplete; | ||||||
| 
 | 
 | ||||||
| use FireflyIII\JsonApi\Rules\IsValidFilter; | use FireflyIII\Models\AccountType; | ||||||
| use FireflyIII\JsonApi\Rules\IsValidPage; |  | ||||||
| use FireflyIII\Support\Http\Api\AccountFilter; | use FireflyIII\Support\Http\Api\AccountFilter; | ||||||
| use FireflyIII\Support\Http\Api\ParsesQueryFilters; | use FireflyIII\Support\Http\Api\ParsesQueryFilters; | ||||||
| use FireflyIII\Support\Request\ChecksLogin; | use FireflyIII\Support\Request\ChecksLogin; | ||||||
| use FireflyIII\Support\Request\ConvertsDataTypes; | use FireflyIII\Support\Request\ConvertsDataTypes; | ||||||
| use Illuminate\Foundation\Http\FormRequest; | use Illuminate\Foundation\Http\FormRequest; | ||||||
| use LaravelJsonApi\Core\Query\QueryParameters; |  | ||||||
| use LaravelJsonApi\Validation\Rule as JsonApiRule; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Class AutocompleteRequest |  * Class AutocompleteRequest | ||||||
| @@ -51,35 +48,44 @@ class AutocompleteRequest extends FormRequest | |||||||
|      */ |      */ | ||||||
|     public function getParameters(): array |     public function getParameters(): array | ||||||
|     { |     { | ||||||
|         $queryParameters = QueryParameters::cast($this->all()); |         $array = [ | ||||||
| 
 |             'date'              => $this->date('date'), | ||||||
|         return [ |             'query'             => $this->clearString((string) $this->get('query')), | ||||||
|             'date'          => $this->dateOrToday($queryParameters, 'date'), |             'size'              => $this->integerFromValue('size'), | ||||||
|             'query'         => $this->arrayOfStrings($queryParameters, 'query'), |             'page'              => $this->integerFromValue('page'), | ||||||
|             'size'          => $this->integerFromQueryParams($queryParameters, 'size', 50), |             'account_types'     => $this->arrayFromValue($this->get('account_types')), | ||||||
|             'account_types' => $this->getAccountTypeParameter($this->arrayOfStrings($queryParameters, 'account_types')), |             'transaction_types' => $this->arrayFromValue($this->get('transaction_types')), | ||||||
|         ]; |         ]; | ||||||
|  |         $array['size'] = $array['size'] < 1 || $array['size'] > 100 ? 15 : $array['size']; | ||||||
|  |         $array['page'] = max($array['page'], 1); | ||||||
|  |         if (null === $array['account_types']) { | ||||||
|  |             $array['account_types'] = []; | ||||||
|  |         } | ||||||
|  |         if (null === $array['transaction_types']) { | ||||||
|  |             $array['transaction_types'] = []; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // remove 'initial balance' from allowed types. its internal
 | ||||||
|  |         $array['account_types'] = array_diff($array['account_types'], [AccountType::INITIAL_BALANCE, AccountType::RECONCILIATION, AccountType::CREDITCARD]); | ||||||
|  |         $array['account_types'] = $this->getAccountTypeParameter($array['account_types']); | ||||||
|  |         return $array; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function rules(): array |     public function rules(): array | ||||||
|     { |     { | ||||||
|  |         $valid = array_keys($this->types); | ||||||
|         return [ |         return [ | ||||||
|             'fields'  => JsonApiRule::notSupported(), |             'date'              => 'nullable|date|after:1900-01-01|before:2100-01-01', | ||||||
|             'filter'  => ['nullable', 'array', new IsValidFilter(['query', 'date', 'account_types'])], |             'query'             => 'nullable|string', | ||||||
|             'include' => JsonApiRule::notSupported(), |             'size'              => 'nullable|integer|min:1|max:100', | ||||||
|             'page'    => ['nullable', 'array', new IsValidPage(['size'])], |             'page'              => 'nullable|integer|min:1', | ||||||
|             'sort'    => JsonApiRule::notSupported(), |             'account_types'     => sprintf('nullable|in:%s', join(',', $valid)), | ||||||
|  |             'transaction_types' => 'nullable|in:todo', | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function getAccountTypeParameter(mixed $types): array |     private function getAccountTypeParameter(array $types): array | ||||||
|     { |     { | ||||||
|         if (is_string($types) && str_contains($types, ',')) { |  | ||||||
|             $types = explode(',', $types); |  | ||||||
|         } |  | ||||||
|         if (!is_iterable($types)) { |  | ||||||
|             $types = [$types]; |  | ||||||
|         } |  | ||||||
|         $return = []; |         $return = []; | ||||||
|         foreach ($types as $type) { |         foreach ($types as $type) { | ||||||
|             $return = array_merge($return, $this->mapAccountTypes($type)); |             $return = array_merge($return, $this->mapAccountTypes($type)); | ||||||
|   | |||||||
| @@ -51,7 +51,11 @@ class FixUnevenAmount extends Command | |||||||
|         $this->convertOldStyleTransfers(); |         $this->convertOldStyleTransfers(); | ||||||
|         $this->fixUnevenAmounts(); |         $this->fixUnevenAmounts(); | ||||||
|         $this->matchCurrencies(); |         $this->matchCurrencies(); | ||||||
|         AccountBalanceCalculator::forceRecalculateAll(); |         if(config('firefly.feature_flags.running_balance_column')) { | ||||||
|  |             $this->friendlyInfo('Will recalculate transaction running balance columns. This may take a LONG time. Please be patient.'); | ||||||
|  |             AccountBalanceCalculator::recalculateAll(true); | ||||||
|  |             $this->friendlyInfo('Done recalculating transaction running balance columns.'); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -33,9 +33,10 @@ use Illuminate\Console\Command; | |||||||
| class CorrectAccountBalance extends Command | class CorrectAccountBalance extends Command | ||||||
| { | { | ||||||
|     use ShowsFriendlyMessages; |     use ShowsFriendlyMessages; | ||||||
|  | 
 | ||||||
|     public const string CONFIG_NAME = '610_correct_balances'; |     public const string CONFIG_NAME = '610_correct_balances'; | ||||||
|     protected $description          = 'Recalculate all account balance amounts'; |     protected $description = 'Recalculate all account balance amounts'; | ||||||
|     protected $signature            = 'firefly-iii:correct-account-balance {--F|force : Force the execution of this command.}'; |     protected $signature   = 'firefly-iii:correct-account-balance {--F|force : Force the execution of this command.}'; | ||||||
| 
 | 
 | ||||||
|     public function handle(): int |     public function handle(): int | ||||||
|     { |     { | ||||||
| @@ -44,23 +45,29 @@ class CorrectAccountBalance extends Command | |||||||
| 
 | 
 | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|  |         if(config('firefly.feature_flags.running_balance_column')) { | ||||||
|  |             $this->friendlyInfo('Will recalculate account balances. This may take a LONG time. Please be patient.'); | ||||||
|  |             $this->markAsExecuted(); | ||||||
|  |             $this->correctBalanceAmounts(); | ||||||
|  |             $this->friendlyInfo('Done recalculating account balances.'); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|         $this->friendlyWarning('This command has been disabled.'); |         $this->friendlyWarning('This command has been disabled.'); | ||||||
|         $this->markAsExecuted(); |  | ||||||
| 
 | 
 | ||||||
|         //        $this->correctBalanceAmounts();
 |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function correctBalanceAmounts(): void |     private function correctBalanceAmounts(): void | ||||||
|     { |     { | ||||||
|         AccountBalanceCalculator::recalculateAll(); |         return; | ||||||
|  |         AccountBalanceCalculator::recalculateAll(true); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function isExecuted(): bool |     private function isExecuted(): bool | ||||||
|     { |     { | ||||||
|         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); |         $configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false); | ||||||
| 
 | 
 | ||||||
|         return (bool)$configVar?->data; |         return (bool) $configVar?->data; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function markAsExecuted(): void |     private function markAsExecuted(): void | ||||||
|   | |||||||
| @@ -95,7 +95,7 @@ class DebugController extends Controller | |||||||
| 
 | 
 | ||||||
|         // also do some recalculations.
 |         // also do some recalculations.
 | ||||||
|         Artisan::call('firefly-iii:trigger-credit-recalculation'); |         Artisan::call('firefly-iii:trigger-credit-recalculation'); | ||||||
|         AccountBalanceCalculator::forceRecalculateAll(); |         AccountBalanceCalculator::recalculateAll(true); | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             Artisan::call('twig:clean'); |             Artisan::call('twig:clean'); | ||||||
|   | |||||||
| @@ -67,8 +67,7 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
|                     $q1->where('account_meta.name', '=', 'account_number'); |                     $q1->where('account_meta.name', '=', 'account_number'); | ||||||
|                     $q1->where('account_meta.data', '=', $json); |                     $q1->where('account_meta.data', '=', $json); | ||||||
|                 } |                 } | ||||||
|             ) |             ); | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|         if (0 !== count($types)) { |         if (0 !== count($types)) { | ||||||
|             $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); |             $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); | ||||||
| @@ -95,7 +94,7 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
| 
 | 
 | ||||||
|     public function findByName(string $name, array $types): ?Account |     public function findByName(string $name, array $types): ?Account | ||||||
|     { |     { | ||||||
|         $query   = $this->userGroup->accounts(); |         $query = $this->userGroup->accounts(); | ||||||
| 
 | 
 | ||||||
|         if (0 !== count($types)) { |         if (0 !== count($types)) { | ||||||
|             $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); |             $query->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); | ||||||
| @@ -119,8 +118,8 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
| 
 | 
 | ||||||
|     public function getAccountCurrency(Account $account): ?TransactionCurrency |     public function getAccountCurrency(Account $account): ?TransactionCurrency | ||||||
|     { |     { | ||||||
|         $type       = $account->accountType->type; |         $type = $account->accountType->type; | ||||||
|         $list       = config('firefly.valid_currency_account_types'); |         $list = config('firefly.valid_currency_account_types'); | ||||||
| 
 | 
 | ||||||
|         // return null if not in this list.
 |         // return null if not in this list.
 | ||||||
|         if (!in_array($type, $list, true)) { |         if (!in_array($type, $list, true)) { | ||||||
| @@ -242,18 +241,17 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // reset the rest to zero.
 |         // reset the rest to zero.
 | ||||||
|         $all  = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE]; |         $all = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE]; | ||||||
|         $this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') |         $this->user->accounts()->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') | ||||||
|             ->whereNotIn('account_types.type', $all) |                    ->whereNotIn('account_types.type', $all) | ||||||
|             ->update(['order' => 0]) |                    ->update(['order' => 0]); | ||||||
|         ; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection |     public function getAccountsByType(array $types, ?array $sort = [], ?array $filters = []): Collection | ||||||
|     { |     { | ||||||
|         $sortable        = ['name', 'active']; // TODO yes this is a duplicate array.
 |         $sortable = ['name', 'active']; // TODO yes this is a duplicate array.
 | ||||||
|         $res             = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types); |         $res      = array_intersect([AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT], $types); | ||||||
|         $query           = $this->userGroup->accounts(); |         $query    = $this->userGroup->accounts(); | ||||||
|         if (0 !== count($types)) { |         if (0 !== count($types)) { | ||||||
|             $query->accountTypeIn($types); |             $query->accountTypeIn($types); | ||||||
|         } |         } | ||||||
| @@ -298,34 +296,36 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
|         return $query->get(['accounts.*']); |         return $query->get(['accounts.*']); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function searchAccount(array $query, array $types, int $limit): Collection |     public function searchAccount(string $query, array $types, int $page, int $limit): Collection | ||||||
|     { |     { | ||||||
|         // search by group, not by user
 |         // search by group, not by user
 | ||||||
|         $dbQuery = $this->userGroup->accounts() |         $dbQuery = $this->userGroup->accounts() | ||||||
|             ->where('active', true) |                                    ->where('active', true) | ||||||
|             ->orderBy('accounts.order', 'ASC') |             ->orderBy('accounts.updated_at', 'ASC') | ||||||
|             ->orderBy('accounts.account_type_id', 'ASC') |                                    ->orderBy('accounts.order', 'ASC') | ||||||
|             ->orderBy('accounts.name', 'ASC') |                                    ->orderBy('accounts.account_type_id', 'ASC') | ||||||
|             ->with(['accountType']) |                                    ->orderBy('accounts.name', 'ASC') | ||||||
|         ; |                                    ->with(['accountType']); | ||||||
|         if (count($query) > 0) { | 
 | ||||||
|             // split query on spaces just in case:
 |         // split query on spaces just in case:
 | ||||||
|  |         if('' !== trim($query)) { | ||||||
|             $dbQuery->where(function (EloquentBuilder $q) use ($query): void { |             $dbQuery->where(function (EloquentBuilder $q) use ($query): void { | ||||||
|                 foreach ($query as $line) { |                 $parts = explode(' ', $query); | ||||||
|                     $parts = explode(' ', $line); |                 foreach ($parts as $part) { | ||||||
|                     foreach ($parts as $part) { |                     $search = sprintf('%%%s%%', $part); | ||||||
|                         $search = sprintf('%%%s%%', $part); |                     $q->orWhereLike('name', $search); | ||||||
|                         $q->orWhereLike('name', $search); |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         if (0 !== count($types)) { |         if (0 !== count($types)) { | ||||||
|             $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); |             $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); | ||||||
|             $dbQuery->whereIn('account_types.type', $types); |             $dbQuery->whereIn('account_types.type', $types); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return $dbQuery->take($limit)->get(['accounts.*']); |         $dbQuery->skip(($page-1) * $limit)->take($limit); | ||||||
|  |         return $dbQuery->get(['accounts.*']); | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[\Override]
 |     #[\Override]
 | ||||||
| @@ -352,19 +352,18 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
|     public function getAccountTypes(Collection $accounts): Collection |     public function getAccountTypes(Collection $accounts): Collection | ||||||
|     { |     { | ||||||
|         return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id') |         return AccountType::leftJoin('accounts', 'accounts.account_type_id', '=', 'account_types.id') | ||||||
|             ->whereIn('accounts.id', $accounts->pluck('id')->toArray()) |                           ->whereIn('accounts.id', $accounts->pluck('id')->toArray()) | ||||||
|             ->get(['accounts.id', 'account_types.type']) |                           ->get(['accounts.id', 'account_types.type']); | ||||||
|         ; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[\Override]
 |     #[\Override]
 | ||||||
|     public function getLastActivity(Collection $accounts): array |     public function getLastActivity(Collection $accounts): array | ||||||
|     { |     { | ||||||
|         return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) |         return Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) | ||||||
|             ->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') |                           ->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') | ||||||
|             ->groupBy('transactions.account_id') |                           ->groupBy('transactions.account_id') | ||||||
|             ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line
 |                           ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) as date_max')])->toArray() // @phpstan-ignore-line
 | ||||||
|         ; |             ; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[\Override]
 |     #[\Override]
 | ||||||
| @@ -373,8 +372,7 @@ class AccountRepository implements AccountRepositoryInterface | |||||||
|         $groupIds = []; |         $groupIds = []; | ||||||
|         $return   = []; |         $return   = []; | ||||||
|         $set      = DB::table('object_groupables')->where('object_groupable_type', Account::class) |         $set      = DB::table('object_groupables')->where('object_groupable_type', Account::class) | ||||||
|             ->whereIn('object_groupable_id', $accounts->pluck('id')->toArray())->get() |                       ->whereIn('object_groupable_id', $accounts->pluck('id')->toArray())->get(); | ||||||
|         ; |  | ||||||
| 
 | 
 | ||||||
|         /** @var \stdClass $row */ |         /** @var \stdClass $row */ | ||||||
|         foreach ($set as $row) { |         foreach ($set as $row) { | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ interface AccountRepositoryInterface | |||||||
|      */ |      */ | ||||||
|     public function resetAccountOrder(): void; |     public function resetAccountOrder(): void; | ||||||
| 
 | 
 | ||||||
|     public function searchAccount(array $query, array $types, int $limit): Collection; |     public function searchAccount(string $query, array $types,int $page, int $limit): Collection; | ||||||
| 
 | 
 | ||||||
|     public function setUser(User $user): void; |     public function setUser(User $user): void; | ||||||
| 
 | 
 | ||||||
|   | |||||||
| @@ -24,10 +24,10 @@ declare(strict_types=1); | |||||||
| namespace FireflyIII\Support\Models; | namespace FireflyIII\Support\Models; | ||||||
| 
 | 
 | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
| use FireflyIII\Exceptions\FireflyException; |  | ||||||
| use FireflyIII\Models\Account; | use FireflyIII\Models\Account; | ||||||
| use FireflyIII\Models\AccountBalance; | use FireflyIII\Models\AccountBalance; | ||||||
| use FireflyIII\Models\Transaction; | use FireflyIII\Models\Transaction; | ||||||
|  | use FireflyIII\Models\TransactionCurrency; | ||||||
| use FireflyIII\Models\TransactionJournal; | use FireflyIII\Models\TransactionJournal; | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| use Illuminate\Support\Facades\Log; | use Illuminate\Support\Facades\Log; | ||||||
| @@ -47,20 +47,15 @@ class AccountBalanceCalculator | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Recalculate all balances. |      * Recalculate all account and transaction balances. | ||||||
|      */ |      */ | ||||||
|     public static function forceRecalculateAll(): void |     public static function recalculateAll(bool $forced): void | ||||||
|     { |  | ||||||
|         Transaction::whereNull('deleted_at')->update(['balance_dirty' => true]); |  | ||||||
|         $object = new self(); |  | ||||||
|         $object->optimizedCalculation(new Collection()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Recalculate all balances. |  | ||||||
|      */ |  | ||||||
|     public static function recalculateAll(): void |  | ||||||
|     { |     { | ||||||
|  |         if ($forced) { | ||||||
|  |             Transaction::whereNull('deleted_at')->update(['balance_dirty' => true]); | ||||||
|  |             // also delete account balances.
 | ||||||
|  |             AccountBalance::whereNotNull('created_at')->delete(); | ||||||
|  |         } | ||||||
|         $object = new self(); |         $object = new self(); | ||||||
|         $object->optimizedCalculation(new Collection()); |         $object->optimizedCalculation(new Collection()); | ||||||
|     } |     } | ||||||
| @@ -68,7 +63,7 @@ class AccountBalanceCalculator | |||||||
|     public static function recalculateForJournal(TransactionJournal $transactionJournal): void |     public static function recalculateForJournal(TransactionJournal $transactionJournal): void | ||||||
|     { |     { | ||||||
|         Log::debug(__METHOD__); |         Log::debug(__METHOD__); | ||||||
|         $object   = new self(); |         $object = new self(); | ||||||
| 
 | 
 | ||||||
|         // recalculate the involved accounts:
 |         // recalculate the involved accounts:
 | ||||||
|         $accounts = new Collection(); |         $accounts = new Collection(); | ||||||
| @@ -84,18 +79,17 @@ class AccountBalanceCalculator | |||||||
|             return '0'; |             return '0'; | ||||||
|         } |         } | ||||||
|         Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); |         Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); | ||||||
|         $query   = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') |         $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||||
|             ->whereNull('transactions.deleted_at') |                             ->whereNull('transactions.deleted_at') | ||||||
|             ->where('transaction_journals.transaction_currency_id', $currencyId) |                             ->where('transaction_journals.transaction_currency_id', $currencyId) | ||||||
|             ->whereNull('transaction_journals.deleted_at') |                             ->whereNull('transaction_journals.deleted_at') | ||||||
|             // this order is the same as GroupCollector
 |             // this order is the same as GroupCollector
 | ||||||
|             ->orderBy('transaction_journals.date', 'DESC') |                             ->orderBy('transaction_journals.date', 'DESC') | ||||||
|             ->orderBy('transaction_journals.order', 'ASC') |                             ->orderBy('transaction_journals.order', 'ASC') | ||||||
|             ->orderBy('transaction_journals.id', 'DESC') |                             ->orderBy('transaction_journals.id', 'DESC') | ||||||
|             ->orderBy('transaction_journals.description', 'DESC') |                             ->orderBy('transaction_journals.description', 'DESC') | ||||||
|             ->orderBy('transactions.amount', 'DESC') |                             ->orderBy('transactions.amount', 'DESC') | ||||||
|             ->where('transactions.account_id', $accountId) |                             ->where('transactions.account_id', $accountId); | ||||||
|         ; |  | ||||||
|         $notBefore->startOfDay(); |         $notBefore->startOfDay(); | ||||||
|         $query->where('transaction_journals.date', '<', $notBefore); |         $query->where('transaction_journals.date', '<', $notBefore); | ||||||
| 
 | 
 | ||||||
| @@ -108,9 +102,9 @@ class AccountBalanceCalculator | |||||||
| 
 | 
 | ||||||
|     private function getAccountBalanceByAccount(int $account, int $currency): AccountBalance |     private function getAccountBalanceByAccount(int $account, int $currency): AccountBalance | ||||||
|     { |     { | ||||||
|         $query                          = AccountBalance::where('title', 'balance')->where('account_id', $account)->where('transaction_currency_id', $currency); |         $query = AccountBalance::where('title', 'balance')->where('account_id', $account)->where('transaction_currency_id', $currency); | ||||||
| 
 | 
 | ||||||
|         $entry                          = $query->first(); |         $entry = $query->first(); | ||||||
|         if (null !== $entry) { |         if (null !== $entry) { | ||||||
|             // Log::debug(sprintf('Found account balance "balance" for account #%d and currency #%d: %s', $account, $currency, $entry->balance));
 |             // Log::debug(sprintf('Found account balance "balance" for account #%d and currency #%d: %s', $account, $currency, $entry->balance));
 | ||||||
| 
 | 
 | ||||||
| @@ -130,12 +124,6 @@ class AccountBalanceCalculator | |||||||
|     private function optimizedCalculation(Collection $accounts, ?Carbon $notBefore = null): void |     private function optimizedCalculation(Collection $accounts, ?Carbon $notBefore = null): void | ||||||
|     { |     { | ||||||
|         Log::debug('start of optimizedCalculation'); |         Log::debug('start of optimizedCalculation'); | ||||||
|         if (false === config('firefly.feature_flags.running_balance_column')) { |  | ||||||
|             Log::debug('optimizedCalculation is disabled, return.'); |  | ||||||
| 
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if ($accounts->count() > 0) { |         if ($accounts->count() > 0) { | ||||||
|             Log::debug(sprintf('Limited to %d account(s)', $accounts->count())); |             Log::debug(sprintf('Limited to %d account(s)', $accounts->count())); | ||||||
|         } |         } | ||||||
| @@ -143,15 +131,14 @@ class AccountBalanceCalculator | |||||||
|         $balances = []; |         $balances = []; | ||||||
|         $count    = 0; |         $count    = 0; | ||||||
|         $query    = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') |         $query    = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') | ||||||
|             ->whereNull('transactions.deleted_at') |                                ->whereNull('transactions.deleted_at') | ||||||
|             ->whereNull('transaction_journals.deleted_at') |                                ->whereNull('transaction_journals.deleted_at') | ||||||
|             // this order is the same as GroupCollector, but in the exact reverse.
 |             // this order is the same as GroupCollector, but in the exact reverse.
 | ||||||
|             ->orderBy('transaction_journals.date', 'asc') |                                ->orderBy('transaction_journals.date', 'asc') | ||||||
|             ->orderBy('transaction_journals.order', 'desc') |                                ->orderBy('transaction_journals.order', 'desc') | ||||||
|             ->orderBy('transaction_journals.id', 'asc') |                                ->orderBy('transaction_journals.id', 'asc') | ||||||
|             ->orderBy('transaction_journals.description', 'asc') |                                ->orderBy('transaction_journals.description', 'asc') | ||||||
|             ->orderBy('transactions.amount', 'asc') |                                ->orderBy('transactions.amount', 'asc'); | ||||||
|         ; |  | ||||||
|         if ($accounts->count() > 0) { |         if ($accounts->count() > 0) { | ||||||
|             $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); |             $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); | ||||||
|         } |         } | ||||||
| @@ -160,17 +147,20 @@ class AccountBalanceCalculator | |||||||
|             $query->where('transaction_journals.date', '>=', $notBefore); |             $query->where('transaction_journals.date', '>=', $notBefore); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $set      = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); |         $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); | ||||||
|  | 
 | ||||||
|  |         // the balance value is an array.
 | ||||||
|  |         // first entry is the balance, second is the date.
 | ||||||
| 
 | 
 | ||||||
|         /** @var Transaction $entry */ |         /** @var Transaction $entry */ | ||||||
|         foreach ($set as $entry) { |         foreach ($set as $entry) { | ||||||
|             // start with empty array:
 |             // start with empty array:
 | ||||||
|             $balances[$entry->account_id]                                  ??= []; |             $balances[$entry->account_id]                                  ??= []; | ||||||
|             $balances[$entry->account_id][$entry->transaction_currency_id] ??= $this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore); |             $balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null]; | ||||||
| 
 | 
 | ||||||
|             // before and after are easy:
 |             // before and after are easy:
 | ||||||
|             $before                                                        = $balances[$entry->account_id][$entry->transaction_currency_id]; |             $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; | ||||||
|             $after                                                         = bcadd($before, $entry->amount); |             $after  = bcadd($before, $entry->amount); | ||||||
|             if (true === $entry->balance_dirty || $accounts->count() > 0) { |             if (true === $entry->balance_dirty || $accounts->count() > 0) { | ||||||
|                 // update the transaction:
 |                 // update the transaction:
 | ||||||
|                 $entry->balance_before = $before; |                 $entry->balance_before = $before; | ||||||
| @@ -181,19 +171,20 @@ class AccountBalanceCalculator | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // then update the array:
 |             // then update the array:
 | ||||||
|             $balances[$entry->account_id][$entry->transaction_currency_id] = $after; |             $balances[$entry->account_id][$entry->transaction_currency_id] = [$after, $entry->date]; | ||||||
|         } |         } | ||||||
|         Log::debug(sprintf('end of optimizedCalculation, corrected %d balance(s)', $count)); |         Log::debug(sprintf('end of optimizedCalculation, corrected %d balance(s)', $count)); | ||||||
|         // then update all transactions.
 |         // then update all transactions.
 | ||||||
| 
 | 
 | ||||||
|         // ?? something with accounts?
 |         // save all collected balances in their respective account objects.
 | ||||||
|  |         $this->storeAccountBalances($balances); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance |     private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance | ||||||
|     { |     { | ||||||
|         $query                          = AccountBalance::where('title', $title)->where('account_id', $account)->where('transaction_journal_id', $journal)->where('transaction_currency_id', $currency); |         $query = AccountBalance::where('title', $title)->where('account_id', $account)->where('transaction_journal_id', $journal)->where('transaction_currency_id', $currency); | ||||||
| 
 | 
 | ||||||
|         $entry                          = $query->first(); |         $entry = $query->first(); | ||||||
|         if (null !== $entry) { |         if (null !== $entry) { | ||||||
|             return $entry; |             return $entry; | ||||||
|         } |         } | ||||||
| @@ -208,145 +199,180 @@ class AccountBalanceCalculator | |||||||
|         return $entry; |         return $entry; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private function recalculateLatest(?Account $account): void | //    private function recalculateLatest(?Account $account): void
 | ||||||
|  | //    {
 | ||||||
|  | //        $query  = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
 | ||||||
|  | //
 | ||||||
|  | //        if (null !== $account) {
 | ||||||
|  | //            $query->where('transactions.account_id', $account->id);
 | ||||||
|  | //        }
 | ||||||
|  | //        $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
 | ||||||
|  | //
 | ||||||
|  | //        // reset account balances:
 | ||||||
|  | //        $this->resetAccountBalancesByAccount('balance', $account);
 | ||||||
|  | //
 | ||||||
|  | //        /** @var \stdClass $row */
 | ||||||
|  | //        foreach ($result as $row) {
 | ||||||
|  | //            $account             = (int) $row->account_id;
 | ||||||
|  | //            $transactionCurrency = (int) $row->transaction_currency_id;
 | ||||||
|  | //            $foreignCurrency     = (int) $row->foreign_currency_id;
 | ||||||
|  | //            $sumAmount           = (string) $row->sum_amount;
 | ||||||
|  | //            $sumForeignAmount    = (string) $row->sum_foreign_amount;
 | ||||||
|  | //            $sumAmount           = '' === $sumAmount ? '0' : $sumAmount;
 | ||||||
|  | //            $sumForeignAmount    = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
 | ||||||
|  | //
 | ||||||
|  | //            // at this point SQLite may return scientific notation because why not. Terrible.
 | ||||||
|  | //            $sumAmount           = app('steam')->floatalize($sumAmount);
 | ||||||
|  | //            $sumForeignAmount    = app('steam')->floatalize($sumForeignAmount);
 | ||||||
|  | //
 | ||||||
|  | //            // first create for normal currency:
 | ||||||
|  | //            $entry               = $this->getAccountBalanceByAccount($account, $transactionCurrency);
 | ||||||
|  | //
 | ||||||
|  | //            try {
 | ||||||
|  | //                $entry->balance = bcadd((string) $entry->balance, $sumAmount);
 | ||||||
|  | //            } catch (\ValueError $e) {
 | ||||||
|  | //                $message = sprintf('[a] Could not add "%s" to "%s": %s', $entry->balance, $sumAmount, $e->getMessage());
 | ||||||
|  | //                Log::error($message);
 | ||||||
|  | //
 | ||||||
|  | //                throw new FireflyException($message, 0, $e);
 | ||||||
|  | //            }
 | ||||||
|  | //            $entry->save();
 | ||||||
|  | //
 | ||||||
|  | //            // then do foreign amount, if present:
 | ||||||
|  | //            if ($foreignCurrency > 0) {
 | ||||||
|  | //                $entry = $this->getAccountBalanceByAccount($account, $foreignCurrency);
 | ||||||
|  | //
 | ||||||
|  | //                try {
 | ||||||
|  | //                    $entry->balance = bcadd((string) $entry->balance, $sumForeignAmount);
 | ||||||
|  | //                } catch (\ValueError $e) {
 | ||||||
|  | //                    $message = sprintf('[b] Could not add "%s" to "%s": %s', $entry->balance, $sumForeignAmount, $e->getMessage());
 | ||||||
|  | //                    Log::error($message);
 | ||||||
|  | //
 | ||||||
|  | //                    throw new FireflyException($message, 0, $e);
 | ||||||
|  | //                }
 | ||||||
|  | //                $entry->save();
 | ||||||
|  | //            }
 | ||||||
|  | //        }
 | ||||||
|  | //        Log::debug(sprintf('Recalculated %d account balance(s)', $result->count()));
 | ||||||
|  | //    }
 | ||||||
|  | 
 | ||||||
|  | //    private function resetAccountBalancesByAccount(string $title, ?Account $account): void
 | ||||||
|  | //    {
 | ||||||
|  | //        if (null === $account) {
 | ||||||
|  | //            $count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']);
 | ||||||
|  | //            Log::debug(sprintf('Set %d account balance(s) to zero.', $count));
 | ||||||
|  | //
 | ||||||
|  | //            return;
 | ||||||
|  | //        }
 | ||||||
|  | //        $count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']);
 | ||||||
|  | //        Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id));
 | ||||||
|  | //    }
 | ||||||
|  | 
 | ||||||
|  | //    /**
 | ||||||
|  | //     * Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after
 | ||||||
|  | //     * journal". Dat betekent, netjes op volgorde van datum en doorrekenen.
 | ||||||
|  | //     *
 | ||||||
|  | //     * Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen.
 | ||||||
|  | //     * Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment.
 | ||||||
|  | //     *
 | ||||||
|  | //     *  1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal.
 | ||||||
|  | //     *  2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen.
 | ||||||
|  | //     *  3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe.
 | ||||||
|  | //     */
 | ||||||
|  | //    private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void
 | ||||||
|  | //    {
 | ||||||
|  | //        $query   = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
 | ||||||
|  | //        $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id');
 | ||||||
|  | //        $query->orderBy('transaction_journals.date', 'asc');
 | ||||||
|  | //        $amounts = [];
 | ||||||
|  | //        if (null !== $account) {
 | ||||||
|  | //            $query->where('transactions.account_id', $account->id);
 | ||||||
|  | //        }
 | ||||||
|  | //        if (null !== $account && null !== $transactionJournal) {
 | ||||||
|  | //            $query->where('transaction_journals.date', '>=', $transactionJournal->date);
 | ||||||
|  | //            $amounts = $this->getStartAmounts($account, $transactionJournal);
 | ||||||
|  | //        }
 | ||||||
|  | //        $result  = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]);
 | ||||||
|  | //
 | ||||||
|  | //        /** @var \stdClass $row */
 | ||||||
|  | //        foreach ($result as $row) {
 | ||||||
|  | //            $account                                 = (int) $row->account_id;
 | ||||||
|  | //            $transactionCurrency                     = (int) $row->transaction_currency_id;
 | ||||||
|  | //            $foreignCurrency                         = (int) $row->foreign_currency_id;
 | ||||||
|  | //            $sumAmount                               = (string) $row->sum_amount;
 | ||||||
|  | //            $sumForeignAmount                        = (string) $row->sum_foreign_amount;
 | ||||||
|  | //            $journalId                               = (int) $row->id;
 | ||||||
|  | //
 | ||||||
|  | //            // check for empty strings
 | ||||||
|  | //            $sumAmount                               = '' === $sumAmount ? '0' : $sumAmount;
 | ||||||
|  | //            $sumForeignAmount                        = '' === $sumForeignAmount ? '0' : $sumForeignAmount;
 | ||||||
|  | //
 | ||||||
|  | //            // new amounts:
 | ||||||
|  | //            $amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount);
 | ||||||
|  | //            $amounts[$account][$foreignCurrency]     = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount);
 | ||||||
|  | //
 | ||||||
|  | //            // first create for normal currency:
 | ||||||
|  | //            $entry                                   = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency);
 | ||||||
|  | //            $entry->balance                          = $amounts[$account][$transactionCurrency];
 | ||||||
|  | //            $entry->save();
 | ||||||
|  | //
 | ||||||
|  | //            // then do foreign amount, if present:
 | ||||||
|  | //            if ($foreignCurrency > 0) {
 | ||||||
|  | //                $entry          = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency);
 | ||||||
|  | //                $entry->balance = $amounts[$account][$foreignCurrency];
 | ||||||
|  | //                $entry->save();
 | ||||||
|  | //            }
 | ||||||
|  | //        }
 | ||||||
|  | //
 | ||||||
|  | //        // select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
 | ||||||
|  | //        //
 | ||||||
|  | //        // from transactions
 | ||||||
|  | //        //
 | ||||||
|  | //        // left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
 | ||||||
|  | //        //
 | ||||||
|  | //        // group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
 | ||||||
|  | //        // order by transaction_journals.date desc
 | ||||||
|  | //    }
 | ||||||
|  | 
 | ||||||
|  | //    private function getStartAmounts(Account $account, TransactionJournal $journal): array
 | ||||||
|  | //    {
 | ||||||
|  | //        exit('here we are 1');
 | ||||||
|  | //
 | ||||||
|  | //        return [];
 | ||||||
|  | //    }
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     private function storeAccountBalances(array $balances): void | ||||||
|     { |     { | ||||||
|         $query  = Transaction::groupBy(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']); |         /** | ||||||
| 
 |          * @var int   $accountId | ||||||
|         if (null !== $account) { |          * @var array $currencies | ||||||
|             $query->where('transactions.account_id', $account->id); |          */ | ||||||
|         } |         foreach ($balances as $accountId => $currencies) { | ||||||
|         $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]); |             /** @var Account $account */ | ||||||
| 
 |             $account = Account::find($accountId); | ||||||
|         // reset account balances:
 |             if (null === $account) { | ||||||
|         $this->resetAccountBalancesByAccount('balance', $account); |                 Log::error(sprintf('Could not find account #%d, will not save account balance.', $accountId)); | ||||||
| 
 |                 continue; | ||||||
|         /** @var \stdClass $row */ |  | ||||||
|         foreach ($result as $row) { |  | ||||||
|             $account             = (int) $row->account_id; |  | ||||||
|             $transactionCurrency = (int) $row->transaction_currency_id; |  | ||||||
|             $foreignCurrency     = (int) $row->foreign_currency_id; |  | ||||||
|             $sumAmount           = (string) $row->sum_amount; |  | ||||||
|             $sumForeignAmount    = (string) $row->sum_foreign_amount; |  | ||||||
|             $sumAmount           = '' === $sumAmount ? '0' : $sumAmount; |  | ||||||
|             $sumForeignAmount    = '' === $sumForeignAmount ? '0' : $sumForeignAmount; |  | ||||||
| 
 |  | ||||||
|             // at this point SQLite may return scientific notation because why not. Terrible.
 |  | ||||||
|             $sumAmount           = app('steam')->floatalize($sumAmount); |  | ||||||
|             $sumForeignAmount    = app('steam')->floatalize($sumForeignAmount); |  | ||||||
| 
 |  | ||||||
|             // first create for normal currency:
 |  | ||||||
|             $entry               = $this->getAccountBalanceByAccount($account, $transactionCurrency); |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 $entry->balance = bcadd((string) $entry->balance, $sumAmount); |  | ||||||
|             } catch (\ValueError $e) { |  | ||||||
|                 $message = sprintf('[a] Could not add "%s" to "%s": %s', $entry->balance, $sumAmount, $e->getMessage()); |  | ||||||
|                 Log::error($message); |  | ||||||
| 
 |  | ||||||
|                 throw new FireflyException($message, 0, $e); |  | ||||||
|             } |             } | ||||||
|             $entry->save(); |  | ||||||
| 
 | 
 | ||||||
|             // then do foreign amount, if present:
 |             /** | ||||||
|             if ($foreignCurrency > 0) { |              * @var int   $currencyId | ||||||
|                 $entry = $this->getAccountBalanceByAccount($account, $foreignCurrency); |              * @var array $balance | ||||||
| 
 |              */ | ||||||
|                 try { |             foreach ($currencies as $currencyId => $balance) { | ||||||
|                     $entry->balance = bcadd((string) $entry->balance, $sumForeignAmount); |                 /** @var TransactionCurrency $currency */ | ||||||
|                 } catch (\ValueError $e) { |                 $currency = TransactionCurrency::find($currencyId); | ||||||
|                     $message = sprintf('[b] Could not add "%s" to "%s": %s', $entry->balance, $sumForeignAmount, $e->getMessage()); |                 if (null === $currency) { | ||||||
|                     Log::error($message); |                     Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId)); | ||||||
| 
 |                     continue; | ||||||
|                     throw new FireflyException($message, 0, $e); |  | ||||||
|                 } |                 } | ||||||
|                 $entry->save(); |                 /** @var AccountBalance $object */ | ||||||
|  |                 $object          = $account->accountBalances()->firstOrCreate(['title' => 'running_balance', 'balance' => '0', 'transaction_currency_id' => $currencyId, 'date' => $balance[1]]); | ||||||
|  |                 $object->balance = $balance[0]; | ||||||
|  |                 $object->date    = $balance[1]; | ||||||
|  |                 $object->save(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         Log::debug(sprintf('Recalculated %d account balance(s)', $result->count())); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private function resetAccountBalancesByAccount(string $title, ?Account $account): void |  | ||||||
|     { |  | ||||||
|         if (null === $account) { |  | ||||||
|             $count = AccountBalance::whereNotNull('updated_at')->where('title', $title)->update(['balance' => '0']); |  | ||||||
|             Log::debug(sprintf('Set %d account balance(s) to zero.', $count)); |  | ||||||
| 
 |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         $count = AccountBalance::where('account_id', $account->id)->where('title', $title)->update(['balance' => '0']); |  | ||||||
|         Log::debug(sprintf('Set %d account balance(s) of account #%d to zero.', $count, $account->id)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * Als je alles opnieuw doet, verzamel je alle transactions en het bedrag en zet je dat neer als "balance after
 |  | ||||||
|      * journal". Dat betekent, netjes op volgorde van datum en doorrekenen.
 |  | ||||||
|      * |  | ||||||
|      * Zodra je een transaction journal verplaatst (datum) moet je dat journal en alle latere journals opnieuw doen. |  | ||||||
|      * Maar dan moet je van de account wel een beginnetje hebben, namelijk de balance tot en met dat moment. |  | ||||||
|      * |  | ||||||
|      *  1. Dus dan search je eerst naar die SUM, som alle transactions eerder dan (niet inclusief) de journal. |  | ||||||
|      *  2. En vanaf daar pak je alle journals op of na de journal (dus ook de journal zelf) en begin je door te tellen. |  | ||||||
|      *  3. Elke voorbij gaande journal entry "balance_after_journal" geef je een update of voeg je toe. |  | ||||||
|      */ |  | ||||||
|     private function recalculateJournals(?Account $account, ?TransactionJournal $transactionJournal): void |  | ||||||
|     { |  | ||||||
|         $query   = Transaction::groupBy(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']); |  | ||||||
|         $query->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id'); |  | ||||||
|         $query->orderBy('transaction_journals.date', 'asc'); |  | ||||||
|         $amounts = []; |  | ||||||
|         if (null !== $account) { |  | ||||||
|             $query->where('transactions.account_id', $account->id); |  | ||||||
|         } |  | ||||||
|         if (null !== $account && null !== $transactionJournal) { |  | ||||||
|             $query->where('transaction_journals.date', '>=', $transactionJournal->date); |  | ||||||
|             $amounts = $this->getStartAmounts($account, $transactionJournal); |  | ||||||
|         } |  | ||||||
|         $result  = $query->get(['transactions.account_id', 'transaction_journals.id', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id', \DB::raw('SUM(transactions.amount) as sum_amount'), \DB::raw('SUM(transactions.foreign_amount) as sum_foreign_amount')]); |  | ||||||
| 
 |  | ||||||
|         /** @var \stdClass $row */ |  | ||||||
|         foreach ($result as $row) { |  | ||||||
|             $account                                 = (int) $row->account_id; |  | ||||||
|             $transactionCurrency                     = (int) $row->transaction_currency_id; |  | ||||||
|             $foreignCurrency                         = (int) $row->foreign_currency_id; |  | ||||||
|             $sumAmount                               = (string) $row->sum_amount; |  | ||||||
|             $sumForeignAmount                        = (string) $row->sum_foreign_amount; |  | ||||||
|             $journalId                               = (int) $row->id; |  | ||||||
| 
 |  | ||||||
|             // check for empty strings
 |  | ||||||
|             $sumAmount                               = '' === $sumAmount ? '0' : $sumAmount; |  | ||||||
|             $sumForeignAmount                        = '' === $sumForeignAmount ? '0' : $sumForeignAmount; |  | ||||||
| 
 |  | ||||||
|             // new amounts:
 |  | ||||||
|             $amounts[$account][$transactionCurrency] = bcadd($amounts[$account][$transactionCurrency] ?? '0', $sumAmount); |  | ||||||
|             $amounts[$account][$foreignCurrency]     = bcadd($amounts[$account][$foreignCurrency] ?? '0', $sumForeignAmount); |  | ||||||
| 
 |  | ||||||
|             // first create for normal currency:
 |  | ||||||
|             $entry                                   = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $transactionCurrency); |  | ||||||
|             $entry->balance                          = $amounts[$account][$transactionCurrency]; |  | ||||||
|             $entry->save(); |  | ||||||
| 
 |  | ||||||
|             // then do foreign amount, if present:
 |  | ||||||
|             if ($foreignCurrency > 0) { |  | ||||||
|                 $entry          = self::getAccountBalanceByJournal('balance_after_journal', $account, $journalId, $foreignCurrency); |  | ||||||
|                 $entry->balance = $amounts[$account][$foreignCurrency]; |  | ||||||
|                 $entry->save(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // select transactions.account_id, transaction_journals.id, transactions.transaction_currency_id, transactions.foreign_currency_id, sum(transactions.amount), sum(transactions.foreign_amount)
 |  | ||||||
|         //
 |  | ||||||
|         // from transactions
 |  | ||||||
|         //
 |  | ||||||
|         // left join transaction_journals ON transaction_journals.id = transactions.transaction_journal_id
 |  | ||||||
|         //
 |  | ||||||
|         // group by account_id, transaction_journals.id, transaction_currency_id, foreign_currency_id
 |  | ||||||
|         // order by transaction_journals.date desc
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private function getStartAmounts(Account $account, TransactionJournal $journal): array |  | ||||||
|     { |  | ||||||
|         exit('here we are 1'); |  | ||||||
| 
 |  | ||||||
|         return []; |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -847,7 +847,8 @@ class FireflyValidator extends Validator | |||||||
|                 ->where('trigger', $trigger) |                 ->where('trigger', $trigger) | ||||||
|                 ->where('response', $response) |                 ->where('response', $response) | ||||||
|                 ->where('delivery', $delivery) |                 ->where('delivery', $delivery) | ||||||
|                 ->where('url', $url)->count(); |                 ->where('url', $url)->count() | ||||||
|  |             ; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user