2018-02-11 08:08:08 +01:00
< ? php
2024-11-25 04:18:55 +01:00
2018-02-11 08:08:08 +01:00
/**
* AccountTransformer . php
2020-02-16 13:57:18 +01:00
* Copyright ( c ) 2019 james @ firefly - iii . org
2018-02-11 08:08:08 +01:00
*
2019-10-02 06:37:26 +02:00
* This file is part of Firefly III ( https :// github . com / firefly - iii ) .
2018-02-11 08:08:08 +01: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-02-11 08:08:08 +01:00
*
2019-10-02 06:37:26 +02:00
* This program is distributed in the hope that it will be useful ,
2018-02-11 08:08:08 +01: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-02-11 08:08:08 +01: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-02-11 08:08:08 +01:00
*/
declare ( strict_types = 1 );
namespace FireflyIII\Transformers ;
2021-04-10 08:02:10 +02:00
2018-02-13 18:24:06 +01:00
use Carbon\Carbon ;
2021-09-18 10:26:12 +02:00
use FireflyIII\Exceptions\FireflyException ;
2018-02-11 08:08:08 +01:00
use FireflyIII\Models\Account ;
2025-01-15 18:54:49 +01:00
use FireflyIII\Models\TransactionCurrency ;
2018-02-16 22:14:34 +01:00
use FireflyIII\Repositories\Account\AccountRepositoryInterface ;
2025-01-15 18:54:49 +01:00
use FireflyIII\Support\Facades\Amount ;
2024-12-22 19:42:06 +01:00
use FireflyIII\Support\Facades\Steam ;
2025-02-15 16:51:13 +01:00
use FireflyIII\Support\Http\Api\ExchangeRateConverter ;
2025-02-10 16:47:59 +01:00
use Illuminate\Support\Facades\Log ;
2022-07-21 16:41:28 +02:00
use Symfony\Component\HttpFoundation\ParameterBag ;
2018-02-11 08:08:08 +01:00
/**
* Class AccountTransformer
*/
2018-12-15 07:59:49 +01:00
class AccountTransformer extends AbstractTransformer
2018-02-11 08:08:08 +01:00
{
2025-07-31 20:35:44 +02:00
protected bool $convertToPrimary ;
protected TransactionCurrency $primary ;
2025-05-04 17:41:26 +02:00
protected AccountRepositoryInterface $repository ;
2018-03-19 19:39:02 +01:00
2018-02-11 20:45:33 +01:00
/**
2018-02-16 22:14:34 +01:00
* AccountTransformer constructor .
2018-02-11 20:45:33 +01:00
*/
2018-12-15 07:59:49 +01:00
public function __construct ()
2018-02-11 20:45:33 +01:00
{
2025-08-01 13:10:11 +02:00
$this -> parameters = new ParameterBag ();
$this -> repository = app ( AccountRepositoryInterface :: class );
2025-07-31 20:35:44 +02:00
$this -> convertToPrimary = Amount :: convertToPrimary ();
$this -> primary = Amount :: getPrimaryCurrency ();
2018-02-11 20:45:33 +01:00
}
2018-02-11 08:08:08 +01:00
/**
2018-02-17 10:47:06 +01:00
* Transform the account .
*
2023-02-22 18:03:31 +01:00
* @ throws FireflyException
2023-12-22 20:12:38 +01:00
*/
2018-02-11 08:08:08 +01:00
public function transform ( Account $account ) : array
{
2025-02-18 10:31:05 +01:00
if ( null === $account -> meta ) {
2025-02-18 10:24:23 +01:00
$account -> meta = [];
}
2025-02-18 10:26:36 +01:00
2018-12-15 07:59:49 +01:00
// get account type:
2025-08-02 07:16:30 +02:00
$accountType = ( string ) config ( sprintf ( 'firefly.shortNamesByFullName.%s' , $account -> full_account_type ));
$liabilityType = ( string ) config ( sprintf ( 'firefly.shortLiabilityNameByFullName.%s' , $account -> full_account_type ));
$liabilityType = '' === $liabilityType ? null : strtolower ( $liabilityType );
$liabilityDirection = $account -> meta [ 'liability_direction' ] ? ? null ;
$accountRole = $this -> getAccountRole ( $account , $accountType );
$hasCurrencySettings = array_key_exists ( 'currency_id' , $account -> meta ) && ( int ) $account -> meta [ 'currency_id' ] > 0 ;
$includeNetWorth = 1 === ( int )( $account -> meta [ 'include_net_worth' ] ? ? 0 );
$longitude = $account -> meta [ 'location' ][ 'longitude' ] ? ? null ;
$latitude = $account -> meta [ 'location' ][ 'latitude' ] ? ? null ;
$zoomLevel = $account -> meta [ 'location' ][ 'zoom_level' ] ? ? null ;
$order = $account -> order ;
2025-02-15 16:51:13 +01:00
// date (for balance etc.)
2025-08-02 07:16:30 +02:00
$date = $this -> getDate ();
2021-02-26 18:09:48 +01:00
$date -> endOfDay ();
2018-12-15 07:59:49 +01:00
2021-03-14 06:20:23 +01:00
// no order for some accounts:
2022-10-30 14:24:37 +01:00
if ( ! in_array ( strtolower ( $accountType ), [ 'liability' , 'liabilities' , 'asset' ], true )) {
2021-03-14 06:20:23 +01:00
$order = null ;
}
2025-01-15 18:54:49 +01:00
2025-08-02 07:16:30 +02:00
// get some listed information from the account meta-data:
[ $creditCardType , $monthlyPaymentDate ] = $this -> getCCInfo ( $account , $accountRole , $accountType );
[ $openingBalance , $openingBalanceDate ] = $this -> getOpeningBalance ( $account , $accountType );
[ $interest , $interestPeriod ] = $this -> getInterest ( $account , $accountType );
// get currencies:
$currency = $this -> primary ; // assume primary currency
if ( $hasCurrencySettings ) {
$currency = $account -> meta [ 'currency' ];
2025-07-23 07:01:10 +02:00
}
2025-08-02 07:16:30 +02:00
// get the current balance:
$finalBalance = Steam :: finalAccountBalance ( $account , $date , $this -> primary , $this -> convertToPrimary );
Log :: debug ( sprintf ( 'Call finalAccountBalance(%s) with date/time "%s"' , var_export ( $this -> convertToPrimary , true ), $date -> toIso8601String ()), $finalBalance );
// set some pc_ default values to NULL:
$pcCurrentBalance = null ;
$pcOpeningBalance = null ;
$pcVirtualBalance = null ;
$pcDebtAmount = null ;
// collect current balances:
$currentBalance = Steam :: bcround ( $finalBalance [ $currency -> code ] ? ? '0' , $currency -> decimal_places );
$openingBalance = Steam :: bcround ( $openingBalance ? ? '0' , $currency -> decimal_places );
$virtualBalance = Steam :: bcround ( $account -> virtual_balance ? ? '0' , $currency -> decimal_places );
$debtAmount = $account -> meta [ 'current_debt' ] ? ? null ;
// convert to primary currency if needed:
if ( $this -> convertToPrimary && $currency -> id !== $this -> primary -> id ) {
Log :: debug ( sprintf ( 'Convert to primary, from %s to %s' , $currency -> code , $this -> primary -> code ));
$converter = new ExchangeRateConverter ();
$pcCurrentBalance = $converter -> convert ( $currency , $this -> primary , $date , $currentBalance );
$pcOpeningBalance = $converter -> convert ( $currency , $this -> primary , $date , $openingBalance );
$pcVirtualBalance = $converter -> convert ( $currency , $this -> primary , $date , $virtualBalance );
$pcDebtAmount = null === $debtAmount ? null : $converter -> convert ( $currency , $this -> primary , $date , $debtAmount );
2025-07-23 07:01:10 +02:00
}
2025-08-02 07:16:30 +02:00
// set opening balance(s) to NULL if the date is null
if ( null === $openingBalanceDate ) {
$openingBalance = null ;
$pcOpeningBalance = null ;
2025-07-23 07:01:10 +02:00
}
2021-03-14 06:20:23 +01:00
2019-12-30 17:49:29 +01:00
return [
2025-08-01 13:10:11 +02:00
'id' => ( string ) $account -> id ,
'created_at' => $account -> created_at -> toAtomString (),
'updated_at' => $account -> updated_at -> toAtomString (),
'active' => $account -> active ,
'order' => $order ,
'name' => $account -> name ,
'type' => strtolower ( $accountType ),
'account_role' => $accountRole ,
2025-08-02 07:16:30 +02:00
// currency information, structured for 6.3.0.
'object_has_currency_setting' => $hasCurrencySettings ,
// currency is object specific or primary, already determined above.
'currency_id' => ( string ) $currency [ 'id' ],
'currency_code' => $currency [ 'code' ],
'currency_symbol' => $currency [ 'symbol' ],
'currency_decimal_places' => $currency [ 'decimal_places' ],
'primary_currency_id' => ( string ) $this -> primary -> id ,
'primary_currency_code' => $this -> primary -> code ,
'primary_currency_symbol' => $this -> primary -> symbol ,
'primary_currency_decimal_places' => $this -> primary -> decimal_places ,
// balances, structured for 6.3.0.
2025-08-01 13:10:11 +02:00
'current_balance' => $currentBalance ,
'pc_current_balance' => $pcCurrentBalance ,
2025-08-02 07:16:30 +02:00
'opening_balance' => $openingBalance ,
'pc_opening_balance' => $pcOpeningBalance ,
'virtual_balance' => $virtualBalance ,
'pc_virtual_balance' => $pcVirtualBalance ,
'debt_amount' => $debtAmount ,
'pc_debt_amount' => $pcDebtAmount ,
'current_balance_date' => $date -> toAtomString (),
'notes' => $account -> meta [ 'notes' ] ? ? null ,
'monthly_payment_date' => $monthlyPaymentDate ,
'credit_card_type' => $creditCardType ,
'account_number' => $account -> meta [ 'account_number' ] ? ? null ,
'iban' => '' === $account -> iban ? null : $account -> iban ,
'bic' => $account -> meta [ 'BIC' ] ? ? null ,
'opening_balance_date' => $openingBalanceDate ,
'liability_type' => $liabilityType ,
'liability_direction' => $liabilityDirection ,
'interest' => $interest ,
'interest_period' => $interestPeriod ,
'include_net_worth' => $includeNetWorth ,
'longitude' => $longitude ,
'latitude' => $latitude ,
'zoom_level' => $zoomLevel ,
'last_activity' => array_key_exists ( 'last_activity' , $account -> meta ) ? $account -> meta [ 'last_activity' ] -> toAtomString () : null ,
'links' => [
2018-02-11 08:08:08 +01:00
[
'rel' => 'self' ,
2025-02-15 16:51:13 +01:00
'uri' => sprintf ( '/accounts/%d' , $account -> id ),
2018-02-11 08:08:08 +01:00
],
],
];
}
2019-01-27 07:48:49 +01:00
2019-01-27 12:30:52 +01:00
private function getAccountRole ( Account $account , string $accountType ) : ? string
2019-01-27 07:48:49 +01:00
{
2025-02-15 16:51:13 +01:00
$accountRole = $account -> meta [ 'account_role' ] ? ? null ;
2025-07-23 07:01:10 +02:00
if ( 'asset' !== $accountType || '' === ( string ) $accountRole ) {
2025-05-27 17:06:15 +02:00
return null ;
2019-01-27 07:48:49 +01:00
}
return $accountRole ;
}
/**
2023-06-21 12:34:58 +02:00
* TODO duplicated in the V2 transformer .
2019-01-27 07:48:49 +01:00
*/
2023-06-21 12:34:58 +02:00
private function getDate () : Carbon
2019-01-27 07:48:49 +01:00
{
2023-06-21 12:34:58 +02:00
if ( null !== $this -> parameters -> get ( 'date' )) {
2025-05-27 17:06:15 +02:00
return $this -> parameters -> get ( 'date' );
2019-01-27 07:48:49 +01:00
}
2025-05-27 17:06:15 +02:00
return today ( config ( 'app.timezone' ));
2019-01-27 07:48:49 +01:00
}
2023-06-21 12:34:58 +02:00
private function getCCInfo ( Account $account , ? string $accountRole , string $accountType ) : array
2019-01-27 07:48:49 +01:00
{
2023-06-21 12:34:58 +02:00
$monthlyPaymentDate = null ;
$creditCardType = null ;
if ( 'ccAsset' === $accountRole && 'asset' === $accountType ) {
2025-02-15 16:51:13 +01:00
$creditCardType = $account -> meta [ 'cc_type' ] ? ? null ;
$monthlyPaymentDate = $account -> meta [ 'cc_monthly_payment_date' ] ? ? null ;
2023-06-21 12:34:58 +02:00
}
if ( null !== $monthlyPaymentDate ) {
// try classic date:
if ( 10 === strlen ( $monthlyPaymentDate )) {
2025-08-02 07:16:30 +02:00
$object = Carbon :: createFromFormat ( '!Y-m-d' , $monthlyPaymentDate , config ( 'app.timezone' ));
2025-05-27 17:06:15 +02:00
if ( ! $object instanceof Carbon ) {
2023-11-28 17:18:31 +01:00
$object = today ( config ( 'app.timezone' ));
}
$monthlyPaymentDate = $object -> toAtomString ();
2023-06-21 12:34:58 +02:00
}
2025-07-23 07:01:10 +02:00
if ( 10 !== strlen (( string ) $monthlyPaymentDate )) {
2023-06-21 12:34:58 +02:00
$monthlyPaymentDate = Carbon :: parse ( $monthlyPaymentDate , config ( 'app.timezone' )) -> toAtomString ();
}
2021-04-01 20:56:11 +02:00
}
2019-01-27 07:48:49 +01:00
2023-06-21 12:34:58 +02:00
return [ $creditCardType , $monthlyPaymentDate ];
2019-01-27 07:48:49 +01:00
}
2025-02-15 16:51:13 +01:00
private function getOpeningBalance ( Account $account , string $accountType ) : array
2019-01-27 07:48:49 +01:00
{
2025-08-01 13:10:11 +02:00
$openingBalance = null ;
$openingBalanceDate = null ;
2025-08-02 07:16:30 +02:00
// $pcOpeningBalance = null;
2019-06-21 19:10:02 +02:00
if ( in_array ( $accountType , [ 'asset' , 'liabilities' ], true )) {
2025-02-15 16:51:13 +01:00
// grab from meta.
2025-08-01 13:10:11 +02:00
$openingBalance = $account -> meta [ 'opening_balance_amount' ] ? ? null ;
$pcOpeningBalance = null ;
$openingBalanceDate = $account -> meta [ 'opening_balance_date' ] ? ? null ;
2019-01-27 07:48:49 +01:00
}
2021-04-10 08:02:10 +02:00
if ( null !== $openingBalanceDate ) {
2025-08-02 07:16:30 +02:00
$object = Carbon :: createFromFormat ( 'Y-m-d H:i:s' , $openingBalanceDate , config ( 'app.timezone' ));
2025-05-27 17:06:15 +02:00
if ( ! $object instanceof Carbon ) {
2023-11-28 17:18:31 +01:00
$object = today ( config ( 'app.timezone' ));
}
$openingBalanceDate = $object -> toAtomString ();
2025-02-15 16:51:13 +01:00
// NOW do conversion.
2025-08-02 07:16:30 +02:00
// if ($this->convertToPrimary && null !== $account->meta['currency']) {
// $converter = new ExchangeRateConverter();
// $pcOpeningBalance = $converter->convert($account->meta['currency'], $this->primary, $object, $openingBalance);
// }
2025-02-15 16:51:13 +01:00
2021-04-01 20:56:11 +02:00
}
2019-01-27 07:48:49 +01:00
2025-08-02 07:16:30 +02:00
return [ $openingBalance , $openingBalanceDate ];
2019-01-27 07:48:49 +01:00
}
2023-06-21 12:34:58 +02:00
private function getInterest ( Account $account , string $accountType ) : array
{
$interest = null ;
$interestPeriod = null ;
if ( 'liabilities' === $accountType ) {
2025-02-15 16:51:13 +01:00
$interest = $account -> meta [ 'interest' ] ? ? null ;
$interestPeriod = $account -> meta [ 'interest_period' ] ? ? null ;
2023-06-21 12:34:58 +02:00
}
return [ $interest , $interestPeriod ];
}
2018-03-05 19:35:58 +01:00
}