mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01: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);
|
||||
}
|
||||
|
||||
@ -83,20 +106,28 @@ 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,8 +9,8 @@
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Helpers\Bank\Yodlee\Transformer;
|
||||
|
||||
namespace App\Helpers\Bank\Nordigen\Transformer;
|
||||
|
||||
/**
|
||||
"date": "string",
|
||||
"sourceId": "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;
|
||||
@ -74,7 +74,7 @@ use Illuminate\Support\Facades\Cache;
|
||||
"holdingDescription": "string",
|
||||
"isin": "string",
|
||||
"status": "POSTED"
|
||||
|
||||
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[id] => 103953585
|
||||
@ -96,7 +96,7 @@ use Illuminate\Support\Facades\Cache;
|
||||
[original] => CHEROKEE NATION TAX TA TAHLEQUAH OK
|
||||
)
|
||||
|
||||
[isManual] =>
|
||||
[isManual] =>
|
||||
[sourceType] => AGGREGATED
|
||||
[date] => 2022-08-03
|
||||
[transactionDate] => 2022-08-03
|
||||
@ -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;
|
||||
|
||||
@ -24,7 +24,7 @@ class YodleeController extends BaseController
|
||||
public function auth(YodleeAuthRequest $request)
|
||||
{
|
||||
|
||||
// create a user at this point
|
||||
// create a user at this point
|
||||
// use the one time token here to pull in the actual user
|
||||
// store the user_account_id on the accounts table
|
||||
|
||||
@ -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,15 +49,15 @@ 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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
$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 = [
|
||||
@ -79,13 +78,11 @@ class YodleeController extends BaseController
|
||||
{
|
||||
$yodlee = new Yodlee($token);
|
||||
|
||||
$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', $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;
|
||||
@ -101,23 +98,23 @@ class YodleeController extends BaseController
|
||||
$bank_integration->balance = $account['current_balance'];
|
||||
$bank_integration->currency = $account['account_currency'];
|
||||
$bank_integration->from_date = now()->subYear();
|
||||
|
||||
|
||||
$bank_integration->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
$company->account->bank_integrations->each(function ($bank_integration) use ($company){
|
||||
|
||||
ProcessBankTransactions::dispatch($company->account->bank_integration_account_id, $bank_integration);
|
||||
$company->account->bank_integrations->each(function ($bank_integration) use ($company) {
|
||||
|
||||
ProcessBankTransactionsYodlee::dispatch($company->account, $bank_integration);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Process Yodlee Refresh Webhook.
|
||||
*
|
||||
*
|
||||
@ -152,70 +149,70 @@ class YodleeController extends BaseController
|
||||
* )
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"info":"REFRESH.PROCESS_COMPLETED",
|
||||
"loginName":"fri21",
|
||||
"data":{
|
||||
"providerAccount":[
|
||||
{
|
||||
"id":10995860,
|
||||
"providerId":16441,
|
||||
"isManual":false,
|
||||
"createdDate":"2017-12-22T05:47:35Z",
|
||||
"aggregationSource":"USER",
|
||||
"status":"SUCCESS",
|
||||
"requestId":"NSyMGo+R4dktywIu3hBIkc3PgWA=",
|
||||
"dataset":[
|
||||
{
|
||||
"name":"BASIC_AGG_DATA",
|
||||
"additionalStatus":"AVAILABLE_DATA_RETRIEVED",
|
||||
"updateEligibility":"ALLOW_UPDATE",
|
||||
"lastUpdated":"2017-12-22T05:48:16Z",
|
||||
"lastUpdateAttempt":"2017-12-22T05:48:16Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"info":"REFRESH.PROCESS_COMPLETED",
|
||||
"loginName":"fri21",
|
||||
"data":{
|
||||
"providerAccount":[
|
||||
{
|
||||
"id":10995860,
|
||||
"providerId":16441,
|
||||
"isManual":false,
|
||||
"createdDate":"2017-12-22T05:47:35Z",
|
||||
"aggregationSource":"USER",
|
||||
"status":"SUCCESS",
|
||||
"requestId":"NSyMGo+R4dktywIu3hBIkc3PgWA=",
|
||||
"dataset":[
|
||||
{
|
||||
"name":"BASIC_AGG_DATA",
|
||||
"additionalStatus":"AVAILABLE_DATA_RETRIEVED",
|
||||
"updateEligibility":"ALLOW_UPDATE",
|
||||
"lastUpdated":"2017-12-22T05:48:16Z",
|
||||
"lastUpdateAttempt":"2017-12-22T05:48:16Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}*/
|
||||
public function refreshWebhook(Request $request)
|
||||
{
|
||||
//we should ignore this one
|
||||
//we should ignore this one
|
||||
nlog("yodlee refresh");
|
||||
nlog($request->all());
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
|
||||
//
|
||||
|
||||
// return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3",
|
||||
"info":"LATEST_BALANCE_UPDATES",
|
||||
"loginName":"user1",
|
||||
"data":{
|
||||
"providerAccountId":658552,
|
||||
"latestBalanceEvent":[
|
||||
{
|
||||
"accountId":12345,
|
||||
"status":"SUCCESS"
|
||||
},
|
||||
{
|
||||
"accountId":12346,
|
||||
"status":"FAILED"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"notificationId":"63c73475-4db5-49ef-8553-8303337ca7c3",
|
||||
"info":"LATEST_BALANCE_UPDATES",
|
||||
"loginName":"user1",
|
||||
"data":{
|
||||
"providerAccountId":658552,
|
||||
"latestBalanceEvent":[
|
||||
{
|
||||
"accountId":12345,
|
||||
"status":"SUCCESS"
|
||||
},
|
||||
{
|
||||
"accountId":12346,
|
||||
"status":"FAILED"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
public function balanceWebhook(Request $request)
|
||||
{
|
||||
|
||||
@ -223,79 +220,79 @@ class YodleeController extends BaseController
|
||||
nlog($request->all());
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
|
||||
//
|
||||
|
||||
// return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"data":[
|
||||
{
|
||||
"autoRefresh":{
|
||||
"additionalStatus":"SCHEDULED",
|
||||
"status":"ENABLED"
|
||||
},
|
||||
"accountIds":[
|
||||
1112645899,
|
||||
1112645898
|
||||
],
|
||||
"loginName":"YSL1555332811628",
|
||||
"providerAccountId":11381459
|
||||
}
|
||||
],
|
||||
"notificationTime":"2019-06-14T04:49:39Z",
|
||||
"notificationId":"4e672150-156048777",
|
||||
"info":"AUTO_REFRESH_UPDATES"
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
"event":{
|
||||
"data":[
|
||||
{
|
||||
"autoRefresh":{
|
||||
"additionalStatus":"SCHEDULED",
|
||||
"status":"ENABLED"
|
||||
},
|
||||
"accountIds":[
|
||||
1112645899,
|
||||
1112645898
|
||||
],
|
||||
"loginName":"YSL1555332811628",
|
||||
"providerAccountId":11381459
|
||||
}
|
||||
],
|
||||
"notificationTime":"2019-06-14T04:49:39Z",
|
||||
"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());
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
|
||||
//
|
||||
|
||||
// return response()->json(['message' => 'Unauthorized'], 403);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
"event": {
|
||||
"notificationId": "64b7ed1a-1530523285",
|
||||
"info": "DATA_UPDATES.USER_DATA",
|
||||
"data": {
|
||||
"userCount": 1,
|
||||
"fromDate": "2017-11-10T10:18:44Z",
|
||||
"toDate": "2017-11-10T11:18:43Z",
|
||||
"userData": [{
|
||||
"user": {
|
||||
"loginName": "YSL1484052178554"
|
||||
},
|
||||
"links": [{
|
||||
"methodType": "GET",
|
||||
"rel": "getUserData",
|
||||
"href": "dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554"
|
||||
/*
|
||||
"event": {
|
||||
"notificationId": "64b7ed1a-1530523285",
|
||||
"info": "DATA_UPDATES.USER_DATA",
|
||||
"data": {
|
||||
"userCount": 1,
|
||||
"fromDate": "2017-11-10T10:18:44Z",
|
||||
"toDate": "2017-11-10T11:18:43Z",
|
||||
"userData": [{
|
||||
"user": {
|
||||
"loginName": "YSL1484052178554"
|
||||
},
|
||||
"links": [{
|
||||
"methodType": "GET",
|
||||
"rel": "getUserData",
|
||||
"href": "dataExtracts/userData?fromDate=2017-11-10T10:18:44Z&toDate=2017-11-10T11:18:43Z&loginName=YSL1484052178554"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
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());
|
||||
|
||||
return response()->json(['message' => 'Success'], 200);
|
||||
|
||||
|
||||
//
|
||||
|
||||
// return response()->json(['message' => 'Unauthorized'], 403);
|
||||
|
@ -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;
|
||||
@ -471,7 +474,7 @@ class BankIntegrationController extends BaseController
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
|
||||
$bank_integrations = BankIntegration::withTrashed()->whereIn('id', $this->transformKeys($ids))->company()->get();
|
||||
|
||||
$bank_integrations->each(function ($bank_integration, $key) use ($action) {
|
||||
@ -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();
|
||||
$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;
|
||||
@ -552,26 +577,44 @@ class BankIntegrationController extends BaseController
|
||||
$bank_integration->nickname = $account['nickname'];
|
||||
$bank_integration->balance = $account['current_balance'];
|
||||
$bank_integration->currency = $account['account_currency'];
|
||||
|
||||
|
||||
$bank_integration->save();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private function refreshAccountsNordigen(Account $account)
|
||||
{
|
||||
|
||||
$account = auth()->user()->account;
|
||||
|
||||
if(Cache::get("throttle_polling:{$account->key}"))
|
||||
return response()->json(BankIntegration::query()->company(), 200);
|
||||
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);
|
||||
|
||||
$account->bank_integrations->each(function ($bank_integration) use ($account){
|
||||
|
||||
ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration);
|
||||
$nordigen = new Nordigen($account->bank_integration_nordigen_client_id, $account->bank_integration_nordigen_client_secret);
|
||||
|
||||
});
|
||||
$accounts = $nordigen->getAccounts();
|
||||
|
||||
Cache::put("throttle_polling:{$account->key}", true, 300);
|
||||
foreach ($accounts as $account) {
|
||||
|
||||
return response()->json(BankIntegration::query()->company(), 200);
|
||||
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'];
|
||||
|
||||
$bank_integration->save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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,14 +728,21 @@ 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 ProcessBankTransactions(auth()->user()->account->bank_integration_account_id, $bank_integration))->handle();
|
||||
(new ProcessBankTransactionsYodlee(auth()->user()->account, $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();
|
||||
|
||||
});
|
||||
|
||||
return response()->json(['message' => 'Fetching transactions....'], 200);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class MatchBankTransactions implements ShouldQueue
|
||||
private $categories;
|
||||
|
||||
private float $available_balance = 0;
|
||||
|
||||
|
||||
private float $applied_amount = 0;
|
||||
|
||||
private array $attachable_invoices = [];
|
||||
@ -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,28 +132,27 @@ 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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
@ -187,7 +185,7 @@ class MatchBankTransactions implements ShouldQueue
|
||||
$this->bts->push($this->bt->id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
@ -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)
|
||||
return $this;
|
||||
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,13 +238,13 @@ 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)
|
||||
return $this;
|
||||
if (!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED)
|
||||
return $this;
|
||||
|
||||
$expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id);
|
||||
$expense->category_id = $this->resolveCategory($input);
|
||||
@ -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,9 +265,9 @@ 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;
|
||||
$this->bt->save();
|
||||
|
||||
@ -278,52 +276,48 @@ 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;
|
||||
$_amount = false;
|
||||
|
||||
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)
|
||||
{
|
||||
$_amount = $this->available_balance;
|
||||
$this->applied_amount += $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) {
|
||||
$_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];
|
||||
$this->attachable_invoices[] = ['id' => $this->invoice->id, 'amount' => $_amount];
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->setExchangeRate()
|
||||
->updateBalance($_amount * -1)
|
||||
->updatePaidToDate($_amount)
|
||||
->setCalculatedStatus()
|
||||
->save();
|
||||
}
|
||||
$this->invoice
|
||||
->service()
|
||||
->setExchangeRate()
|
||||
->updateBalance($_amount * -1)
|
||||
->updatePaidToDate($_amount)
|
||||
->setCalculatedStatus()
|
||||
->save();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}, 2);
|
||||
|
||||
if(!$this->invoice)
|
||||
if (!$this->invoice)
|
||||
return;
|
||||
|
||||
|
||||
/* Create Payment */
|
||||
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
|
||||
@ -336,7 +330,7 @@ class MatchBankTransactions implements ShouldQueue
|
||||
$payment->currency_id = $this->bt->currency_id;
|
||||
$payment->is_manual = false;
|
||||
$payment->date = $this->bt->date ? Carbon::parse($this->bt->date) : now();
|
||||
|
||||
|
||||
|
||||
/* Bank Transfer! */
|
||||
$payment_type_id = 1;
|
||||
@ -344,15 +338,14 @@ class MatchBankTransactions implements ShouldQueue
|
||||
$payment->saveQuietly();
|
||||
|
||||
$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'],
|
||||
@ -365,24 +358,24 @@ class MatchBankTransactions implements ShouldQueue
|
||||
$this->invoice->next_send_date = null;
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->applyNumber()
|
||||
->touchPdf()
|
||||
->save();
|
||||
->service()
|
||||
->applyNumber()
|
||||
->touchPdf()
|
||||
->save();
|
||||
|
||||
$payment->ledger()
|
||||
->updatePaymentBalance($amount * -1);
|
||||
->updatePaymentBalance($amount * -1);
|
||||
|
||||
$this->invoice
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($this->applied_amount*-1, $amount)
|
||||
->save();
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($this->applied_amount * -1, $amount)
|
||||
->save();
|
||||
|
||||
$this->invoice = $this->invoice
|
||||
->service()
|
||||
->workFlow()
|
||||
->save();
|
||||
->service()
|
||||
->workFlow()
|
||||
->save();
|
||||
|
||||
/* Update Invoice balance */
|
||||
event(new PaymentWasCreated($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
@ -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;
|
||||
@ -419,7 +411,7 @@ class MatchBankTransactions implements ShouldQueue
|
||||
|
||||
return $ec->id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -452,4 +444,4 @@ class MatchBankTransactions implements ShouldQueue
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
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,14 +91,13 @@ 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))
|
||||
{
|
||||
$this->bank_integration->disabled_upstream = true;
|
||||
$this->bank_integration->save();
|
||||
$this->stop_loop = false;
|
||||
return;
|
||||
if (!$yodlee->getAccount($this->bank_integration->bank_account_id)) {
|
||||
$this->bank_integration->disabled_upstream = true;
|
||||
$this->bank_integration->save();
|
||||
$this->stop_loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
@ -111,10 +114,10 @@ class ProcessBankTransactions implements ShouldQueue
|
||||
$count = $transaction_count->transaction->TOTAL->count;
|
||||
|
||||
//get transactions array
|
||||
$transactions = $yodlee->getTransactions($data);
|
||||
$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;
|
||||
@ -129,21 +132,20 @@ class ProcessBankTransactions implements ShouldQueue
|
||||
|
||||
/*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())
|
||||
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,[
|
||||
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();
|
||||
@ -166,4 +168,4 @@ class ProcessBankTransactions implements ShouldQueue
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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
|
||||
// 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,22 +294,22 @@ 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,17 +505,18 @@ 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);
|
||||
return 0;
|
||||
|
||||
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(),
|
||||
@ -38,4 +40,4 @@ class BankIntegrationFactory extends Factory
|
||||
'is_deleted' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -38,10 +38,10 @@ class YodleeApiTest extends TestCase
|
||||
parent::setUp();
|
||||
|
||||
// if(!config('ninja.yodlee.client_id'))
|
||||
$this->markTestSkipped('Skip test no Yodlee API credentials found');
|
||||
$this->markTestSkipped('Skip test no Yodlee API credentials found');
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function testExpenseGenerationFromBankFeed()
|
||||
@ -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]);
|
||||
@ -117,7 +117,7 @@ class YodleeApiTest extends TestCase
|
||||
$bt->date = now()->format('Y-m-d');
|
||||
$bt->transaction_id = 123456;
|
||||
$bt->save();
|
||||
|
||||
|
||||
$data['transactions'][] = [
|
||||
'id' => $bt->id,
|
||||
'invoice_ids' => $invoice->hashed_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;
|
||||
@ -186,42 +186,42 @@ class YodleeApiTest extends TestCase
|
||||
// $bank_integration->nickname = $account['nickname'];
|
||||
// $bank_integration->balance = $account['current_balance'];
|
||||
// $bank_integration->currency = $account['account_currency'];
|
||||
|
||||
// $bank_integration->save();
|
||||
|
||||
// ProcessBankTransactions::dispatchSync('sbMem62e1e69547bfb1', $bank_integration);
|
||||
// $bank_integration->save();
|
||||
|
||||
// }
|
||||
// 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);
|
||||
|
||||
// $bt = BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->first();
|
||||
// BankService::dispatchSync($this->company->id, $this->company->db);
|
||||
|
||||
// nlog(BankTransaction::where('company_id', $this->company->id)->pluck('invoice_ids'));
|
||||
// $bt = BankTransaction::where('invoice_ids', $this->invoice->hashed_id)->first();
|
||||
|
||||
// $this->assertNotNull($bt);
|
||||
// nlog(BankTransaction::where('company_id', $this->company->id)->pluck('invoice_ids'));
|
||||
|
||||
// $this->assertEquals(BankTransaction::STATUS_MATCHED, $bt->status_id);
|
||||
// $this->assertNotNull($bt);
|
||||
|
||||
// }
|
||||
// $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'
|
||||
],
|
||||
]);
|
||||
@ -258,7 +258,7 @@ class YodleeApiTest extends TestCase
|
||||
$invoice = $transaction->first(function ($value, $key) {
|
||||
|
||||
return str_contains($value->description, 'tinker');
|
||||
|
||||
|
||||
});
|
||||
|
||||
$this->assertNotNull($invoice);
|
||||
@ -286,179 +286,179 @@ class YodleeApiTest extends TestCase
|
||||
$this->assertNotNull($access_token);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
|
||||
[transactionCategory] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[id] => 1
|
||||
[source] => SYSTEM
|
||||
[classification] => PERSONAL
|
||||
[category] => Uncategorized
|
||||
[type] => UNCATEGORIZE
|
||||
[highLevelCategoryId] => 10000017
|
||||
[highLevelCategoryName] => Uncategorized
|
||||
[defaultCategoryName] => Uncategorized
|
||||
[defaultHighLevelCategoryName] => Uncategorized
|
||||
)
|
||||
[transactionCategory] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[id] => 1
|
||||
[source] => SYSTEM
|
||||
[classification] => PERSONAL
|
||||
[category] => Uncategorized
|
||||
[type] => UNCATEGORIZE
|
||||
[highLevelCategoryId] => 10000017
|
||||
[highLevelCategoryName] => Uncategorized
|
||||
[defaultCategoryName] => Uncategorized
|
||||
[defaultHighLevelCategoryName] => Uncategorized
|
||||
)
|
||||
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[id] => 2
|
||||
[source] => SYSTEM
|
||||
[classification] => PERSONAL
|
||||
[category] => Automotive/Fuel
|
||||
[type] => EXPENSE
|
||||
[detailCategory] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[id] => 1041
|
||||
[name] => Registration/Licensing
|
||||
)
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[id] => 2
|
||||
[source] => SYSTEM
|
||||
[classification] => PERSONAL
|
||||
[category] => Automotive/Fuel
|
||||
[type] => EXPENSE
|
||||
[detailCategory] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[id] => 1041
|
||||
[name] => Registration/Licensing
|
||||
)
|
||||
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[id] => 1145
|
||||
[name] => Automotive
|
||||
)
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[id] => 1145
|
||||
[name] => Automotive
|
||||
)
|
||||
|
||||
[2] => stdClass Object
|
||||
(
|
||||
[id] => 1218
|
||||
[name] => Auto Fees/Penalties
|
||||
)
|
||||
[2] => stdClass Object
|
||||
(
|
||||
[id] => 1218
|
||||
[name] => Auto Fees/Penalties
|
||||
)
|
||||
|
||||
[3] => stdClass Object
|
||||
(
|
||||
[id] => 1260
|
||||
[name] => Car Appraisers
|
||||
)
|
||||
[3] => stdClass Object
|
||||
(
|
||||
[id] => 1260
|
||||
[name] => Car Appraisers
|
||||
)
|
||||
|
||||
[4] => stdClass Object
|
||||
(
|
||||
[id] => 1261
|
||||
[name] => Car Dealers
|
||||
)
|
||||
[4] => stdClass Object
|
||||
(
|
||||
[id] => 1261
|
||||
[name] => Car Dealers
|
||||
)
|
||||
|
||||
[5] => stdClass Object
|
||||
(
|
||||
[id] => 1262
|
||||
[name] => Car Dealers and Leasing
|
||||
)
|
||||
[5] => stdClass Object
|
||||
(
|
||||
[id] => 1262
|
||||
[name] => Car Dealers and Leasing
|
||||
)
|
||||
|
||||
[6] => stdClass Object
|
||||
(
|
||||
[id] => 1263
|
||||
[name] => Car Parts and Accessories
|
||||
)
|
||||
[6] => stdClass Object
|
||||
(
|
||||
[id] => 1263
|
||||
[name] => Car Parts and Accessories
|
||||
)
|
||||
|
||||
[7] => stdClass Object
|
||||
(
|
||||
[id] => 1264
|
||||
[name] => Car Wash and Detail
|
||||
)
|
||||
[7] => stdClass Object
|
||||
(
|
||||
[id] => 1264
|
||||
[name] => Car Wash and Detail
|
||||
)
|
||||
|
||||
[8] => stdClass Object
|
||||
(
|
||||
[id] => 1265
|
||||
[name] => Classic and Antique Car
|
||||
)
|
||||
[8] => stdClass Object
|
||||
(
|
||||
[id] => 1265
|
||||
[name] => Classic and Antique Car
|
||||
)
|
||||
|
||||
[9] => stdClass Object
|
||||
(
|
||||
[id] => 1267
|
||||
[name] => Maintenance and Repair
|
||||
)
|
||||
[9] => stdClass Object
|
||||
(
|
||||
[id] => 1267
|
||||
[name] => Maintenance and Repair
|
||||
)
|
||||
|
||||
[10] => stdClass Object
|
||||
(
|
||||
[id] => 1268
|
||||
[name] => Motorcycles/Mopeds/Scooters
|
||||
)
|
||||
[10] => stdClass Object
|
||||
(
|
||||
[id] => 1268
|
||||
[name] => Motorcycles/Mopeds/Scooters
|
||||
)
|
||||
|
||||
[11] => stdClass Object
|
||||
(
|
||||
[id] => 1269
|
||||
[name] => Oil and Lube
|
||||
)
|
||||
[11] => stdClass Object
|
||||
(
|
||||
[id] => 1269
|
||||
[name] => Oil and Lube
|
||||
)
|
||||
|
||||
[12] => stdClass Object
|
||||
(
|
||||
[id] => 1270
|
||||
[name] => Motorcycle Repair
|
||||
)
|
||||
[12] => stdClass Object
|
||||
(
|
||||
[id] => 1270
|
||||
[name] => Motorcycle Repair
|
||||
)
|
||||
|
||||
[13] => stdClass Object
|
||||
(
|
||||
[id] => 1271
|
||||
[name] => RVs and Motor Homes
|
||||
)
|
||||
[13] => stdClass Object
|
||||
(
|
||||
[id] => 1271
|
||||
[name] => RVs and Motor Homes
|
||||
)
|
||||
|
||||
[14] => stdClass Object
|
||||
(
|
||||
[id] => 1272
|
||||
[name] => Motorcycle Sales
|
||||
)
|
||||
[14] => stdClass Object
|
||||
(
|
||||
[id] => 1272
|
||||
[name] => Motorcycle Sales
|
||||
)
|
||||
|
||||
[15] => stdClass Object
|
||||
(
|
||||
[id] => 1273
|
||||
[name] => Salvage Yards
|
||||
)
|
||||
[15] => stdClass Object
|
||||
(
|
||||
[id] => 1273
|
||||
[name] => Salvage Yards
|
||||
)
|
||||
|
||||
[16] => stdClass Object
|
||||
(
|
||||
[id] => 1274
|
||||
[name] => Smog Check
|
||||
)
|
||||
[16] => stdClass Object
|
||||
(
|
||||
[id] => 1274
|
||||
[name] => Smog Check
|
||||
)
|
||||
|
||||
[17] => stdClass Object
|
||||
(
|
||||
[id] => 1275
|
||||
[name] => Tires
|
||||
)
|
||||
[17] => stdClass Object
|
||||
(
|
||||
[id] => 1275
|
||||
[name] => Tires
|
||||
)
|
||||
|
||||
[18] => stdClass Object
|
||||
(
|
||||
[id] => 1276
|
||||
[name] => Towing
|
||||
)
|
||||
[18] => stdClass Object
|
||||
(
|
||||
[id] => 1276
|
||||
[name] => Towing
|
||||
)
|
||||
|
||||
[19] => stdClass Object
|
||||
(
|
||||
[id] => 1277
|
||||
[name] => Transmissions
|
||||
)
|
||||
[19] => stdClass Object
|
||||
(
|
||||
[id] => 1277
|
||||
[name] => Transmissions
|
||||
)
|
||||
|
||||
[20] => stdClass Object
|
||||
(
|
||||
[id] => 1278
|
||||
[name] => Used Cars
|
||||
)
|
||||
[20] => stdClass Object
|
||||
(
|
||||
[id] => 1278
|
||||
[name] => Used Cars
|
||||
)
|
||||
|
||||
[21] => stdClass Object
|
||||
(
|
||||
[id] => 1240
|
||||
[name] => e-Charging
|
||||
)
|
||||
[21] => stdClass Object
|
||||
(
|
||||
[id] => 1240
|
||||
[name] => e-Charging
|
||||
)
|
||||
|
||||
[22] => stdClass Object
|
||||
(
|
||||
[id] => 1266
|
||||
[name] => Gas Stations
|
||||
)
|
||||
[22] => stdClass Object
|
||||
(
|
||||
[id] => 1266
|
||||
[name] => Gas Stations
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
[highLevelCategoryId] => 10000003
|
||||
[highLevelCategoryName] => Automotive Expenses
|
||||
[defaultCategoryName] => Automotive Expenses
|
||||
[defaultHighLevelCategoryName] => Automotive Expenses
|
||||
)
|
||||
[highLevelCategoryId] => 10000003
|
||||
[highLevelCategoryName] => Automotive Expenses
|
||||
[defaultCategoryName] => Automotive Expenses
|
||||
[defaultHighLevelCategoryName] => Automotive Expenses
|
||||
)
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
|
||||
public function testGetCategories()
|
||||
@ -467,113 +467,113 @@ class YodleeApiTest extends TestCase
|
||||
$yodlee = new Yodlee('sbMem62e1e69547bfb2');
|
||||
|
||||
$transactions = $yodlee->getTransactionCategories();
|
||||
|
||||
|
||||
$this->assertIsArray($transactions->transactionCategory);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
[2022-08-05 01:29:45] local.INFO: stdClass Object
|
||||
(
|
||||
[account] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[providerAccountId] => 11308693
|
||||
[accountName] => My CD - 8878
|
||||
[accountStatus] => ACTIVE
|
||||
[accountNumber] => xxxx8878
|
||||
[aggregationSource] => USER
|
||||
[isAsset] => 1
|
||||
[balance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 49778.07
|
||||
)
|
||||
/**
|
||||
[2022-08-05 01:29:45] local.INFO: stdClass Object
|
||||
(
|
||||
[account] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[providerAccountId] => 11308693
|
||||
[accountName] => My CD - 8878
|
||||
[accountStatus] => ACTIVE
|
||||
[accountNumber] => xxxx8878
|
||||
[aggregationSource] => USER
|
||||
[isAsset] => 1
|
||||
[balance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 49778.07
|
||||
)
|
||||
|
||||
[id] => 12331861
|
||||
[includeInNetWorth] => 1
|
||||
[providerId] => 18769
|
||||
[providerName] => Dag Site Captcha
|
||||
[isManual] =>
|
||||
[currentBalance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 49778.07
|
||||
)
|
||||
[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
|
||||
)
|
||||
[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
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[providerAccountId] => 11308693
|
||||
[accountName] => Joint Savings - 7159
|
||||
[accountStatus] => ACTIVE
|
||||
[accountNumber] => xxxx7159
|
||||
[aggregationSource] => USER
|
||||
[isAsset] => 1
|
||||
[balance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
)
|
||||
[1] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[providerAccountId] => 11308693
|
||||
[accountName] => Joint Savings - 7159
|
||||
[accountStatus] => ACTIVE
|
||||
[accountNumber] => xxxx7159
|
||||
[aggregationSource] => USER
|
||||
[isAsset] => 1
|
||||
[balance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
|
||||
[id] => 12331860
|
||||
[includeInNetWorth] => 1
|
||||
[providerId] => 18769
|
||||
[providerName] => Dag Site Captcha
|
||||
[isManual] =>
|
||||
[availableBalance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
[id] => 12331860
|
||||
[includeInNetWorth] => 1
|
||||
[providerId] => 18769
|
||||
[providerName] => Dag Site Captcha
|
||||
[isManual] =>
|
||||
[availableBalance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
|
||||
[currentBalance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
[currentBalance] => stdClass Object
|
||||
(
|
||||
[currency] => USD
|
||||
[amount] => 186277.45
|
||||
)
|
||||
|
||||
[accountType] => SAVINGS
|
||||
[displayedName] => LYDIA
|
||||
[createdDate] => 2022-07-28T06:55:33Z
|
||||
[classification] => PERSONAL
|
||||
[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
|
||||
)
|
||||
[accountType] => SAVINGS
|
||||
[displayedName] => LYDIA
|
||||
[createdDate] => 2022-07-28T06:55:33Z
|
||||
[classification] => PERSONAL
|
||||
[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
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
*/
|
||||
)
|
||||
*/
|
||||
public function testGetAccounts()
|
||||
{
|
||||
|
||||
@ -585,51 +585,51 @@ class YodleeApiTest extends TestCase
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
[2022-08-05 01:36:34] local.INFO: stdClass Object
|
||||
(
|
||||
[transaction] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[id] => 103953585
|
||||
[amount] => stdClass Object
|
||||
(
|
||||
[amount] => 480.66
|
||||
[currency] => USD
|
||||
)
|
||||
/**
|
||||
[2022-08-05 01:36:34] local.INFO: stdClass Object
|
||||
(
|
||||
[transaction] => Array
|
||||
(
|
||||
[0] => stdClass Object
|
||||
(
|
||||
[CONTAINER] => bank
|
||||
[id] => 103953585
|
||||
[amount] => stdClass Object
|
||||
(
|
||||
[amount] => 480.66
|
||||
[currency] => USD
|
||||
)
|
||||
|
||||
[categoryType] => UNCATEGORIZE
|
||||
[categoryId] => 1
|
||||
[category] => Uncategorized
|
||||
[categorySource] => SYSTEM
|
||||
[highLevelCategoryId] => 10000017
|
||||
[createdDate] => 2022-08-04T21:50:17Z
|
||||
[lastUpdated] => 2022-08-04T21:50:17Z
|
||||
[description] => stdClass Object
|
||||
(
|
||||
[original] => CHEROKEE NATION TAX TA TAHLEQUAH OK
|
||||
)
|
||||
[categoryType] => UNCATEGORIZE
|
||||
[categoryId] => 1
|
||||
[category] => Uncategorized
|
||||
[categorySource] => SYSTEM
|
||||
[highLevelCategoryId] => 10000017
|
||||
[createdDate] => 2022-08-04T21:50:17Z
|
||||
[lastUpdated] => 2022-08-04T21:50:17Z
|
||||
[description] => stdClass Object
|
||||
(
|
||||
[original] => CHEROKEE NATION TAX TA TAHLEQUAH OK
|
||||
)
|
||||
|
||||
[isManual] =>
|
||||
[sourceType] => AGGREGATED
|
||||
[date] => 2022-08-03
|
||||
[transactionDate] => 2022-08-03
|
||||
[postDate] => 2022-08-03
|
||||
[status] => POSTED
|
||||
[accountId] => 12331794
|
||||
[runningBalance] => stdClass Object
|
||||
(
|
||||
[amount] => 480.66
|
||||
[currency] => USD
|
||||
)
|
||||
[isManual] =>
|
||||
[sourceType] => AGGREGATED
|
||||
[date] => 2022-08-03
|
||||
[transactionDate] => 2022-08-03
|
||||
[postDate] => 2022-08-03
|
||||
[status] => POSTED
|
||||
[accountId] => 12331794
|
||||
[runningBalance] => stdClass Object
|
||||
(
|
||||
[amount] => 480.66
|
||||
[currency] => USD
|
||||
)
|
||||
|
||||
[checkNumber] => 998
|
||||
)
|
||||
[checkNumber] => 998
|
||||
)
|
||||
|
||||
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
public function testGetTransactions()
|
||||
{
|
||||
@ -654,7 +654,7 @@ class YodleeApiTest extends TestCase
|
||||
'fromDate' => '2000-10-10', /// YYYY-MM-DD
|
||||
];
|
||||
|
||||
$accounts = $yodlee->getTransactions($data);
|
||||
$accounts = $yodlee->getTransactions($data);
|
||||
|
||||
$this->assertIsArray($accounts);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user