@ -80,240 +80,9 @@ class Accounts extends AbstractTask
}
/**
* @ param array $transaction
* @ param array $array
*
* @ return array
* @ throws ImporterErrorException
*/
private function processTransaction ( array $transaction ) : array
{
app ( 'log' ) -> debug ( 'Now in Accounts::processTransaction()' );
/*
* Try to find the source and destination accounts in the transaction .
*
* The source account will default back to the user ' s submitted default account .
* So when everything fails , the transaction will be an expense for amount X .
*/
$sourceArray = $this -> getSourceArray ( $transaction );
$destArray = $this -> getDestinationArray ( $transaction );
$source = $this -> findAccount ( $sourceArray , $this -> account );
$destination = $this -> findAccount ( $destArray , null );
/*
* First , set source and destination in the transaction array :
*/
$transaction = $this -> setSource ( $transaction , $source );
$transaction = $this -> setDestination ( $transaction , $destination );
$transaction [ 'type' ] = $this -> determineType ( $source [ 'type' ], $destination [ 'type' ]);
app ( 'log' ) -> debug ( sprintf ( 'Transaction type is set to "%s"' , $transaction [ 'type' ]));
app ( 'log' ) -> debug ( 'Source is now:' , $source );
app ( 'log' ) -> debug ( 'Destination is now:' , $destination );
$amount = ( string ) $transaction [ 'amount' ];
$amount = '' === $amount ? '0' : $amount ;
if ( '0' === $amount ) {
app ( 'log' ) -> error ( 'Amount is ZERO. This will give trouble further down the line.' );
}
/*
* If the amount is positive , the transaction is a deposit . We switch Source
* and Destination and see if we can still handle the transaction , but only if the transaction
* isn ' t already a deposit ( it has to be a withdrawal ) .
*
*/
if ( 'withdrawal' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
// amount is positive
app ( 'log' ) -> debug ( sprintf ( '%s is positive and type is "%s", switch source/destination' , $amount , $transaction [ 'type' ]));
$transaction = $this -> setSource ( $transaction , $destination );
$transaction = $this -> setDestination ( $transaction , $source );
$transaction [ 'type' ] = $this -> determineType ( $destination [ 'type' ], $source [ 'type' ]);
app ( 'log' ) -> debug ( 'Source is now:' , $destination ); // yes this is correct.
app ( 'log' ) -> debug ( 'Destination is now:' , $source ); // yes this is correct.
// switch variables because processing further ahead will otherwise be messed up:
[ $source , $destination ] = [ $destination , $source ];
}
/*
* If the amount is positive and the type is a transfer , switch accounts around .
*/
if ( 'transfer' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a transfer, and amount is positive, will switch accounts.' );
$transaction = $this -> setSource ( $transaction , $destination );
$transaction = $this -> setDestination ( $transaction , $source );
app ( 'log' ) -> debug ( 'Source is now:' , $destination ); // yes this is correct!
app ( 'log' ) -> debug ( 'Destination is now:' , $source ); // yes this is correct!
// also switch amount and foreign currency amount, if both are present.
// if this data is missing, Firefly III will break later either way.
if ( $this -> hasAllAmountInformation ( $transaction )) {
app ( 'log' ) -> debug ( 'This transfer has all necessary (foreign) currency + amount information, so swap these too.' );
$transaction = $this -> swapCurrencyInformation ( $transaction );
}
}
/*
* If deposit and amount is positive , do nothing .
*/
if ( 'deposit' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a deposit, and amount is positive. Will not change account types.' );
}
/*
* If deposit and amount is positive , but the source is not a revenue , fall back to
* some " original-field-name " values ( if they exist ) and hope for the best .
*/
if (
'deposit' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' ) && 'revenue' !== $source [ 'type' ] && '' !== ( string ) $source [ 'type' ]
) {
app ( 'log' ) -> warning (
sprintf (
'Transaction is a deposit, and amount is positive, but source is not a revenue ("%s"). Will fall back to original field names.' ,
$source [ 'type' ]
)
);
$newSource = [
'id' => null ,
'name' => $transaction [ 'original-opposing-name' ] ? ? '(no name)' ,
'iban' => $transaction [ 'original-opposing-iban' ] ? ? null ,
'number' => $transaction [ 'original-opposing-number' ] ? ? null ,
'bic' => null ,
];
$transaction = $this -> setSource ( $transaction , $newSource );
}
/*
* If amount is negative and type is transfer , make sure accounts are " original " .
*/
if ( 'transfer' === $transaction [ 'type' ] && - 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a transfer, and amount is negative, must not change accounts.' );
$transaction = $this -> setSource ( $transaction , $source );
$transaction = $this -> setDestination ( $transaction , $destination );
app ( 'log' ) -> debug ( 'Source is now:' , $source );
app ( 'log' ) -> debug ( 'Destination is now:' , $destination );
}
/*
* Final check . If the type is " withdrawal " but the destination account found is " revenue "
* we found the wrong one . Just submit the name and hope for the best .
*/
if ( 'revenue' === $destination [ 'type' ] && 'withdrawal' === $transaction [ 'type' ]) {
app ( 'log' ) -> warning ( 'The found destination account is of type revenue but this is a withdrawal. Out of cheese error.' );
app ( 'log' ) -> debug (
sprintf ( 'Data importer will submit name "%s" and IBAN "%s" and let Firefly III sort it out.' , $destination [ 'name' ], $destination [ 'iban' ])
);
$transaction [ 'destination_id' ] = null ;
$transaction [ 'destination_name' ] = $destination [ 'name' ];
$transaction [ 'destination_iban' ] = $destination [ 'iban' ];
}
/*
* Same but for the other way around .
* If type is " deposit " but the source account is an expense account .
* Submit just the name .
*/
if ( 'expense' === $source [ 'type' ] && 'deposit' === $transaction [ 'type' ]) {
app ( 'log' ) -> warning ( 'The found source account is of type expense but this is a deposit. Out of cheese error.' );
app ( 'log' ) -> debug ( sprintf ( 'Data importer will submit name "%s" and IBAN "%s" and let Firefly III sort it out.' , $source [ 'name' ], $source [ 'iban' ]));
$transaction [ 'source_id' ] = null ;
$transaction [ 'source_name' ] = $source [ 'name' ];
$transaction [ 'source_iban' ] = $source [ 'iban' ];
}
/*
* if new source or destination ID is filled in , drop the other fields :
*/
if ( 0 !== $transaction [ 'source_id' ] && null !== $transaction [ 'source_id' ]) {
$transaction [ 'source_name' ] = null ;
$transaction [ 'source_iban' ] = null ;
$transaction [ 'source_number' ] = null ;
}
if ( 0 !== $transaction [ 'destination_id' ] && null !== $transaction [ 'destination_id' ]) {
$transaction [ 'destination_name' ] = null ;
$transaction [ 'destination_iban' ] = null ;
$transaction [ 'destination_number' ] = null ;
}
if ( $this -> hasAllCurrencies ( $transaction )) {
app ( 'log' ) -> debug ( 'Final validation of foreign amount and or normal transaction amount' );
// withdrawal
if ( 'withdrawal' === $transaction [ 'type' ]) {
// currency info must match $source
// so if we can switch them around we will.
if ( $transaction [ 'currency_code' ] !== $source [ 'currency_code' ]
&& $transaction [ 'foreign_currency_code' ] === $source [ 'currency_code' ]) {
app ( 'log' ) -> debug ( 'Source account accepts %s, so foreign / native numbers are switched now.' );
$amount = $transaction [ 'amount' ] ? ? '0' ;
$currency = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'foreign_amount' ] ? ? '0' ;
$transaction [ 'currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $amount ;
$transaction [ 'foreign_currency_code' ] = $currency ;
}
}
// deposit
if ( 'deposit' === $transaction [ 'type' ]) {
// currency info must match $destination,
// so if we can switch them around we will.
if ( $transaction [ 'currency_code' ] !== $destination [ 'currency_code' ]
&& $transaction [ 'foreign_currency_code' ] === $destination [ 'currency_code' ]) {
app ( 'log' ) -> debug ( 'Destination account accepts %s, so foreign / native numbers are switched now.' );
$amount = $transaction [ 'amount' ] ? ? '0' ;
$currency = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'foreign_amount' ] ? ? '0' ;
$transaction [ 'currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $amount ;
$transaction [ 'foreign_currency_code' ] = $currency ;
}
}
app ( 'log' ) -> debug ( 'Final validation of foreign amount and or normal transaction amount finished.' );
}
return $transaction ;
}
/**
* @ param array $transaction
*
* @ return array
*/
private function getSourceArray ( array $transaction ) : array
{
return [
'transaction_type' => $transaction [ 'type' ],
'id' => $transaction [ 'source_id' ],
'name' => $transaction [ 'source_name' ],
'iban' => $transaction [ 'source_iban' ] ? ? null ,
'number' => $transaction [ 'source_number' ] ? ? null ,
'bic' => $transaction [ 'source_bic' ] ? ? null ,
'direction' => 'source' ,
];
}
/**
* @ param array $transaction
*
* @ return array
*/
private function getDestinationArray ( array $transaction ) : array
{
return [
'transaction_type' => $transaction [ 'type' ],
'id' => $transaction [ 'destination_id' ],
'name' => $transaction [ 'destination_name' ],
'iban' => $transaction [ 'destination_iban' ] ? ? null ,
'number' => $transaction [ 'destination_number' ] ? ? null ,
'bic' => $transaction [ 'destination_bic' ] ? ? null ,
'direction' => 'destination' ,
];
}
/**
* @ param array $array
*
* @ param Account | null $defaultAccount
* @ param Account | null $defaultAccount
*
* @ return array
* @ throws ImporterErrorException
@ -415,27 +184,34 @@ class Accounts extends AbstractTask
}
/**
* @ param string $value
* @ param string $iban
* @ param string $transactionType
*
* @ return Account | null
* @ throws ImporterErrorException
*/
private function findById ( string $valu e ) : ? Account
private function findByIban ( string $iban , string $transactionTyp e ) : ? Account
{
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with ID "%s"' , $value ));
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with IBAN "%s"' , $iban ));
$url = SecretManager :: getBaseUrl ();
$token = SecretManager :: getAccessToken ();
$request = new GetSearchAccountRequest ( $url , $token );
$request -> setVerify ( config ( 'importer.connection.verify' ));
$request -> setTimeOut ( config ( 'importer.connection.timeout' ));
$request -> setField ( 'id ' );
$request -> setQuery ( $value );
$request -> setField ( 'iban ' );
$request -> setQuery ( $iban );
/** @var GetAccountsResponse $response */
try {
$response = $request -> get ();
} catch ( GrumpyApiHttpException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
if ( 0 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found NOTHING in findbyiban.' );
return null ;
}
if ( 1 === count ( $response )) {
/** @var Account $account */
try {
@ -443,46 +219,65 @@ class Accounts extends AbstractTask
} catch ( ApiException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
// catch impossible combination "expense" with "deposit"
if ( 'expense' === $account -> type && 'deposit' === $transactionType ) {
app ( 'log' ) -> debug (
sprintf (
'Out of cheese error (IBAN). Found Found %s account #%d based on IBAN "%s". But not going to use expense/deposit combi.' ,
$account -> type ,
$account -> id ,
$iban
)
);
app ( 'log' ) -> debug ( 'Firefly III will have to make the correct decision.' );
app ( 'log' ) -> debug ( sprintf ( '[a] Found %s account #%d based on ID "%s"' , $account -> type , $account -> id , $value ));
return $account ;
return null ;
}
app ( 'log' ) -> debug ( sprintf ( '[a] Found %s account #%d based on IBAN "%s"' , $account -> type , $account -> id , $iban ));
// to fix issue #4293, Firefly III will ignore this account if it's an expense or a revenue account.
if ( in_array ( $account -> type , [ 'expense' , 'revenue' ], true )) {
app ( 'log' ) -> debug ( '[a] Data importer will pretend not to have found anything. Firefly III must handle the IBAN.' );
return null ;
}
return $account ;
}
app ( 'log' ) -> debug ( 'Found NOTHING in findById.' );
if ( 2 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found 2 results, Firefly III will have to make the correct decision.' );
return null ;
}
app ( 'log' ) -> debug ( sprintf ( 'Found %d result(s), Firefly III will have to make the correct decision.' , count ( $response )));
return null ;
}
/**
* @ param string $iban
* @ param string $transactionType
* @ param string $value
*
* @ return Account | null
* @ throws ImporterErrorException
*/
private function findByIban ( string $iban , string $transactionTyp e ) : ? Account
private function findById ( string $valu e ) : ? Account
{
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with IBAN "%s"' , $iban ));
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with ID "%s"' , $value ));
$url = SecretManager :: getBaseUrl ();
$token = SecretManager :: getAccessToken ();
$request = new GetSearchAccountRequest ( $url , $token );
$request -> setVerify ( config ( 'importer.connection.verify' ));
$request -> setTimeOut ( config ( 'importer.connection.timeout' ));
$request -> setField ( 'iban ' );
$request -> setQuery ( $iban );
$request -> setField ( 'id ' );
$request -> setQuery ( $value );
/** @var GetAccountsResponse $response */
try {
$response = $request -> get ();
} catch ( GrumpyApiHttpException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
if ( 0 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found NOTHING in findbyiban.' );
return null ;
}
if ( 1 === count ( $response )) {
/** @var Account $account */
try {
@ -490,46 +285,63 @@ class Accounts extends AbstractTask
} catch ( ApiException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
// catch impossible combination "expense" with "deposit"
if ( 'expense' === $account -> type && 'deposit' === $transactionType ) {
app ( 'log' ) -> debug (
sprintf (
'Out of cheese error (IBAN). Found Found %s account #%d based on IBAN "%s". But not going to use expense/deposit combi.' ,
$account -> type ,
$account -> id ,
$iban
)
);
app ( 'log' ) -> debug ( 'Firefly III will have to make the correct decision.' );
return null ;
}
app ( 'log' ) -> debug ( sprintf ( '[a] Found %s account #%d based on IBAN "%s"' , $account -> type , $account -> id , $iban ));
app ( 'log' ) -> debug ( sprintf ( '[a] Found %s account #%d based on ID "%s"' , $account -> type , $account -> id , $value ));
// to fix issue #4293, Firefly III will ignore this account if it's an expense or a revenue account.
if ( in_array ( $account -> type , [ 'expense' , 'revenue' ], true )) {
app ( 'log' ) -> debug ( '[a] Data importer will pretend not to have found anything. Firefly III must handle the IBAN.' );
return $account ;
}
return null ;
}
app ( 'log' ) -> debug ( 'Found NOTHING in findById.' );
return null ;
}
return $account ;
/**
* @ param string $name
*
* @ return Account | null
* @ throws ImporterErrorException
*/
private function findByName ( string $name ) : ? Account
{
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with name "%s"' , $name ));
$url = SecretManager :: getBaseUrl ();
$token = SecretManager :: getAccessToken ();
$request = new GetSearchAccountRequest ( $url , $token );
$request -> setVerify ( config ( 'importer.connection.verify' ));
$request -> setTimeOut ( config ( 'importer.connection.timeout' ));
$request -> setField ( 'name' );
$request -> setQuery ( $name );
/** @var GetAccountsResponse $response */
try {
$response = $request -> get ();
} catch ( GrumpyApiHttpException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
if ( 2 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found 2 results, Firefly III will have to make the correct decision.' );
if ( 0 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found NOTHING in findbyname.' );
return null ;
}
app ( 'log' ) -> debug ( sprintf ( 'Found %d result(s), Firefly III will have to make the correct decision.' , count ( $response )));
/** @var Account $account */
foreach ( $response as $account ) {
if ( in_array ( $account -> type , [ AccountType :: ASSET , AccountType :: LOAN , AccountType :: DEBT , AccountType :: MORTGAGE ], true )
&& strtolower ( $account -> name ) === strtolower ( $name )) {
app ( 'log' ) -> debug ( sprintf ( '[b] Found "%s" account #%d based on name "%s"' , $account -> type , $account -> id , $name ));
return $account ;
}
}
app ( 'log' ) -> debug (
sprintf ( 'Found %d account(s) searching for "%s" but not going to use them. Firefly III must handle the values.' , count ( $response ), $name )
);
return null ;
}
/**
* @ param string $accountNumber
* @ param string $transactionType
* @ param string $accountNumber
* @ param string $transactionType
*
* @ return Account | null
* @ throws ImporterErrorException
@ -601,80 +413,272 @@ class Accounts extends AbstractTask
}
/**
* @ param string $name
* @ param array $transaction
*
* @ return Account | null
* @ throws ImporterErrorException
* @ return array
*/
private function findByName ( string $name ) : ? Account
private function getDestinationArray ( array $transaction ) : array
{
app ( 'log' ) -> debug ( sprintf ( 'Going to search account with name "%s"' , $name ));
$url = SecretManager :: getBaseUrl ();
$token = SecretManager :: getAccessToken ();
$request = new GetSearchAccountRequest ( $url , $token );
$request -> setVerify ( config ( 'importer.connection.verify' ));
$request -> setTimeOut ( config ( 'importer.connection.timeout' ));
$request -> setField ( 'name' );
$request -> setQuery ( $name );
/** @var GetAccountsResponse $response */
try {
$response = $request -> get ();
} catch ( GrumpyApiHttpException $e ) {
throw new ImporterErrorException ( $e -> getMessage ());
}
if ( 0 === count ( $response )) {
app ( 'log' ) -> debug ( 'Found NOTHING in findbyname.' );
return null ;
}
/** @var Account $account */
foreach ( $response as $account ) {
if ( in_array ( $account -> type , [ AccountType :: ASSET , AccountType :: LOAN , AccountType :: DEBT , AccountType :: MORTGAGE ], true )
&& strtolower ( $account -> name ) === strtolower ( $name )) {
app ( 'log' ) -> debug ( sprintf ( '[b] Found "%s" account #%d based on name "%s"' , $account -> type , $account -> id , $name ));
return [
'transaction_type' => $transaction [ 'type' ],
'id' => $transaction [ 'destination_id' ],
'name' => $transaction [ 'destination_name' ],
'iban' => $transaction [ 'destination_iban' ] ? ? null ,
'number' => $transaction [ 'destination_number' ] ? ? null ,
'bic' => $transaction [ 'destination_bic' ] ? ? null ,
'direction' => 'destination' ,
];
}
return $account ;
}
}
app ( 'log' ) -> debug (
sprintf ( 'Found %d account(s) searching for "%s" but not going to use them. Firefly III must handle the values.' , count ( $response ), $name )
);
/**
* @ param array $transaction
*
* @ return array
*/
private function getSourceArray ( array $transaction ) : array
{
return [
'transaction_type' => $transaction [ 'type' ],
'id' => $transaction [ 'source_id' ],
'name' => $transaction [ 'source_name' ],
'iban' => $transaction [ 'source_iban' ] ? ? null ,
'number' => $transaction [ 'source_number' ] ? ? null ,
'bic' => $transaction [ 'source_bic' ] ? ? null ,
'direction' => 'source' ,
];
}
return null ;
/**
* Basic check for currency info .
*
* @ param array $transaction
*
* @ return bool
*/
private function hasAllAmountInformation ( array $transaction ) : bool
{
return
array_key_exists ( 'amount' , $transaction )
&& array_key_exists ( 'foreign_amount' , $transaction )
&& ( array_key_exists ( 'foreign_currency_code' , $transaction ) || array_key_exists ( 'foreign_currency_id' , $transaction ))
&& ( array_key_exists ( 'currency_code' , $transaction ) || array_key_exists ( 'currency_id' , $transaction ));
}
/**
* @ param array $transaction
* @ param array $source
* @ param array $transaction
*
* @ return array
* @ return bool
*/
private function setSource ( array $transaction , array $source ) : array
private function hasAllCurrencies ( array $transaction ) : bool
{
return $this -> setTransactionAccount ( 'source' , $transaction , $source );
$transaction [ 'foreign_currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'currency_code' ] = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'amount' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $transaction [ 'foreign_amount' ] ? ? '' ;
return '' !== ( string ) $transaction [ 'currency_code' ] && '' !== ( string ) $transaction [ 'foreign_currency_code' ]
&& '' !== ( string ) $transaction [ 'amount' ]
&& '' !== ( string ) $transaction [ 'foreign_amount' ];
}
/**
* @ param string $direction
* @ param array $transaction
* @ param array $account
* @ param array $transaction
*
* @ return array
* @ throws ImporterErrorException
*/
private function setTransactionAccount ( string $direction , array $transaction , array $account ) : array
private function processTransaction ( array $transaction ) : array
{
$transaction [ sprintf ( '%s_id' , $direction )] = $account [ 'id' ];
$transaction [ sprintf ( '%s_name' , $direction )] = $account [ 'name' ];
$transaction [ sprintf ( '%s_iban' , $direction )] = $account [ 'iban' ];
$transaction [ sprintf ( '%s_number' , $direction )] = $account [ 'number' ];
$transaction [ sprintf ( '%s_bic' , $direction )] = $account [ 'bic' ];
app ( 'log' ) -> debug ( 'Now in Accounts::processTransaction()' );
/*
* Try to find the source and destination accounts in the transaction .
*
* The source account will default back to the user ' s submitted default account .
* So when everything fails , the transaction will be an expense for amount X .
*/
$sourceArray = $this -> getSourceArray ( $transaction );
$destArray = $this -> getDestinationArray ( $transaction );
$source = $this -> findAccount ( $sourceArray , $this -> account );
$destination = $this -> findAccount ( $destArray , null );
/*
* First , set source and destination in the transaction array :
*/
$transaction = $this -> setSource ( $transaction , $source );
$transaction = $this -> setDestination ( $transaction , $destination );
$transaction [ 'type' ] = $this -> determineType ( $source [ 'type' ], $destination [ 'type' ]);
app ( 'log' ) -> debug ( sprintf ( 'Transaction type is set to "%s"' , $transaction [ 'type' ]));
app ( 'log' ) -> debug ( 'Source is now:' , $source );
app ( 'log' ) -> debug ( 'Destination is now:' , $destination );
$amount = ( string ) $transaction [ 'amount' ];
$amount = '' === $amount ? '0' : $amount ;
if ( '0' === $amount ) {
app ( 'log' ) -> error ( 'Amount is ZERO. This will give trouble further down the line.' );
}
/*
* If the amount is positive , the transaction is a deposit . We switch Source
* and Destination and see if we can still handle the transaction , but only if the transaction
* isn ' t already a deposit ( it has to be a withdrawal ) .
*
*/
if ( 'withdrawal' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
// amount is positive
app ( 'log' ) -> debug ( sprintf ( '%s is positive and type is "%s", switch source/destination' , $amount , $transaction [ 'type' ]));
$transaction = $this -> setSource ( $transaction , $destination );
$transaction = $this -> setDestination ( $transaction , $source );
$transaction [ 'type' ] = $this -> determineType ( $destination [ 'type' ], $source [ 'type' ]);
app ( 'log' ) -> debug ( 'Source is now:' , $destination ); // yes this is correct.
app ( 'log' ) -> debug ( 'Destination is now:' , $source ); // yes this is correct.
// switch variables because processing further ahead will otherwise be messed up:
[ $source , $destination ] = [ $destination , $source ];
}
/*
* If the amount is positive and the type is a transfer , switch accounts around .
*/
if ( 'transfer' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a transfer, and amount is positive, will switch accounts.' );
$transaction = $this -> setSource ( $transaction , $destination );
$transaction = $this -> setDestination ( $transaction , $source );
app ( 'log' ) -> debug ( 'Source is now:' , $destination ); // yes this is correct!
app ( 'log' ) -> debug ( 'Destination is now:' , $source ); // yes this is correct!
// also switch amount and foreign currency amount, if both are present.
// if this data is missing, Firefly III will break later either way.
if ( $this -> hasAllAmountInformation ( $transaction )) {
app ( 'log' ) -> debug ( 'This transfer has all necessary (foreign) currency + amount information, so swap these too.' );
$transaction = $this -> swapCurrencyInformation ( $transaction );
}
}
/*
* If deposit and amount is positive , do nothing .
*/
if ( 'deposit' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a deposit, and amount is positive. Will not change account types.' );
}
/*
* If deposit and amount is positive , but the source is not a revenue , fall back to
* some " original-field-name " values ( if they exist ) and hope for the best .
*/
if (
'deposit' === $transaction [ 'type' ] && 1 === bccomp ( $amount , '0' ) && 'revenue' !== $source [ 'type' ] && '' !== ( string ) $source [ 'type' ]
) {
app ( 'log' ) -> warning (
sprintf (
'Transaction is a deposit, and amount is positive, but source is not a revenue ("%s"). Will fall back to original field names.' ,
$source [ 'type' ]
)
);
$newSource = [
'id' => null ,
'name' => $transaction [ 'original-opposing-name' ] ? ? '(no name)' ,
'iban' => $transaction [ 'original-opposing-iban' ] ? ? null ,
'number' => $transaction [ 'original-opposing-number' ] ? ? null ,
'bic' => null ,
];
$transaction = $this -> setSource ( $transaction , $newSource );
}
/*
* If amount is negative and type is transfer , make sure accounts are " original " .
*/
if ( 'transfer' === $transaction [ 'type' ] && - 1 === bccomp ( $amount , '0' )) {
app ( 'log' ) -> debug ( 'Transaction is a transfer, and amount is negative, must not change accounts.' );
$transaction = $this -> setSource ( $transaction , $source );
$transaction = $this -> setDestination ( $transaction , $destination );
app ( 'log' ) -> debug ( 'Source is now:' , $source );
app ( 'log' ) -> debug ( 'Destination is now:' , $destination );
}
/*
* Final check . If the type is " withdrawal " but the destination account found is " revenue "
* we found the wrong one . Just submit the name and hope for the best .
*/
if ( 'revenue' === $destination [ 'type' ] && 'withdrawal' === $transaction [ 'type' ]) {
app ( 'log' ) -> warning ( 'The found destination account is of type revenue but this is a withdrawal. Out of cheese error.' );
app ( 'log' ) -> debug (
sprintf ( 'Data importer will submit name "%s" and IBAN "%s" and let Firefly III sort it out.' , $destination [ 'name' ], $destination [ 'iban' ])
);
$transaction [ 'destination_id' ] = null ;
$transaction [ 'destination_name' ] = $destination [ 'name' ];
$transaction [ 'destination_iban' ] = $destination [ 'iban' ];
}
/*
* Same but for the other way around .
* If type is " deposit " but the source account is an expense account .
* Submit just the name .
*/
if ( 'expense' === $source [ 'type' ] && 'deposit' === $transaction [ 'type' ]) {
app ( 'log' ) -> warning ( 'The found source account is of type expense but this is a deposit. Out of cheese error.' );
app ( 'log' ) -> debug ( sprintf ( 'Data importer will submit name "%s" and IBAN "%s" and let Firefly III sort it out.' , $source [ 'name' ], $source [ 'iban' ]));
$transaction [ 'source_id' ] = null ;
$transaction [ 'source_name' ] = $source [ 'name' ];
$transaction [ 'source_iban' ] = $source [ 'iban' ];
}
/*
* if new source or destination ID is filled in , drop the other fields :
*/
if ( 0 !== $transaction [ 'source_id' ] && null !== $transaction [ 'source_id' ]) {
$transaction [ 'source_name' ] = null ;
$transaction [ 'source_iban' ] = null ;
$transaction [ 'source_number' ] = null ;
}
if ( 0 !== $transaction [ 'destination_id' ] && null !== $transaction [ 'destination_id' ]) {
$transaction [ 'destination_name' ] = null ;
$transaction [ 'destination_iban' ] = null ;
$transaction [ 'destination_number' ] = null ;
}
if ( $this -> hasAllCurrencies ( $transaction )) {
app ( 'log' ) -> debug ( 'Final validation of foreign amount and or normal transaction amount' );
// withdrawal
if ( 'withdrawal' === $transaction [ 'type' ]) {
// currency info must match $source
// so if we can switch them around we will.
if ( $transaction [ 'currency_code' ] !== $source [ 'currency_code' ]
&& $transaction [ 'foreign_currency_code' ] === $source [ 'currency_code' ]) {
app ( 'log' ) -> debug ( 'Source account accepts %s, so foreign / native numbers are switched now.' );
$amount = $transaction [ 'amount' ] ? ? '0' ;
$currency = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'foreign_amount' ] ? ? '0' ;
$transaction [ 'currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $amount ;
$transaction [ 'foreign_currency_code' ] = $currency ;
}
}
// deposit
if ( 'deposit' === $transaction [ 'type' ]) {
// currency info must match $destination,
// so if we can switch them around we will.
if ( $transaction [ 'currency_code' ] !== $destination [ 'currency_code' ]
&& $transaction [ 'foreign_currency_code' ] === $destination [ 'currency_code' ]) {
app ( 'log' ) -> debug ( 'Destination account accepts %s, so foreign / native numbers are switched now.' );
$amount = $transaction [ 'amount' ] ? ? '0' ;
$currency = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'foreign_amount' ] ? ? '0' ;
$transaction [ 'currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $amount ;
$transaction [ 'foreign_currency_code' ] = $currency ;
}
}
app ( 'log' ) -> debug ( 'Final validation of foreign amount and or normal transaction amount finished.' );
}
return $transaction ;
}
/**
* @ param array $transaction
* @ param array $source
* @ param array $transaction
* @ param array $source
*
* @ return array
*/
@ -684,23 +688,36 @@ class Accounts extends AbstractTask
}
/**
* Basic check for currency info .
* @ param array $transaction
* @ param array $source
*
* @ param array $transaction
* @ return array
*/
private function setSource ( array $transaction , array $source ) : array
{
return $this -> setTransactionAccount ( 'source' , $transaction , $source );
}
/**
* @ param string $direction
* @ param array $transaction
* @ param array $account
*
* @ return bool
* @ return array
*/
private function hasAllAmountInformation ( array $transaction ) : bool
private function setTransactionAccount ( string $direction , array $transaction , array $account ) : array
{
return
array_key_exists ( 'amount' , $transaction )
&& array_key_exists ( 'foreign_amount' , $transaction )
&& ( array_key_exists ( 'foreign_currency_code' , $transaction ) || array_key_exists ( 'foreign_currency_id' , $transaction ))
&& ( array_key_exists ( 'currency_code' , $transaction ) || array_key_exists ( 'currency_id' , $transaction ));
$transaction [ sprintf ( '%s_id' , $direction )] = $account [ 'id' ];
$transaction [ sprintf ( '%s_name' , $direction )] = $account [ 'name' ];
$transaction [ sprintf ( '%s_iban' , $direction )] = $account [ 'iban' ];
$transaction [ sprintf ( '%s_number' , $direction )] = $account [ 'number' ];
$transaction [ sprintf ( '%s_bic' , $direction )] = $account [ 'bic' ];
return $transaction ;
}
/**
* @ param array $transaction
* @ param array $transaction
*
* @ return array
*/
@ -732,21 +749,4 @@ class Accounts extends AbstractTask
return $transaction ;
}
/**
* @ param array $transaction
*
* @ return bool
*/
private function hasAllCurrencies ( array $transaction ) : bool
{
$transaction [ 'foreign_currency_code' ] = $transaction [ 'foreign_currency_code' ] ? ? '' ;
$transaction [ 'currency_code' ] = $transaction [ 'currency_code' ] ? ? '' ;
$transaction [ 'amount' ] = $transaction [ 'amount' ] ? ? '' ;
$transaction [ 'foreign_amount' ] = $transaction [ 'foreign_amount' ] ? ? '' ;
return '' !== ( string ) $transaction [ 'currency_code' ] && '' !== ( string ) $transaction [ 'foreign_currency_code' ]
&& '' !== ( string ) $transaction [ 'amount' ]
&& '' !== ( string ) $transaction [ 'foreign_amount' ];
}
}