mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 06:37:33 -05:00 
			
		
		
		
	wip: changes
This commit is contained in:
		
							parent
							
								
									ef48bd150c
								
							
						
					
					
						commit
						b54b626332
					
				@ -7,9 +7,11 @@
 | 
			
		||||
 * @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
 | 
			
		||||
 *
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 *
 | 
			
		||||
 * Documentation of Api-Usage: https://developer.gocardless.com/bank-account-data/overview
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Bank\Yodlee;
 | 
			
		||||
namespace App\Helpers\Bank\Nordigen;
 | 
			
		||||
 | 
			
		||||
use App\Exceptions\NordigenApiException;
 | 
			
		||||
use App\Helpers\Bank\Nordigen\Transformer\AccountTransformer;
 | 
			
		||||
@ -48,79 +50,143 @@ $requisitionId = $session["requisition_id"];
 | 
			
		||||
 | 
			
		||||
class Nordigen
 | 
			
		||||
{
 | 
			
		||||
    public bool $test_mode = false;
 | 
			
		||||
    public bool $test_mode = false; // https://developer.gocardless.com/bank-account-data/sandbox
 | 
			
		||||
 | 
			
		||||
    public string $sandbox_institutionId = "SANDBOXFINANCE_SFIN0000";
 | 
			
		||||
 | 
			
		||||
    protected \Nordigen\NordigenPHP\API\NordigenClient $client;
 | 
			
		||||
 | 
			
		||||
    protected string $secret_id;
 | 
			
		||||
 | 
			
		||||
    protected string $secret_key;
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    public function __construct(string $client_id, string $client_secret)
 | 
			
		||||
    {
 | 
			
		||||
        $this->secret_id = config('ninja.nordigen.secret_id');
 | 
			
		||||
 | 
			
		||||
        $this->secret_key = config('ninja.nordigen.secret_key');
 | 
			
		||||
        $this->client = new \Nordigen\NordigenPHP\API\NordigenClient($client_id, $client_secret);
 | 
			
		||||
 | 
			
		||||
        $this->client = new \Nordigen\NordigenPHP\API\NordigenClient($this->secret_id, $this->secret_key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // metadata-section for frontend
 | 
			
		||||
    public function getInstitutions()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if ($this->test_mode)
 | 
			
		||||
            return (array) $this->client->institution->getInstitution($this->sandbox_institutionId);
 | 
			
		||||
 | 
			
		||||
        return $this->client->institution->getInstitutions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getValidAccounts()
 | 
			
		||||
    // requisition-section
 | 
			
		||||
    public function createRequisition(string $redirect, string $initutionId)
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->test_mode && $initutionId != $this->sandbox_institutionId)
 | 
			
		||||
            throw new \Exception('invalid institutionId while in test-mode');
 | 
			
		||||
 | 
			
		||||
        return $this->client->requisition->createRequisition($redirect, $initutionId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getRequisition(string $requisitionId)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client->requisition->getRequisition($requisitionId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function cleanupRequisitions()
 | 
			
		||||
    {
 | 
			
		||||
        $requisitions = $this->client->requisition->getRequisitions();
 | 
			
		||||
 | 
			
		||||
        foreach ($requisitions as $requisition) {
 | 
			
		||||
            // filter to expired OR older than 7 days created and no accounts
 | 
			
		||||
            if ($requisition->status == "EXPIRED" || (sizeOf($requisition->accounts) != 0 && strtotime($requisition->created) > (new \DateTime())->modify('-7 days')))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            $this->client->requisition->deleteRequisition($requisition->id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // account-section: these methods should be used to get data of connected accounts
 | 
			
		||||
    public function getAccounts()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        // get all valid requisitions
 | 
			
		||||
        $requisitions = $this->client->requisition->getRequisitions();
 | 
			
		||||
 | 
			
		||||
        // fetch all valid accounts for activated requisitions
 | 
			
		||||
        $accounts = [];
 | 
			
		||||
        $nordigen_accountIds = [];
 | 
			
		||||
        foreach ($requisitions as $requisition) {
 | 
			
		||||
            foreach ($requisition->accounts as $account) {
 | 
			
		||||
                $account = $account = $this->client->account($account);
 | 
			
		||||
 | 
			
		||||
                array_push($accounts, $account);
 | 
			
		||||
            foreach ($requisition->accounts as $accountId) {
 | 
			
		||||
                array_push($nordigen_accountIds, $accountId);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $accounts;
 | 
			
		||||
        $nordigen_accountIds = array_unique($nordigen_accountIds);
 | 
			
		||||
 | 
			
		||||
        $nordigen_accounts = [];
 | 
			
		||||
        foreach ($nordigen_accountIds as $accountId) {
 | 
			
		||||
            $nordigen_account = $this->getAccount($accountId);
 | 
			
		||||
 | 
			
		||||
            array_push($nordigen_accounts, $nordigen_account);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return $nordigen_accounts;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function cleanup()
 | 
			
		||||
    public function getAccount(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $out = new \stdClass();
 | 
			
		||||
 | 
			
		||||
        $out->data = $this->client->account($account_id)->getAccountDetails();
 | 
			
		||||
        $out->metadata = $this->client->account($account_id)->getAccountMetaData();
 | 
			
		||||
        $out->balances = $this->client->account($account_id)->getAccountBalances();
 | 
			
		||||
        $out->institution = $this->client->institution->getInstitution($out->metadata["institution_id"]);
 | 
			
		||||
 | 
			
		||||
        $it = new AccountTransformer();
 | 
			
		||||
        return $it->transform($out);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isAccountActive(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $account = $this->client->account($account_id)->getAccountMetaData();
 | 
			
		||||
 | 
			
		||||
            if ($account["status"] != "READY")
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            // TODO: check for not-found exception
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * this method will remove all according requisitions => this can result in removing multiple accounts, if a user reuses a requisition
 | 
			
		||||
     */
 | 
			
		||||
    public function deleteAccount(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        // get all valid requisitions
 | 
			
		||||
        $requisitions = $this->client->requisition->getRequisitions();
 | 
			
		||||
 | 
			
		||||
        // TODO: filter to older than 2 days created AND (no accounts or invalid)
 | 
			
		||||
 | 
			
		||||
        // fetch all valid accounts for activated requisitions
 | 
			
		||||
        foreach ($requisitions as $requisition) {
 | 
			
		||||
            $this->client->requisition->deleteRequisition($requisition->id);
 | 
			
		||||
            foreach ($requisition->accounts as $accountId) {
 | 
			
		||||
 | 
			
		||||
                if ($accountId) {
 | 
			
		||||
                    $this->client->requisition->deleteRequisition($accountId);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    // account-section: these methods should be used to get data of connected accounts
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccountMetaData(string $account_id)
 | 
			
		||||
    public function getTransactions(string $accountId, string $dateFrom = null)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client->account($account_id)->getAccountMetaData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccountDetails(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client->account($account_id)->getAccountDetails();
 | 
			
		||||
    }
 | 
			
		||||
        return $this->client->account($accountId)->getAccountTransactions($dateFrom);
 | 
			
		||||
 | 
			
		||||
    public function getAccountBalances(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client->account($account_id)->getAccountBalances();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getAccountTransactions(string $account_id)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->client->account($account_id)->getAccountTransactions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,55 +9,79 @@
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Bank\Yodlee\Transformer;
 | 
			
		||||
namespace App\Helpers\Bank\Nordigen\Transformer;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\Bank\AccountTransformerInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
[0] => stdClass Object
 | 
			
		||||
(
 | 
			
		||||
    [CONTAINER] => bank
 | 
			
		||||
    [providerAccountId] => 11308693
 | 
			
		||||
    [accountName] => My CD - 8878
 | 
			
		||||
    [accountStatus] => ACTIVE
 | 
			
		||||
    [accountNumber] => xxxx8878
 | 
			
		||||
    [aggregationSource] => USER
 | 
			
		||||
    [isAsset] => 1
 | 
			
		||||
    [balance] => stdClass Object
 | 
			
		||||
    [data] => stdClass Object
 | 
			
		||||
        (
 | 
			
		||||
            [currency] => USD
 | 
			
		||||
            [amount] => 49778.07
 | 
			
		||||
            [resourceId] => XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 | 
			
		||||
            [iban] => DE0286055592XXXXXXXXXX
 | 
			
		||||
            [currency] => EUR
 | 
			
		||||
            [ownerName] => Max Mustermann
 | 
			
		||||
            [product] => GiroKomfort
 | 
			
		||||
            [bic] => WELADE8LXXX
 | 
			
		||||
            [usage] => PRIV
 | 
			
		||||
        )
 | 
			
		||||
    [metadata] => stdClass Object
 | 
			
		||||
        (
 | 
			
		||||
            [id] => XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
 | 
			
		||||
            [created] => 2022-12-05T18:41:53.986028Z
 | 
			
		||||
            [last_accessed] => 2023-10-29T08:35:34.003611Z
 | 
			
		||||
            [iban] => DE0286055592XXXXXXXXXX
 | 
			
		||||
            [institution_id] => STADT_KREISSPARKASSE_LEIPZIG_WELADE8LXXX
 | 
			
		||||
            [status] => READY
 | 
			
		||||
            [owner_name] => Max Mustermann
 | 
			
		||||
        )
 | 
			
		||||
    [balances] => stdClass Object
 | 
			
		||||
        (
 | 
			
		||||
            [balances]: [
 | 
			
		||||
                {
 | 
			
		||||
                    [balanceAmount]: {
 | 
			
		||||
                        [amount] => 9825.64
 | 
			
		||||
                        [currency] => EUR
 | 
			
		||||
                    },
 | 
			
		||||
                    [balanceType] => closingBooked
 | 
			
		||||
                    [referenceDate] => 2023-12-01
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    [balanceAmount[: {
 | 
			
		||||
                        [amount] => 10325.64
 | 
			
		||||
                        [currency] => EUR
 | 
			
		||||
                    },
 | 
			
		||||
                    [balanceType] => interimAvailable
 | 
			
		||||
                    [creditLimitIncluded]: true,
 | 
			
		||||
                    [referenceDate] => 2023-12-01
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
    [institution] => stdClass Object
 | 
			
		||||
        (
 | 
			
		||||
            [id] => STADT_KREISSPARKASSE_LEIPZIG_WELADE8LXXX
 | 
			
		||||
            [name] => Stadt- und Kreissparkasse Leipzig
 | 
			
		||||
            [bic] => WELADE8LXXX
 | 
			
		||||
            [transaction_total_days] => 360
 | 
			
		||||
            [countries] => [
 | 
			
		||||
                "DE"
 | 
			
		||||
            ],
 | 
			
		||||
            [logo] => https://storage.googleapis.com/gc-prd-institution_icons-production/DE/PNG/sparkasse.png
 | 
			
		||||
            [supported_payments] => {
 | 
			
		||||
                [single-payment] => [
 | 
			
		||||
                    "SCT",
 | 
			
		||||
                    "ISCT"
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            [supported_features] => [
 | 
			
		||||
                "card_accounts",
 | 
			
		||||
                "payments",
 | 
			
		||||
                "pending_transactions"
 | 
			
		||||
            ],
 | 
			
		||||
            [identification_codes] => []
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    [id] => 12331861
 | 
			
		||||
    [includeInNetWorth] => 1
 | 
			
		||||
    [providerId] => 18769
 | 
			
		||||
    [providerName] => Dag Site Captcha
 | 
			
		||||
    [isManual] => 
 | 
			
		||||
    [currentBalance] => stdClass Object
 | 
			
		||||
        (
 | 
			
		||||
            [currency] => USD
 | 
			
		||||
            [amount] => 49778.07
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    [accountType] => CD
 | 
			
		||||
    [displayedName] => LORETTA
 | 
			
		||||
    [createdDate] => 2022-07-28T06:55:33Z
 | 
			
		||||
    [lastUpdated] => 2022-07-28T06:56:09Z
 | 
			
		||||
    [dataset] => Array
 | 
			
		||||
        (
 | 
			
		||||
            [0] => stdClass Object
 | 
			
		||||
                (
 | 
			
		||||
                    [name] => BASIC_AGG_DATA
 | 
			
		||||
                    [additionalStatus] => AVAILABLE_DATA_RETRIEVED
 | 
			
		||||
                    [updateEligibility] => ALLOW_UPDATE
 | 
			
		||||
                    [lastUpdated] => 2022-07-28T06:55:50Z
 | 
			
		||||
                    [lastUpdateAttempt] => 2022-07-28T06:55:50Z
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
    )
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -65,16 +89,15 @@ use App\Helpers\Bank\AccountTransformerInterface;
 | 
			
		||||
class AccountTransformer implements AccountTransformerInterface
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function transform($yodlee_account)
 | 
			
		||||
    public function transform($nordigen_account)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $data = [];
 | 
			
		||||
 | 
			
		||||
        if(!property_exists($yodlee_account, 'account'))
 | 
			
		||||
        if (!property_exists($nordigen_account, 'data') || !property_exists($nordigen_account, 'metadata') || !property_exists($nordigen_account, 'balances') || !property_exists($nordigen_account, 'institution'))
 | 
			
		||||
            return $data;
 | 
			
		||||
 | 
			
		||||
        foreach($yodlee_account->account as $account)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($nordigen_account->account as $account) {
 | 
			
		||||
            $data[] = $this->transformAccount($account);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -84,19 +107,27 @@ class AccountTransformer implements AccountTransformerInterface
 | 
			
		||||
    public function transformAccount($account)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $used_balance = $account->balances[0];
 | 
			
		||||
        // prefer entry with closingBooked
 | 
			
		||||
        foreach ($account->balances as $entry) {
 | 
			
		||||
            if ($entry->balanceType === 'closingBooked') { // available: closingBooked, interimAvailable
 | 
			
		||||
                $used_balance = $entry;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            'id' => $account->id,
 | 
			
		||||
            'id' => $account->data->id,
 | 
			
		||||
            'account_type' => $account->CONTAINER,
 | 
			
		||||
            // 'account_name' => $account->accountName,
 | 
			
		||||
            'account_name' => property_exists($account, 'accountName') ? $account->accountName : $account->nickname,
 | 
			
		||||
            'account_status' => $account->accountStatus,
 | 
			
		||||
            'account_number' => property_exists($account, 'accountNumber') ? '**** ' . substr($account?->accountNumber, -7) : '',
 | 
			
		||||
            'provider_account_id' => $account->providerAccountId,
 | 
			
		||||
            'provider_id' => $account->providerId,
 | 
			
		||||
            'provider_name' => $account->providerName,
 | 
			
		||||
            'nickname' => property_exists($account, 'nickname') ? $account->nickname : '',
 | 
			
		||||
            'current_balance' => property_exists($account, 'currentBalance') ? $account->currentBalance->amount : 0,
 | 
			
		||||
            'account_currency' => property_exists($account, 'currency') ? $account->currentBalance->currency : '',
 | 
			
		||||
            'account_name' => $account->data->iban,
 | 
			
		||||
            'account_status' => $account->metadata->status,
 | 
			
		||||
            'account_number' => '**** ' . substr($account->data->iban, -7),
 | 
			
		||||
            'provider_account_id' => $account->data->iban,
 | 
			
		||||
            'provider_id' => $account->institution_id,
 | 
			
		||||
            'provider_name' => $account->institution->name,
 | 
			
		||||
            'nickname' => property_exists($account->data, 'owner_name') ? $account->data->owner_name : '',
 | 
			
		||||
            'current_balance' => (int) property_exists($used_balance, 'balanceAmount') ? $used_balance->balanceAmount->amount : 0,
 | 
			
		||||
            'account_currency' => property_exists($used_balance, 'balanceAmount') ? $used_balance->balanceAmount->currency : '',
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Bank\Yodlee\Transformer;
 | 
			
		||||
namespace App\Helpers\Bank\Nordigen\Transformer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
"date": "string",
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@
 | 
			
		||||
 * @license https://www.elastic.co/licensing/elastic-license
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace App\Helpers\Bank\Yodlee\Transformer;
 | 
			
		||||
namespace App\Helpers\Bank\Nordigen\Transformer;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\Bank\BankRevenueInterface;
 | 
			
		||||
use App\Utils\Traits\AppSetup;
 | 
			
		||||
@ -122,11 +122,10 @@ class IncomeTransformer implements BankRevenueInterface
 | 
			
		||||
 | 
			
		||||
        $data = [];
 | 
			
		||||
 | 
			
		||||
        if(!property_exists($transaction, 'transaction'))
 | 
			
		||||
        if (!property_exists($transaction, 'transaction'))
 | 
			
		||||
            return $data;
 | 
			
		||||
 | 
			
		||||
        foreach($transaction->transaction as $transaction)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($transaction->transaction as $transaction) {
 | 
			
		||||
            $data[] = $this->transformTransaction($transaction);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -154,7 +153,7 @@ class IncomeTransformer implements BankRevenueInterface
 | 
			
		||||
    {
 | 
			
		||||
        //CREDIT / DEBIT
 | 
			
		||||
 | 
			
		||||
        if(property_exists($transaction, 'highLevelCategoryId') && $transaction->highLevelCategoryId == 10000012)
 | 
			
		||||
        if (property_exists($transaction, 'highLevelCategoryId') && $transaction->highLevelCategoryId == 10000012)
 | 
			
		||||
            return 'CREDIT';
 | 
			
		||||
 | 
			
		||||
        return 'DEBIT';
 | 
			
		||||
@ -166,15 +165,15 @@ class IncomeTransformer implements BankRevenueInterface
 | 
			
		||||
 | 
			
		||||
        $currencies = Cache::get('currencies');
 | 
			
		||||
 | 
			
		||||
        if (! $currencies) {
 | 
			
		||||
        if (!$currencies) {
 | 
			
		||||
            $this->buildCache(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $currency = $currencies->filter(function ($item) use($code){
 | 
			
		||||
        $currency = $currencies->filter(function ($item) use ($code) {
 | 
			
		||||
            return $item->code == $code;
 | 
			
		||||
        })->first();
 | 
			
		||||
 | 
			
		||||
        if($currency)
 | 
			
		||||
        if ($currency)
 | 
			
		||||
            return $currency->id;
 | 
			
		||||
 | 
			
		||||
        return 1;
 | 
			
		||||
 | 
			
		||||
@ -14,13 +14,14 @@ namespace App\Http\Controllers\Bank;
 | 
			
		||||
use App\Helpers\Bank\Nordigen\Nordigen;
 | 
			
		||||
use App\Http\Controllers\BaseController;
 | 
			
		||||
use App\Http\Requests\Yodlee\YodleeAuthRequest;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsNordigen;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
 | 
			
		||||
class YodleeController extends BaseController
 | 
			
		||||
class NordigenController extends BaseController
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    // TODO!!!!!
 | 
			
		||||
    public function auth(YodleeAuthRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -35,21 +36,21 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
        //ensure user is enterprise!!
 | 
			
		||||
 | 
			
		||||
        if ($company->account->bank_integration_account_id) {
 | 
			
		||||
        if ($company->account->bank_integration_nordigen_client_id && $company->account->bank_integration_nordigen_client_id) {
 | 
			
		||||
 | 
			
		||||
            $flow = 'edit';
 | 
			
		||||
 | 
			
		||||
            $token = $company->account->bank_integration_account_id;
 | 
			
		||||
            $token = $company->account->bank_integration_nordigen_client_id;
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            $flow = 'add';
 | 
			
		||||
 | 
			
		||||
            $response = $yodlee->createUser($company);
 | 
			
		||||
            $response = $nordigen->createUser($company);
 | 
			
		||||
 | 
			
		||||
            $token = $response->user->loginName;
 | 
			
		||||
 | 
			
		||||
            $company->account->bank_integration_account_id = $token;
 | 
			
		||||
            $company->account->bank_integration_nordigen_client_id = $token;
 | 
			
		||||
 | 
			
		||||
            $company->push();
 | 
			
		||||
 | 
			
		||||
@ -107,7 +108,7 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
        $company->account->bank_integrations->each(function ($bank_integration) use ($company) {
 | 
			
		||||
 | 
			
		||||
            ProcessBankTransactions::dispatch($company->account->bank_integration_account_id, $bank_integration);
 | 
			
		||||
            ProcessBankTransactionsNordigen::dispatch($company->account, $bank_integration);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ namespace App\Http\Controllers\Bank;
 | 
			
		||||
use App\Helpers\Bank\Yodlee\Yodlee;
 | 
			
		||||
use App\Http\Controllers\BaseController;
 | 
			
		||||
use App\Http\Requests\Yodlee\YodleeAuthRequest;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsYodlee;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
 | 
			
		||||
@ -35,14 +35,13 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
        //ensure user is enterprise!!
 | 
			
		||||
 | 
			
		||||
        if($company->account->bank_integration_account_id){
 | 
			
		||||
        if ($company->account->bank_integration_yodlee_account_id) {
 | 
			
		||||
 | 
			
		||||
            $flow = 'edit';
 | 
			
		||||
 | 
			
		||||
            $token = $company->account->bank_integration_account_id;
 | 
			
		||||
            $token = $company->account->bank_integration_yodlee_account_id;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
            $flow = 'add';
 | 
			
		||||
 | 
			
		||||
@ -50,7 +49,7 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
            $token = $response->user->loginName;
 | 
			
		||||
 | 
			
		||||
            $company->account->bank_integration_account_id = $token;
 | 
			
		||||
            $company->account->bank_integration_yodlee_account_id = $token;
 | 
			
		||||
 | 
			
		||||
            $company->push();
 | 
			
		||||
 | 
			
		||||
@ -58,7 +57,7 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
        $yodlee = new Yodlee($token);
 | 
			
		||||
 | 
			
		||||
        if($request->has('window_closed') && $request->input("window_closed") == "true")
 | 
			
		||||
        if ($request->has('window_closed') && $request->input("window_closed") == "true")
 | 
			
		||||
            $this->getAccounts($company, $token);
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
@ -81,11 +80,9 @@ class YodleeController extends BaseController
 | 
			
		||||
 | 
			
		||||
        $accounts = $yodlee->getAccounts();
 | 
			
		||||
 | 
			
		||||
        foreach($accounts as $account)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($accounts as $account) {
 | 
			
		||||
 | 
			
		||||
            if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists())
 | 
			
		||||
            {
 | 
			
		||||
            if (!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $company->id)->exists()) {
 | 
			
		||||
                $bank_integration = new BankIntegration();
 | 
			
		||||
                $bank_integration->company_id = $company->id;
 | 
			
		||||
                $bank_integration->account_id = $company->account_id;
 | 
			
		||||
@ -108,9 +105,9 @@ class YodleeController extends BaseController
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $company->account->bank_integrations->each(function ($bank_integration) use ($company){
 | 
			
		||||
        $company->account->bank_integrations->each(function ($bank_integration) use ($company) {
 | 
			
		||||
 | 
			
		||||
            ProcessBankTransactions::dispatch($company->account->bank_integration_account_id, $bank_integration);
 | 
			
		||||
            ProcessBankTransactionsYodlee::dispatch($company->account, $bank_integration);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@ -180,10 +177,10 @@ class YodleeController extends BaseController
 | 
			
		||||
            ]
 | 
			
		||||
         }
 | 
			
		||||
      }
 | 
			
		||||
}*/
 | 
			
		||||
   }*/
 | 
			
		||||
    public function refreshWebhook(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
//we should ignore this one
 | 
			
		||||
        //we should ignore this one
 | 
			
		||||
        nlog("yodlee refresh");
 | 
			
		||||
        nlog($request->all());
 | 
			
		||||
 | 
			
		||||
@ -194,8 +191,8 @@ class YodleeController extends BaseController
 | 
			
		||||
        // return response()->json(['message' => 'Unauthorized'], 403);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
    {
 | 
			
		||||
       "event":{
 | 
			
		||||
          "notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3",
 | 
			
		||||
          "info":"LATEST_BALANCE_UPDATES",
 | 
			
		||||
@ -214,8 +211,8 @@ class YodleeController extends BaseController
 | 
			
		||||
             ]
 | 
			
		||||
          }
 | 
			
		||||
       }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    public function balanceWebhook(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -229,8 +226,8 @@ class YodleeController extends BaseController
 | 
			
		||||
        // return response()->json(['message' => 'Unauthorized'], 403);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
{  
 | 
			
		||||
    /*
 | 
			
		||||
    {
 | 
			
		||||
       "event":{
 | 
			
		||||
          "data":[
 | 
			
		||||
             {
 | 
			
		||||
@ -250,11 +247,11 @@ class YodleeController extends BaseController
 | 
			
		||||
          "notificationId":"4e672150-156048777",
 | 
			
		||||
          "info":"AUTO_REFRESH_UPDATES"
 | 
			
		||||
       }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    public function refreshUpdatesWebhook(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
//notifies a user if there are problems with yodlee accessing the data
 | 
			
		||||
        //notifies a user if there are problems with yodlee accessing the data
 | 
			
		||||
        nlog("update refresh");
 | 
			
		||||
        nlog($request->all());
 | 
			
		||||
 | 
			
		||||
@ -266,8 +263,8 @@ class YodleeController extends BaseController
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
"event": {
 | 
			
		||||
    /*
 | 
			
		||||
    "event": {
 | 
			
		||||
        "notificationId": "64b7ed1a-1530523285",
 | 
			
		||||
        "info": "DATA_UPDATES.USER_DATA",
 | 
			
		||||
        "data": {
 | 
			
		||||
@ -285,11 +282,11 @@ class YodleeController extends BaseController
 | 
			
		||||
                }]
 | 
			
		||||
            }]
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    public function dataUpdatesWebhook(Request $request)
 | 
			
		||||
    {
 | 
			
		||||
//this is the main hook we use for notifications
 | 
			
		||||
        //this is the main hook we use for notifications
 | 
			
		||||
 | 
			
		||||
        nlog("data refresh");
 | 
			
		||||
        nlog($request->all());
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ namespace App\Http\Controllers;
 | 
			
		||||
use App\Factory\BankIntegrationFactory;
 | 
			
		||||
use App\Filters\BankIntegrationFilters;
 | 
			
		||||
use App\Helpers\Bank\Yodlee\Yodlee;
 | 
			
		||||
use App\Helpers\Bank\Nordigen\Nordigen;
 | 
			
		||||
use App\Http\Requests\BankIntegration\AdminBankIntegrationRequest;
 | 
			
		||||
use App\Http\Requests\BankIntegration\BulkBankIntegrationRequest;
 | 
			
		||||
use App\Http\Requests\BankIntegration\CreateBankIntegrationRequest;
 | 
			
		||||
@ -22,7 +23,9 @@ use App\Http\Requests\BankIntegration\EditBankIntegrationRequest;
 | 
			
		||||
use App\Http\Requests\BankIntegration\ShowBankIntegrationRequest;
 | 
			
		||||
use App\Http\Requests\BankIntegration\StoreBankIntegrationRequest;
 | 
			
		||||
use App\Http\Requests\BankIntegration\UpdateBankIntegrationRequest;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsYodlee;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsNordigen;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use App\Repositories\BankIntegrationRepository;
 | 
			
		||||
use App\Services\Bank\BankMatchingService;
 | 
			
		||||
@ -521,23 +524,45 @@ class BankIntegrationController extends BaseController
 | 
			
		||||
     */
 | 
			
		||||
    public function refreshAccounts(AdminBankIntegrationRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        // As yodlee is the first integration we don't need to perform switches yet, however
 | 
			
		||||
        // if we add additional providers we can reuse this class
 | 
			
		||||
        $account = auth()->user()->account;
 | 
			
		||||
 | 
			
		||||
        $bank_account_id = auth()->user()->account->bank_integration_account_id;
 | 
			
		||||
        $this->refreshAccountsYodlee($account);
 | 
			
		||||
 | 
			
		||||
        if(!$bank_account_id)
 | 
			
		||||
        $this->refreshAccountsNordigen($account);
 | 
			
		||||
 | 
			
		||||
        if (Cache::get("throttle_polling:{$account->key}"))
 | 
			
		||||
            return response()->json(BankIntegration::query()->company(), 200);
 | 
			
		||||
 | 
			
		||||
        // Processing transactions for each bank account
 | 
			
		||||
        $account->bank_integrations->where("integration_type", BankIntegration::INTEGRATION_TYPE_YODLEE)->each(function ($bank_integration) use ($account) {
 | 
			
		||||
 | 
			
		||||
            ProcessBankTransactionsYodlee::dispatch($account, $bank_integration);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $account->bank_integrations->where("integration_type", BankIntegration::INTEGRATION_TYPE_NORDIGEN)->each(function ($bank_integration) use ($account) {
 | 
			
		||||
 | 
			
		||||
            ProcessBankTransactionsNordigen::dispatch($account, $bank_integration);
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Cache::put("throttle_polling:{$account->key}", true, 300);
 | 
			
		||||
 | 
			
		||||
        return response()->json(BankIntegration::query()->company(), 200);
 | 
			
		||||
    }
 | 
			
		||||
    private function refreshAccountsYodlee(Account $account)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (!$account->bank_integration_yodlee_account_id)
 | 
			
		||||
            return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
 | 
			
		||||
 | 
			
		||||
        $yodlee = new Yodlee($bank_account_id);
 | 
			
		||||
        $yodlee = new Yodlee($account->bank_integration_yodlee_account_id);
 | 
			
		||||
 | 
			
		||||
        $accounts = $yodlee->getAccounts();
 | 
			
		||||
 | 
			
		||||
        foreach($accounts as $account)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($accounts as $account) {
 | 
			
		||||
 | 
			
		||||
            if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', auth()->user()->company()->id)->exists())
 | 
			
		||||
            {
 | 
			
		||||
            if (!BankIntegration::where("integration_type", BankIntegration::INTEGRATION_TYPE_YODLEE)->where('bank_account_id', $account['id'])->where('company_id', auth()->user()->company()->id)->exists()) {
 | 
			
		||||
                $bank_integration = new BankIntegration();
 | 
			
		||||
                $bank_integration->company_id = auth()->user()->company()->id;
 | 
			
		||||
                $bank_integration->account_id = auth()->user()->account_id;
 | 
			
		||||
@ -554,24 +579,42 @@ class BankIntegrationController extends BaseController
 | 
			
		||||
                $bank_integration->currency = $account['account_currency'];
 | 
			
		||||
 | 
			
		||||
                $bank_integration->save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private function refreshAccountsNordigen(Account $account)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $account = auth()->user()->account;
 | 
			
		||||
        if (!$account->bank_integration_nordigen_client_id || !$account->bank_integration_nordigen_client_secret)
 | 
			
		||||
            return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
 | 
			
		||||
 | 
			
		||||
        if(Cache::get("throttle_polling:{$account->key}"))
 | 
			
		||||
            return response()->json(BankIntegration::query()->company(), 200);
 | 
			
		||||
        $nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
 | 
			
		||||
 | 
			
		||||
        $account->bank_integrations->each(function ($bank_integration) use ($account){
 | 
			
		||||
        $accounts = $nordigen->getAccounts();
 | 
			
		||||
 | 
			
		||||
            ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration);
 | 
			
		||||
        foreach ($accounts as $account) {
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
            if (!BankIntegration::where("integration_type", BankIntegration::INTEGRATION_TYPE_NORDIGEN)->where('bank_account_id', $account['id'])->where('company_id', auth()->user()->company()->id)->exists()) {
 | 
			
		||||
                $bank_integration = new BankIntegration();
 | 
			
		||||
                $bank_integration->company_id = auth()->user()->company()->id;
 | 
			
		||||
                $bank_integration->account_id = auth()->user()->account_id;
 | 
			
		||||
                $bank_integration->user_id = auth()->user()->id;
 | 
			
		||||
                $bank_integration->bank_account_id = $account['id'];
 | 
			
		||||
                $bank_integration->bank_account_type = $account['account_type'];
 | 
			
		||||
                $bank_integration->bank_account_name = $account['account_name'];
 | 
			
		||||
                $bank_integration->bank_account_status = $account['account_status'];
 | 
			
		||||
                $bank_integration->bank_account_number = $account['account_number'];
 | 
			
		||||
                $bank_integration->provider_id = $account['provider_id'];
 | 
			
		||||
                $bank_integration->provider_name = $account['provider_name'];
 | 
			
		||||
                $bank_integration->nickname = $account['nickname'];
 | 
			
		||||
                $bank_integration->balance = $account['current_balance'];
 | 
			
		||||
                $bank_integration->currency = $account['account_currency'];
 | 
			
		||||
 | 
			
		||||
        Cache::put("throttle_polling:{$account->key}", true, 300);
 | 
			
		||||
                $bank_integration->save();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        return response()->json(BankIntegration::query()->company(), 200);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -614,20 +657,36 @@ class BankIntegrationController extends BaseController
 | 
			
		||||
    public function removeAccount(AdminBankIntegrationRequest $request, $acc_id)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $bank_account_id = auth()->user()->account->bank_integration_account_id;
 | 
			
		||||
        $account = auth()->user()->account;
 | 
			
		||||
 | 
			
		||||
        if(!$bank_account_id)
 | 
			
		||||
        $bank_integration = BankIntegration::withTrashed()->where('bank_account_id', $acc_id)->company()->firstOrFail();
 | 
			
		||||
 | 
			
		||||
        if ($bank_integration->integration_type == BankIntegration::INTEGRATION_TYPE_YODLEE)
 | 
			
		||||
            $this->removeAccountYodlee($account, $bank_integration);
 | 
			
		||||
        else if ($bank_integration->integration_type == BankIntegration::INTEGRATION_TYPE_NORDIGEN)
 | 
			
		||||
            $this->removeAccountNordigen($account, $bank_integration); 
 | 
			
		||||
        
 | 
			
		||||
        $this->bank_integration_repo->delete($bank_integration);
 | 
			
		||||
 | 
			
		||||
        return $this->itemResponse($bank_integration->fresh());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function removeAccountYodlee(Account $account, BankIntegration $bank_integration)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$account->bank_integration_yodlee_account_id)
 | 
			
		||||
            return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
 | 
			
		||||
 | 
			
		||||
        $bi = BankIntegration::withTrashed()->where('bank_account_id', $acc_id)->company()->firstOrFail();
 | 
			
		||||
        $yodlee = new Yodlee($account->bank_integration_yodlee_account_id);
 | 
			
		||||
        $yodlee->deleteAccount($bank_integration->bank_account_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        $yodlee = new Yodlee($bank_account_id);
 | 
			
		||||
        $res = $yodlee->deleteAccount($acc_id);
 | 
			
		||||
 | 
			
		||||
        $this->bank_integration_repo->delete($bi);
 | 
			
		||||
 | 
			
		||||
        return $this->itemResponse($bi->fresh());
 | 
			
		||||
    private function removeAccountNordigen(Account $account, BankIntegration $bank_integration)
 | 
			
		||||
    {
 | 
			
		||||
        if (!$account->bank_integration_nordigen_client_id || !$account->bank_integration_nordigen_client_secret)
 | 
			
		||||
            return response()->json(['message' => 'Not yet authenticated with Bank Integration service'], 400);
 | 
			
		||||
 | 
			
		||||
        $nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
 | 
			
		||||
        $nordigen->deleteAccount($bank_integration->bank_account_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -669,10 +728,17 @@ class BankIntegrationController extends BaseController
 | 
			
		||||
     */
 | 
			
		||||
    public function getTransactions(AdminBankIntegrationRequest $request)
 | 
			
		||||
    {
 | 
			
		||||
        // Yodlee
 | 
			
		||||
        auth()->user()->account->bank_integrations->where("integration_type", BankIntegration::INTEGRATION_TYPE_YODLEE)->each(function ($bank_integration) {
 | 
			
		||||
 | 
			
		||||
        auth()->user()->account->bank_integrations->each(function ($bank_integration) {
 | 
			
		||||
            (new ProcessBankTransactionsYodlee(auth()->user()->account, $bank_integration))->handle();
 | 
			
		||||
 | 
			
		||||
            (new ProcessBankTransactions(auth()->user()->account->bank_integration_account_id, $bank_integration))->handle();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Nordigen
 | 
			
		||||
        auth()->user()->account->bank_integrations->where("integration_type", BankIntegration::INTEGRATION_TYPE_NORDIGEN)->each(function ($bank_integration) {
 | 
			
		||||
 | 
			
		||||
            (new ProcessBankTransactionsYodlee(auth()->user()->account, $bank_integration))->handle();
 | 
			
		||||
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,7 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    // TODO: what are these categories, and for what do we need them
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -93,33 +94,31 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $this->company = Company::find($this->company_id);
 | 
			
		||||
 | 
			
		||||
        if($this->company->account->bank_integration_account_id)
 | 
			
		||||
            $yodlee = new Yodlee($this->company->account->bank_integration_account_id);
 | 
			
		||||
        if ($this->company->account->bank_integration_yodlee_account_id)
 | 
			
		||||
            $yodlee = new Yodlee($this->company->account->bank_integration_yodlee_account_id);
 | 
			
		||||
        else
 | 
			
		||||
            $yodlee = false;
 | 
			
		||||
 | 
			
		||||
        $bank_categories = Cache::get('bank_categories');
 | 
			
		||||
 | 
			
		||||
        if(!$bank_categories && $yodlee){
 | 
			
		||||
        if (!$bank_categories && $yodlee) {
 | 
			
		||||
            $_categories = $yodlee->getTransactionCategories();
 | 
			
		||||
            $this->categories = collect($_categories->transactionCategory);
 | 
			
		||||
            Cache::forever('bank_categories', $this->categories);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->categories = collect($bank_categories);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach($this->input as $input)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($this->input as $input) {
 | 
			
		||||
            nlog($input);
 | 
			
		||||
 | 
			
		||||
            if(array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) >= 1)
 | 
			
		||||
            if (array_key_exists('invoice_ids', $input) && strlen($input['invoice_ids']) >= 1)
 | 
			
		||||
                $this->matchInvoicePayment($input);
 | 
			
		||||
            elseif(array_key_exists('payment_id', $input) && strlen($input['payment_id']) >= 1)
 | 
			
		||||
            elseif (array_key_exists('payment_id', $input) && strlen($input['payment_id']) >= 1)
 | 
			
		||||
                $this->linkPayment($input);
 | 
			
		||||
            elseif(array_key_exists('expense_id', $input) && strlen($input['expense_id']) >= 1)
 | 
			
		||||
            elseif (array_key_exists('expense_id', $input) && strlen($input['expense_id']) >= 1)
 | 
			
		||||
                $this->linkExpense($input);
 | 
			
		||||
            elseif((array_key_exists('vendor_id', $input) && strlen($input['vendor_id']) >= 1) || (array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) >= 1))
 | 
			
		||||
            elseif ((array_key_exists('vendor_id', $input) && strlen($input['vendor_id']) >= 1) || (array_key_exists('ninja_category_id', $input) && strlen($input['ninja_category_id']) >= 1))
 | 
			
		||||
                $this->matchExpense($input);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -133,12 +132,11 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $invoices = explode(",", $invoice_hashed_ids);
 | 
			
		||||
 | 
			
		||||
        if(count($invoices) >= 1) 
 | 
			
		||||
        {
 | 
			
		||||
        if (count($invoices) >= 1) {
 | 
			
		||||
 | 
			
		||||
            foreach($invoices as $invoice){
 | 
			
		||||
            foreach ($invoices as $invoice) {
 | 
			
		||||
 | 
			
		||||
                if(is_string($invoice) && strlen($invoice) > 1)
 | 
			
		||||
                if (is_string($invoice) && strlen($invoice) > 1)
 | 
			
		||||
                    $collection->push($this->decodePrimaryKey($invoice));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -147,14 +145,14 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        return $collection;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function checkPayable($invoices) :bool
 | 
			
		||||
    private function checkPayable($invoices): bool
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        foreach($invoices as $invoice){
 | 
			
		||||
        foreach ($invoices as $invoice) {
 | 
			
		||||
 | 
			
		||||
            $invoice->service()->markSent();
 | 
			
		||||
 | 
			
		||||
            if(!$invoice->isPayable())
 | 
			
		||||
            if (!$invoice->isPayable())
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
@ -168,12 +166,12 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $this->bt = BankTransaction::find($input['id']);
 | 
			
		||||
 | 
			
		||||
        if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
        if (!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
            return $this;
 | 
			
		||||
 | 
			
		||||
        $expense = Expense::withTrashed()->find($input['expense_id']);
 | 
			
		||||
 | 
			
		||||
        if($expense && !$expense->transaction_id) {
 | 
			
		||||
        if ($expense && !$expense->transaction_id) {
 | 
			
		||||
 | 
			
		||||
            $expense->transaction_id = $this->bt->id;
 | 
			
		||||
            $expense->save();
 | 
			
		||||
@ -197,12 +195,12 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $this->bt = BankTransaction::find($input['id']);
 | 
			
		||||
 | 
			
		||||
        if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
        if (!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
            return $this;
 | 
			
		||||
 | 
			
		||||
        $payment = Payment::withTrashed()->find($input['payment_id']);
 | 
			
		||||
 | 
			
		||||
        if($payment && !$payment->transaction_id) {
 | 
			
		||||
        if ($payment && !$payment->transaction_id) {
 | 
			
		||||
 | 
			
		||||
            $payment->transaction_id = $this->bt->id;
 | 
			
		||||
            $payment->save();
 | 
			
		||||
@ -218,18 +216,18 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchInvoicePayment($input) :self
 | 
			
		||||
    private function matchInvoicePayment($input): self
 | 
			
		||||
    {
 | 
			
		||||
        $this->bt = BankTransaction::find($input['id']);
 | 
			
		||||
 | 
			
		||||
            if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
        if (!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
            return $this;
 | 
			
		||||
 | 
			
		||||
        $_invoices = Invoice::withTrashed()->find($this->getInvoices($input['invoice_ids']));
 | 
			
		||||
 | 
			
		||||
        $amount = $this->bt->amount;
 | 
			
		||||
 | 
			
		||||
        if($_invoices && $this->checkPayable($_invoices)){
 | 
			
		||||
        if ($_invoices && $this->checkPayable($_invoices)) {
 | 
			
		||||
 | 
			
		||||
            $this->createPayment($_invoices, $amount);
 | 
			
		||||
 | 
			
		||||
@ -240,12 +238,12 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function matchExpense($input) :self
 | 
			
		||||
    private function matchExpense($input): self
 | 
			
		||||
    {
 | 
			
		||||
        //if there is a category id, pull it from Yodlee and insert - or just reuse!!
 | 
			
		||||
        $this->bt = BankTransaction::find($input['id']);
 | 
			
		||||
 | 
			
		||||
            if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
        if (!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
 | 
			
		||||
            return $this;
 | 
			
		||||
 | 
			
		||||
        $expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id);
 | 
			
		||||
@ -258,7 +256,7 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        $expense->transaction_reference = $this->bt->description;
 | 
			
		||||
        $expense->transaction_id = $this->bt->id;
 | 
			
		||||
 | 
			
		||||
        if(array_key_exists('vendor_id', $input))
 | 
			
		||||
        if (array_key_exists('vendor_id', $input))
 | 
			
		||||
            $expense->vendor_id = $input['vendor_id'];
 | 
			
		||||
 | 
			
		||||
        $expense->invoice_documents = $this->company->invoice_expense_documents;
 | 
			
		||||
@ -267,7 +265,7 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $this->bt->expense_id = $expense->id;
 | 
			
		||||
 | 
			
		||||
        if(array_key_exists('vendor_id', $input))
 | 
			
		||||
        if (array_key_exists('vendor_id', $input))
 | 
			
		||||
            $this->bt->vendor_id = $input['vendor_id'];
 | 
			
		||||
 | 
			
		||||
        $this->bt->status_id = BankTransaction::STATUS_CONVERTED;
 | 
			
		||||
@ -278,33 +276,29 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function createPayment($invoices, float $amount) :void
 | 
			
		||||
    private function createPayment($invoices, float $amount): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->available_balance = $amount;
 | 
			
		||||
 | 
			
		||||
        \DB::connection(config('database.default'))->transaction(function () use($invoices) {
 | 
			
		||||
        \DB::connection(config('database.default'))->transaction(function () use ($invoices) {
 | 
			
		||||
 | 
			
		||||
            $invoices->each(function ($invoice) use ($invoices){
 | 
			
		||||
            $invoices->each(function ($invoice) use ($invoices) {
 | 
			
		||||
 | 
			
		||||
                $this->invoice = Invoice::withTrashed()->where('id', $invoice->id)->lockForUpdate()->first();
 | 
			
		||||
 | 
			
		||||
                $_amount = false;
 | 
			
		||||
 | 
			
		||||
                    if(floatval($this->invoice->balance) < floatval($this->available_balance) && $this->available_balance > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                if (floatval($this->invoice->balance) < floatval($this->available_balance) && $this->available_balance > 0) {
 | 
			
		||||
                    $_amount = $this->invoice->balance;
 | 
			
		||||
                    $this->applied_amount += $this->invoice->balance;
 | 
			
		||||
                    $this->available_balance = $this->available_balance - $this->invoice->balance;
 | 
			
		||||
                    }
 | 
			
		||||
                    elseif(floatval($this->invoice->balance) >= floatval($this->available_balance) && $this->available_balance > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                } elseif (floatval($this->invoice->balance) >= floatval($this->available_balance) && $this->available_balance > 0) {
 | 
			
		||||
                    $_amount = $this->available_balance;
 | 
			
		||||
                    $this->applied_amount += $this->available_balance;
 | 
			
		||||
                    $this->available_balance = 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                    if($_amount)
 | 
			
		||||
                    {
 | 
			
		||||
                if ($_amount) {
 | 
			
		||||
 | 
			
		||||
                    $this->attachable_invoices[] = ['id' => $this->invoice->id, 'amount' => $_amount];
 | 
			
		||||
 | 
			
		||||
@ -321,7 +315,7 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        }, 2);
 | 
			
		||||
 | 
			
		||||
        if(!$this->invoice)
 | 
			
		||||
        if (!$this->invoice)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        /* Create Payment */
 | 
			
		||||
@ -345,14 +339,13 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $payment->service()->applyNumber()->save();
 | 
			
		||||
 | 
			
		||||
        if($payment->client->getSetting('send_email_on_mark_paid'))
 | 
			
		||||
        if ($payment->client->getSetting('send_email_on_mark_paid'))
 | 
			
		||||
            $payment->service()->sendEmail();
 | 
			
		||||
 | 
			
		||||
        $this->setExchangeRate($payment);
 | 
			
		||||
 | 
			
		||||
        /* Create a payment relationship to the invoice entity */
 | 
			
		||||
        foreach($this->attachable_invoices as $attachable_invoice)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($this->attachable_invoices as $attachable_invoice) {
 | 
			
		||||
 | 
			
		||||
            $payment->invoices()->attach($attachable_invoice['id'], [
 | 
			
		||||
                'amount' => $attachable_invoice['amount'],
 | 
			
		||||
@ -376,7 +369,7 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        $this->invoice
 | 
			
		||||
            ->client
 | 
			
		||||
            ->service()
 | 
			
		||||
             ->updateBalanceAndPaidToDate($this->applied_amount*-1, $amount)
 | 
			
		||||
            ->updateBalanceAndPaidToDate($this->applied_amount * -1, $amount)
 | 
			
		||||
            ->save();
 | 
			
		||||
 | 
			
		||||
        $this->invoice = $this->invoice
 | 
			
		||||
@ -394,24 +387,23 @@ class MatchBankTransactions implements ShouldQueue
 | 
			
		||||
        $this->bt->save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function resolveCategory($input) :?int
 | 
			
		||||
    private function resolveCategory($input): ?int
 | 
			
		||||
    {
 | 
			
		||||
        if(array_key_exists('ninja_category_id', $input) && (int)$input['ninja_category_id'] > 1){
 | 
			
		||||
        if (array_key_exists('ninja_category_id', $input) && (int) $input['ninja_category_id'] > 1) {
 | 
			
		||||
            $this->bt->ninja_category_id = $input['ninja_category_id'];
 | 
			
		||||
            $this->bt->save();
 | 
			
		||||
 | 
			
		||||
            return (int)$input['ninja_category_id'];
 | 
			
		||||
            return (int) $input['ninja_category_id'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $category = $this->categories->firstWhere('highLevelCategoryId', $this->bt->category_id);
 | 
			
		||||
 | 
			
		||||
        $ec = ExpenseCategory::where('company_id', $this->bt->company_id)->where('bank_category_id', $this->bt->category_id)->first();
 | 
			
		||||
 | 
			
		||||
        if($ec)
 | 
			
		||||
        if ($ec)
 | 
			
		||||
            return $ec->id;
 | 
			
		||||
 | 
			
		||||
        if($category)
 | 
			
		||||
        {
 | 
			
		||||
        if ($category) {
 | 
			
		||||
            $ec = ExpenseCategoryFactory::create($this->bt->company_id, $this->bt->user_id);
 | 
			
		||||
            $ec->bank_category_id = $this->bt->category_id;
 | 
			
		||||
            $ec->name = $category->highLevelCategoryName;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										166
									
								
								app/Jobs/Bank/ProcessBankTransactionsNordigen.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								app/Jobs/Bank/ProcessBankTransactionsNordigen.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,166 @@
 | 
			
		||||
<?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;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\Bank\Nordigen\Nordigen;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use App\Models\BankTransaction;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
use App\Services\Bank\BankMatchingService;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
 | 
			
		||||
class ProcessBankTransactionsNordigen implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    private Account $account;
 | 
			
		||||
 | 
			
		||||
    private BankIntegration $bank_integration;
 | 
			
		||||
 | 
			
		||||
    private ?string $from_date;
 | 
			
		||||
 | 
			
		||||
    private bool $stop_loop = true;
 | 
			
		||||
 | 
			
		||||
    private int $skip = 0;
 | 
			
		||||
 | 
			
		||||
    public Company $company;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new job instance.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(Account $account, BankIntegration $bank_integration)
 | 
			
		||||
    {
 | 
			
		||||
        $this->account = $account;
 | 
			
		||||
        $this->bank_integration = $bank_integration;
 | 
			
		||||
        $this->from_date = $bank_integration->from_date;
 | 
			
		||||
        $this->company = $this->bank_integration->company;
 | 
			
		||||
 | 
			
		||||
        if ($this->bank_integration->integration_type != BankIntegration::INTEGRATION_TYPE_NORDIGEN)
 | 
			
		||||
            throw new \Exception("Invalid BankIntegration Type");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the job.
 | 
			
		||||
     *
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        set_time_limit(0);
 | 
			
		||||
 | 
			
		||||
        //Loop through everything until we are up to date
 | 
			
		||||
        $this->from_date = $this->from_date ?: '2021-01-01';
 | 
			
		||||
 | 
			
		||||
        do {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $this->processTransactions();
 | 
			
		||||
            } catch (\Exception $e) {
 | 
			
		||||
                nlog("{$this->account->bank_integration_nordigen_client_id} - exited abnormally => " . $e->getMessage());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        while ($this->stop_loop);
 | 
			
		||||
 | 
			
		||||
        BankMatchingService::dispatch($this->company->id, $this->company->db);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private function processTransactions()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $nordigen = new Nordigen($this->account->bank_integration_nordigen_client_id, $this->account->bank_integration_nordigen_client_secret); // TODO: maybe implement credentials
 | 
			
		||||
 | 
			
		||||
        if (!$nordigen->isAccountActive($this->bank_integration->bank_account_id)) {
 | 
			
		||||
            $this->bank_integration->disabled_upstream = true;
 | 
			
		||||
            $this->bank_integration->save();
 | 
			
		||||
            $this->stop_loop = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $data = [
 | 
			
		||||
            'top' => 500,
 | 
			
		||||
            'fromDate' => $this->from_date,
 | 
			
		||||
            'accountId' => $this->bank_integration->bank_account_id,
 | 
			
		||||
            'skip' => $this->skip,
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        //Get transaction count object
 | 
			
		||||
        $transactions = $nordigen->getTransactions($this->bank_integration->bank_account_id, $this->from_date);
 | 
			
		||||
 | 
			
		||||
        //Get int count
 | 
			
		||||
        $count = sizeof($transactions->transactions->booked);
 | 
			
		||||
 | 
			
		||||
        //if no transactions, update the from_date and move on
 | 
			
		||||
        if (count($transactions) == 0) {
 | 
			
		||||
 | 
			
		||||
            $this->bank_integration->from_date = now()->subDays(2);
 | 
			
		||||
            $this->bank_integration->disabled_upstream = false;
 | 
			
		||||
            $this->bank_integration->save();
 | 
			
		||||
            $this->stop_loop = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Harvest the company
 | 
			
		||||
 | 
			
		||||
        MultiDB::setDb($this->company->db);
 | 
			
		||||
 | 
			
		||||
        /*Get the user */
 | 
			
		||||
        $user_id = $this->company->owner()->id;
 | 
			
		||||
 | 
			
		||||
        /* Unguard the model to perform batch inserts */
 | 
			
		||||
        BankTransaction::unguard();
 | 
			
		||||
 | 
			
		||||
        $now = now();
 | 
			
		||||
 | 
			
		||||
        foreach ($transactions as $transaction) {
 | 
			
		||||
 | 
			
		||||
            if (BankTransaction::where('transaction_id', $transaction['transaction_id'])->where('company_id', $this->company->id)->withTrashed()->exists())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            //this should be much faster to insert than using ::create()
 | 
			
		||||
            $bt = \DB::table('bank_transactions')->insert(
 | 
			
		||||
                array_merge($transaction, [
 | 
			
		||||
                    'company_id' => $this->company->id,
 | 
			
		||||
                    'user_id' => $user_id,
 | 
			
		||||
                    'bank_integration_id' => $this->bank_integration->id,
 | 
			
		||||
                    'created_at' => $now,
 | 
			
		||||
                    'updated_at' => $now,
 | 
			
		||||
                ])
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $this->skip = $this->skip + 500;
 | 
			
		||||
 | 
			
		||||
        if ($count < 500) {
 | 
			
		||||
            $this->stop_loop = false;
 | 
			
		||||
            $this->bank_integration->from_date = now()->subDays(2);
 | 
			
		||||
            $this->bank_integration->save();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,8 +11,10 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs\Bank;
 | 
			
		||||
 | 
			
		||||
use App\Helpers\Bank\Yodlee\Nordigen;
 | 
			
		||||
use App\Helpers\Bank\Yodlee\Yodlee;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use App\Models\BankTransaction;
 | 
			
		||||
use App\Models\Company;
 | 
			
		||||
@ -24,11 +26,11 @@ use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
 | 
			
		||||
class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
class ProcessBankTransactionsYodlee implements ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    private string $bank_integration_account_id;
 | 
			
		||||
    private Account $account;
 | 
			
		||||
 | 
			
		||||
    private BankIntegration $bank_integration;
 | 
			
		||||
 | 
			
		||||
@ -43,13 +45,16 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new job instance.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct(string $bank_integration_account_id, BankIntegration $bank_integration)
 | 
			
		||||
    public function __construct(Account $account, BankIntegration $bank_integration)
 | 
			
		||||
    {
 | 
			
		||||
        $this->bank_integration_account_id = $bank_integration_account_id;
 | 
			
		||||
        $this->account = $account;
 | 
			
		||||
        $this->bank_integration = $bank_integration;
 | 
			
		||||
        $this->from_date = $bank_integration->from_date;
 | 
			
		||||
        $this->company = $this->bank_integration->company;
 | 
			
		||||
 | 
			
		||||
        if ($this->bank_integration->integration_type != BankIntegration::INTEGRATION_TYPE_YODLEE)
 | 
			
		||||
            throw new \Exception("Invalid BankIntegration Type");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -66,18 +71,17 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
        //Loop through everything until we are up to date
 | 
			
		||||
        $this->from_date = $this->from_date ?: '2021-01-01';
 | 
			
		||||
 | 
			
		||||
        do{
 | 
			
		||||
        do {
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                $this->processTransactions();
 | 
			
		||||
            }
 | 
			
		||||
            catch(\Exception $e) {
 | 
			
		||||
                nlog("{$this->bank_integration_account_id} - exited abnormally => ". $e->getMessage());
 | 
			
		||||
            } catch (\Exception $e) {
 | 
			
		||||
                nlog("{$this->account->bank_integration_yodlee_account_id} - exited abnormally => " . $e->getMessage());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        while($this->stop_loop);
 | 
			
		||||
        while ($this->stop_loop);
 | 
			
		||||
 | 
			
		||||
        BankMatchingService::dispatch($this->company->id, $this->company->db);
 | 
			
		||||
 | 
			
		||||
@ -87,10 +91,9 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
    private function processTransactions()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $yodlee = new Yodlee($this->bank_integration_account_id);
 | 
			
		||||
        $yodlee = new Yodlee($this->account->bank_integration_yodlee_account_id);
 | 
			
		||||
 | 
			
		||||
        if(!$yodlee->getAccount($this->bank_integration->bank_account_id)) 
 | 
			
		||||
        {
 | 
			
		||||
        if (!$yodlee->getAccount($this->bank_integration->bank_account_id)) {
 | 
			
		||||
            $this->bank_integration->disabled_upstream = true;
 | 
			
		||||
            $this->bank_integration->save();
 | 
			
		||||
            $this->stop_loop = false;
 | 
			
		||||
@ -114,7 +117,7 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
        $transactions = $yodlee->getTransactions($data);
 | 
			
		||||
 | 
			
		||||
        //if no transactions, update the from_date and move on
 | 
			
		||||
        if(count($transactions) == 0){
 | 
			
		||||
        if (count($transactions) == 0) {
 | 
			
		||||
 | 
			
		||||
            $this->bank_integration->from_date = now()->subDays(2);
 | 
			
		||||
            $this->bank_integration->disabled_upstream = false;
 | 
			
		||||
@ -135,15 +138,14 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $now = now();
 | 
			
		||||
 | 
			
		||||
        foreach($transactions as $transaction)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($transactions as $transaction) {
 | 
			
		||||
 | 
			
		||||
            if(BankTransaction::where('transaction_id', $transaction['transaction_id'])->where('company_id', $this->company->id)->withTrashed()->exists())
 | 
			
		||||
            if (BankTransaction::where('transaction_id', $transaction['transaction_id'])->where('company_id', $this->company->id)->withTrashed()->exists())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            //this should be much faster to insert than using ::create()
 | 
			
		||||
            $bt = \DB::table('bank_transactions')->insert(
 | 
			
		||||
                array_merge($transaction,[
 | 
			
		||||
                array_merge($transaction, [
 | 
			
		||||
                    'company_id' => $this->company->id,
 | 
			
		||||
                    'user_id' => $user_id,
 | 
			
		||||
                    'bank_integration_id' => $this->bank_integration->id,
 | 
			
		||||
@ -157,7 +159,7 @@ class ProcessBankTransactions implements ShouldQueue
 | 
			
		||||
 | 
			
		||||
        $this->skip = $this->skip + 500;
 | 
			
		||||
 | 
			
		||||
        if($count < 500){
 | 
			
		||||
        if ($count < 500) {
 | 
			
		||||
            $this->stop_loop = false;
 | 
			
		||||
            $this->bank_integration->from_date = now()->subDays(2);
 | 
			
		||||
            $this->bank_integration->save();
 | 
			
		||||
@ -11,9 +11,11 @@
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs\Ninja;
 | 
			
		||||
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsYodlee;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsNordigen;
 | 
			
		||||
use App\Libraries\MultiDB;
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
@ -46,28 +48,38 @@ class BankTransactionSync implements ShouldQueue
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //multiDB environment, need to
 | 
			
		||||
        foreach (MultiDB::$dbs as $db) 
 | 
			
		||||
        {
 | 
			
		||||
        foreach (MultiDB::$dbs as $db) {
 | 
			
		||||
            MultiDB::setDB($db);
 | 
			
		||||
 | 
			
		||||
            nlog("syncing transactions");
 | 
			
		||||
            nlog("syncing transactions - yodlee");
 | 
			
		||||
 | 
			
		||||
                $a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){
 | 
			
		||||
            Account::with('bank_integrations')->whereNotNull('bank_integration_yodlee_account_id')->cursor()->each(function ($account) {
 | 
			
		||||
 | 
			
		||||
                    // $queue = Ninja::isHosted() ? 'bank' : 'default';
 | 
			
		||||
                if ($account->isPaid() && $account->plan == 'enterprise') {
 | 
			
		||||
 | 
			
		||||
                    if($account->isPaid() && $account->plan == 'enterprise')
 | 
			
		||||
                    {
 | 
			
		||||
                    $account->bank_integrations()->where('integration_type', BankIntegration::INTEGRATION_TYPE_YODLEE)->andWhere('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account) {
 | 
			
		||||
 | 
			
		||||
                        $account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){
 | 
			
		||||
                            
 | 
			
		||||
                            (new ProcessBankTransactions($account->bank_integration_account_id, $bank_integration))->handle();
 | 
			
		||||
                        (new ProcessBankTransactionsYodlee($account, $bank_integration))->handle();
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            nlog("syncing transactions - nordigen");
 | 
			
		||||
 | 
			
		||||
            Account::with('bank_integrations')->whereNotNull('bank_integration_nordigen_client_id')->andWhereNotNull('bank_integration_nordigen_client_secret')->cursor()->each(function ($account) {
 | 
			
		||||
 | 
			
		||||
                $account->bank_integrations()->where('integration_type', BankIntegration::INTEGRATION_TYPE_NORDIGEN)->andWhere('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account) {
 | 
			
		||||
 | 
			
		||||
                    (new ProcessBankTransactionsNordigen($account, $bank_integration))->handle();
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            nlog("syncing transactions - done");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,8 @@ class Account extends BaseModel
 | 
			
		||||
        'set_react_as_default_ap',
 | 
			
		||||
        'inapp_transaction_id',
 | 
			
		||||
        'num_users',
 | 
			
		||||
        'bank_integration_nordigen_client_id',
 | 
			
		||||
        'bank_integration_nordigen_client_secret',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -156,7 +158,7 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function getPlan()
 | 
			
		||||
    {
 | 
			
		||||
        if(Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
        if (Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
            return '';
 | 
			
		||||
 | 
			
		||||
        return $this->plan ?: '';
 | 
			
		||||
@ -165,7 +167,7 @@ class Account extends BaseModel
 | 
			
		||||
    public function hasFeature($feature)
 | 
			
		||||
    {
 | 
			
		||||
        $plan_details = $this->getPlanDetails();
 | 
			
		||||
        $self_host = ! Ninja::isNinja();
 | 
			
		||||
        $self_host = !Ninja::isNinja();
 | 
			
		||||
 | 
			
		||||
        switch ($feature) {
 | 
			
		||||
 | 
			
		||||
@ -187,35 +189,35 @@ class Account extends BaseModel
 | 
			
		||||
            case self::FEATURE_API:
 | 
			
		||||
            case self::FEATURE_CLIENT_PORTAL_PASSWORD:
 | 
			
		||||
            case self::FEATURE_CUSTOM_URL:
 | 
			
		||||
                return $self_host || ! empty($plan_details);
 | 
			
		||||
                return $self_host || !empty($plan_details);
 | 
			
		||||
 | 
			
		||||
            // Pro; No trial allowed, unless they're trialing enterprise with an active pro plan
 | 
			
		||||
            case self::FEATURE_MORE_CLIENTS:
 | 
			
		||||
                return $self_host || ! empty($plan_details) && (! $plan_details['trial'] || ! empty($this->getPlanDetails(false, false)));
 | 
			
		||||
                return $self_host || !empty($plan_details) && (!$plan_details['trial'] || !empty($this->getPlanDetails(false, false)));
 | 
			
		||||
 | 
			
		||||
            // White Label
 | 
			
		||||
            case self::FEATURE_WHITE_LABEL:
 | 
			
		||||
                if (! $self_host && $plan_details && ! $plan_details['expires']) {
 | 
			
		||||
                if (!$self_host && $plan_details && !$plan_details['expires']) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            // Fallthrough
 | 
			
		||||
            // no break
 | 
			
		||||
            case self::FEATURE_REMOVE_CREATED_BY:
 | 
			
		||||
                return ! empty($plan_details); // A plan is required even for self-hosted users
 | 
			
		||||
                return !empty($plan_details); // A plan is required even for self-hosted users
 | 
			
		||||
 | 
			
		||||
            // Enterprise; No Trial allowed; grandfathered for old pro users
 | 
			
		||||
            case self::FEATURE_USERS:// Grandfathered for old Pro users
 | 
			
		||||
            case self::FEATURE_USERS: // Grandfathered for old Pro users
 | 
			
		||||
                if ($plan_details && $plan_details['trial']) {
 | 
			
		||||
                    // Do they have a non-trial plan?
 | 
			
		||||
                    $plan_details = $this->getPlanDetails(false, false);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return $self_host || ! empty($plan_details) && ($plan_details['plan'] == self::PLAN_ENTERPRISE);
 | 
			
		||||
                return $self_host || !empty($plan_details) && ($plan_details['plan'] == self::PLAN_ENTERPRISE);
 | 
			
		||||
 | 
			
		||||
            // Enterprise; No Trial allowed
 | 
			
		||||
            case self::FEATURE_DOCUMENTS:
 | 
			
		||||
            case self::FEATURE_USER_PERMISSIONS:
 | 
			
		||||
                return $self_host || ! empty($plan_details) && $plan_details['plan'] == self::PLAN_ENTERPRISE && ! $plan_details['trial'];
 | 
			
		||||
                return $self_host || !empty($plan_details) && $plan_details['plan'] == self::PLAN_ENTERPRISE && !$plan_details['trial'];
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return false;
 | 
			
		||||
@ -224,16 +226,16 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function isPaid()
 | 
			
		||||
    {
 | 
			
		||||
        return Ninja::isNinja() ? ($this->isPaidHostedClient() && ! $this->isTrial()) : $this->hasFeature(self::FEATURE_WHITE_LABEL);
 | 
			
		||||
        return Ninja::isNinja() ? ($this->isPaidHostedClient() && !$this->isTrial()) : $this->hasFeature(self::FEATURE_WHITE_LABEL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isPaidHostedClient()
 | 
			
		||||
    {
 | 
			
		||||
        if (! Ninja::isNinja()) {
 | 
			
		||||
        if (!Ninja::isNinja()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return $this->plan == 'pro' || $this->plan == 'enterprise';
 | 
			
		||||
@ -241,11 +243,11 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function isFreeHostedClient()
 | 
			
		||||
    {
 | 
			
		||||
        if (! Ninja::isNinja()) {
 | 
			
		||||
        if (!Ninja::isNinja()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
        if ($this->plan_expires && Carbon::parse($this->plan_expires)->lt(now()))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return $this->plan == 'free' || is_null($this->plan) || empty($this->plan);
 | 
			
		||||
@ -253,7 +255,7 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function isEnterpriseClient()
 | 
			
		||||
    {
 | 
			
		||||
        if (! Ninja::isNinja()) {
 | 
			
		||||
        if (!Ninja::isNinja()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -262,7 +264,7 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function isTrial()
 | 
			
		||||
    {
 | 
			
		||||
        if (! Ninja::isNinja()) {
 | 
			
		||||
        if (!Ninja::isNinja()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -273,7 +275,7 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function startTrial($plan)
 | 
			
		||||
    {
 | 
			
		||||
        if (! Ninja::isNinja()) {
 | 
			
		||||
        if (!Ninja::isNinja()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -292,20 +294,20 @@ class Account extends BaseModel
 | 
			
		||||
        $price = $this->plan_price;
 | 
			
		||||
        $trial_plan = $this->trial_plan;
 | 
			
		||||
 | 
			
		||||
        if ((! $plan || $plan == self::PLAN_FREE) && (! $trial_plan || ! $include_trial)) {
 | 
			
		||||
        if ((!$plan || $plan == self::PLAN_FREE) && (!$trial_plan || !$include_trial)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $trial_active = false;
 | 
			
		||||
 | 
			
		||||
        //14 day trial
 | 
			
		||||
        $duration = 60*60*24*14;
 | 
			
		||||
        $duration = 60 * 60 * 24 * 14;
 | 
			
		||||
 | 
			
		||||
        if ($trial_plan && $include_trial) {
 | 
			
		||||
            $trial_started = $this->trial_started;
 | 
			
		||||
            $trial_expires = Carbon::parse($this->trial_started)->addSeconds($duration);
 | 
			
		||||
 | 
			
		||||
            if($trial_expires->greaterThan(now())){
 | 
			
		||||
            if ($trial_expires->greaterThan(now())) {
 | 
			
		||||
                $trial_active = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -324,23 +326,23 @@ class Account extends BaseModel
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (! $include_inactive && ! $plan_active && ! $trial_active) {
 | 
			
		||||
        if (!$include_inactive && !$plan_active && !$trial_active) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Should we show plan details or trial details?
 | 
			
		||||
        if (($plan && ! $trial_plan) || ! $include_trial) {
 | 
			
		||||
        if (($plan && !$trial_plan) || !$include_trial) {
 | 
			
		||||
            $use_plan = true;
 | 
			
		||||
        } elseif (! $plan && $trial_plan) {
 | 
			
		||||
        } elseif (!$plan && $trial_plan) {
 | 
			
		||||
            $use_plan = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            // There is both a plan and a trial
 | 
			
		||||
            if (! empty($plan_active) && empty($trial_active)) {
 | 
			
		||||
            if (!empty($plan_active) && empty($trial_active)) {
 | 
			
		||||
                $use_plan = true;
 | 
			
		||||
            } elseif (empty($plan_active) && ! empty($trial_active)) {
 | 
			
		||||
            } elseif (empty($plan_active) && !empty($trial_active)) {
 | 
			
		||||
                $use_plan = false;
 | 
			
		||||
            } elseif (! empty($plan_active) && ! empty($trial_active)) {
 | 
			
		||||
            } elseif (!empty($plan_active) && !empty($trial_active)) {
 | 
			
		||||
                // Both are active; use whichever is a better plan
 | 
			
		||||
                if ($plan == self::PLAN_ENTERPRISE) {
 | 
			
		||||
                    $use_plan = true;
 | 
			
		||||
@ -385,20 +387,19 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function getDailyEmailLimit()
 | 
			
		||||
    {
 | 
			
		||||
        if($this->is_flagged)
 | 
			
		||||
        if ($this->is_flagged)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0)
 | 
			
		||||
        if (Carbon::createFromTimestamp($this->created_at)->diffInWeeks() == 0)
 | 
			
		||||
            return 20;
 | 
			
		||||
 | 
			
		||||
        if(Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 2 && !$this->payment_id)
 | 
			
		||||
        if (Carbon::createFromTimestamp($this->created_at)->diffInWeeks() <= 2 && !$this->payment_id)
 | 
			
		||||
            return 20;
 | 
			
		||||
 | 
			
		||||
        if($this->isPaid()){
 | 
			
		||||
        if ($this->isPaid()) {
 | 
			
		||||
            $limit = $this->paid_plan_email_quota;
 | 
			
		||||
            $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 50;
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
        } else {
 | 
			
		||||
            $limit = $this->free_plan_email_quota;
 | 
			
		||||
            $limit += Carbon::createFromTimestamp($this->created_at)->diffInMonths() * 10;
 | 
			
		||||
        }
 | 
			
		||||
@ -408,21 +409,21 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function emailsSent()
 | 
			
		||||
    {
 | 
			
		||||
        if(is_null(Cache::get($this->key)))
 | 
			
		||||
        if (is_null(Cache::get($this->key)))
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        return Cache::get($this->key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function emailQuotaExceeded() :bool
 | 
			
		||||
    public function emailQuotaExceeded(): bool
 | 
			
		||||
    {
 | 
			
		||||
        if(is_null(Cache::get($this->key)))
 | 
			
		||||
        if (is_null(Cache::get($this->key)))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            if(Cache::get($this->key) > $this->getDailyEmailLimit()) {
 | 
			
		||||
            if (Cache::get($this->key) > $this->getDailyEmailLimit()) {
 | 
			
		||||
 | 
			
		||||
                if(is_null(Cache::get("throttle_notified:{$this->key}"))) {
 | 
			
		||||
                if (is_null(Cache::get("throttle_notified:{$this->key}"))) {
 | 
			
		||||
 | 
			
		||||
                    App::forgetInstance('translator');
 | 
			
		||||
                    $t = app('translator');
 | 
			
		||||
@ -437,32 +438,31 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
                    Cache::put("throttle_notified:{$this->key}", true, 60 * 24);
 | 
			
		||||
 | 
			
		||||
                    if(config('ninja.notification.slack'))
 | 
			
		||||
                    if (config('ninja.notification.slack'))
 | 
			
		||||
                        $this->companies()->first()->notification(new EmailQuotaNotification($this))->ninja();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Exception $e){
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            \Sentry\captureMessage("I encountered an error with email quotas for account {$this->key} - defaulting to SEND");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function gmailCredentialNotification() :bool
 | 
			
		||||
    public function gmailCredentialNotification(): bool
 | 
			
		||||
    {
 | 
			
		||||
        nlog("checking if gmail credential notification has already been sent");
 | 
			
		||||
 | 
			
		||||
        if(is_null(Cache::get($this->key)))
 | 
			
		||||
        if (is_null(Cache::get($this->key)))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        nlog("Sending notification");
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            if(is_null(Cache::get("gmail_credentials_notified:{$this->key}"))) {
 | 
			
		||||
            if (is_null(Cache::get("gmail_credentials_notified:{$this->key}"))) {
 | 
			
		||||
 | 
			
		||||
                App::forgetInstance('translator');
 | 
			
		||||
                $t = app('translator');
 | 
			
		||||
@ -477,14 +477,13 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
                Cache::put("gmail_credentials_notified:{$this->key}", true, 60 * 24);
 | 
			
		||||
 | 
			
		||||
                if(config('ninja.notification.slack'))
 | 
			
		||||
                if (config('ninja.notification.slack'))
 | 
			
		||||
                    $this->companies()->first()->notification(new GmailCredentialNotification($this))->ninja();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch(\Exception $e){
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            \Sentry\captureMessage("I encountered an error with sending with gmail for account {$this->key}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -506,16 +505,17 @@ class Account extends BaseModel
 | 
			
		||||
 | 
			
		||||
    public function getTrialDays()
 | 
			
		||||
    {
 | 
			
		||||
        if($this->payment_id)
 | 
			
		||||
        if ($this->payment_id)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        $plan_expires = Carbon::parse($this->plan_expires);
 | 
			
		||||
 | 
			
		||||
        if(!$this->payment_id && $plan_expires->gt(now())){
 | 
			
		||||
        if (!$this->payment_id && $plan_expires->gt(now())) {
 | 
			
		||||
 | 
			
		||||
            $diff = $plan_expires->diffInDays();
 | 
			
		||||
 | 
			
		||||
            if($diff > 14);
 | 
			
		||||
            if ($diff > 14)
 | 
			
		||||
                ;
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
            return $diff;
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ class BankIntegration extends BaseModel
 | 
			
		||||
    use Excludable;
 | 
			
		||||
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'integration_type',
 | 
			
		||||
        'bank_account_name',
 | 
			
		||||
        'provider_name',
 | 
			
		||||
        'bank_account_number',
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@
 | 
			
		||||
namespace Database\Factories;
 | 
			
		||||
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use Illuminate\Database\Eloquent\Factories\Factory;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
@ -25,6 +26,7 @@ class BankIntegrationFactory extends Factory
 | 
			
		||||
    public function definition()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            'integration_type' => BankIntegration::INTEGRATION_TYPE_NONE,
 | 
			
		||||
            'provider_name' => $this->faker->company(),
 | 
			
		||||
            'provider_id' => 1,
 | 
			
		||||
            'bank_account_name' => $this->faker->catchPhrase(),
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use App\Models\Account;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
@ -13,15 +14,22 @@ return new class extends Migration {
 | 
			
		||||
     */
 | 
			
		||||
    public function up()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::table('bank_integration', function (Blueprint $table) {
 | 
			
		||||
            $table->string('integration_type')->nullable();
 | 
			
		||||
        Schema::table('bank_integrations', function (Blueprint $table) {
 | 
			
		||||
            $table->string('integration_type')->default(BankIntegration::INTEGRATION_TYPE_NONE);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // migrate old account to be used with yodlee
 | 
			
		||||
        BankIntegration::query()->whereNull('integration_type')->cursor()->each(function ($bank_integration) {
 | 
			
		||||
        BankIntegration::query()->where('integration_type', BankIntegration::INTEGRATION_TYPE_NONE)->whereNotNull('account_id')->cursor()->each(function ($bank_integration) {
 | 
			
		||||
            $bank_integration->integration_type = BankIntegration::INTEGRATION_TYPE_YODLEE;
 | 
			
		||||
            $bank_integration->save();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // MAYBE migration of account->bank_account_id etc
 | 
			
		||||
        Schema::table('accounts', function (Blueprint $table) {
 | 
			
		||||
            $table->renameColumn('bank_integration_account_id', 'bank_integration_yodlee_account_id');
 | 
			
		||||
            $table->string('bank_integration_nordigen_secret_id')->nullable();
 | 
			
		||||
            $table->string('bank_integration_nordigen_secret_key')->nullable();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -31,8 +39,6 @@ return new class extends Migration {
 | 
			
		||||
     */
 | 
			
		||||
    public function down()
 | 
			
		||||
    {
 | 
			
		||||
        Schema::table('bank_integration', function (Blueprint $table) {
 | 
			
		||||
            $table->dropColumn('integration_id');
 | 
			
		||||
        });
 | 
			
		||||
        //
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,49 +1,49 @@
 | 
			
		||||
{
 | 
			
		||||
    "/js/app.js": "/js/app.js",
 | 
			
		||||
    "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js",
 | 
			
		||||
    "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js",
 | 
			
		||||
    "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js",
 | 
			
		||||
    "/js/clients/payments/forte-ach-payment.js": "/js/clients/payments/forte-ach-payment.js",
 | 
			
		||||
    "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js",
 | 
			
		||||
    "/js/clients/payments/stripe-klarna.js": "/js/clients/payments/stripe-klarna.js",
 | 
			
		||||
    "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js",
 | 
			
		||||
    "/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js",
 | 
			
		||||
    "/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js",
 | 
			
		||||
    "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js",
 | 
			
		||||
    "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js",
 | 
			
		||||
    "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js",
 | 
			
		||||
    "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js",
 | 
			
		||||
    "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js",
 | 
			
		||||
    "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js",
 | 
			
		||||
    "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js",
 | 
			
		||||
    "/js/setup/setup.js": "/js/setup/setup.js",
 | 
			
		||||
    "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js",
 | 
			
		||||
    "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js",
 | 
			
		||||
    "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js",
 | 
			
		||||
    "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js",
 | 
			
		||||
    "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js",
 | 
			
		||||
    "/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js",
 | 
			
		||||
    "/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js",
 | 
			
		||||
    "/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js",
 | 
			
		||||
    "/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js",
 | 
			
		||||
    "/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js",
 | 
			
		||||
    "/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js",
 | 
			
		||||
    "/js/clients/payment_methods/braintree-ach.js": "/js/clients/payment_methods/braintree-ach.js",
 | 
			
		||||
    "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js",
 | 
			
		||||
    "/js/clients/statements/view.js": "/js/clients/statements/view.js",
 | 
			
		||||
    "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js",
 | 
			
		||||
    "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js",
 | 
			
		||||
    "/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js",
 | 
			
		||||
    "/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js",
 | 
			
		||||
    "/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js",
 | 
			
		||||
    "/js/clients/payments/stripe-bancontact.js": "/js/clients/payments/stripe-bancontact.js",
 | 
			
		||||
    "/js/clients/payments/stripe-becs.js": "/js/clients/payments/stripe-becs.js",
 | 
			
		||||
    "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js",
 | 
			
		||||
    "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js",
 | 
			
		||||
    "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js",
 | 
			
		||||
    "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js",
 | 
			
		||||
    "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js",
 | 
			
		||||
    "/css/app.css": "/css/app.css",
 | 
			
		||||
    "/css/card-js.min.css": "/css/card-js.min.css",
 | 
			
		||||
    "/vendor/clipboard.min.js": "/vendor/clipboard.min.js"
 | 
			
		||||
    "/js/app.js": "/js/app.js?id=7b6124b74168ccb1cc7da22f7a2bc9ed",
 | 
			
		||||
    "/js/clients/payment_methods/authorize-authorize-card.js": "/js/clients/payment_methods/authorize-authorize-card.js?id=b6723e0b8ea33f1f50617fa5f289a9d3",
 | 
			
		||||
    "/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=faf4828cc6b3b73b69c53d3046661884",
 | 
			
		||||
    "/js/clients/payments/forte-credit-card-payment.js": "/js/clients/payments/forte-credit-card-payment.js?id=1ecc2e5ed666e5c6fae7830b5ab5c77a",
 | 
			
		||||
    "/js/clients/payments/forte-ach-payment.js": "/js/clients/payments/forte-ach-payment.js?id=04cadfa45e77d49e8253b9ffbc000767",
 | 
			
		||||
    "/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=0c520b9a787b6b9031300330e060a7f5",
 | 
			
		||||
    "/js/clients/payments/stripe-klarna.js": "/js/clients/payments/stripe-klarna.js?id=2529dac592a6c34028addedf1198bcf2",
 | 
			
		||||
    "/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=f2b6ebf3c1da387c6268d6e0a28b8c65",
 | 
			
		||||
    "/js/clients/purchase_orders/action-selectors.js": "/js/clients/purchase_orders/action-selectors.js?id=f8e554acde01ad91784e1046ef4ecdb4",
 | 
			
		||||
    "/js/clients/purchase_orders/accept.js": "/js/clients/purchase_orders/accept.js?id=9bb483a89a887f753e49c0b635d6276a",
 | 
			
		||||
    "/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=752e2bb6390f1a422e31868cf2a2bf67",
 | 
			
		||||
    "/js/clients/payments/stripe-sofort.js": "/js/clients/payments/stripe-sofort.js?id=6b3381f59d2ef53cdd85a2435f54c2c3",
 | 
			
		||||
    "/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=018ecad3a1bcc1ecc47f76754a573ff2",
 | 
			
		||||
    "/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=eea8dc5452e299f2e4148f5a0e168613",
 | 
			
		||||
    "/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=07a94a1d7649b1bb2f6fdfe35b0cf4a1",
 | 
			
		||||
    "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=1e58e219878ce3f3ee4d313346ad5f68",
 | 
			
		||||
    "/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=6e7c8ab039a239727317ae8622de10db",
 | 
			
		||||
    "/js/setup/setup.js": "/js/setup/setup.js?id=cba079b7c249f2aa73731e1fa952d646",
 | 
			
		||||
    "/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=cf50b5ba1fcd1d184bf0c10d710672c8",
 | 
			
		||||
    "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=682de6347049b32c9488f39c78a68ace",
 | 
			
		||||
    "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=ecfc8b8db2b8aec42ca295b5e6c75974",
 | 
			
		||||
    "/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=e1c0599d6f7dc163b549a6df0b3490b4",
 | 
			
		||||
    "/js/clients/payments/braintree-credit-card.js": "/js/clients/payments/braintree-credit-card.js?id=e051c84bfaf6b63a4971181e3ece6ecb",
 | 
			
		||||
    "/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=6ff0f8ea53b30fe242706586399e61e8",
 | 
			
		||||
    "/js/clients/payments/wepay-credit-card.js": "/js/clients/payments/wepay-credit-card.js?id=a7c2aef52dfdb7e6bef25abbf5373917",
 | 
			
		||||
    "/js/clients/payment_methods/wepay-bank-account.js": "/js/clients/payment_methods/wepay-bank-account.js?id=be64a69a5fdf374ba3af7030db1d5155",
 | 
			
		||||
    "/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=3869bc6d80acc83f81d9afe8efaae728",
 | 
			
		||||
    "/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=dcebf12d3742e39c47676e2439426e6e",
 | 
			
		||||
    "/js/clients/payments/eway-credit-card.js": "/js/clients/payments/eway-credit-card.js?id=27274d334aed0824ce4654fa22132f7f",
 | 
			
		||||
    "/js/clients/payment_methods/braintree-ach.js": "/js/clients/payment_methods/braintree-ach.js?id=93f6f8c0a45cd46cd4d4c123f05ae9e7",
 | 
			
		||||
    "/js/clients/payments/square-credit-card.js": "/js/clients/payments/square-credit-card.js?id=238e7001420a22b001856193689a1e70",
 | 
			
		||||
    "/js/clients/statements/view.js": "/js/clients/statements/view.js?id=632aa120ab205dcc5807606a45844b4a",
 | 
			
		||||
    "/js/clients/payments/razorpay-aio.js": "/js/clients/payments/razorpay-aio.js?id=df93901708dc49a732cbe0a11c8e6404",
 | 
			
		||||
    "/js/clients/payments/stripe-sepa.js": "/js/clients/payments/stripe-sepa.js?id=77d4e397d193196e482af80737bff64a",
 | 
			
		||||
    "/js/clients/payment_methods/authorize-checkout-card.js": "/js/clients/payment_methods/authorize-checkout-card.js?id=e58bdaeadf150e9fe8fa75c8540ae6c2",
 | 
			
		||||
    "/js/clients/payments/stripe-giropay.js": "/js/clients/payments/stripe-giropay.js?id=9839796e7c08d6f4f372c03a8a5543f6",
 | 
			
		||||
    "/js/clients/payments/stripe-acss.js": "/js/clients/payments/stripe-acss.js?id=4c3c5ee61948e8f49b174e1c1fae084c",
 | 
			
		||||
    "/js/clients/payments/stripe-bancontact.js": "/js/clients/payments/stripe-bancontact.js?id=dfcd1f2f7080177c4dcbc58432bf4167",
 | 
			
		||||
    "/js/clients/payments/stripe-becs.js": "/js/clients/payments/stripe-becs.js?id=c7ad959f7b79be68618d2937943aef95",
 | 
			
		||||
    "/js/clients/payments/stripe-eps.js": "/js/clients/payments/stripe-eps.js?id=749cba1332a29baa444b37cee2ade2d7",
 | 
			
		||||
    "/js/clients/payments/stripe-ideal.js": "/js/clients/payments/stripe-ideal.js?id=f0e2e00fa779a20967a2ea9489bf4fcb",
 | 
			
		||||
    "/js/clients/payments/stripe-przelewy24.js": "/js/clients/payments/stripe-przelewy24.js?id=448b197a1d94b4408e130b5b8b1c2e53",
 | 
			
		||||
    "/js/clients/payments/stripe-browserpay.js": "/js/clients/payments/stripe-browserpay.js?id=d0658f7d90db9869fe79a84851f91234",
 | 
			
		||||
    "/js/clients/payments/stripe-fpx.js": "/js/clients/payments/stripe-fpx.js?id=62317369167d31654d18ecdb75ca5a45",
 | 
			
		||||
    "/css/app.css": "/css/app.css?id=0cb847167b91d8db2ca50d30e0d691ae",
 | 
			
		||||
    "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ada60afcedcb7c",
 | 
			
		||||
    "/vendor/clipboard.min.js": "/vendor/clipboard.min.js?id=15f52a1ee547f2bdd46e56747332ca2d"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ use App\Factory\BankTransactionFactory;
 | 
			
		||||
use App\Helpers\Bank\Yodlee\Yodlee;
 | 
			
		||||
use App\Helpers\Invoice\InvoiceSum;
 | 
			
		||||
use App\Jobs\Bank\MatchBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactions;
 | 
			
		||||
use App\Jobs\Bank\ProcessBankTransactionsYodlee;
 | 
			
		||||
use App\Models\BankIntegration;
 | 
			
		||||
use App\Models\BankTransaction;
 | 
			
		||||
use App\Models\Expense;
 | 
			
		||||
@ -72,13 +72,13 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
        $expense = Expense::where('transaction_reference', 'Fuel')->first();
 | 
			
		||||
 | 
			
		||||
        $this->assertNotNull($expense);
 | 
			
		||||
        $this->assertEquals(10, (int)$expense->amount);
 | 
			
		||||
        $this->assertEquals(10, (int) $expense->amount);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIncomeMatchingAndPaymentGeneration()
 | 
			
		||||
    {
 | 
			
		||||
        $this->account->bank_integration_account_id = 'sbMem62e1e69547bfb2';
 | 
			
		||||
        $this->account->bank_integration_yodlee_account_id = 'sbMem62e1e69547bfb2';
 | 
			
		||||
        $this->account->save();
 | 
			
		||||
 | 
			
		||||
        $invoice = Invoice::factory()->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $this->client->id]);
 | 
			
		||||
@ -130,7 +130,7 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $this->assertNotNull($payment);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(10, (int)$payment->amount);
 | 
			
		||||
        $this->assertEquals(10, (int) $payment->amount);
 | 
			
		||||
        $this->assertEquals(4, $payment->status_id);
 | 
			
		||||
        $this->assertEquals(1, $payment->invoices()->count());
 | 
			
		||||
 | 
			
		||||
@ -148,7 +148,7 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
 | 
			
		||||
        $transactions = $yodlee->getTransactionCategories();
 | 
			
		||||
 | 
			
		||||
        $this->assertTrue(property_exists($transactions,'transactionCategory'));
 | 
			
		||||
        $this->assertTrue(property_exists($transactions, 'transactionCategory'));
 | 
			
		||||
 | 
			
		||||
        $t = collect($transactions->transactionCategory);
 | 
			
		||||
 | 
			
		||||
@ -160,17 +160,17 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//     public function testFunctionalMatching()
 | 
			
		||||
    //     public function testFunctionalMatching()
 | 
			
		||||
//     {
 | 
			
		||||
 | 
			
		||||
//         $yodlee = new Yodlee('sbMem62e1e69547bfb1');
 | 
			
		||||
    //         $yodlee = new Yodlee('sbMem62e1e69547bfb1');
 | 
			
		||||
 | 
			
		||||
//         $accounts = $yodlee->getAccounts(); 
 | 
			
		||||
    //         $accounts = $yodlee->getAccounts();
 | 
			
		||||
 | 
			
		||||
//         foreach($accounts as $account)
 | 
			
		||||
    //         foreach($accounts as $account)
 | 
			
		||||
//         {
 | 
			
		||||
 | 
			
		||||
//             if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $this->company->id)->exists())
 | 
			
		||||
    //             if(!BankIntegration::where('bank_account_id', $account['id'])->where('company_id', $this->company->id)->exists())
 | 
			
		||||
//             {
 | 
			
		||||
//                 $bank_integration = new BankIntegration();
 | 
			
		||||
//                 $bank_integration->company_id = $this->company->id;
 | 
			
		||||
@ -187,41 +187,41 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
//                 $bank_integration->balance = $account['current_balance'];
 | 
			
		||||
//                 $bank_integration->currency = $account['account_currency'];
 | 
			
		||||
 | 
			
		||||
//                 $bank_integration->save();
 | 
			
		||||
    //                 $bank_integration->save();
 | 
			
		||||
 | 
			
		||||
//                 ProcessBankTransactions::dispatchSync('sbMem62e1e69547bfb1', $bank_integration);
 | 
			
		||||
    //                 ProcessBankTransactionsYodlee::dispatchSync('sbMem62e1e69547bfb1', $bank_integration);
 | 
			
		||||
 | 
			
		||||
//             }
 | 
			
		||||
    //             }
 | 
			
		||||
//         }
 | 
			
		||||
 | 
			
		||||
//         $this->assertGreaterThan(0, BankIntegration::count());
 | 
			
		||||
    //         $this->assertGreaterThan(0, BankIntegration::count());
 | 
			
		||||
//         $this->assertGreaterThan(0, BankTransaction::count());
 | 
			
		||||
 | 
			
		||||
//         $this->invoice->company_id = $this->company->id;
 | 
			
		||||
    //         $this->invoice->company_id = $this->company->id;
 | 
			
		||||
//         $this->invoice->number = "XXXXXX8501";
 | 
			
		||||
//         $this->invoice->save();
 | 
			
		||||
 | 
			
		||||
//         BankService::dispatchSync($this->company->id, $this->company->db);
 | 
			
		||||
    //         BankService::dispatchSync($this->company->id, $this->company->db);
 | 
			
		||||
 | 
			
		||||
//         $bt = BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->first();
 | 
			
		||||
    //         $bt = BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->first();
 | 
			
		||||
 | 
			
		||||
// nlog(BankTransaction::where('company_id', $this->company->id)->pluck('invoice_ids'));
 | 
			
		||||
    // nlog(BankTransaction::where('company_id', $this->company->id)->pluck('invoice_ids'));
 | 
			
		||||
 | 
			
		||||
//         $this->assertNotNull($bt);
 | 
			
		||||
    //         $this->assertNotNull($bt);
 | 
			
		||||
 | 
			
		||||
//         $this->assertEquals(BankTransaction::STATUS_MATCHED, $bt->status_id);
 | 
			
		||||
    //         $this->assertEquals(BankTransaction::STATUS_MATCHED, $bt->status_id);
 | 
			
		||||
 | 
			
		||||
//     }
 | 
			
		||||
    //     }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function testDataMatching()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $transaction = collect([
 | 
			
		||||
            (object)[
 | 
			
		||||
            (object) [
 | 
			
		||||
                'description' => 'tinkertonkton'
 | 
			
		||||
            ],
 | 
			
		||||
            (object)[
 | 
			
		||||
            (object) [
 | 
			
		||||
                'description' => 'spud'
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
@ -242,10 +242,10 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $transaction = collect([
 | 
			
		||||
            (object)[
 | 
			
		||||
            (object) [
 | 
			
		||||
                'description' => 'tinker and spice'
 | 
			
		||||
            ],
 | 
			
		||||
            (object)[
 | 
			
		||||
            (object) [
 | 
			
		||||
                'description' => 'spud with water'
 | 
			
		||||
            ],
 | 
			
		||||
        ]);
 | 
			
		||||
@ -286,7 +286,7 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
        $this->assertNotNull($access_token);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
       [transactionCategory] => Array
 | 
			
		||||
            (
 | 
			
		||||
@ -458,7 +458,7 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
                        [defaultHighLevelCategoryName] => Automotive Expenses
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function testGetCategories()
 | 
			
		||||
@ -473,9 +473,9 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
[2022-08-05 01:29:45] local.INFO: stdClass Object
 | 
			
		||||
(
 | 
			
		||||
    /**
 | 
			
		||||
    [2022-08-05 01:29:45] local.INFO: stdClass Object
 | 
			
		||||
    (
 | 
			
		||||
        [account] => Array
 | 
			
		||||
            (
 | 
			
		||||
                [0] => stdClass Object
 | 
			
		||||
@ -573,7 +573,7 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
                            )
 | 
			
		||||
 | 
			
		||||
                    )
 | 
			
		||||
*/
 | 
			
		||||
    */
 | 
			
		||||
    public function testGetAccounts()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -585,9 +585,9 @@ class YodleeApiTest extends TestCase
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
[2022-08-05 01:36:34] local.INFO: stdClass Object
 | 
			
		||||
(
 | 
			
		||||
    /**
 | 
			
		||||
    [2022-08-05 01:36:34] local.INFO: stdClass Object
 | 
			
		||||
    (
 | 
			
		||||
        [transaction] => Array
 | 
			
		||||
            (
 | 
			
		||||
                [0] => stdClass Object
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user