2022-08-12 05:41:55 +02:00
< ? php
/**
* Credit Ninja ( https :// invoiceninja . com ) .
*
* @ link https :// github . com / invoiceninja / invoiceninja source repository
*
* @ copyright Copyright ( c ) 2022. Credit Ninja LLC ( https :// invoiceninja . com )
*
* @ license https :// www . elastic . co / licensing / elastic - license
*/
namespace App\Jobs\Bank ;
2023-10-26 04:57:44 +02:00
use App\Helpers\Bank\Yodlee\Transformer\AccountTransformer ;
2022-08-12 05:41:55 +02:00
use App\Helpers\Bank\Yodlee\Yodlee ;
2022-08-12 07:25:18 +02:00
use App\Libraries\MultiDB ;
2023-12-01 14:30:33 +01:00
use App\Models\Account ;
2022-08-12 05:41:55 +02:00
use App\Models\BankIntegration ;
use App\Models\BankTransaction ;
2022-09-14 11:12:50 +02:00
use App\Models\Company ;
2023-10-26 04:57:44 +02:00
use App\Notifications\Ninja\GenericNinjaAdminNotification ;
2022-11-11 05:28:49 +01:00
use App\Services\Bank\BankMatchingService ;
2022-08-12 05:41:55 +02:00
use Illuminate\Bus\Queueable ;
use Illuminate\Contracts\Queue\ShouldQueue ;
2023-06-24 00:05:30 +02:00
use Illuminate\Foundation\Events\Dispatchable ;
2022-08-12 05:41:55 +02:00
use Illuminate\Queue\InteractsWithQueue ;
2023-06-25 06:44:38 +02:00
use Illuminate\Queue\Middleware\WithoutOverlapping ;
2022-08-12 05:41:55 +02:00
use Illuminate\Queue\SerializesModels ;
2023-12-01 14:30:33 +01:00
class ProcessBankTransactionsYodlee implements ShouldQueue
2022-08-12 05:41:55 +02:00
{
use Dispatchable , InteractsWithQueue , Queueable , SerializesModels ;
2023-12-01 14:30:33 +01:00
private Account $account ;
2022-08-12 05:41:55 +02:00
private BankIntegration $bank_integration ;
2022-09-14 07:35:51 +02:00
private ? string $from_date ;
2022-08-12 05:41:55 +02:00
2022-08-17 08:37:05 +02:00
private bool $stop_loop = true ;
2022-09-14 11:07:12 +02:00
private int $skip = 0 ;
2022-09-14 11:12:50 +02:00
public Company $company ;
2022-08-12 05:41:55 +02:00
/**
* Create a new job instance .
*/
2023-12-01 14:30:33 +01:00
public function __construct ( Account $account , BankIntegration $bank_integration )
2022-08-12 05:41:55 +02:00
{
2023-12-01 14:30:33 +01:00
$this -> account = $account ;
2022-08-12 05:41:55 +02:00
$this -> bank_integration = $bank_integration ;
2022-09-14 00:59:04 +02:00
$this -> from_date = $bank_integration -> from_date ;
2022-09-14 11:12:50 +02:00
$this -> company = $this -> bank_integration -> company ;
2022-08-12 05:41:55 +02:00
}
/**
* Execute the job .
*
*
* @ return void
*/
public function handle ()
{
2023-12-12 07:08:40 +01:00
if ( $this -> bank_integration -> integration_type != BankIntegration :: INTEGRATION_TYPE_YODLEE )
throw new \Exception ( " Invalid BankIntegration Type " );
2022-09-14 01:33:49 +02:00
set_time_limit ( 0 );
2022-08-12 05:41:55 +02:00
2022-09-14 11:49:23 +02:00
//Loop through everything until we are up to date
2022-09-21 13:03:04 +02:00
$this -> from_date = $this -> from_date ? : '2021-01-01' ;
2022-09-14 00:54:59 +02:00
2023-12-18 15:58:16 +01:00
nlog ( " Yodlee: Processing transactions for account: { $this -> bank_integration -> account -> key } " );
2022-09-14 01:33:49 +02:00
2023-02-16 02:36:09 +01:00
do {
2022-11-04 02:55:17 +01:00
try {
$this -> processTransactions ();
2023-12-01 14:30:33 +01:00
} catch ( \Exception $e ) {
2023-12-18 15:58:16 +01:00
nlog ( " Yodlee: { $this -> bank_integration -> bank_account_id } - exited abnormally => " . $e -> getMessage ());
2023-06-25 06:44:38 +02:00
$content = [
2023-12-18 15:58:16 +01:00
" Processing transactions for account: { $this -> bank_integration -> bank_account_id } failed " ,
2023-06-25 06:44:38 +02:00
" Exception Details => " ,
$e -> getMessage (),
];
2023-06-27 08:48:26 +02:00
$this -> bank_integration -> company -> notification ( new GenericNinjaAdminNotification ( $content )) -> ninja ();
2022-11-04 02:55:17 +01:00
return ;
}
2023-02-16 02:36:09 +01:00
} while ( $this -> stop_loop );
2022-08-17 08:37:05 +02:00
2022-11-11 05:28:49 +01:00
BankMatchingService :: dispatch ( $this -> company -> id , $this -> company -> db );
2022-08-17 08:37:05 +02:00
}
private function processTransactions ()
{
2023-12-13 16:32:51 +01:00
$yodlee = new Yodlee ( $this -> account -> bank_integration_account_id );
2022-08-12 05:41:55 +02:00
2023-12-01 14:30:33 +01:00
if ( ! $yodlee -> getAccount ( $this -> bank_integration -> bank_account_id )) {
$this -> bank_integration -> disabled_upstream = true ;
$this -> bank_integration -> save ();
$this -> stop_loop = false ;
return ;
2022-11-04 02:55:17 +01:00
}
2023-07-23 05:16:15 +02:00
try {
$account_summary = $yodlee -> getAccountSummary ( $this -> bank_integration -> bank_account_id );
2023-12-10 08:18:31 +01:00
if ( $account_summary ) {
2023-07-23 05:16:15 +02:00
$at = new AccountTransformer ();
$account = $at -> transform ( $account_summary );
2023-12-10 08:18:31 +01:00
if ( $account [ 0 ][ 'current_balance' ]) {
2023-08-09 10:29:33 +02:00
$this -> bank_integration -> balance = $account [ 0 ][ 'current_balance' ];
$this -> bank_integration -> currency = $account [ 0 ][ 'account_currency' ];
2023-08-21 00:26:08 +02:00
$this -> bank_integration -> bank_account_status = $account [ 0 ][ 'account_status' ];
2023-07-26 01:27:16 +02:00
$this -> bank_integration -> save ();
}
2023-12-10 08:18:31 +01:00
2023-07-23 05:16:15 +02:00
}
2023-12-10 08:18:31 +01:00
} catch ( \Exception $e ) {
nlog ( " YODLEE: unable to update account summary for { $this -> bank_integration -> bank_account_id } => " . $e -> getMessage ());
2023-07-23 05:16:15 +02:00
}
2022-08-12 05:41:55 +02:00
$data = [
'top' => 500 ,
2022-09-14 11:07:12 +02:00
'fromDate' => $this -> from_date ,
2022-08-12 05:41:55 +02:00
'accountId' => $this -> bank_integration -> bank_account_id ,
2022-09-14 11:07:12 +02:00
'skip' => $this -> skip ,
2022-08-12 05:41:55 +02:00
];
2022-09-14 08:57:47 +02:00
//Get transaction count object
2022-08-17 08:37:05 +02:00
$transaction_count = $yodlee -> getTransactionCount ( $data );
2022-09-14 00:54:59 +02:00
2022-09-14 08:57:47 +02:00
//Get int count
2022-08-17 08:37:05 +02:00
$count = $transaction_count -> transaction -> TOTAL -> count ;
2022-09-14 08:57:47 +02:00
//get transactions array
2023-12-01 14:30:33 +01:00
$transactions = $yodlee -> getTransactions ( $data );
2022-08-12 05:41:55 +02:00
2022-09-14 08:57:47 +02:00
//if no transactions, update the from_date and move on
2023-12-01 14:30:33 +01:00
if ( count ( $transactions ) == 0 ) {
2022-11-04 02:55:17 +01:00
$this -> bank_integration -> from_date = now () -> subDays ( 2 );
$this -> bank_integration -> disabled_upstream = false ;
2022-09-14 08:57:47 +02:00
$this -> bank_integration -> save ();
2022-09-14 09:53:38 +02:00
$this -> stop_loop = false ;
2022-09-14 07:52:54 +02:00
return ;
2022-09-14 08:57:47 +02:00
}
2022-09-14 07:52:54 +02:00
2022-09-14 08:57:47 +02:00
//Harvest the company
2022-09-14 01:33:49 +02:00
2022-09-14 11:14:00 +02:00
MultiDB :: setDb ( $this -> company -> db );
2022-09-14 01:33:49 +02:00
2022-09-14 08:57:47 +02:00
/*Get the user */
2022-09-14 11:14:00 +02:00
$user_id = $this -> company -> owner () -> id ;
2023-12-01 14:30:33 +01:00
2022-09-14 08:57:47 +02:00
/* Unguard the model to perform batch inserts */
2022-08-17 03:52:16 +02:00
BankTransaction :: unguard ();
2022-09-14 08:57:47 +02:00
$now = now ();
2022-08-12 05:41:55 +02:00
2023-12-01 14:30:33 +01:00
foreach ( $transactions as $transaction ) {
2023-12-18 15:58:16 +01:00
if ( BankTransaction :: query () -> where ( 'transaction_id' , $transaction [ 'transaction_id' ]) -> where ( 'company_id' , $this -> company -> id ) -> where ( 'bank_integration_id' , $this -> bank_integration -> id ) -> withTrashed () -> exists ()) { // @turbo124 was not scoped to bank_integration_id => from my pov this should be present, because when an account was historized (is_deleted) a transaction can occur multiple (in the archived bank_integration and in the new one
2022-08-12 05:41:55 +02:00
continue ;
2023-02-16 02:36:09 +01:00
}
2022-08-12 05:41:55 +02:00
2022-08-17 05:43:16 +02:00
//this should be much faster to insert than using ::create()
$bt = \DB :: table ( 'bank_transactions' ) -> insert (
2023-12-01 14:30:33 +01:00
array_merge ( $transaction , [
2022-09-14 11:14:00 +02:00
'company_id' => $this -> company -> id ,
2022-08-17 03:52:16 +02:00
'user_id' => $user_id ,
'bank_integration_id' => $this -> bank_integration -> id ,
2022-09-14 08:57:47 +02:00
'created_at' => $now ,
'updated_at' => $now ,
2022-08-17 03:52:16 +02:00
])
2022-08-17 05:43:16 +02:00
);
2022-08-12 05:41:55 +02:00
}
2022-09-14 10:25:30 +02:00
2022-09-14 11:07:12 +02:00
$this -> skip = $this -> skip + 500 ;
2022-09-14 01:33:49 +02:00
2023-12-01 14:30:33 +01:00
if ( $count < 500 ) {
2022-08-17 08:37:05 +02:00
$this -> stop_loop = false ;
2022-11-04 02:55:17 +01:00
$this -> bank_integration -> from_date = now () -> subDays ( 2 );
2022-09-14 11:07:12 +02:00
$this -> bank_integration -> save ();
2022-09-14 09:35:13 +02:00
}
2022-08-12 05:41:55 +02:00
}
2023-06-25 06:44:38 +02:00
2022-09-14 01:40:08 +02:00
2023-06-25 06:44:38 +02:00
public function middleware ()
{
2023-12-13 16:32:51 +01:00
return [ new WithoutOverlapping ( $this -> account -> bank_integration_account_id )];
2022-08-12 05:41:55 +02:00
}
2023-06-25 06:44:38 +02:00
public function backoff ()
{
return [ rand ( 10 , 15 ), rand ( 30 , 40 ), rand ( 60 , 79 ), rand ( 160 , 200 ), rand ( 3000 , 5000 )];
}
2023-12-01 14:30:33 +01:00
}