mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Lots of new code for Spectre import.
This commit is contained in:
parent
4e0319bacc
commit
5177619301
@ -172,6 +172,7 @@ class FileConfigurator implements ConfiguratorInterface
|
||||
'column-mapping-complete' => false, // so mapping is not complete.
|
||||
'apply-rules' => true,
|
||||
'match-bills' => false,
|
||||
'auto-start' => false,
|
||||
];
|
||||
$config = $this->job->configuration ?? [];
|
||||
$finalConfig = array_merge($defaultConfig, $config);
|
||||
|
@ -22,7 +22,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Import\Configuration;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Support\Import\Configuration\Spectre\HaveAccounts;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -52,7 +54,29 @@ class SpectreConfigurator implements ConfiguratorInterface
|
||||
*/
|
||||
public function configureJob(array $data): bool
|
||||
{
|
||||
die('cannot store config');
|
||||
$config = $this->job->configuration;
|
||||
$stage = $config['stage'];
|
||||
$status = $this->job->status;
|
||||
Log::debug(sprintf('in getNextData(), for stage "%s".', $stage));
|
||||
switch ($stage) {
|
||||
case 'have-accounts':
|
||||
/** @var HaveAccounts $class */
|
||||
$class = app(HaveAccounts::class);
|
||||
$class->setJob($this->job);
|
||||
$class->storeConfiguration($data);
|
||||
|
||||
// update job for next step and set to "configured".
|
||||
$config = $this->job->configuration;
|
||||
$config['stage'] = 'have-account-mapping';
|
||||
$this->job->configuration = $config;
|
||||
$this->job->status = 'configured';
|
||||
$this->job->save();
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot store configuration when job is in state "%s"', $stage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,13 +86,34 @@ class SpectreConfigurator implements ConfiguratorInterface
|
||||
*/
|
||||
public function getNextData(): array
|
||||
{
|
||||
Log::debug('in getNextData(), user will be redirected next.');
|
||||
// update config to tell Firefly the user is redirected.
|
||||
$config = $this->job->configuration;
|
||||
$config['is-redirected'] = true;
|
||||
$config['stage'] = 'redirected';
|
||||
$config = $this->job->configuration;
|
||||
$stage = $config['stage'];
|
||||
$status = $this->job->status;
|
||||
Log::debug(sprintf('in getNextData(), for stage "%s".', $stage));
|
||||
switch ($stage) {
|
||||
case 'has-token':
|
||||
// simply redirect to Spectre.
|
||||
$config['is-redirected'] = true;
|
||||
$config['stage'] = 'user-logged-in';
|
||||
$status = 'configured';
|
||||
break;
|
||||
case 'have-accounts':
|
||||
// use special class:
|
||||
/** @var HaveAccounts $class */
|
||||
$class = app(HaveAccounts::class);
|
||||
$class->setJob($this->job);
|
||||
$data = $class->getData();
|
||||
|
||||
return $data;
|
||||
default:
|
||||
return [];
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// update config and status:
|
||||
$this->job->configuration = $config;
|
||||
$this->job->status = 'configured';
|
||||
$this->job->status = $status;
|
||||
$this->job->save();
|
||||
|
||||
return $this->job->configuration;
|
||||
@ -79,10 +124,22 @@ class SpectreConfigurator implements ConfiguratorInterface
|
||||
*/
|
||||
public function getNextView(): string
|
||||
{
|
||||
Log::debug('Send user redirect view');
|
||||
$config = $this->job->configuration;
|
||||
$stage = $config['stage'];
|
||||
Log::debug(sprintf('in getNextView(), for stage "%s".', $stage));
|
||||
switch ($stage) {
|
||||
case 'has-token':
|
||||
// redirect to Spectre.
|
||||
return 'import.spectre.redirect';
|
||||
break;
|
||||
case 'have-accounts':
|
||||
return 'import.spectre.accounts';
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
break;
|
||||
|
||||
// sends the user to spectre.
|
||||
return 'import.spectre.redirect';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,17 +157,20 @@ class SpectreConfigurator implements ConfiguratorInterface
|
||||
*/
|
||||
public function isJobConfigured(): bool
|
||||
{
|
||||
Log::debug('in isJobConfigured');
|
||||
// job is configured (and can start) when token is empty:
|
||||
$config = $this->job->configuration;
|
||||
if ($config['has-token'] === false && $config['is-redirected'] === true) {
|
||||
Log::debug('has-token is false, is-redirected is true, return true');
|
||||
$stage = $config['stage'];
|
||||
Log::debug(sprintf('in isJobConfigured(), for stage "%s".', $stage));
|
||||
switch ($stage) {
|
||||
case 'has-token':
|
||||
case 'have-accounts':
|
||||
Log::debug('isJobConfigured returns false');
|
||||
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
Log::debug('isJobConfigured returns true');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug('return false');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,15 +179,17 @@ class SpectreConfigurator implements ConfiguratorInterface
|
||||
public function setJob(ImportJob $job)
|
||||
{
|
||||
$defaultConfig = [
|
||||
'has-token' => false,
|
||||
'token' => '',
|
||||
'token-expires' => 0,
|
||||
'token-url' => '',
|
||||
'is-redirected' => false,
|
||||
'customer' => null,
|
||||
'login' => null,
|
||||
'stage' => 'initial',
|
||||
'accounts' => [],
|
||||
'has-token' => false,
|
||||
'token' => '',
|
||||
'token-expires' => 0,
|
||||
'token-url' => '',
|
||||
'is-redirected' => false,
|
||||
'customer' => null,
|
||||
'login' => null,
|
||||
'stage' => 'initial',
|
||||
'accounts' => '',
|
||||
'accounts-mapped' => '',
|
||||
'auto-start' => true,
|
||||
];
|
||||
$extendedStatus = $job->extended_status;
|
||||
$extendedStatus['steps'] = 100;
|
||||
|
@ -26,12 +26,16 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
|
||||
use FireflyIII\Services\Spectre\Exception\DuplicatedCustomerException;
|
||||
use FireflyIII\Services\Spectre\Exception\SpectreException;
|
||||
use FireflyIII\Services\Spectre\Object\Account;
|
||||
use FireflyIII\Services\Spectre\Object\Customer;
|
||||
use FireflyIII\Services\Spectre\Object\Login;
|
||||
use FireflyIII\Services\Spectre\Object\Token;
|
||||
use FireflyIII\Services\Spectre\Request\CreateTokenRequest;
|
||||
use FireflyIII\Services\Spectre\Request\ListAccountsRequest;
|
||||
use FireflyIII\Services\Spectre\Request\ListCustomersRequest;
|
||||
use FireflyIII\Services\Spectre\Request\ListLoginsRequest;
|
||||
use FireflyIII\Services\Spectre\Request\ListTransactionsRequest;
|
||||
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@ -99,6 +103,8 @@ class SpectreRoutine implements RoutineInterface
|
||||
* if attempt failed: job status is error, save a warning somewhere?
|
||||
* if success, try to get accounts. Save in config key 'accounts'. set status: have-accounts and "configuring"
|
||||
*
|
||||
* have-accounts: make user link accounts and select accounts to import from.
|
||||
*
|
||||
* If job is "configuring" and stage "have-accounts" then present the accounts and make user link them to
|
||||
* own asset accounts. Store this mapping, set config to "have-account-mapping" and job status configured".
|
||||
*
|
||||
@ -111,90 +117,35 @@ class SpectreRoutine implements RoutineInterface
|
||||
public function run(): bool
|
||||
{
|
||||
if ('configured' === $this->job->status) {
|
||||
$this->repository->updateStatus($this->job,'running');
|
||||
$this->repository->updateStatus($this->job, 'running');
|
||||
}
|
||||
|
||||
|
||||
Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key));
|
||||
set_time_limit(0);
|
||||
|
||||
// check if job has token first!
|
||||
$config = $this->job->configuration;
|
||||
$hasToken = $config['has-token'] ?? false;
|
||||
if ($hasToken === false) {
|
||||
Log::debug('Job has no token');
|
||||
// create customer if user does not have one:
|
||||
$customer = $this->getCustomer();
|
||||
Log::debug(sprintf('Customer ID is %s', $customer->getId()));
|
||||
// use customer to request a token:
|
||||
$uri = route('import.status', [$this->job->key]);
|
||||
$token = $this->getToken($customer, $uri);
|
||||
Log::debug(sprintf('Token is %s', $token->getToken()));
|
||||
$config = $this->job->configuration;
|
||||
$stage = $config['stage'];
|
||||
|
||||
// update job, give it the token:
|
||||
$config = $this->job->configuration;
|
||||
$config['has-token'] = true;
|
||||
$config['token'] = $token->getToken();
|
||||
$config['token-expires'] = $token->getExpiresAt()->format('U');
|
||||
$config['token-url'] = $token->getConnectUrl();
|
||||
$this->job->configuration = $config;
|
||||
|
||||
Log::debug('Job config is now', $config);
|
||||
|
||||
// update job, set status to "configuring".
|
||||
$this->job->status = 'configuring';
|
||||
$this->job->save();
|
||||
Log::debug(sprintf('Job status is now %s', $this->job->status));
|
||||
|
||||
return true;
|
||||
switch ($stage) {
|
||||
case 'initial':
|
||||
// get customer and token:
|
||||
$this->runStageInitial();
|
||||
break;
|
||||
case 'has-token':
|
||||
// import routine does nothing at this point:
|
||||
break;
|
||||
case 'user-logged-in':
|
||||
$this->runStageLoggedIn();
|
||||
break;
|
||||
case 'have-account-mapping':
|
||||
$this->runStageHaveMapping();
|
||||
break;
|
||||
default:
|
||||
throw new FireflyException(sprintf('Cannot handle stage %s', $stage));
|
||||
}
|
||||
$isRedirected = $config['is-redirected'] ?? false;
|
||||
if ($isRedirected === true) {
|
||||
// update job to say it's running
|
||||
$extended = $this->job->extended_status;
|
||||
$this->job->status = 'running';
|
||||
$extended['steps'] = 100;
|
||||
$extended['done'] = 1;
|
||||
$this->job->extended_status = $extended;
|
||||
$this->job->save();
|
||||
}
|
||||
// is job running?
|
||||
if ($this->job->status === 'running') {
|
||||
|
||||
// list all logins:
|
||||
$customer = $this->getCustomer();
|
||||
$request = new ListLoginsRequest($this->job->user);
|
||||
$request->setCustomer($customer);
|
||||
$request->call();
|
||||
$logins = $request->getLogins();
|
||||
/** @var Login $final */
|
||||
$final = null;
|
||||
// loop logins, find the latest with no error in it:
|
||||
$time = 0;
|
||||
/** @var Login $login */
|
||||
foreach($logins as $login) {
|
||||
$attempt = $login->getLastAttempt();
|
||||
$attemptTime = intval($attempt->getCreatedAt()->format('U'));
|
||||
if($attemptTime > $time && is_null($attempt->getFailErrorClass())) {
|
||||
$time = $attemptTime;
|
||||
$final = $login;
|
||||
}
|
||||
}
|
||||
if(is_null($final)) {
|
||||
throw new FireflyException('No valid login attempt found.');
|
||||
}
|
||||
var_dump($final);
|
||||
|
||||
//var_dump($logins);
|
||||
exit;
|
||||
|
||||
// assume user has "used" the token.
|
||||
// ...
|
||||
// now what?
|
||||
Log::debug('Token has been used. User was redirected. Now check status with Spectre and respond?');
|
||||
|
||||
throw new FireflyException('Application cannot handle this.');
|
||||
}
|
||||
var_dump($config);
|
||||
exit;
|
||||
|
||||
throw new FireflyException('Application cannot handle this.');
|
||||
}
|
||||
@ -204,7 +155,7 @@ class SpectreRoutine implements RoutineInterface
|
||||
*/
|
||||
public function setJob(ImportJob $job)
|
||||
{
|
||||
$this->job = $job;
|
||||
$this->job = $job;
|
||||
$this->repository = app(ImportJobRepositoryInterface::class);
|
||||
$this->repository->setUser($job->user);
|
||||
}
|
||||
@ -289,4 +240,124 @@ class SpectreRoutine implements RoutineInterface
|
||||
return $request->getToken();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* @throws SpectreException
|
||||
*/
|
||||
protected function runStageInitial(): void
|
||||
{
|
||||
Log::debug('In runStageInitial()');
|
||||
|
||||
// create customer if user does not have one:
|
||||
$customer = $this->getCustomer();
|
||||
Log::debug(sprintf('Customer ID is %s', $customer->getId()));
|
||||
|
||||
// use customer to request a token:
|
||||
$uri = route('import.status', [$this->job->key]);
|
||||
$token = $this->getToken($customer, $uri);
|
||||
Log::debug(sprintf('Token is %s', $token->getToken()));
|
||||
|
||||
// update job, give it the token:
|
||||
$config = $this->job->configuration;
|
||||
$config['has-token'] = true;
|
||||
$config['token'] = $token->getToken();
|
||||
$config['token-expires'] = $token->getExpiresAt()->format('U');
|
||||
$config['token-url'] = $token->getConnectUrl();
|
||||
$config['stage'] = 'has-token';
|
||||
$this->job->configuration = $config;
|
||||
|
||||
Log::debug('Job config is now', $config);
|
||||
|
||||
// update job, set status to "configuring".
|
||||
$this->job->status = 'configuring';
|
||||
$this->job->save();
|
||||
Log::debug(sprintf('Job status is now %s', $this->job->status));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* @throws SpectreException
|
||||
*/
|
||||
protected function runStageLoggedIn(): void
|
||||
{
|
||||
Log::debug('In runStageLoggedIn');
|
||||
// list all logins:
|
||||
$customer = $this->getCustomer();
|
||||
$request = new ListLoginsRequest($this->job->user);
|
||||
$request->setCustomer($customer);
|
||||
$request->call();
|
||||
$logins = $request->getLogins();
|
||||
/** @var Login $final */
|
||||
$final = null;
|
||||
// loop logins, find the latest with no error in it:
|
||||
$time = 0;
|
||||
/** @var Login $login */
|
||||
foreach ($logins as $login) {
|
||||
$attempt = $login->getLastAttempt();
|
||||
$attemptTime = intval($attempt->getCreatedAt()->format('U'));
|
||||
if ($attemptTime > $time && is_null($attempt->getFailErrorClass())) {
|
||||
$time = $attemptTime;
|
||||
$final = $login;
|
||||
}
|
||||
}
|
||||
if (is_null($final)) {
|
||||
throw new FireflyException('No valid login attempt found.');
|
||||
}
|
||||
|
||||
// list the users accounts using this login.
|
||||
$accountRequest = new ListAccountsRequest($this->job->user);
|
||||
$accountRequest->setLogin($login);
|
||||
$accountRequest->call();
|
||||
$accounts = $accountRequest->getAccounts();
|
||||
|
||||
// store accounts in job:
|
||||
$all = [];
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$all[] = $account->toArray();
|
||||
}
|
||||
|
||||
// update job:
|
||||
$config = $this->job->configuration;
|
||||
$config['accounts'] = $all;
|
||||
$config['login'] = $login->toArray();
|
||||
$config['stage'] = 'have-accounts';
|
||||
$this->job->configuration = $config;
|
||||
$this->job->status = 'configuring';
|
||||
$this->job->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function runStageHaveMapping()
|
||||
{
|
||||
// for each spectre account id in 'account-mappings'.
|
||||
// find FF account
|
||||
// get transactions.
|
||||
// import?!
|
||||
$config = $this->job->configuration;
|
||||
$accounts = $config['accounts'] ?? [];
|
||||
/** @var array $accountArray */
|
||||
foreach ($accounts as $accountArray) {
|
||||
$account = new Account($accountArray);
|
||||
$importId = intval($config['accounts-mapped'][$account->getid()] ?? 0);
|
||||
$doImport = $importId !== 0 ? true : false;
|
||||
if (!$doImport) {
|
||||
continue;
|
||||
}
|
||||
// import into account
|
||||
$listTransactionsRequest = new ListTransactionsRequest($this->job->user);
|
||||
$listTransactionsRequest->setAccount($account);
|
||||
$listTransactionsRequest->call();
|
||||
$transactions = $listTransactionsRequest->getTransactions();
|
||||
var_dump($transactions);exit;
|
||||
|
||||
}
|
||||
var_dump($config);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
101
app/Services/Spectre/Object/Account.php
Normal file
101
app/Services/Spectre/Object/Account.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* Account.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Spectre\Object;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Class Account
|
||||
*/
|
||||
class Account extends SpectreObject
|
||||
{
|
||||
/** @var float */
|
||||
private $balance;
|
||||
/** @var Carbon */
|
||||
private $createdAt;
|
||||
/** @var string */
|
||||
private $currencyCode;
|
||||
/** @var array */
|
||||
private $extra = [];
|
||||
/** @var int */
|
||||
private $id;
|
||||
/** @var int */
|
||||
private $loginId;
|
||||
/** @var string */
|
||||
private $name;
|
||||
/** @var string */
|
||||
private $nature;
|
||||
/** @var Carbon */
|
||||
private $updatedAt;
|
||||
|
||||
/**
|
||||
* Account constructor.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data)
|
||||
{
|
||||
$this->id = $data['id'];
|
||||
$this->loginId = $data['login_id'];
|
||||
$this->currencyCode = $data['currency_code'];
|
||||
$this->balance = $data['balance'];
|
||||
$this->name = $data['name'];
|
||||
$this->nature = $data['nature'];
|
||||
$this->createdAt = new Carbon($data['created_at']);
|
||||
$this->updatedAt = new Carbon($data['updated_at']);
|
||||
|
||||
foreach ($data['extra'] as $key => $value) {
|
||||
$this->extra[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = [
|
||||
'balance' => $this->balance,
|
||||
'created_at' => $this->createdAt->toIso8601String(),
|
||||
'currency_code' => $this->currencyCode,
|
||||
'extra' => $this->extra,
|
||||
'id' => $this->id,
|
||||
'login_id' => $this->loginId,
|
||||
'name' => $this->name,
|
||||
'nature' => $this->nature,
|
||||
'updated_at' => $this->updatedAt->toIso8601String(),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
@ -163,6 +163,46 @@ class Attempt extends SpectreObject
|
||||
return $this->failMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = [
|
||||
'api_mode' => $this->apiMode,
|
||||
'api_version' => $this->apiVersion,
|
||||
'automatic_fetch' => $this->automaticFetch,
|
||||
'categorize' => $this->categorize,
|
||||
'created_at' => $this->createdAt->toIso8601String(),
|
||||
'consent_given_at' => $this->consentGivenAt->toIso8601String(),
|
||||
'consent_types' => $this->consentTypes,
|
||||
'custom_fields' => $this->customFields,
|
||||
'daily_refresh' => $this->dailyRefresh,
|
||||
'device_type' => $this->deviceType,
|
||||
'user_agent' => $this->userAgent,
|
||||
'remote_ip' => $this->remoteIp,
|
||||
'exclude_accounts' => $this->excludeAccounts,
|
||||
'fail_at' => $this->failAt->toIso8601String(),
|
||||
'fail_error_class' => $this->failErrorClass,
|
||||
'fail_message' => $this->failMessage,
|
||||
'fetch_type' => $this->fetchType,
|
||||
'finished' => $this->finished,
|
||||
'finished_recent' => $this->finishedRecent,
|
||||
'from_date' => $this->fromDate->toIso8601String(),
|
||||
'id' => $this->id,
|
||||
'interactive' => $this->interactive,
|
||||
'locale' => $this->locale,
|
||||
'partial' => $this->partial,
|
||||
'show_consent_confirmation' => $this->showConsentInformation,
|
||||
'stages' => $this->stages,
|
||||
'store_credentials' => $this->storeCredentials,
|
||||
'success_at' => $this->successAt->toIso8601String(),
|
||||
'to_date' => $this->toDate->toIso8601String(),
|
||||
'updated_at' => $this->updatedAt->toIso8601String(),
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -37,4 +37,12 @@ class Holder extends SpectreObject
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -96,6 +96,14 @@ class Login extends SpectreObject
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Attempt
|
||||
*/
|
||||
@ -104,5 +112,35 @@ class Login extends SpectreObject
|
||||
return $this->lastAttempt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$array = [
|
||||
'consent_given_at' => $this->consentGivenAt->toIso8601String(),
|
||||
'consent_types' => $this->consentTypes,
|
||||
'country_code' => $this->countryCode,
|
||||
'created_at' => $this->createdAt->toIso8601String(),
|
||||
'updated_at' => $this->updatedAt->toIso8601String(),
|
||||
'customer_id' => $this->customerId,
|
||||
'daily_refresh' => $this->dailyRefresh,
|
||||
'holder_info' => $this->holderInfo->toArray(),
|
||||
'id' => $this->id,
|
||||
'last_attempt' => $this->lastAttempt->toArray(),
|
||||
'last_success_at' => $this->lastSuccessAt->toIso8601String(),
|
||||
'next_refresh_possible_at' => $this->nextRefreshPossibleAt,
|
||||
'provider_code' => $this->providerCode,
|
||||
'provider_id' => $this->providerId,
|
||||
'provider_name' => $this->providerName,
|
||||
'show_consent_confirmation' => $this->showConsentConfirmation,
|
||||
'status' => $this->status,
|
||||
'store_credentials' => $this->storeCredentials,
|
||||
|
||||
];
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
}
|
41
app/Services/Spectre/Object/Transaction.php
Normal file
41
app/Services/Spectre/Object/Transaction.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Transaction.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Spectre\Object;
|
||||
|
||||
/**
|
||||
* Class Transaction
|
||||
*/
|
||||
class Transaction extends SpectreObject
|
||||
{
|
||||
/**
|
||||
* Transaction constructor.
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data) {
|
||||
var_dump($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
93
app/Services/Spectre/Request/ListAccountsRequest.php
Normal file
93
app/Services/Spectre/Request/ListAccountsRequest.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* ListAccountsRequest.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Spectre\Request;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Services\Spectre\Exception\SpectreException;
|
||||
use FireflyIII\Services\Spectre\Object\Account;
|
||||
use FireflyIII\Services\Spectre\Object\Login;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class ListAccountsRequest
|
||||
*/
|
||||
class ListAccountsRequest extends SpectreRequest
|
||||
{
|
||||
/** @var array */
|
||||
private $accounts = [];
|
||||
/** @var Login */
|
||||
private $login;
|
||||
|
||||
/**
|
||||
* @throws SpectreException
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function call(): void
|
||||
{
|
||||
$hasNextPage = true;
|
||||
$nextId = 0;
|
||||
while ($hasNextPage) {
|
||||
Log::debug(sprintf('Now calling ListAccountsRequest for next_id %d', $nextId));
|
||||
$parameters = ['from_id' => $nextId, 'login_id' => $this->login->getId()];
|
||||
$uri = '/api/v3/accounts?' . http_build_query($parameters);
|
||||
$response = $this->sendSignedSpectreGet($uri, []);
|
||||
|
||||
// count entries:
|
||||
Log::debug(sprintf('Found %d entries in data-array', count($response['data'])));
|
||||
|
||||
// extract next ID
|
||||
$hasNextPage = false;
|
||||
if (isset($response['meta']['next_id']) && intval($response['meta']['next_id']) > $nextId) {
|
||||
$hasNextPage = true;
|
||||
$nextId = $response['meta']['next_id'];
|
||||
Log::debug(sprintf('Next ID is now %d.', $nextId));
|
||||
} else {
|
||||
Log::debug('No next page.');
|
||||
}
|
||||
|
||||
// store customers:
|
||||
foreach ($response['data'] as $accountArray) {
|
||||
$this->accounts[] = new Account($accountArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAccounts(): array
|
||||
{
|
||||
return $this->accounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Login $login
|
||||
*/
|
||||
public function setLogin(Login $login): void
|
||||
{
|
||||
$this->login = $login;
|
||||
}
|
||||
|
||||
|
||||
}
|
90
app/Services/Spectre/Request/ListTransactionsRequest.php
Normal file
90
app/Services/Spectre/Request/ListTransactionsRequest.php
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/**
|
||||
* ListTransactionsRequest.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Spectre\Request;
|
||||
|
||||
use FireflyIII\Services\Spectre\Object\Account;
|
||||
use FireflyIII\Services\Spectre\Object\Transaction;
|
||||
use Log;
|
||||
/**
|
||||
* Class ListTransactionsRequest
|
||||
*/
|
||||
class ListTransactionsRequest extends SpectreRequest
|
||||
{
|
||||
/** @var Account */
|
||||
private $account;
|
||||
/** @var array */
|
||||
private $transactions = [];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function call(): void
|
||||
{
|
||||
$hasNextPage = true;
|
||||
$nextId = 0;
|
||||
while ($hasNextPage) {
|
||||
Log::debug(sprintf('Now calling ListTransactionsRequest for next_id %d', $nextId));
|
||||
$parameters = ['from_id' => $nextId,'account_id' => $this->account->getId()];
|
||||
$uri = '/api/v3/transactions?' . http_build_query($parameters);
|
||||
$response = $this->sendSignedSpectreGet($uri, []);
|
||||
|
||||
// count entries:
|
||||
Log::debug(sprintf('Found %d entries in data-array', count($response['data'])));
|
||||
|
||||
// extract next ID
|
||||
$hasNextPage = false;
|
||||
if (isset($response['meta']['next_id']) && intval($response['meta']['next_id']) > $nextId) {
|
||||
$hasNextPage = true;
|
||||
$nextId = $response['meta']['next_id'];
|
||||
Log::debug(sprintf('Next ID is now %d.', $nextId));
|
||||
} else {
|
||||
Log::debug('No next page.');
|
||||
}
|
||||
|
||||
// store customers:
|
||||
foreach ($response['data'] as $transactionArray) {
|
||||
$this->transactions[] = new Transaction($transactionArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTransactions(): array
|
||||
{
|
||||
return $this->transactions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Account $account
|
||||
*/
|
||||
public function setAccount(Account $account): void
|
||||
{
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
|
||||
}
|
157
app/Support/Import/Configuration/Spectre/HaveAccounts.php
Normal file
157
app/Support/Import/Configuration/Spectre/HaveAccounts.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
/**
|
||||
* HaveAccounts.php
|
||||
* Copyright (c) 2018 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Import\Configuration\Spectre;
|
||||
|
||||
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\ImportJob;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\Import\Configuration\ConfigurationInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class HaveAccounts
|
||||
*/
|
||||
class HaveAccounts implements ConfigurationInterface
|
||||
{
|
||||
/** @var ImportJob */
|
||||
private $job;
|
||||
|
||||
/**
|
||||
* Get the data necessary to show the configuration screen.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
/** @var CurrencyRepositoryInterface $currencyRepository */
|
||||
$currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$data = [];
|
||||
$config = $this->job->configuration;
|
||||
$collection = $accountRepository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$dbAccounts = [];
|
||||
/** @var Account $dbAccount */
|
||||
foreach ($collection as $dbAccount) {
|
||||
$id = $dbAccount->id;
|
||||
$currencyId = intval($dbAccount->getMeta('currency_id'));
|
||||
$currency = $currencyRepository->find($currencyId);
|
||||
$dbAccounts[$id] = [
|
||||
'account' => $dbAccount,
|
||||
'currency' => is_null($currency->id) ? $defaultCurrency : $currency,
|
||||
];
|
||||
}
|
||||
|
||||
// loop Spectre accounts:
|
||||
/**
|
||||
* @var int $index
|
||||
* @var array $spectreAccount
|
||||
*/
|
||||
foreach ($config['accounts'] as $index => $spectreAccount) {
|
||||
// find accounts with currency code
|
||||
$code = $spectreAccount['currency_code'];
|
||||
$selection = $this->filterAccounts($dbAccounts, $code);
|
||||
$config['accounts'][$index]['options'] = app('expandedform')->makeSelectList($selection);
|
||||
}
|
||||
|
||||
|
||||
$data = [
|
||||
'config' => $config,
|
||||
];
|
||||
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return possible warning to user.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWarningMessage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ImportJob $job
|
||||
*
|
||||
* @return ConfigurationInterface
|
||||
*/
|
||||
public function setJob(ImportJob $job)
|
||||
{
|
||||
$this->job = $job;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the result.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function storeConfiguration(array $data): bool
|
||||
{
|
||||
$accounts = $data['spectre_account_id'] ?? [];
|
||||
$mapping = [];
|
||||
foreach ($accounts as $spectreId) {
|
||||
$spectreId = intval($spectreId);
|
||||
$doImport = intval($data['do_import'][$spectreId] ?? 0) === 1;
|
||||
$account = intval($data['import'][$spectreId] ?? 0);
|
||||
if ($doImport) {
|
||||
$mapping[$spectreId] = $account;
|
||||
}
|
||||
}
|
||||
$config = $this->job->configuration;
|
||||
$config['accounts-mapped'] = $mapping;
|
||||
$this->job->configuration = $config;
|
||||
$this->job->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $dbAccounts
|
||||
* @param string $code
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function filterAccounts(array $dbAccounts, string $code): Collection
|
||||
{
|
||||
$collection = new Collection;
|
||||
foreach ($dbAccounts as $accountId => $data) {
|
||||
if ($data['currency']->code === $code) {
|
||||
$collection->push($data['account']);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
16
public/js/ff/import/status.js
vendored
16
public/js/ff/import/status.js
vendored
@ -174,14 +174,20 @@ function jobIsStalled(data) {
|
||||
|
||||
/**
|
||||
* This function tells Firefly start the job. It will also initialize a re-check in 500ms time.
|
||||
* Only when job is in "configured" state.
|
||||
*/
|
||||
function startJob() {
|
||||
// disable the button, add loading thing.
|
||||
$('.start-job').prop('disabled', true).text('...');
|
||||
$.post(jobStartUri, {_token: token}).fail(reportOnSubmitError);
|
||||
if (job.status === "configured") {
|
||||
console.log("Job auto started!");
|
||||
// disable the button, add loading thing.
|
||||
$('.start-job').prop('disabled', true).text('...');
|
||||
$.post(jobStartUri, {_token: token}).fail(reportOnSubmitError);
|
||||
|
||||
// check status, every 500 ms.
|
||||
timeOutId = setTimeout(checkJobStatus, startInterval);
|
||||
// check status, every 500 ms.
|
||||
timeOutId = setTimeout(checkJobStatus, startInterval);
|
||||
return;
|
||||
}
|
||||
console.log("Job not auto started because state is " + job.status);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if accounts.count == 0 and page == 0 %}
|
||||
{% if accounts.count == 0 and page == 1 %}
|
||||
{% include 'partials.empty' with {what: what, type: 'accounts',route: route('accounts.create', [what])} %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -5,7 +5,7 @@
|
||||
</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="radio">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
{{ Form.checkbox('create_another', '1', false, {'id': name ~ '_return_to_form'}) }}
|
||||
{{ trans('form.returnHereExplanation') }}
|
||||
|
@ -47,7 +47,7 @@
|
||||
</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="radio"><label>
|
||||
<div class="checkbox"><label>
|
||||
{{ Form.checkbox('apply_rules', '1',
|
||||
job.configuration.apply_rules == '1', {'id': 'apply_rules_label'}) }}
|
||||
{{ trans('import.file_apply_rules_description') }}
|
||||
@ -61,7 +61,7 @@
|
||||
</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="radio"><label>
|
||||
<div class="checkbox"><label>
|
||||
{{ Form.checkbox('match_bills', '1',
|
||||
job.configuration.match_bills == '1', {'id': 'match_bills_label'}) }}
|
||||
{{ trans('import.file_match_bills_description') }}
|
||||
@ -77,7 +77,7 @@
|
||||
</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="radio"><label>
|
||||
<div class="checkbox"><label>
|
||||
{{ Form.checkbox('specifics['~type~']', '1',
|
||||
job.configuration.specifics[type] == '1', {'id': type ~ '_label'}) }}
|
||||
{{ specific.description }}
|
||||
|
88
resources/views/import/spectre/accounts.twig
Normal file
88
resources/views/import/spectre/accounts.twig
Normal file
@ -0,0 +1,88 @@
|
||||
{% extends "./layout/default" %}
|
||||
|
||||
{% block breadcrumbs %}
|
||||
{{ Breadcrumbs.render }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<form class="form-horizontal" action="{{ route('import.configure.post',[job.key]) }}" method="post">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||
<div class="box box-default">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">{{ trans('import.spectre_accounts_title') }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<p>
|
||||
{{ trans('import.spectre_accounts_text')|raw }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ trans('list.account_on_spectre') }}</th>
|
||||
<th>{{ trans('list.account') }}</th>
|
||||
<th>{{ trans('list.do_import') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in data.config.accounts %}
|
||||
<tr>
|
||||
<td>
|
||||
<input type="hidden" name="spectre_account_id[]" value="{{ account.id }}" />
|
||||
{{ account.nature|capitalize }} "<strong>{{ account.name }}</strong>" ({{ formatAmountBySymbol(account.balance, account.currency_code~' ') }})<br />
|
||||
{% for name, value in account.extra %}
|
||||
{% if not value is iterable and name != 'sort_code' and name !='current_date' and name != 'available_amount' and name !='current_time' and name != 'last_posted_transaction_id' %}
|
||||
{{ name|capitalize }}: {{ value }}<br />
|
||||
{% endif %}
|
||||
{% if name == 'available_amount' %}
|
||||
Available amount: {{ formatAmountBySymbol(value, account.currency_code~' ') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
<select class="form-control" name="import[{{ account.id }}]">
|
||||
{% for id,name in account.options %}
|
||||
<option value="{{ id }}" label="{{ name }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" value="1" name="do_import[{{ account.id }}]" checked>
|
||||
{{ 'spectre_do_import'|_ }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<button type="submit" class="btn pull-right btn-success">
|
||||
{{ ('submit')|_ }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
{% endblock %}
|
@ -145,6 +145,7 @@
|
||||
<script type="text/javascript">
|
||||
|
||||
// some useful translations.
|
||||
var langImportTimeOutError = '(time out thing)';
|
||||
var langImportSingleError = '{{ trans('import.status_errors_single')|escape('js') }}';
|
||||
var langImportMultiError = '{{ trans('import.status_errors_multi')|escape('js') }}';
|
||||
var jobStatusUri = '{{ route('import.status.json', [job.key]) }}';
|
||||
|
Loading…
Reference in New Issue
Block a user