2015-02-07 08:23:44 +01:00
< ? php
2022-12-24 05:06:39 +01:00
2016-05-20 12:41:23 +02:00
/**
* Steam . php
2020-02-16 13:56:52 +01:00
* Copyright ( c ) 2019 james @ firefly - iii . org
2016-05-20 12:41:23 +02:00
*
2019-10-02 06:37:26 +02:00
* This file is part of Firefly III ( https :// github . com / firefly - iii ) .
2016-10-05 06:52:15 +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 .
2017-10-21 08:40:00 +02:00
*
2019-10-02 06:37:26 +02:00
* This program is distributed in the hope that it will be useful ,
2017-10-21 08:40:00 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2019-10-02 06:37:26 +02:00
* GNU Affero General Public License for more details .
2017-10-21 08:40:00 +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 />.
2016-05-20 12:41:23 +02:00
*/
2017-04-09 07:44:22 +02:00
declare ( strict_types = 1 );
2015-02-07 08:23:44 +01:00
namespace FireflyIII\Support ;
use Carbon\Carbon ;
2021-09-18 10:20:19 +02:00
use FireflyIII\Exceptions\FireflyException ;
2015-02-07 08:23:44 +01:00
use FireflyIII\Models\Account ;
2015-07-10 07:39:59 +02:00
use FireflyIII\Models\Transaction ;
2020-03-20 04:37:45 +01:00
use FireflyIII\Models\TransactionCurrency ;
2017-06-05 22:11:54 +02:00
use Illuminate\Support\Collection ;
2024-12-26 09:16:17 +01:00
use Illuminate\Support\Facades\DB ;
2023-11-08 20:06:23 +01:00
use Illuminate\Support\Facades\Log ;
2024-12-28 10:58:01 +01:00
use FireflyIII\Support\Facades\Amount ;
2015-02-07 08:23:44 +01:00
/**
2017-11-15 12:25:49 +01:00
* Class Steam .
2015-02-07 08:23:44 +01:00
*/
class Steam
{
2024-12-22 20:32:58 +01:00
public function getAccountCurrency ( Account $account ) : ? TransactionCurrency
2024-12-22 08:43:12 +01:00
{
2024-12-26 05:25:46 +01:00
$type = $account -> accountType -> type ;
$list = config ( 'firefly.valid_currency_account_types' );
2024-12-22 08:43:12 +01:00
2024-12-22 20:32:58 +01:00
// return null if not in this list.
if ( ! in_array ( $type , $list , true )) {
return null ;
2024-12-22 08:43:12 +01:00
}
2024-12-22 20:32:58 +01:00
$result = $account -> accountMeta -> where ( 'name' , 'currency_id' ) -> first ();
if ( null === $result ) {
return null ;
}
2024-12-22 20:37:54 +01:00
2024-12-22 20:32:58 +01:00
return TransactionCurrency :: find (( int ) $result -> data );
2024-12-22 19:42:06 +01:00
}
2024-12-22 16:41:55 +01:00
2024-12-26 05:11:32 +01:00
public function finalAccountBalanceInRange ( Account $account , Carbon $start , Carbon $end , bool $convertToNative ) : array
2024-12-22 16:41:55 +01:00
{
2024-12-22 20:32:58 +01:00
// expand period.
$start -> subDay () -> startOfDay ();
$end -> addDay () -> endOfDay ();
2024-12-26 05:11:32 +01:00
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' )));
2024-12-22 16:41:55 +01:00
2024-12-22 20:32:58 +01:00
// set up cache
2024-12-26 05:25:46 +01:00
$cache = new CacheProperties ();
2024-12-22 16:41:55 +01:00
$cache -> addProperty ( $account -> id );
2024-12-22 20:32:58 +01:00
$cache -> addProperty ( 'final-balance-in-range' );
2024-12-22 16:41:55 +01:00
$cache -> addProperty ( $start );
$cache -> addProperty ( $end );
if ( $cache -> has ()) {
2024-12-27 22:03:16 +01:00
return $cache -> get ();
2024-12-22 19:42:06 +01:00
}
2024-12-26 05:25:46 +01:00
$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 ;
2024-12-26 05:11:32 +01:00
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' ;
}
2024-12-25 07:13:41 +01:00
$currencies = [
2024-12-22 20:32:58 +01:00
$currency -> id => $currency ,
$defaultCurrency -> id => $defaultCurrency ,
];
2024-12-22 19:42:06 +01:00
2024-12-26 05:11:32 +01:00
$startBalance [ $currency -> code ] ? ? = '0' ;
2024-12-26 05:25:46 +01:00
$balances [ $formatted ] = $startBalance ;
2024-12-26 05:11:32 +01:00
Log :: debug ( 'Final start balance: ' , $startBalance );
2024-12-25 07:13:41 +01:00
2024-12-26 05:11:32 +01:00
// sums up the balance changes per day, for foreign, native and normal amounts.
2024-12-26 10:24:39 +01:00
$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' ),
]
)
;
2024-12-26 05:11:32 +01:00
2024-12-26 05:25:46 +01:00
$currentBalance = $startBalance ;
2024-12-22 19:42:06 +01:00
/** @var Transaction $entry */
foreach ( $set as $entry ) {
2024-12-22 20:32:58 +01:00
// normal, native and foreign amount
2024-12-26 05:25:46 +01:00
$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 );
2024-12-26 05:11:32 +01:00
// find currency of this entry.
2024-12-26 05:25:46 +01:00
$currencies [ $entry -> transaction_currency_id ] ? ? = TransactionCurrency :: find ( $entry -> transaction_currency_id );
2025-01-05 07:31:26 +01:00
2025-01-04 19:25:43 +01:00
/** @var TransactionCurrency $entryCurrency */
2024-12-26 05:25:46 +01:00
$entryCurrency = $currencies [ $entry -> transaction_currency_id ];
2024-12-26 05:11:32 +01:00
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.
2024-12-26 10:24:39 +01:00
if ( $convertToNative ) {
Log :: debug ( sprintf ( 'Amount is %s %s, foreign amount is %s, native amount is %s' , $entryCurrency -> code , $this -> bcround ( $modified , 2 ), $this -> bcround ( $foreignModified , 2 ), $this -> bcround ( $nativeModified , 2 )));
2024-12-26 08:53:16 +01:00
// if the currency is the default currency add to native balance + currency balance
2024-12-26 10:24:39 +01:00
if ( $entry -> transaction_currency_id === $defaultCurrency -> id ) {
2024-12-26 08:53:16 +01:00
Log :: debug ( 'Add amount to native.' );
2024-12-26 10:24:39 +01:00
$currentBalance [ 'native_balance' ] = bcadd ( $currentBalance [ 'native_balance' ], $modified );
2024-12-26 08:53:16 +01:00
}
2024-12-26 05:11:32 +01:00
// add to native balance.
2024-12-26 10:24:39 +01:00
if ( $entry -> foreign_currency_id !== $defaultCurrency -> id ) {
2024-12-26 08:53:16 +01:00
// this check is not necessary, because if the foreign currency is the same as the default currency, the native amount is zero.
// so adding this would mean nothing.
2024-12-26 10:24:39 +01:00
$currentBalance [ 'native_balance' ] = bcadd ( $currentBalance [ 'native_balance' ], $nativeModified );
2024-12-26 08:53:16 +01:00
}
2024-12-26 10:24:39 +01:00
if ( $entry -> foreign_currency_id === $defaultCurrency -> id ) {
$currentBalance [ 'native_balance' ] = bcadd ( $currentBalance [ 'native_balance' ], $foreignModified );
2024-12-26 05:11:32 +01:00
}
// add to balance if is the same.
2024-12-26 05:25:46 +01:00
if ( $entry -> transaction_currency_id === $accountCurrency ? -> id ) {
$currentBalance [ 'balance' ] = bcadd ( $currentBalance [ 'balance' ], $modified );
2024-12-26 05:11:32 +01:00
}
// add currency balance
2024-12-26 08:53:16 +01:00
$currentBalance [ $entryCurrency -> code ] = bcadd ( $currentBalance [ $entryCurrency -> code ] ? ? '0' , $modified );
2024-12-22 19:42:06 +01:00
}
2024-12-26 05:25:46 +01:00
if ( ! $convertToNative ) {
2024-12-26 05:11:32 +01:00
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.
2024-12-26 05:25:46 +01:00
$currentBalance [ 'balance' ] = bcadd ( $currentBalance [ 'balance' ] ? ? '0' , $modified );
2024-12-26 05:11:32 +01:00
// add to GBP, as expected.
2024-12-26 08:53:16 +01:00
$currentBalance [ $entryCurrency -> code ] = bcadd ( $currentBalance [ $entryCurrency -> code ] ? ? '0' , $modified );
2024-12-22 19:42:06 +01:00
}
2024-12-22 20:32:58 +01:00
$balances [ $carbon -> format ( 'Y-m-d' )] = $currentBalance ;
2024-12-26 05:25:46 +01:00
Log :: debug ( 'Updated entry' , $currentBalance );
2024-12-22 19:42:06 +01:00
}
$cache -> store ( $balances );
2024-12-26 08:53:16 +01:00
Log :: debug ( 'End of method' );
2024-12-22 19:42:06 +01:00
return $balances ;
}
2024-12-22 20:37:54 +01:00
public function finalAccountsBalance ( Collection $accounts , Carbon $date ) : array
{
2024-12-22 20:32:58 +01:00
$balances = [];
2017-06-05 22:11:54 +02:00
foreach ( $accounts as $account ) {
2024-12-22 20:32:58 +01:00
$balances [ $account -> id ] = $this -> finalAccountBalance ( $account , $date );
2015-07-10 07:39:59 +02:00
}
2024-12-22 20:37:54 +01:00
2024-12-22 20:32:58 +01:00
return $balances ;
2023-06-21 12:34:58 +02:00
}
2022-12-29 19:42:26 +01:00
/**
* https :// stackoverflow . com / questions / 1642614 / how - to - ceil - floor - and - round - bcmath - numbers
*/
public function bcround ( ? string $number , int $precision = 0 ) : string
{
if ( null === $number ) {
return '0' ;
}
if ( '' === trim ( $number )) {
return '0' ;
}
// if the number contains "E", it's in scientific notation, so we need to convert it to a normal number first.
if ( false !== stripos ( $number , 'e' )) {
2023-05-15 06:33:30 +02:00
$number = sprintf ( '%.12f' , $number );
2022-12-29 19:42:26 +01:00
}
2023-12-29 09:01:42 +01:00
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
2022-12-29 19:42:26 +01:00
if ( str_contains ( $number , '.' )) {
2023-12-20 19:35:52 +01:00
if ( '-' !== $number [ 0 ]) {
2024-12-26 05:25:46 +01:00
return bcadd ( $number , '0.' . str_repeat ( '0' , $precision ) . '5' , $precision );
2022-12-29 19:42:26 +01:00
}
2024-12-26 05:25:46 +01:00
return bcsub ( $number , '0.' . str_repeat ( '0' , $precision ) . '5' , $precision );
2022-12-29 19:42:26 +01:00
}
return $number ;
}
2022-03-29 14:59:58 +02:00
public function filterSpaces ( string $string ) : string
{
$search = [
" \ u { 0001} " , // start of heading
" \ u { 0002} " , // start of text
" \ u { 0003} " , // end of text
" \ u { 0004} " , // end of transmission
" \ u { 0005} " , // enquiry
" \ u { 0006} " , // ACK
" \ u { 0007} " , // BEL
" \ u { 0008} " , // backspace
" \ u { 000E} " , // shift out
" \ u { 000F} " , // shift in
" \ u { 0010} " , // data link escape
" \ u { 0011} " , // DC1
" \ u { 0012} " , // DC2
" \ u { 0013} " , // DC3
" \ u { 0014} " , // DC4
" \ u { 0015} " , // NAK
" \ u { 0016} " , // SYN
" \ u { 0017} " , // ETB
" \ u { 0018} " , // CAN
" \ u { 0019} " , // EM
" \ u { 001A} " , // SUB
" \ u { 001B} " , // escape
" \ u { 001C} " , // file separator
" \ u { 001D} " , // group separator
" \ u { 001E} " , // record separator
" \ u { 001F} " , // unit separator
" \ u { 007F} " , // DEL
" \ u { 00A0} " , // non-breaking space
" \ u { 1680} " , // ogham space mark
" \ u { 180E} " , // mongolian vowel separator
" \ u { 2000} " , // en quad
" \ u { 2001} " , // em quad
" \ u { 2002} " , // en space
" \ u { 2003} " , // em space
" \ u { 2004} " , // three-per-em space
" \ u { 2005} " , // four-per-em space
" \ u { 2006} " , // six-per-em space
" \ u { 2007} " , // figure space
" \ u { 2008} " , // punctuation space
" \ u { 2009} " , // thin space
" \ u { 200A} " , // hair space
" \ u { 200B} " , // zero width space
" \ u { 202F} " , // narrow no-break space
" \ u { 3000} " , // ideographic space
" \ u { FEFF} " , // zero width no -break space
2024-05-18 06:42:09 +02:00
" \x20 " , // plain old normal space,
2024-05-18 06:49:29 +02:00
' ' ,
2022-03-29 14:59:58 +02:00
];
2022-09-24 17:43:49 +02:00
// clear zalgo text
2023-03-08 20:40:51 +01:00
$string = preg_replace ( '/(\pM{2})\pM+/u' , '\1' , $string );
2024-05-18 06:42:09 +02:00
$string = preg_replace ( '/\s+/' , '' , $string );
2022-09-24 17:43:49 +02:00
2022-03-29 14:59:58 +02:00
return str_replace ( $search , '' , $string );
}
2024-12-22 19:42:06 +01:00
/**
* Returns the balance of an account at exact moment given . Array with at least one value .
*
* " balance " the balance in whatever currency the account has , so the sum of all transaction that happen to have
* THAT currency .
* " native_balance " the balance according to the " native_amount " + " native_foreign_amount " fields .
* " ABC " the balance in this particular currency code ( may repeat for each found currency ) .
2024-12-24 16:56:31 +01:00
*
* Het maakt niet uit of de native currency wel of niet gelijk is aan de account currency .
* Optelsom zou hetzelfde moeten zijn . Als het EUR is en de rekening ook is native_amount 0.
* Zo niet is amount 0 en native_amount het bedrag .
*
* Eerst een som van alle transacties in de native currency . Alle EUR bij elkaar opgeteld .
* Om te weten wat er nog meer op de rekening gebeurt , pak alles waar currency niet EUR is , en de foreign ook niet ,
* en tel native_amount erbij op .
* Daarna pak je alle transacties waar currency niet EUR is , en de foreign wel , en tel foreign_amount erbij op .
*
* Wil je niks weten van native currencies , pak je :
*
* Eerst een som van alle transacties gegroepeerd op currency . Einde .
2024-12-22 19:42:06 +01:00
*/
public function finalAccountBalance ( Account $account , Carbon $date ) : array
{
2025-01-01 16:38:58 +01:00
$cache = new CacheProperties ();
2024-12-31 08:17:35 +01:00
$cache -> addProperty ( $account -> id );
$cache -> addProperty ( $date );
if ( $cache -> has ()) {
return $cache -> get ();
}
2024-12-24 16:56:31 +01:00
Log :: debug ( sprintf ( 'Now in finalAccountBalance(#%d, "%s", "%s")' , $account -> id , $account -> name , $date -> format ( 'Y-m-d H:i:s' )));
2024-12-31 08:17:35 +01:00
2024-12-28 10:58:01 +01:00
$native = Amount :: getDefaultCurrencyByUserGroup ( $account -> user -> userGroup );
$convertToNative = Amount :: convertToNative ( $account -> user );
2024-12-24 10:29:07 +01:00
$accountCurrency = $this -> getAccountCurrency ( $account );
$hasCurrency = null !== $accountCurrency ;
2024-12-24 16:56:31 +01:00
$currency = $hasCurrency ? $accountCurrency : $native ;
2024-12-25 07:13:41 +01:00
$return = [];
2024-12-24 16:56:31 +01:00
2024-12-22 19:42:06 +01:00
// first, the "balance", as described earlier.
2024-12-24 16:56:31 +01:00
if ( $convertToNative ) {
// normal balance
2024-12-26 05:25:46 +01:00
$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' )
;
2024-12-24 16:56:31 +01:00
// plus virtual balance, if the account has a virtual_balance in the native currency
2024-12-25 07:13:41 +01:00
if ( $native -> id === $accountCurrency ? -> id ) {
2024-12-24 16:56:31 +01:00
$return [ 'balance' ] = bcadd ( '' === ( string ) $account -> virtual_balance ? '0' : $account -> virtual_balance , $return [ 'balance' ]);
}
2024-12-31 08:09:51 +01:00
// Log::debug(sprintf('balance is (%s only) %s (with virtual balance)', $native->code, $this->bcround($return['balance'], 2)));
2024-12-24 16:56:31 +01:00
// native balance
$return [ 'native_balance' ] = ( string ) $account -> transactions ()
2024-12-26 05:25:46 +01:00
-> 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' )
;
2024-12-24 16:56:31 +01:00
// plus native virtual balance.
2024-12-24 06:34:12 +01:00
$return [ 'native_balance' ] = bcadd ( '' === ( string ) $account -> native_virtual_balance ? '0' : $account -> native_virtual_balance , $return [ 'native_balance' ]);
2024-12-31 08:09:51 +01:00
// Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $this->bcround($return['native_balance'])));
2024-12-24 16:56:31 +01:00
// plus foreign transactions in THIS currency.
$sum = ( string ) $account -> transactions ()
2024-12-26 05:25:46 +01:00
-> 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' )
;
2024-12-24 16:56:31 +01:00
$return [ 'native_balance' ] = bcadd ( $return [ 'native_balance' ], $sum );
2024-12-31 08:05:25 +01:00
// Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $this->bcround($sum), $this->bcround($return['native_balance'])));
2024-12-22 19:42:06 +01:00
}
2024-12-24 16:56:31 +01:00
// balance(s) in other (all) currencies.
2024-12-26 05:25:46 +01:00
$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' );
2024-12-31 08:05:25 +01:00
// Log::debug('All balances are (joined)', $others);
2024-12-24 10:29:07 +01:00
// if the account has no own currency preference, drop balance in favor of native balance
2024-12-24 16:56:31 +01:00
if ( $hasCurrency && ! $convertToNative ) {
2024-12-25 07:13:41 +01:00
$return [ 'balance' ] = $others [ $currency -> code ] ? ? '0' ;
2024-12-24 19:03:47 +01:00
$return [ 'native_balance' ] = $others [ $currency -> code ] ? ? '0' ;
2024-12-31 08:05:25 +01:00
// Log::debug(sprintf('Set balance + native_balance to %s', $return['balance']));
2024-12-24 16:56:31 +01:00
}
2024-12-24 19:03:47 +01:00
// if the currency is the same as the native currency, set the native_balance to the balance for consistency.
2024-12-25 07:13:41 +01:00
// if($currency->id === $native->id) {
// $return['native_balance'] = $return['balance'];
// }
2024-12-24 19:03:47 +01:00
2025-01-04 09:01:44 +01:00
if ( ! $hasCurrency && array_key_exists ( 'balance' , $return )) {
2024-12-31 08:09:51 +01:00
// Log::debug('Account has no currency preference, dropping balance in favor of native balance.');
2024-12-26 05:25:46 +01:00
$sum = bcadd ( $return [ 'balance' ], $return [ 'native_balance' ]);
2024-12-31 08:09:51 +01:00
// Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum));
2024-12-24 16:56:31 +01:00
$return [ 'native_balance' ] = $sum ;
2024-12-24 10:29:07 +01:00
unset ( $return [ 'balance' ]);
}
2024-12-26 05:25:46 +01:00
$final = array_merge ( $return , $others );
2024-12-31 08:05:25 +01:00
// Log::debug('Return is', $final);
2024-12-31 08:17:35 +01:00
$cache -> store ( $final );
2024-12-26 05:25:46 +01:00
2024-12-31 08:09:51 +01:00
return array_merge ( $return , $others );
// Log::debug('Return is', $final);
2024-12-22 19:42:06 +01:00
}
2024-12-26 05:11:32 +01:00
public function filterAccountBalances ( array $total , Account $account , bool $convertToNative , ? TransactionCurrency $currency = null ) : array
{
Log :: debug ( sprintf ( 'filterAccountBalances(#%d)' , $account -> id ));
2024-12-25 11:59:15 +01:00
$return = [];
2024-12-26 05:11:32 +01:00
foreach ( $total as $key => $value ) {
2024-12-25 11:59:15 +01:00
$return [ $key ] = $this -> filterAccountBalance ( $value , $account , $convertToNative , $currency );
}
2024-12-26 05:11:32 +01:00
Log :: debug ( sprintf ( 'end of filterAccountBalances(#%d)' , $account -> id ));
2024-12-26 05:25:46 +01:00
2024-12-25 11:59:15 +01:00
return $return ;
}
2024-12-26 05:11:32 +01:00
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 )) {
2024-12-25 11:59:15 +01:00
Log :: debug ( sprintf ( 'Return empty array for account #%d' , $account -> id ));
2024-12-26 05:25:46 +01:00
2024-12-25 11:59:15 +01:00
return [];
}
$defaultCurrency = app ( 'amount' ) -> getDefaultCurrency ();
2024-12-26 05:11:32 +01:00
if ( $convertToNative ) {
2024-12-25 11:59:15 +01:00
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 ]);
}
if ( null !== $currency && $defaultCurrency -> id !== $currency -> id ) {
Log :: debug ( sprintf ( 'Unset balance for account #%d' , $account -> id ));
unset ( $set [ 'balance' ]);
}
if ( null === $currency ) {
Log :: debug ( sprintf ( 'TEMP DO NOT Drop defaultCurrency balance for account #%d' , $account -> id ));
2024-12-26 05:25:46 +01:00
// unset($set[$this->defaultCurrency->code]);
2024-12-25 11:59:15 +01:00
}
}
2024-12-26 05:11:32 +01:00
if ( ! $convertToNative ) {
2024-12-25 11:59:15 +01:00
if ( null === $currency ) {
Log :: debug ( sprintf ( 'Unset native_balance and make defaultCurrency balance the balance for account #%d' , $account -> id ));
2024-12-26 05:11:32 +01:00
$set [ 'balance' ] = $set [ $defaultCurrency -> code ] ? ? '0' ;
2024-12-25 11:59:15 +01:00
unset ( $set [ 'native_balance' ], $set [ $defaultCurrency -> code ]);
}
if ( null !== $currency ) {
Log :: debug ( sprintf ( 'Unset native_balance + defaultCurrency + currencyCode balance for account #%d' , $account -> id ));
unset ( $set [ 'native_balance' ], $set [ $defaultCurrency -> code ], $set [ $currency -> code ]);
}
}
// put specific value first in array.
if ( array_key_exists ( 'native_balance' , $set )) {
$set = [ 'native_balance' => $set [ 'native_balance' ]] + $set ;
}
if ( array_key_exists ( 'balance' , $set )) {
$set = [ 'balance' => $set [ 'balance' ]] + $set ;
}
Log :: debug ( sprintf ( 'Return #%d' , $account -> id ), $set );
return $set ;
}
2024-12-22 19:42:06 +01:00
private function groupAndSumTransactions ( array $array , string $group , string $field ) : array
{
$return = [];
foreach ( $array as $item ) {
$groupKey = $item [ $group ] ? ? 'unknown' ;
$return [ $groupKey ] = bcadd ( $return [ $groupKey ] ? ? '0' , $item [ $field ]);
}
2024-12-22 20:37:54 +01:00
2024-12-22 19:42:06 +01:00
return $return ;
}
2023-05-29 13:56:55 +02:00
/**
2023-02-22 18:14:14 +01:00
* @ throws FireflyException
*/
public function getHostName ( string $ipAddress ) : string
{
2024-12-22 06:33:37 +01:00
$host = '' ;
2023-02-22 18:14:14 +01:00
try {
$hostName = gethostbyaddr ( $ipAddress );
2024-12-22 20:37:54 +01:00
} catch ( \Exception $e ) {
2024-12-22 06:33:37 +01:00
app ( 'log' ) -> error ( $e -> getMessage ());
$hostName = $ipAddress ;
}
if ( '' !== ( string ) $hostName && $hostName !== $ipAddress ) {
$host = $hostName ;
2023-02-22 18:14:14 +01:00
}
2023-12-20 19:35:52 +01:00
2024-12-22 06:33:37 +01:00
return ( string ) $host ;
2023-02-22 18:14:14 +01:00
}
2016-04-05 22:00:03 +02:00
public function getLastActivities ( array $accounts ) : array
2016-01-20 15:23:36 +01:00
{
$list = [];
2024-12-26 05:25:46 +01:00
$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
2023-12-20 19:35:52 +01:00
;
2016-01-20 15:23:36 +01:00
2023-11-05 19:41:37 +01:00
/** @var Transaction $entry */
2016-01-20 15:23:36 +01:00
foreach ( $set as $entry ) {
2024-12-26 05:25:46 +01:00
$date = new Carbon ( $entry -> max_date , config ( 'app.timezone' ));
2020-07-17 18:52:34 +02:00
$date -> setTimezone ( config ( 'app.timezone' ));
2024-12-24 10:29:07 +01:00
$list [( int ) $entry -> account_id ] = $date ;
2016-01-20 15:23:36 +01:00
}
return $list ;
}
2021-09-18 10:26:12 +02:00
/**
* Get user ' s locale .
*/
public function getLocale () : string // get preference
{
$locale = app ( 'preferences' ) -> get ( 'locale' , config ( 'firefly.default_locale' , 'equal' )) -> data ;
2023-11-28 05:31:26 +01:00
if ( is_array ( $locale )) {
$locale = 'equal' ;
}
2021-09-18 10:26:12 +02:00
if ( 'equal' === $locale ) {
$locale = $this -> getLanguage ();
}
2024-07-31 13:09:55 +02:00
$locale = ( string ) $locale ;
2023-11-28 05:31:26 +01:00
2021-09-18 10:26:12 +02:00
// Check for Windows to replace the locale correctly.
2023-12-20 19:35:52 +01:00
if ( 'WIN' === strtoupper ( substr ( PHP_OS , 0 , 3 ))) {
2021-09-18 10:26:12 +02:00
$locale = str_replace ( '_' , '-' , $locale );
}
return $locale ;
}
/**
2023-06-21 12:34:58 +02:00
* Get user ' s language .
*
* @ throws FireflyException
*/
public function getLanguage () : string // get preference
{
$preference = app ( 'preferences' ) -> get ( 'language' , config ( 'firefly.default_language' , 'en_US' )) -> data ;
if ( ! is_string ( $preference )) {
throw new FireflyException ( sprintf ( 'Preference "language" must be a string, but is unexpectedly a "%s".' , gettype ( $preference )));
}
2023-12-20 19:35:52 +01:00
2023-10-14 07:04:07 +02:00
return str_replace ( '-' , '_' , $preference );
2023-06-21 12:34:58 +02:00
}
2021-09-18 10:26:12 +02:00
public function getLocaleArray ( string $locale ) : array
{
return [
sprintf ( '%s.utf8' , $locale ),
sprintf ( '%s.UTF-8' , $locale ),
];
}
2022-03-29 14:59:58 +02:00
/**
* Returns the previous URL but refuses to send you to specific URLs .
*
* - outside domain
* - to JS files , API or JSON routes
*
* Uses the session ' s previousUrl () function as inspired by GitHub user @ z1r0 -
*
* session () -> previousUrl () uses getSafeUrl () so we can safely return it :
*/
public function getSafePreviousUrl () : string
{
2023-12-29 09:01:42 +01:00
// Log::debug(sprintf('getSafePreviousUrl: "%s"', session()->previousUrl()));
2022-03-29 14:59:58 +02:00
return session () -> previousUrl () ? ? route ( 'index' );
}
/**
* Make sure URL is safe .
*/
public function getSafeUrl ( string $unknownUrl , string $safeUrl ) : string
{
2023-12-29 09:01:42 +01:00
// Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
2024-12-26 05:25:46 +01:00
$returnUrl = $safeUrl ;
$unknownHost = parse_url ( $unknownUrl , PHP_URL_HOST );
$safeHost = parse_url ( $safeUrl , PHP_URL_HOST );
2022-03-29 14:59:58 +02:00
if ( null !== $unknownHost && $unknownHost === $safeHost ) {
$returnUrl = $unknownUrl ;
}
// URL must not lead to weird pages
$forbiddenWords = [ 'jscript' , 'json' , 'debug' , 'serviceworker' , 'offline' , 'delete' , '/login' , '/attachments/view' ];
2024-12-22 20:37:54 +01:00
if ( \Str :: contains ( $returnUrl , $forbiddenWords )) {
2022-03-29 14:59:58 +02:00
$returnUrl = $safeUrl ;
}
return $returnUrl ;
}
2017-06-17 22:49:44 +02:00
public function negative ( string $amount ) : string
{
2021-04-05 06:14:13 +02:00
if ( '' === $amount ) {
return '0' ;
}
2022-03-26 18:13:02 +01:00
$amount = $this -> floatalize ( $amount );
2017-11-15 12:25:49 +01:00
if ( 1 === bccomp ( $amount , '0' )) {
2017-06-17 22:49:44 +02:00
$amount = bcmul ( $amount , '-1' );
}
return $amount ;
}
2017-06-20 21:04:25 +02:00
/**
2023-06-21 12:34:58 +02:00
* https :// framework . zend . com / downloads / archives
*
* Convert a scientific notation to float
* Additionally fixed a problem with PHP <= 5.2 . x with big integers
*/
public function floatalize ( string $value ) : string
{
2024-12-26 05:25:46 +01:00
$value = strtoupper ( $value );
2023-06-21 12:34:58 +02:00
if ( ! str_contains ( $value , 'E' )) {
return $value ;
}
2024-07-24 14:57:51 +02:00
Log :: debug ( sprintf ( 'Floatalizing %s' , $value ));
2023-06-21 12:34:58 +02:00
2024-07-31 13:09:55 +02:00
$number = substr ( $value , 0 , ( int ) strpos ( $value , 'E' ));
2023-06-21 12:34:58 +02:00
if ( str_contains ( $number , '.' )) {
2024-07-31 13:09:55 +02:00
$post = strlen ( substr ( $number , ( int ) strpos ( $number , '.' ) + 1 ));
$mantis = substr ( $value , ( int ) strpos ( $value , 'E' ) + 1 );
2023-06-21 12:34:58 +02:00
if ( $mantis < 0 ) {
2024-07-31 13:09:55 +02:00
$post += abs (( int ) $mantis );
2023-06-21 12:34:58 +02:00
}
2023-12-20 19:35:52 +01:00
2023-06-21 12:34:58 +02:00
// TODO careless float could break financial math.
2024-07-31 13:09:55 +02:00
return number_format (( float ) $value , $post , '.' , '' );
2023-06-21 12:34:58 +02:00
}
2023-12-20 19:35:52 +01:00
2023-06-21 12:34:58 +02:00
// TODO careless float could break financial math.
2024-07-31 13:09:55 +02:00
return number_format (( float ) $value , 0 , '.' , '' );
2023-06-21 12:34:58 +02:00
}
2024-03-18 20:25:30 +01:00
public function opposite ( ? string $amount = null ) : ? string
2017-06-20 21:04:25 +02:00
{
2018-04-02 14:50:17 +02:00
if ( null === $amount ) {
2018-03-24 14:05:29 +01:00
return null ;
}
2021-01-26 19:28:35 +01:00
2020-10-23 19:11:25 +02:00
return bcmul ( $amount , '-1' );
2017-06-20 21:04:25 +02:00
}
2018-07-15 10:00:08 +02:00
public function phpBytes ( string $string ) : int
2015-07-19 14:30:20 +02:00
{
2021-01-26 19:28:35 +01:00
$string = str_replace ([ 'kb' , 'mb' , 'gb' ], [ 'k' , 'm' , 'g' ], strtolower ( $string ));
2015-07-19 14:30:20 +02:00
2020-10-24 16:59:56 +02:00
if ( false !== stripos ( $string , 'k' )) {
2015-07-19 14:30:20 +02:00
// has a K in it, remove the K and multiply by 1024.
2021-01-26 19:28:35 +01:00
$bytes = bcmul ( rtrim ( $string , 'k' ), '1024' );
2015-07-19 14:30:20 +02:00
2024-07-31 13:09:55 +02:00
return ( int ) $bytes ;
2015-07-19 14:30:20 +02:00
}
2020-10-24 16:59:56 +02:00
if ( false !== stripos ( $string , 'm' )) {
2015-07-19 14:30:20 +02:00
// has a M in it, remove the M and multiply by 1048576.
2021-01-26 19:28:35 +01:00
$bytes = bcmul ( rtrim ( $string , 'm' ), '1048576' );
2015-07-19 14:30:20 +02:00
2024-07-31 13:09:55 +02:00
return ( int ) $bytes ;
2015-07-19 14:30:20 +02:00
}
2020-10-24 16:59:56 +02:00
if ( false !== stripos ( $string , 'g' )) {
2016-08-05 20:54:59 +02:00
// has a G in it, remove the G and multiply by (1024)^3.
2021-01-26 19:28:35 +01:00
$bytes = bcmul ( rtrim ( $string , 'g' ), '1073741824' );
2016-08-05 20:54:59 +02:00
2024-07-31 13:09:55 +02:00
return ( int ) $bytes ;
2016-08-05 20:54:59 +02:00
}
2024-07-31 13:09:55 +02:00
return ( int ) $string ;
2015-07-19 14:30:20 +02:00
}
2017-02-11 09:34:04 +01:00
public function positive ( string $amount ) : string
{
2021-04-05 06:14:13 +02:00
if ( '' === $amount ) {
return '0' ;
}
2023-12-20 19:35:52 +01:00
2022-05-04 20:32:51 +02:00
try {
2023-12-20 19:35:52 +01:00
if ( - 1 === bccomp ( $amount , '0' )) {
2022-05-04 20:32:51 +02:00
$amount = bcmul ( $amount , '-1' );
}
2024-12-22 20:37:54 +01:00
} catch ( \ValueError $e ) {
2023-12-29 09:01:42 +01:00
Log :: error ( sprintf ( 'ValueError in Steam::positive("%s"): %s' , $amount , $e -> getMessage ()));
Log :: error ( $e -> getTraceAsString ());
2023-12-20 19:35:52 +01:00
2022-05-04 20:32:51 +02:00
return '0' ;
2017-02-11 09:34:04 +01:00
}
return $amount ;
}
2015-03-29 08:14:32 +02:00
}