2018-02-16 16:43:25 +01:00
< ? php
2018-05-11 10:08:34 +02:00
2018-02-16 16:43:25 +01:00
/**
* TransactionFactory . php
* Copyright ( c ) 2018 thegrumpydictator @ gmail . com
*
* This file is part of Firefly III .
*
* Firefly III is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Firefly III is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with Firefly III . If not , see < http :// www . gnu . org / licenses />.
*/
2018-05-11 10:08:34 +02:00
declare ( strict_types = 1 );
2018-02-16 16:43:25 +01:00
namespace FireflyIII\Factory ;
2018-05-06 20:42:07 +02:00
use FireflyIII\Exceptions\FireflyException ;
2019-03-08 05:47:51 +01:00
use FireflyIII\Models\Account ;
2018-06-13 19:03:18 +02:00
use FireflyIII\Models\AccountType ;
2018-02-16 16:43:25 +01:00
use FireflyIII\Models\Transaction ;
2019-03-08 05:47:51 +01:00
use FireflyIII\Models\TransactionCurrency ;
2018-02-16 16:43:25 +01:00
use FireflyIII\Models\TransactionJournal ;
2018-03-11 14:13:23 +01:00
use FireflyIII\Models\TransactionType ;
2019-03-08 05:47:51 +01:00
use FireflyIII\Repositories\Account\AccountRepositoryInterface ;
2019-03-17 17:05:16 +01:00
use FireflyIII\Support\NullArrayObject ;
2018-02-16 16:43:25 +01:00
use FireflyIII\User ;
2019-03-18 16:52:49 +01:00
use Illuminate\Database\QueryException ;
2018-02-16 16:43:25 +01:00
use Illuminate\Support\Collection ;
2018-05-10 09:10:16 +02:00
use Log ;
2018-02-16 16:43:25 +01:00
/**
* Class TransactionFactory
*/
class TransactionFactory
{
2019-03-08 05:47:51 +01:00
/** @var AccountRepositoryInterface */
private $accountRepository ;
/** @var TransactionJournal */
private $journal ;
2018-12-03 15:57:15 +01:00
/** @var User */
private $user ;
2018-09-06 12:29:32 +02:00
/**
* Constructor .
*/
public function __construct ()
{
2018-12-15 07:59:02 +01:00
if ( 'testing' === config ( 'app.env' )) {
2018-09-06 12:29:32 +02:00
Log :: warning ( sprintf ( '%s should not be instantiated in the TEST environment!' , \get_class ( $this )));
}
2019-03-08 05:47:51 +01:00
$this -> accountRepository = app ( AccountRepositoryInterface :: class );
2018-09-06 12:29:32 +02:00
}
2018-02-16 16:43:25 +01:00
/**
2019-03-08 05:47:51 +01:00
* @ param Account $account
* @ param TransactionCurrency $currency
* @ param string $amount
2018-02-16 16:43:25 +01:00
*
2019-03-08 05:47:51 +01:00
* @ return Transaction | null
2018-02-16 16:43:25 +01:00
*/
2019-03-08 05:47:51 +01:00
public function create ( Account $account , TransactionCurrency $currency , string $amount ) : ? Transaction
2018-02-16 16:43:25 +01:00
{
2019-03-18 16:52:49 +01:00
$result = null ;
$data = [
'reconciled' => false ,
'account_id' => $account -> id ,
'transaction_journal_id' => $this -> journal -> id ,
'description' => null ,
'transaction_currency_id' => $currency -> id ,
'amount' => $amount ,
'foreign_amount' => null ,
'foreign_currency_id' => null ,
'identifier' => 0 ,
];
try {
$result = Transaction :: create ( $data );
} catch ( QueryException $e ) {
Log :: error ( sprintf ( 'Could not create transaction: %s' , $e -> getMessage ()), $data );
}
2019-03-08 05:47:51 +01:00
if ( null !== $result ) {
Log :: debug (
sprintf (
'Created transaction #%d (%s %s), part of journal #%d' , $result -> id ,
$currency -> code , $amount , $this -> journal -> id
)
);
}
return $result ;
2018-02-16 16:43:25 +01:00
}
/**
2019-03-17 17:05:16 +01:00
* @ param NullArrayObject $data
* @ param TransactionCurrency $currency
* @ param TransactionCurrency | null $foreignCurrency
2018-02-16 16:43:25 +01:00
*
* @ return Collection
2018-05-06 20:42:07 +02:00
* @ throws FireflyException
2018-02-16 16:43:25 +01:00
*/
2019-03-17 17:05:16 +01:00
public function createPair ( NullArrayObject $data , TransactionCurrency $currency , ? TransactionCurrency $foreignCurrency ) : Collection
2018-02-16 16:43:25 +01:00
{
2019-03-18 16:52:49 +01:00
$sourceAccount = $this -> getAccount ( 'source' , $data [ 'source' ], ( int ) $data [ 'source_id' ], $data [ 'source_name' ]);
$destinationAccount = $this -> getAccount ( 'destination' , $data [ 'destination' ], ( int ) $data [ 'destination_id' ], $data [ 'destination_name' ]);
2019-03-08 05:47:51 +01:00
$amount = $this -> getAmount ( $data [ 'amount' ]);
2019-03-17 17:05:16 +01:00
$foreignAmount = $this -> getForeignAmount ( $data [ 'foreign_amount' ]);
2018-07-06 19:06:08 +02:00
2019-03-18 16:52:49 +01:00
$this -> makeDramaOverAccountTypes ( $sourceAccount , $destinationAccount );
2019-03-08 05:47:51 +01:00
$one = $this -> create ( $sourceAccount , $currency , app ( 'steam' ) -> negative ( $amount ));
$two = $this -> create ( $destinationAccount , $currency , app ( 'steam' ) -> positive ( $amount ));
2018-08-25 21:33:22 +02:00
2019-03-17 17:05:16 +01:00
$one -> reconciled = $data [ 'reconciled' ] ? ? false ;
$two -> reconciled = $data [ 'reconciled' ] ? ? false ;
// add foreign currency info to $one and $two if necessary.
if ( null !== $foreignCurrency ) {
$one -> foreign_currency_id = $foreignCurrency -> id ;
$two -> foreign_currency_id = $foreignCurrency -> id ;
$one -> foreign_amount = $foreignAmount ;
$two -> foreign_amount = $foreignAmount ;
}
$one -> save ();
$two -> save ();
2019-03-08 05:47:51 +01:00
return new Collection ([ $one , $two ]);
2018-02-19 19:44:46 +01:00
2019-03-08 05:47:51 +01:00
}
2018-08-25 21:33:22 +02:00
/**
2019-03-08 05:47:51 +01:00
* @ param string $direction
* @ param Account | null $source
* @ param int | null $sourceId
* @ param string | null $sourceName
2018-08-25 21:33:22 +02:00
*
2019-03-08 05:47:51 +01:00
* @ return Account
2018-08-25 21:33:22 +02:00
* @ throws FireflyException
*/
2019-03-18 16:52:49 +01:00
public function getAccount ( string $direction , ? Account $source , ? int $sourceId , ? string $sourceName ) : Account
2018-08-25 21:33:22 +02:00
{
2019-03-17 17:05:16 +01:00
Log :: debug ( sprintf ( 'Now in getAccount(%s)' , $direction ));
Log :: debug ( sprintf ( 'Parameters: ((account), %s, %s)' , var_export ( $sourceId , true ), var_export ( $sourceName , true )));
2019-03-08 05:47:51 +01:00
// expected type of source account, in order of preference
2019-03-23 08:10:59 +01:00
/** @var array $array */
$array = config ( 'firefly.expected_source_types' );
2019-03-08 05:47:51 +01:00
$expectedTypes = $array [ $direction ];
unset ( $array );
// and now try to find it, based on the type of transaction.
$transactionType = $this -> journal -> transactionType -> type ;
Log :: debug (
sprintf (
2019-03-17 17:05:16 +01:00
'Based on the fact that the transaction is a %s, the %s account should be in: %s' , $transactionType , $direction ,
2019-03-08 05:47:51 +01:00
implode ( ', ' , $expectedTypes [ $transactionType ])
)
);
// first attempt, check the "source" object.
if ( null !== $source && $source -> user_id === $this -> user -> id && \in_array ( $source -> accountType -> type , $expectedTypes [ $transactionType ], true )) {
Log :: debug ( sprintf ( 'Found "account" object for %s: #%d, %s' , $direction , $source -> id , $source -> name ));
return $source ;
}
// second attempt, find by ID.
if ( null !== $sourceId ) {
$source = $this -> accountRepository -> findNull ( $sourceId );
if ( null !== $source && \in_array ( $source -> accountType -> type , $expectedTypes [ $transactionType ], true )) {
2019-03-18 16:52:49 +01:00
Log :: debug (
sprintf ( 'Found "account_id" object for %s: #%d, "%s" of type %s' , $direction , $source -> id , $source -> name , $source -> accountType -> type )
);
2019-03-08 05:47:51 +01:00
return $source ;
}
}
// third attempt, find by name.
if ( null !== $sourceName ) {
// find by preferred type.
$source = $this -> accountRepository -> findByName ( $sourceName , [ $expectedTypes [ $transactionType ][ 0 ]]);
// or any type.
$source = $source ? ? $this -> accountRepository -> findByName ( $sourceName , $expectedTypes [ $transactionType ]);
if ( null !== $source ) {
Log :: debug ( sprintf ( 'Found "account_name" object for %s: #%d, %s' , $direction , $source -> id , $source -> name ));
return $source ;
}
}
2019-03-18 16:52:49 +01:00
if ( null === $sourceName && \in_array ( AccountType :: CASH , $expectedTypes [ $transactionType ], true )) {
return $this -> accountRepository -> getCashAccount ();
}
2019-03-17 17:05:16 +01:00
$sourceName = $sourceName ? ? '(no name)' ;
2019-03-08 05:47:51 +01:00
// final attempt, create it.
$preferredType = $expectedTypes [ $transactionType ][ 0 ];
if ( AccountType :: ASSET === $preferredType ) {
throw new FireflyException ( sprintf ( 'TransactionFactory: Cannot create asset account with ID #%d or name "%s".' , $sourceId , $sourceName ));
}
return $this -> accountRepository -> store (
[
'account_type_id' => null ,
'accountType' => $preferredType ,
'name' => $sourceName ,
'active' => true ,
'iban' => null ,
]
);
}
/**
* @ param string $amount
*
* @ return string
* @ throws FireflyException
*/
2019-03-18 16:52:49 +01:00
public function getAmount ( string $amount ) : string
2019-03-08 05:47:51 +01:00
{
if ( '' === $amount ) {
throw new FireflyException ( sprintf ( 'The amount cannot be an empty string: "%s"' , $amount ));
2018-08-25 21:33:22 +02:00
}
2019-03-08 05:47:51 +01:00
if ( 0 === bccomp ( '0' , $amount )) {
throw new FireflyException ( sprintf ( 'The amount seems to be zero: "%s"' , $amount ));
2018-08-25 21:33:22 +02:00
}
2019-03-08 05:47:51 +01:00
return $amount ;
2018-08-25 21:33:22 +02:00
}
2019-03-17 17:05:16 +01:00
/**
* @ param string | null $amount
*
* @ return string
*/
2019-03-18 16:52:49 +01:00
public function getForeignAmount ( ? string $amount ) : ? string
2019-03-17 17:05:16 +01:00
{
if ( null === $amount ) {
Log :: debug ( 'No foreign amount info in array. Return NULL' );
return null ;
}
if ( '' === $amount ) {
Log :: debug ( 'Foreign amount is empty string, return NULL.' );
return null ;
}
if ( 0 === bccomp ( '0' , $amount )) {
Log :: debug ( 'Foreign amount is 0.0, return NULL.' );
return null ;
}
Log :: debug ( sprintf ( 'Foreign amount is %s' , $amount ));
return $amount ;
}
2018-08-25 21:33:22 +02:00
2019-03-18 16:52:49 +01:00
/**
* This method will throw a Firefly III Exception of the source and destination account types are not OK .
*
* @ throws FireflyException
*
* @ param Account $source
* @ param Account $destination
*/
public function makeDramaOverAccountTypes ( Account $source , Account $destination ) : void
{
// if the source is X, then Y is allowed as destination.
2019-03-23 08:10:59 +01:00
$combinations = config ( 'firefly.source_dests' );
2019-03-18 16:52:49 +01:00
$sourceType = $source -> accountType -> type ;
$destType = $destination -> accountType -> type ;
$journalType = $this -> journal -> transactionType -> type ;
$allowed = $combinations [ $journalType ][ $sourceType ] ? ? [];
if ( ! \in_array ( $destType , $allowed , true )) {
throw new FireflyException (
sprintf (
'Journal of type "%s" has a source account of type "%s" and cannot accept a "%s"-account as destination, but only accounts of: %s' , $journalType , $sourceType ,
$destType , implode ( ', ' , $combinations [ $journalType ][ $sourceType ])
)
);
}
}
2018-02-18 16:35:26 +01:00
2019-03-18 16:52:49 +01:00
/**
* @ param TransactionJournal $journal
*/
public function setJournal ( TransactionJournal $journal ) : void
{
$this -> journal = $journal ;
}
/**
* @ param User $user
*/
public function setUser ( User $user ) : void
{
$this -> user = $user ;
$this -> accountRepository -> setUser ( $user );
}
2018-03-05 19:35:58 +01:00
}