2018-02-21 08:51:30 +01:00
< ? php
2024-11-25 04:18:55 +01:00
2018-02-21 08:51:30 +01:00
/**
* JournalUpdateService . php
2020-02-16 13:56:35 +01:00
* Copyright ( c ) 2019 james @ firefly - iii . org
2018-02-21 08:51:30 +01:00
*
2019-10-02 06:37:26 +02:00
* This file is part of Firefly III ( https :// github . com / firefly - iii ) .
2018-02-21 08:51:30 +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-21 08:51:30 +01:00
*
2019-10-02 06:37:26 +02:00
* This program is distributed in the hope that it will be useful ,
2018-02-21 08:51:30 +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-21 08:51:30 +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-21 08:51:30 +01:00
*/
declare ( strict_types = 1 );
2018-02-21 18:42:15 +01:00
namespace FireflyIII\Services\Internal\Update ;
2018-02-21 08:51:30 +01:00
2019-04-06 08:10:50 +02:00
use Carbon\Carbon ;
2022-12-30 20:25:04 +01:00
use Carbon\Exceptions\InvalidDateException ;
2023-12-20 16:43:15 +01:00
use Carbon\Exceptions\InvalidFormatException ;
2025-05-12 13:25:01 +02:00
use FireflyIII\Enums\AccountTypeEnum ;
2024-11-23 16:14:47 +01:00
use FireflyIII\Enums\TransactionTypeEnum ;
2023-06-21 05:55:57 +02:00
use FireflyIII\Events\TriggeredAuditLog ;
2019-04-06 08:10:50 +02:00
use FireflyIII\Exceptions\FireflyException ;
use FireflyIII\Factory\TagFactory ;
use FireflyIII\Factory\TransactionJournalMetaFactory ;
use FireflyIII\Factory\TransactionTypeFactory ;
use FireflyIII\Models\Account ;
2018-02-21 08:51:30 +01:00
use FireflyIII\Models\Transaction ;
2019-04-06 08:10:50 +02:00
use FireflyIII\Models\TransactionGroup ;
2018-02-21 08:51:30 +01:00
use FireflyIII\Models\TransactionJournal ;
2019-04-06 08:10:50 +02:00
use FireflyIII\Repositories\Account\AccountRepositoryInterface ;
use FireflyIII\Repositories\Bill\BillRepositoryInterface ;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface ;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface ;
2025-02-23 12:35:13 +01:00
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface ;
2025-06-01 14:00:35 +02:00
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface ;
2018-02-23 15:12:47 +01:00
use FireflyIII\Services\Internal\Support\JournalServiceTrait ;
2024-11-23 09:06:29 +01:00
use FireflyIII\Support\Facades\FireflyConfig ;
2025-12-07 16:47:04 +01:00
use FireflyIII\Support\Facades\Preferences ;
2025-12-17 08:43:39 +01:00
use FireflyIII\Support\Facades\Steam ;
2019-04-06 08:10:50 +02:00
use FireflyIII\Support\NullArrayObject ;
use FireflyIII\Validation\AccountValidator ;
2024-07-30 18:04:39 +02:00
use Illuminate\Support\Facades\Log ;
2019-02-12 21:49:28 +01:00
2018-02-21 08:51:30 +01:00
/**
* Class to centralise code that updates a journal given the input by system .
*
* Class JournalUpdateService
*/
class JournalUpdateService
{
2018-02-23 15:12:47 +01:00
use JournalServiceTrait ;
2019-02-12 21:49:28 +01:00
2025-12-07 16:47:04 +01:00
private BillRepositoryInterface $billRepository ;
private CurrencyRepositoryInterface $currencyRepository ;
2025-06-01 14:00:35 +02:00
private TransactionGroupRepositoryInterface $transactionGroupRepository ;
2025-12-07 16:47:04 +01:00
private array $data ;
private ? Account $destinationAccount = null ;
private ? Transaction $destinationTransaction = null ;
private array $metaDate
2025-12-30 16:13:28 +01:00
= [ 'interest_date' , 'book_date' , 'process_date' , 'due_date' , 'payment_date' , 'invoice_date' , '_internal_previous_date' ];
2025-12-07 16:47:04 +01:00
private array $metaString
= [
'sepa_cc' ,
'sepa_ct_op' ,
'sepa_ct_id' ,
'sepa_db' ,
'sepa_country' ,
'sepa_ep' ,
'sepa_ci' ,
'sepa_batch_id' ,
'recurrence_id' ,
'internal_reference' ,
'bunq_payment_id' ,
'external_id' ,
'external_url' ,
];
private ? Account $sourceAccount = null ;
private ? Transaction $sourceTransaction = null ;
private ? TransactionGroup $transactionGroup = null ;
private ? TransactionJournal $transactionJournal = null ;
private string $startCompareHash = '' ;
2019-04-06 08:10:50 +02:00
2018-09-06 12:29:32 +02:00
/**
2019-04-06 08:10:50 +02:00
* JournalUpdateService constructor .
2018-09-06 12:29:32 +02:00
*/
public function __construct ()
{
2025-06-02 05:31:18 +02:00
$this -> billRepository = app ( BillRepositoryInterface :: class );
$this -> categoryRepository = app ( CategoryRepositoryInterface :: class );
$this -> budgetRepository = app ( BudgetRepositoryInterface :: class );
$this -> tagFactory = app ( TagFactory :: class );
$this -> accountRepository = app ( AccountRepositoryInterface :: class );
$this -> currencyRepository = app ( CurrencyRepositoryInterface :: class );
2025-06-01 14:00:35 +02:00
$this -> transactionGroupRepository = app ( TransactionGroupRepositoryInterface :: class );
2019-04-06 08:10:50 +02:00
}
public function setData ( array $data ) : void
{
$this -> data = $data ;
}
public function setTransactionGroup ( TransactionGroup $transactionGroup ) : void
{
2025-12-26 20:07:06 +01:00
$this -> transactionGroup = $transactionGroup ;
2019-04-06 08:10:50 +02:00
$this -> billRepository -> setUser ( $transactionGroup -> user );
$this -> categoryRepository -> setUser ( $transactionGroup -> user );
$this -> budgetRepository -> setUser ( $transactionGroup -> user );
$this -> tagFactory -> setUser ( $transactionGroup -> user );
$this -> accountRepository -> setUser ( $transactionGroup -> user );
2025-06-01 14:00:35 +02:00
$this -> transactionGroupRepository -> setUser ( $transactionGroup -> user );
2025-05-31 07:56:20 +02:00
$this -> destinationAccount = null ;
2021-03-10 06:34:03 +01:00
$this -> destinationTransaction = null ;
2025-05-31 07:56:20 +02:00
$this -> sourceAccount = null ;
$this -> sourceTransaction = null ;
2025-06-02 05:31:18 +02:00
$this -> startCompareHash = $this -> transactionGroupRepository -> getCompareHash ( $transactionGroup );
2019-04-06 08:10:50 +02:00
}
public function setTransactionJournal ( TransactionJournal $transactionJournal ) : void
{
$this -> transactionJournal = $transactionJournal ;
}
public function update () : void
{
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Now in %s' , __METHOD__ ));
Log :: debug ( sprintf ( 'Now in JournalUpdateService for journal #%d.' , $this -> transactionJournal -> id ));
2021-06-13 07:04:18 +02:00
2025-11-09 09:08:03 +01:00
$this -> data [ 'reconciled' ] ? ? = null ;
2021-06-13 07:04:18 +02:00
2019-04-06 08:10:50 +02:00
// can we update account data using the new type?
if ( $this -> hasValidAccounts ()) {
2024-11-24 15:23:17 +01:00
Log :: info ( 'Account info is valid, now update.' );
2019-04-06 08:10:50 +02:00
// update accounts:
$this -> updateAccounts ();
// then also update transaction journal type ID:
$this -> updateType ();
$this -> transactionJournal -> refresh ();
2018-09-06 12:29:32 +02:00
}
2019-04-06 08:10:50 +02:00
// find and update bill, if possible.
$this -> updateBill ();
// update journal fields.
$this -> updateField ( 'description' );
$this -> updateField ( 'date' );
$this -> updateField ( 'order' );
$this -> transactionJournal -> save ();
$this -> transactionJournal -> refresh ();
2020-03-18 20:55:31 +01:00
$this -> updateCategory ();
$this -> updateBudget ();
$this -> updateTags ();
2021-03-11 06:29:07 +01:00
$this -> updateReconciled ();
2020-03-18 20:55:31 +01:00
$this -> updateNotes ();
$this -> updateMeta ();
$this -> updateCurrency ();
$this -> updateAmount ();
$this -> updateForeignAmount ();
2019-04-06 08:10:50 +02:00
2025-11-28 19:01:15 +01:00
Preferences :: mark ();
2019-04-06 08:10:50 +02:00
$this -> transactionJournal -> refresh ();
2024-07-31 08:20:19 +02:00
Log :: debug ( 'Done with update journal routine' );
2018-09-06 12:29:32 +02:00
}
2023-06-21 12:34:58 +02:00
private function hasValidAccounts () : bool
2019-04-06 08:10:50 +02:00
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in hasValidAccounts().' );
2024-11-25 04:18:55 +01:00
2023-06-21 12:34:58 +02:00
return $this -> hasValidSourceAccount () && $this -> hasValidDestinationAccount ();
}
private function hasValidSourceAccount () : bool
{
2025-12-26 20:07:06 +01:00
$sourceId = $this -> data [ 'source_id' ] ? ? null ;
$sourceName = $this -> data [ 'source_name' ] ? ? null ;
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Now in hasValidSourceAccount("%s","%s").' , $sourceId , $sourceName ));
2023-06-21 12:34:58 +02:00
if ( ! $this -> hasFields ([ 'source_id' , 'source_name' ])) {
$origSourceAccount = $this -> getOriginalSourceAccount ();
2025-05-31 07:56:20 +02:00
$sourceId = $origSourceAccount -> id ;
$sourceName = $origSourceAccount -> name ;
2018-02-21 08:58:06 +01:00
}
2018-02-21 08:51:30 +01:00
2023-06-21 12:34:58 +02:00
// make new account validator.
$expectedType = $this -> getExpectedType ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( '(a) Expected type (new or unchanged) is %s' , $expectedType ));
2023-06-21 12:34:58 +02:00
// make a new validator.
/** @var AccountValidator $validator */
2025-12-26 20:07:06 +01:00
$validator = app ( AccountValidator :: class );
2023-06-21 12:34:58 +02:00
$validator -> setTransactionType ( $expectedType );
$validator -> setUser ( $this -> transactionJournal -> user );
2025-12-26 20:07:06 +01:00
$result = $validator -> validateSource ([ 'id' => $sourceId , 'name' => $sourceName ]);
2025-12-30 16:13:28 +01:00
Log :: debug ( sprintf ( 'hasValidSourceAccount(%d, "%s") will return %s' , $sourceId , $sourceName , var_export ( $result , true )));
2023-06-21 12:34:58 +02:00
2025-12-30 16:13:28 +01:00
// TODO type overrule the account validator may have a different opinion on the transaction type.
2023-06-21 12:34:58 +02:00
// validate submitted info:
return $result ;
2019-04-06 08:10:50 +02:00
}
2023-06-21 12:34:58 +02:00
private function hasFields ( array $fields ) : bool
2019-04-06 08:10:50 +02:00
{
2025-12-26 20:07:06 +01:00
return array_any ( $fields , fn ( $field ) : bool => array_key_exists ( $field , $this -> data ));
2019-04-06 08:10:50 +02:00
}
private function getOriginalSourceAccount () : Account
{
2025-05-27 17:06:15 +02:00
if ( ! $this -> sourceAccount instanceof Account ) {
2025-05-31 07:56:20 +02:00
$source = $this -> getSourceTransaction ();
2019-04-06 08:10:50 +02:00
$this -> sourceAccount = $source -> account ;
}
return $this -> sourceAccount ;
}
private function getSourceTransaction () : Transaction
{
2025-05-27 17:06:15 +02:00
if ( ! $this -> sourceTransaction instanceof Transaction ) {
2025-01-04 07:39:16 +01:00
/** @var null|Transaction $result */
2025-05-31 07:56:20 +02:00
$result = $this -> transactionJournal -> transactions () -> with ([ 'account' ]) -> where ( 'amount' , '<' , 0 ) -> first ();
2025-01-04 07:10:37 +01:00
$this -> sourceTransaction = $result ;
2019-04-06 08:10:50 +02:00
}
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'getSourceTransaction: %s' , $this -> sourceTransaction -> amount ));
2018-02-21 08:51:30 +01:00
2019-04-06 08:10:50 +02:00
return $this -> sourceTransaction ;
}
2018-05-29 07:25:04 +02:00
2019-04-06 08:10:50 +02:00
/**
2023-06-21 12:34:58 +02:00
* This method returns the current or expected type of the journal ( in case of a change ) based on the data in the
* array .
2018-02-23 16:21:28 +01:00
*
2023-06-21 12:34:58 +02:00
* If the array contains key 'type' and the value is correct , this is returned . Otherwise , the original type is
* returned .
2023-06-21 05:55:57 +02:00
*/
2023-06-21 12:34:58 +02:00
private function getExpectedType () : string
2023-06-21 05:55:57 +02:00
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in getExpectedType()' );
2023-06-21 12:34:58 +02:00
if ( $this -> hasFields ([ 'type' ])) {
return ucfirst ( 'opening-balance' === $this -> data [ 'type' ] ? 'opening balance' : $this -> data [ 'type' ]);
2023-06-21 05:55:57 +02:00
}
2023-06-21 12:34:58 +02:00
return $this -> transactionJournal -> transactionType -> type ;
2019-04-06 08:10:50 +02:00
}
private function hasValidDestinationAccount () : bool
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in hasValidDestinationAccount().' );
2025-12-26 20:07:06 +01:00
$destId = $this -> data [ 'destination_id' ] ? ? null ;
$destName = $this -> data [ 'destination_name' ] ? ? null ;
2019-04-06 08:10:50 +02:00
if ( ! $this -> hasFields ([ 'destination_id' , 'destination_name' ])) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'No destination info submitted, grab the original data.' );
2019-04-06 08:10:50 +02:00
$destination = $this -> getOriginalDestinationAccount ();
2025-05-31 07:56:20 +02:00
$destId = $destination -> id ;
$destName = $destination -> name ;
2019-04-06 08:10:50 +02:00
}
// make new account validator.
2025-12-26 20:07:06 +01:00
$expectedType = $this -> getExpectedType ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( '(b) Expected type (new or unchanged) is %s' , $expectedType ));
2019-04-06 08:10:50 +02:00
// make a new validator.
/** @var AccountValidator $validator */
2025-12-26 20:07:06 +01:00
$validator = app ( AccountValidator :: class );
2019-04-06 08:10:50 +02:00
$validator -> setTransactionType ( $expectedType );
$validator -> setUser ( $this -> transactionJournal -> user );
$validator -> source = $this -> getValidSourceAccount ();
2025-05-31 07:56:20 +02:00
$result = $validator -> validateDestination ([ 'id' => $destId , 'name' => $destName ]);
2025-12-30 16:13:28 +01:00
Log :: debug ( sprintf ( 'hasValidDestinationAccount(%d, "%s") will return %s' , $destId , $destName , var_export ( $result , true )));
2019-04-06 08:10:50 +02:00
2022-10-30 11:43:17 +01:00
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
2020-03-31 07:41:48 +02:00
2019-04-06 08:10:50 +02:00
// validate submitted info:
return $result ;
}
2023-06-21 12:34:58 +02:00
private function getOriginalDestinationAccount () : Account
2019-04-06 08:10:50 +02:00
{
2025-05-27 17:06:15 +02:00
if ( ! $this -> destinationAccount instanceof Account ) {
2025-05-31 07:56:20 +02:00
$destination = $this -> getDestinationTransaction ();
2023-06-21 12:34:58 +02:00
$this -> destinationAccount = $destination -> account ;
2021-03-12 18:31:19 +01:00
}
2023-06-21 12:34:58 +02:00
return $this -> destinationAccount ;
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
/**
* Get destination transaction .
*/
private function getDestinationTransaction () : Transaction
{
2025-05-27 17:06:15 +02:00
if ( ! $this -> destinationTransaction instanceof Transaction ) {
2025-01-04 07:39:16 +01:00
/** @var null|Transaction $result */
2025-05-31 07:56:20 +02:00
$result = $this -> transactionJournal -> transactions () -> where ( 'amount' , '>' , 0 ) -> first ();
2025-01-04 07:10:37 +01:00
$this -> destinationTransaction = $result ;
2023-06-21 12:34:58 +02:00
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
return $this -> destinationTransaction ;
2021-03-12 18:31:19 +01:00
}
/**
2023-06-21 12:34:58 +02:00
* Does a validation and returns the source account . This method will break if the source isn ' t really valid .
2021-03-12 18:31:19 +01:00
*/
2023-06-21 12:34:58 +02:00
private function getValidSourceAccount () : Account
2021-03-12 18:31:19 +01:00
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in getValidSourceAccount().' );
2023-06-21 12:34:58 +02:00
if ( ! $this -> hasFields ([ 'source_id' , 'source_name' ])) {
return $this -> getOriginalSourceAccount ();
2019-04-06 08:10:50 +02:00
}
2023-06-21 12:34:58 +02:00
2025-12-26 20:07:06 +01:00
$sourceInfo = [
2025-05-31 07:56:20 +02:00
'id' => ( int )( $this -> data [ 'source_id' ] ? ? null ),
'name' => $this -> data [ 'source_name' ] ? ? null ,
'iban' => $this -> data [ 'source_iban' ] ? ? null ,
2023-06-21 12:34:58 +02:00
'number' => $this -> data [ 'source_number' ] ? ? null ,
2025-05-31 07:56:20 +02:00
'bic' => $this -> data [ 'source_bic' ] ? ? null ,
2023-06-21 12:34:58 +02:00
];
$expectedType = $this -> getExpectedType ();
2023-12-20 19:35:52 +01:00
2023-06-21 12:34:58 +02:00
try {
$result = $this -> getAccount ( $expectedType , 'source' , $sourceInfo );
} catch ( FireflyException $e ) {
2024-11-24 15:23:17 +01:00
Log :: error ( sprintf ( 'Cant get the valid source account: %s' , $e -> getMessage ()));
2023-06-21 12:34:58 +02:00
$result = $this -> getOriginalSourceAccount ();
2021-03-12 18:31:19 +01:00
}
2019-04-06 08:10:50 +02:00
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'getValidSourceAccount() will return #%d ("%s")' , $result -> id , $result -> name ));
2023-06-21 12:34:58 +02:00
return $result ;
2019-04-06 08:10:50 +02:00
}
/**
* Will update the source and destination accounts of this journal . Assumes they are valid .
*/
private function updateAccounts () : void
{
2025-12-26 20:07:06 +01:00
$source = $this -> getValidSourceAccount ();
$destination = $this -> getValidDestinationAccount ();
2019-04-06 08:10:50 +02:00
// cowardly refuse to update if both accounts are the same.
if ( $source -> id === $destination -> id ) {
2024-11-24 15:23:17 +01:00
Log :: error ( sprintf ( 'Source + dest accounts are equal (%d, "%s")' , $source -> id , $source -> name ));
2019-04-06 08:10:50 +02:00
return ;
2018-02-23 16:21:28 +01:00
}
2019-04-06 08:10:50 +02:00
2020-10-24 18:40:17 +02:00
$origSourceTransaction = $this -> getSourceTransaction ();
$origSourceTransaction -> account () -> associate ( $source );
$origSourceTransaction -> save ();
2019-04-06 08:10:50 +02:00
2025-12-26 20:07:06 +01:00
$destTransaction = $this -> getDestinationTransaction ();
2019-07-20 16:02:50 +02:00
$destTransaction -> account () -> associate ( $destination );
$destTransaction -> save ();
// refresh transactions.
$this -> sourceTransaction -> refresh ();
$this -> destinationTransaction -> refresh ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Will set source to #%d ("%s")' , $source -> id , $source -> name ));
Log :: debug ( sprintf ( 'Will set dest to #%d ("%s")' , $destination -> id , $destination -> name ));
2019-04-06 08:10:50 +02:00
}
/**
2023-06-21 12:34:58 +02:00
* Does a validation and returns the destination account . This method will break if the dest isn ' t really valid .
2019-04-06 08:10:50 +02:00
*/
2023-06-21 12:34:58 +02:00
private function getValidDestinationAccount () : Account
2019-04-06 08:10:50 +02:00
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in getValidDestinationAccount().' );
2023-06-21 12:34:58 +02:00
if ( ! $this -> hasFields ([ 'destination_id' , 'destination_name' ])) {
return $this -> getOriginalDestinationAccount ();
2020-03-18 20:55:31 +01:00
}
2025-12-26 20:07:06 +01:00
$destInfo = [
2025-05-31 07:56:20 +02:00
'id' => ( int )( $this -> data [ 'destination_id' ] ? ? null ),
'name' => $this -> data [ 'destination_name' ] ? ? null ,
'iban' => $this -> data [ 'destination_iban' ] ? ? null ,
2023-06-21 12:34:58 +02:00
'number' => $this -> data [ 'destination_number' ] ? ? null ,
2025-05-31 07:56:20 +02:00
'bic' => $this -> data [ 'destination_bic' ] ? ? null ,
2023-06-21 12:34:58 +02:00
];
// make new account validator.
$expectedType = $this -> getExpectedType ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( '(c) Expected type (new or unchanged) is %s' , $expectedType ));
2023-12-20 19:35:52 +01:00
2019-04-06 08:10:50 +02:00
try {
2023-06-21 12:34:58 +02:00
$result = $this -> getAccount ( $expectedType , 'destination' , $destInfo );
2019-04-06 08:10:50 +02:00
} catch ( FireflyException $e ) {
2024-11-24 15:23:17 +01:00
Log :: error ( sprintf ( 'getValidDestinationAccount() threw unexpected error: %s' , $e -> getMessage ()));
2023-06-21 12:34:58 +02:00
$result = $this -> getOriginalDestinationAccount ();
}
return $result ;
}
/**
* Updates journal transaction type .
*/
private function updateType () : void
{
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Now in updateType()' );
2023-06-21 12:34:58 +02:00
if ( $this -> hasFields ([ 'type' ])) {
2025-12-26 20:07:06 +01:00
$type = 'opening-balance' === $this -> data [ 'type' ] ? 'opening balance' : $this -> data [ 'type' ];
2024-11-24 15:23:17 +01:00
Log :: debug (
2023-06-21 12:34:58 +02:00
sprintf (
'Trying to change journal #%d from a %s to a %s.' ,
$this -> transactionJournal -> id ,
$this -> transactionJournal -> transactionType -> type ,
$type
)
);
/** @var TransactionTypeFactory $typeFactory */
$typeFactory = app ( TransactionTypeFactory :: class );
2025-05-31 07:56:20 +02:00
$result = $typeFactory -> find ( $this -> data [ 'type' ]);
2023-06-21 12:34:58 +02:00
if ( null !== $result ) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Changed transaction type!' );
2023-06-21 12:34:58 +02:00
$this -> transactionJournal -> transaction_type_id = $result -> id ;
$this -> transactionJournal -> save ();
return ;
}
2021-03-12 18:31:19 +01:00
return ;
}
2024-11-24 15:23:17 +01:00
Log :: debug ( 'No type field present.' );
2019-04-06 08:10:50 +02:00
}
/**
* Update journal bill information .
*/
private function updateBill () : void
{
$type = $this -> transactionJournal -> transactionType -> type ;
if ((
2025-12-26 20:07:06 +01:00
array_key_exists ( 'bill_id' , $this -> data )
2023-05-29 13:56:55 +02:00
|| array_key_exists ( 'bill_name' , $this -> data )
2025-12-26 20:07:06 +01:00
)
2025-01-03 09:05:19 +01:00
&& TransactionTypeEnum :: WITHDRAWAL -> value === $type
2019-04-06 08:10:50 +02:00
) {
2025-05-31 07:56:20 +02:00
$billId = ( int )( $this -> data [ 'bill_id' ] ? ? 0 );
$billName = ( string )( $this -> data [ 'bill_name' ] ? ? '' );
$bill = $this -> billRepository -> findBill ( $billId , $billName );
2021-09-18 10:20:19 +02:00
$this -> transactionJournal -> bill_id = $bill ? -> id ;
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Updated bill ID' );
2019-04-06 08:10:50 +02:00
}
}
/**
2023-06-21 05:55:57 +02:00
* Update journal generic field . Cannot be set to NULL .
2019-04-06 08:10:50 +02:00
*/
2023-06-21 05:55:57 +02:00
private function updateField ( string $fieldName ) : void
2019-04-06 08:10:50 +02:00
{
2025-05-31 07:22:42 +02:00
if ( array_key_exists ( $fieldName , $this -> data ) && '' !== ( string ) $this -> data [ $fieldName ]) {
2025-12-26 20:07:06 +01:00
$value = $this -> data [ $fieldName ];
2023-06-21 05:55:57 +02:00
if ( 'date' === $fieldName ) {
2023-12-20 19:35:52 +01:00
if ( ! $value instanceof Carbon ) {
2023-06-21 05:55:57 +02:00
$value = new Carbon ( $value );
}
2024-11-22 06:04:32 +01:00
$value -> setTimezone ( config ( 'app.timezone' ));
// 2024-11-22, overrule timezone with UTC and store it as UTC.
2025-01-03 14:56:06 +01:00
if ( true === FireflyConfig :: get ( 'utc' , false ) -> data ) {
2024-11-22 06:04:32 +01:00
$value -> setTimezone ( 'UTC' );
}
2023-06-21 05:55:57 +02:00
// do some parsing.
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Create date value from string "%s".' , $value ));
2024-11-06 14:10:34 +01:00
$this -> transactionJournal -> date_tz = $value -> format ( 'e' );
2025-12-30 16:19:05 +01:00
$res = $value -> gt ( $this -> transactionJournal -> date );
2025-12-30 16:13:28 +01:00
Log :: debug ( sprintf ( 'Old date: %s, new date: %s' , $this -> transactionJournal -> date -> toW3cString (), $value -> toW3cString ()));
2025-12-30 16:19:05 +01:00
2025-12-30 16:13:28 +01:00
/** @var TransactionJournalMetaFactory $factory */
2025-12-30 16:19:05 +01:00
$factory = app ( TransactionJournalMetaFactory :: class );
$set = [
2025-12-30 16:13:28 +01:00
'journal' => $this -> transactionJournal ,
'name' => '_internal_previous_date' ,
'data' => null ,
];
2025-12-30 16:19:05 +01:00
if ( $res ) {
2025-12-30 16:13:28 +01:00
Log :: debug ( 'Transaction is set to be AFTER its current date. Save also the "_internal_previous_date"-field.' );
$set [ 'data' ] = clone $this -> transactionJournal -> date ;
}
2025-12-30 16:19:05 +01:00
if ( ! $res ) {
2025-12-30 16:13:28 +01:00
Log :: debug ( 'Transaction is NOT set to be AFTER its current date. Remove the "_internal_previous_date"-field.' );
}
$factory -> updateOrCreate ( $set );
2023-06-21 05:55:57 +02:00
}
2025-12-07 16:47:04 +01:00
event ( new TriggeredAuditLog ( $this -> transactionJournal -> user , $this -> transactionJournal , sprintf ( 'update_%s' , $fieldName ), $this -> transactionJournal -> { $fieldName }, $value ));
2023-06-21 05:55:57 +02:00
2023-12-20 19:35:52 +01:00
$this -> transactionJournal -> { $fieldName } = $value ; // @phpstan-ignore-line
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Updated %s' , $fieldName ));
2019-04-06 08:10:50 +02:00
}
2018-02-23 16:21:28 +01:00
}
2023-06-21 12:34:58 +02:00
private function updateCategory () : void
2019-04-06 08:10:50 +02:00
{
2023-06-21 12:34:58 +02:00
// update category
if ( $this -> hasFields ([ 'category_id' , 'category_name' ])) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Will update category.' );
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
$this -> storeCategory ( $this -> transactionJournal , new NullArrayObject ( $this -> data ));
2023-06-21 05:55:57 +02:00
}
2023-06-21 12:34:58 +02:00
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
private function updateBudget () : void
{
// update budget
if ( $this -> hasFields ([ 'budget_id' , 'budget_name' ])) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Will update budget.' );
2023-06-21 12:34:58 +02:00
$this -> storeBudget ( $this -> transactionJournal , new NullArrayObject ( $this -> data ));
}
// is transfer? remove budget
2025-01-03 09:05:56 +01:00
if ( TransactionTypeEnum :: TRANSFER -> value === $this -> transactionJournal -> transactionType -> type ) {
2023-06-21 12:34:58 +02:00
$this -> transactionJournal -> budgets () -> sync ([]);
}
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
private function updateTags () : void
{
if ( $this -> hasFields ([ 'tags' ])) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Will update tags.' );
2023-06-21 12:34:58 +02:00
$tags = $this -> data [ 'tags' ] ? ? null ;
$this -> storeTags ( $this -> transactionJournal , $tags );
2019-04-06 08:10:50 +02:00
}
2023-06-21 12:34:58 +02:00
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
private function updateReconciled () : void
{
if ( array_key_exists ( 'reconciled' , $this -> data ) && is_bool ( $this -> data [ 'reconciled' ])) {
$this -> transactionJournal -> transactions () -> update ([ 'reconciled' => $this -> data [ 'reconciled' ]]);
2023-06-21 05:55:57 +02:00
}
2023-06-21 12:34:58 +02:00
}
2023-06-21 05:55:57 +02:00
2023-06-21 12:34:58 +02:00
private function updateNotes () : void
{
// update notes.
if ( $this -> hasFields ([ 'notes' ])) {
2025-05-31 07:22:42 +02:00
$notes = '' === ( string ) $this -> data [ 'notes' ] ? null : $this -> data [ 'notes' ];
2023-06-21 12:34:58 +02:00
$this -> storeNotes ( $this -> transactionJournal , $notes );
}
2019-04-06 08:10:50 +02:00
}
2020-03-18 20:55:31 +01:00
private function updateMeta () : void
{
// update meta fields.
// first string
if ( $this -> hasFields ( $this -> metaString )) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Meta string fields are present.' );
2020-03-18 20:55:31 +01:00
$this -> updateMetaFields ();
}
// then date fields.
if ( $this -> hasFields ( $this -> metaDate )) {
2024-11-24 15:23:17 +01:00
Log :: debug ( 'Meta date fields are present.' );
2020-03-18 20:55:31 +01:00
$this -> updateMetaDateFields ();
}
}
2023-06-21 12:34:58 +02:00
private function updateMetaFields () : void
2018-02-23 16:21:28 +01:00
{
2019-04-06 08:10:50 +02:00
/** @var TransactionJournalMetaFactory $factory */
$factory = app ( TransactionJournalMetaFactory :: class );
2023-06-21 12:34:58 +02:00
foreach ( $this -> metaString as $field ) {
2019-04-06 08:10:50 +02:00
if ( $this -> hasFields ([ $field ])) {
2023-06-21 12:34:58 +02:00
$value = '' === $this -> data [ $field ] ? null : $this -> data [ $field ];
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Field "%s" is present ("%s"), try to update it.' , $field , $value ));
2025-12-26 20:07:06 +01:00
$set = [
2019-04-06 08:10:50 +02:00
'journal' => $this -> transactionJournal ,
2025-05-31 07:56:20 +02:00
'name' => $field ,
'data' => $value ,
2019-04-06 08:10:50 +02:00
];
$factory -> updateOrCreate ( $set );
}
2018-02-23 16:21:28 +01:00
}
2019-04-06 08:10:50 +02:00
}
2018-02-23 16:21:28 +01:00
2023-06-21 12:34:58 +02:00
private function updateMetaDateFields () : void
2019-04-06 08:10:50 +02:00
{
/** @var TransactionJournalMetaFactory $factory */
$factory = app ( TransactionJournalMetaFactory :: class );
2023-06-21 12:34:58 +02:00
foreach ( $this -> metaDate as $field ) {
2019-04-06 08:10:50 +02:00
if ( $this -> hasFields ([ $field ])) {
2023-06-21 12:34:58 +02:00
try {
2025-05-31 07:22:42 +02:00
$value = '' === ( string ) $this -> data [ $field ] ? null : new Carbon ( $this -> data [ $field ]);
2025-12-26 20:07:06 +01:00
} catch ( InvalidDateException | InvalidFormatException $e ) { // @phpstan-ignore-line
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( '%s is not a valid date value: %s' , $this -> data [ $field ], $e -> getMessage ()));
2023-06-21 12:34:58 +02:00
return ;
}
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Field "%s" is present ("%s"), try to update it.' , $field , $value ));
2019-04-06 08:10:50 +02:00
$set = [
'journal' => $this -> transactionJournal ,
2025-05-31 07:56:20 +02:00
'name' => $field ,
'data' => $value ,
2019-04-06 08:10:50 +02:00
];
$factory -> updateOrCreate ( $set );
2024-11-06 14:10:34 +01:00
// also set date with timezone.
$set = [
'journal' => $this -> transactionJournal ,
2025-05-31 07:56:20 +02:00
'name' => sprintf ( '%s_tz' , $field ),
'data' => $value ? -> format ( 'e' ),
2024-11-06 14:10:34 +01:00
];
$factory -> updateOrCreate ( $set );
2019-04-06 08:10:50 +02:00
}
}
2018-02-23 16:21:28 +01:00
}
2023-06-21 12:34:58 +02:00
private function updateCurrency () : void
2020-03-18 20:55:31 +01:00
{
2023-06-21 12:34:58 +02:00
// update transactions.
if ( ! $this -> hasFields ([ 'currency_id' , 'currency_code' ])) {
return ;
}
2025-12-26 20:07:06 +01:00
$currencyId = $this -> data [ 'currency_id' ] ? ? null ;
$currencyCode = $this -> data [ 'currency_code' ] ? ? null ;
$currency = $this -> currencyRepository -> findCurrency ( $currencyId , $currencyCode );
2023-11-05 16:55:16 +01:00
// update currency everywhere.
$this -> transactionJournal -> transaction_currency_id = $currency -> id ;
$this -> transactionJournal -> save ();
2025-12-26 20:07:06 +01:00
$source = $this -> getSourceTransaction ();
$source -> transaction_currency_id = $currency -> id ;
2023-11-05 16:55:16 +01:00
$source -> save ();
2025-12-26 20:07:06 +01:00
$dest = $this -> getDestinationTransaction ();
$dest -> transaction_currency_id = $currency -> id ;
2023-11-05 16:55:16 +01:00
$dest -> save ();
// refresh transactions.
$this -> sourceTransaction -> refresh ();
$this -> destinationTransaction -> refresh ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Updated currency to #%d (%s)' , $currency -> id , $currency -> code ));
2020-03-18 20:55:31 +01:00
}
2023-06-21 12:34:58 +02:00
private function updateAmount () : void
2020-03-18 20:55:31 +01:00
{
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Now in %s' , __METHOD__ ));
2023-06-21 12:34:58 +02:00
if ( ! $this -> hasFields ([ 'amount' ])) {
return ;
2020-03-18 20:55:31 +01:00
}
2023-06-21 12:34:58 +02:00
2025-12-26 20:07:06 +01:00
$value = $this -> data [ 'amount' ] ? ? '' ;
2025-12-24 07:45:03 +01:00
Log :: debug ( sprintf ( '[a] Amount is now "%s"' , $value ));
2023-12-20 19:35:52 +01:00
2023-06-21 12:34:58 +02:00
try {
$amount = $this -> getAmount ( $value );
} catch ( FireflyException $e ) {
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'getAmount("%s") returns error: %s' , $value , $e -> getMessage ()));
2023-06-21 12:34:58 +02:00
return ;
}
2025-12-24 07:45:03 +01:00
Log :: debug ( sprintf ( '[b] Amount is now "%s"' , $value ));
2025-05-31 07:56:20 +02:00
$origSourceTransaction = $this -> getSourceTransaction ();
2025-12-24 07:45:03 +01:00
$destTransaction = $this -> getDestinationTransaction ();
$originalSourceAmount = $origSourceTransaction -> amount ;
$originalDestAmount = $destTransaction -> amount ;
2025-12-17 08:47:31 +01:00
$origSourceTransaction -> amount = Steam :: negative ( $amount );
2024-07-31 08:20:19 +02:00
$origSourceTransaction -> balance_dirty = true ;
2025-12-22 04:36:14 +01:00
$destTransaction -> amount = Steam :: positive ( $amount );
$destTransaction -> balance_dirty = true ;
2023-06-21 12:34:58 +02:00
$destTransaction -> save ();
2025-12-24 07:45:03 +01:00
$origSourceTransaction -> save ();
2023-06-21 12:34:58 +02:00
// refresh transactions.
$this -> sourceTransaction -> refresh ();
$this -> destinationTransaction -> refresh ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Updated amount to "%s"' , $amount ));
2025-12-07 16:47:04 +01:00
2025-12-26 20:07:06 +01:00
$group = $this -> transactionGroup ;
2025-12-15 14:26:50 +01:00
if ( null === $group ) {
2025-12-15 14:19:43 +01:00
$group = $this -> transactionJournal ? -> transactionGroup ;
}
2025-12-24 07:45:03 +01:00
if ( null === $group || null === $this -> transactionJournal ) {
2025-12-15 14:19:43 +01:00
return ;
}
2025-12-24 07:45:03 +01:00
if ( 0 === bccomp ( $origSourceTransaction -> amount , $originalSourceAmount )) {
Log :: debug ( 'Amount was not actually changed, return.' );
2025-12-26 20:07:06 +01:00
2025-12-24 07:45:03 +01:00
return ;
2025-12-20 20:12:07 +01:00
}
2025-12-24 07:45:03 +01:00
Log :: debug ( 'Amount was changed.' );
2025-12-26 20:07:06 +01:00
$transfer = TransactionTypeEnum :: TRANSFER -> value === $this -> transactionJournal -> transactionType -> type ;
$withdrawal = TransactionTypeEnum :: WITHDRAWAL -> value === $this -> transactionJournal -> transactionType -> type ;
$deposit = TransactionTypeEnum :: DEPOSIT -> value === $this -> transactionJournal -> transactionType -> type ;
$makePositive = $transfer || $deposit ? true : false ;
2025-12-24 07:45:03 +01:00
// assume withdrawal, use the source for amount (negative), and destination for currency.
2025-12-26 20:07:06 +01:00
$originalAmount = $originalSourceAmount ;
$recordCurrency = $destTransaction -> transactionCurrency ;
2025-12-24 07:45:03 +01:00
Log :: debug ( sprintf ( 'Transaction is a %s, original amount is %s and currency is %s' , $this -> transactionJournal -> transactionType -> type , $originalAmount , $recordCurrency -> code ));
if ( $withdrawal || $transfer ) {
Log :: debug ( 'Use these values to record a changed withdrawal amount' );
}
if ( ! $withdrawal && ! $transfer ) {
$originalAmount = $originalDestAmount ;
$recordCurrency = $origSourceTransaction -> transactionCurrency ;
Log :: debug ( 'Use destination amount to record a changed withdrawal amount' );
Log :: debug ( sprintf ( 'Transaction is a %s, original amount now is %s and currency is now %s' , $this -> transactionJournal -> transactionType -> type , $originalAmount , $recordCurrency -> code ));
}
2025-12-26 20:07:06 +01:00
$originalAmount = $makePositive ? Steam :: positive ( $originalAmount ) : Steam :: negative ( $originalAmount );
$value = $makePositive ? Steam :: positive ( $value ) : Steam :: negative ( $value );
2025-12-24 07:45:03 +01:00
// should not return in NULL but seems to do.
event ( new TriggeredAuditLog (
2025-12-26 20:07:06 +01:00
$group -> user ,
$group ,
'update_amount' ,
[
'currency_symbol' => $recordCurrency -> symbol ,
'decimal_places' => $recordCurrency -> decimal_places ,
'amount' => $originalAmount ,
],
[
'currency_symbol' => $recordCurrency -> symbol ,
'decimal_places' => $recordCurrency -> decimal_places ,
'amount' => $value ,
]
));
2020-03-18 20:55:31 +01:00
}
2023-06-21 12:34:58 +02:00
private function updateForeignAmount () : void
2019-04-06 08:10:50 +02:00
{
2023-06-21 12:34:58 +02:00
// amount, foreign currency.
if ( ! $this -> hasFields ([ 'foreign_currency_id' , 'foreign_currency_code' , 'foreign_amount' ])) {
return ;
2019-04-06 08:10:50 +02:00
}
2021-03-11 06:29:07 +01:00
2025-05-31 07:56:20 +02:00
$amount = $this -> data [ 'foreign_amount' ] ? ? null ;
$foreignAmount = $this -> getForeignAmount ( $amount );
$source = $this -> getSourceTransaction ();
$dest = $this -> getDestinationTransaction ();
2023-06-21 12:34:58 +02:00
$foreignCurrency = $source -> foreignCurrency ;
// find currency in data array
2025-05-31 07:56:20 +02:00
$newForeignId = $this -> data [ 'foreign_currency_id' ] ? ? null ;
$newForeignCode = $this -> data [ 'foreign_currency_code' ] ? ? null ;
2025-02-23 12:28:27 +01:00
$foreignCurrency = $this -> currencyRepository -> findCurrencyNull ( $newForeignId , $newForeignCode )
2025-12-07 16:47:04 +01:00
? ? $foreignCurrency ;
2023-06-21 12:34:58 +02:00
// not the same as normal currency
if ( null !== $foreignCurrency && $foreignCurrency -> id === $this -> transactionJournal -> transaction_currency_id ) {
2024-11-24 15:23:17 +01:00
Log :: error ( sprintf ( 'Foreign currency is equal to normal currency (%s)' , $foreignCurrency -> code ));
2023-06-21 12:34:58 +02:00
return ;
}
// add foreign currency info to source and destination if possible.
if ( null !== $foreignCurrency && null !== $foreignAmount ) {
$source -> foreign_currency_id = $foreignCurrency -> id ;
2025-12-17 08:47:31 +01:00
$source -> foreign_amount = Steam :: negative ( $foreignAmount );
2023-06-21 12:34:58 +02:00
$source -> save ();
2024-07-30 18:04:39 +02:00
// if the transaction is a TRANSFER, and the foreign amount and currency are set (like they seem to be)
// the correct fields to update in the destination transaction are NOT the foreign amount and currency
// but rather the normal amount and currency. This is new behavior.
2025-12-26 20:07:06 +01:00
$isTransfer = TransactionTypeEnum :: TRANSFER -> value === $this -> transactionJournal -> transactionType -> type ;
2025-05-12 13:25:01 +02:00
// also check if it is not between an asset account and a liability, because then the same rule applies.
2025-12-26 20:07:06 +01:00
$isBetween = $this -> isBetweenAssetAndLiability ();
2025-05-12 13:25:01 +02:00
if ( $isTransfer || $isBetween ) {
2024-07-30 18:04:39 +02:00
Log :: debug ( 'Switch amounts, store in amount and not foreign_amount' );
2024-07-31 08:20:19 +02:00
$dest -> transaction_currency_id = $foreignCurrency -> id ;
2025-12-17 08:47:31 +01:00
$dest -> amount = Steam :: positive ( $foreignAmount );
$dest -> foreign_amount = Steam :: positive ( $source -> amount );
2025-05-31 07:56:20 +02:00
$dest -> foreign_currency_id = $source -> transaction_currency_id ;
2024-07-30 18:04:39 +02:00
}
2025-05-12 13:25:01 +02:00
if ( ! $isTransfer && ! $isBetween ) {
2024-07-31 08:20:19 +02:00
$dest -> foreign_currency_id = $foreignCurrency -> id ;
2025-12-17 08:47:31 +01:00
$dest -> foreign_amount = Steam :: positive ( $foreignAmount );
2024-07-30 18:04:39 +02:00
}
2023-06-21 12:34:58 +02:00
$dest -> save ();
2024-11-24 15:23:17 +01:00
Log :: debug (
2023-06-21 05:55:57 +02:00
sprintf (
2023-06-21 12:34:58 +02:00
'Update foreign info to %s (#%d) %s' ,
$foreignCurrency -> code ,
$foreignCurrency -> id ,
$foreignAmount
2023-06-21 05:55:57 +02:00
)
);
2021-03-12 18:31:19 +01:00
2023-06-21 12:34:58 +02:00
// refresh transactions.
$this -> sourceTransaction -> refresh ();
$this -> destinationTransaction -> refresh ();
2021-03-12 18:31:19 +01:00
return ;
}
2023-06-21 12:34:58 +02:00
if ( '0' === $amount ) {
$source -> foreign_currency_id = null ;
2025-05-31 07:56:20 +02:00
$source -> foreign_amount = null ;
2023-06-21 12:34:58 +02:00
$source -> save ();
2025-12-26 20:07:06 +01:00
$dest -> foreign_currency_id = null ;
$dest -> foreign_amount = null ;
2023-06-21 12:34:58 +02:00
$dest -> save ();
2024-11-24 15:23:17 +01:00
Log :: debug ( sprintf ( 'Foreign amount is "%s" so remove foreign amount info.' , $amount ));
2023-06-21 12:34:58 +02:00
}
2024-11-24 15:23:17 +01:00
Log :: info ( 'Not enough info to update foreign currency info.' );
2023-06-21 12:34:58 +02:00
// refresh transactions.
$this -> sourceTransaction -> refresh ();
$this -> destinationTransaction -> refresh ();
2021-03-11 06:29:07 +01:00
}
2025-05-13 12:40:59 +02:00
2025-05-12 13:25:01 +02:00
private function isBetweenAssetAndLiability () : bool
{
2025-09-07 07:56:10 +02:00
/** @var null|Transaction $sourceTransaction */
2025-12-26 20:07:06 +01:00
$sourceTransaction = $this -> transactionJournal -> transactions () -> where ( 'amount' , '<' , 0 ) -> first ();
2025-05-12 13:25:01 +02:00
2025-09-07 07:56:10 +02:00
/** @var null|Transaction $destinationTransaction */
2025-05-12 13:25:01 +02:00
$destinationTransaction = $this -> transactionJournal -> transactions () -> where ( 'amount' , '>' , 0 ) -> first ();
if ( null === $sourceTransaction || null === $destinationTransaction ) {
Log :: warning ( 'Either transaction is false, stop.' );
return false ;
}
if ( null === $sourceTransaction -> foreign_amount || null === $destinationTransaction -> foreign_amount ) {
Log :: warning ( 'Either foreign amount is false, stop.' );
return false ;
}
2025-12-26 20:07:06 +01:00
$source = $sourceTransaction -> account ;
$destination = $destinationTransaction -> account ;
2025-05-12 13:25:01 +02:00
if ( null === $source || null === $destination ) {
Log :: warning ( 'Either is false, stop.' );
return false ;
}
2025-12-26 20:07:06 +01:00
$sourceTypes = [ AccountTypeEnum :: LOAN -> value , AccountTypeEnum :: DEBT -> value , AccountTypeEnum :: MORTGAGE -> value ];
2025-05-12 13:25:01 +02:00
// source is liability, destination is asset
if ( in_array ( $source -> accountType -> type , $sourceTypes , true ) && AccountTypeEnum :: ASSET -> value === $destination -> accountType -> type ) {
Log :: debug ( 'Source is a liability account, destination is an asset account, return TRUE.' );
return true ;
}
// source is asset, destination is liability
if ( in_array ( $destination -> accountType -> type , $sourceTypes , true ) && AccountTypeEnum :: ASSET -> value === $source -> accountType -> type ) {
Log :: debug ( 'Destination is a liability account, source is an asset account, return TRUE.' );
return true ;
}
return false ;
}
2025-05-31 07:22:42 +02:00
2025-06-02 05:31:18 +02:00
public function isCompareHashChanged () : bool
{
2025-06-01 14:00:35 +02:00
Log :: debug ( sprintf ( 'Now in %s' , __METHOD__ ));
2025-06-14 12:36:22 +02:00
$compareHash = hash ( 'sha256' , sprintf ( '%s' , Carbon :: now () -> getTimestamp ()));
if ( ! $this -> transactionGroup instanceof TransactionGroup ) {
2025-06-14 10:18:58 +02:00
$compareHash = $this -> transactionGroupRepository -> getCompareHash ( $this -> transactionJournal -> transactionGroup );
}
2025-06-14 12:36:22 +02:00
if ( $this -> transactionGroup instanceof TransactionGroup ) {
2025-06-14 10:18:58 +02:00
$this -> transactionGroupRepository -> getCompareHash ( $this -> transactionGroup );
}
2025-06-01 14:00:35 +02:00
Log :: debug ( sprintf ( 'Compare hash is "%s".' , $compareHash ));
Log :: debug ( sprintf ( 'Start compare hash is "%s".' , $this -> startCompareHash ));
return $compareHash !== $this -> startCompareHash ;
2025-05-31 07:22:42 +02:00
}
2018-03-05 19:35:58 +01:00
}