First working version of a working Spectre import.

This commit is contained in:
James Cole
2018-05-19 21:13:00 +02:00
parent 04953b5645
commit 2c206bba64
17 changed files with 552 additions and 172 deletions

View File

@@ -24,8 +24,17 @@ declare(strict_types=1);
namespace FireflyIII\Support\Import\Routine\Spectre;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account as LocalAccount;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Services\Spectre\Object\Account as SpectreAccount;
use FireflyIII\Services\Spectre\Object\Transaction as SpectreTransaction;
use FireflyIII\Services\Spectre\Request\ListTransactionsRequest;
use FireflyIII\Support\Import\Routine\File\OpposingAccountMapper;
use Log;
/**
* Class ImportDataHandler
@@ -34,18 +43,37 @@ use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
*/
class ImportDataHandler
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var ImportJob */
private $importJob;
/** @var OpposingAccountMapper */
private $mapper;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
*
* @throws FireflyException
*/
public function run()
public function run(): void
{
die('here we are');
Log::debug('Now in ImportDataHandler::run()');
$config = $this->importJob->configuration;
$accounts = $config['accounts'] ?? [];
Log::debug(sprintf('Count of accounts in array is %d', \count($accounts)));
if (\count($accounts) === 0) {
throw new FireflyException('There are no accounts in this import job. Cannot continue.');
}
$toImport = $config['account_mapping'] ?? [];
foreach ($toImport as $spectreId => $localId) {
if ((int)$localId > 0) {
Log::debug(sprintf('Will get transactions from Spectre account #%d and save them in Firefly III account #%d', $spectreId, $localId));
$spectreAccount = $this->getSpectreAccount((int)$spectreId);
$localAccount = $this->getLocalAccount((int)$localId);
$set = $this->getTransactions($spectreAccount, $localAccount);
$this->repository->setTransactions($this->importJob, $set);
}
}
}
/**
@@ -55,8 +83,188 @@ class ImportDataHandler
*/
public function setImportJob(ImportJob $importJob): void
{
$this->importJob = $importJob;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->importJob = $importJob;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->mapper = app(OpposingAccountMapper::class);
$this->accountRepository->setUser($importJob->user);
$this->repository->setUser($importJob->user);
$this->mapper->setUser($importJob->user);
}
/**
* @param array $transactions
* @param SpectreAccount $spectreAccount
* @param LocalAccount $originalSource
*
* @return array
*/
private function convertToArray(array $transactions, SpectreAccount $spectreAccount, LocalAccount $originalSource): array
{
$array = [];
$total = \count($transactions);
Log::debug(sprintf('Now in ImportDataHandler::convertToArray() with count %d', \count($transactions)));
/** @var SpectreTransaction $transaction */
foreach ($transactions as $index => $transaction) {
Log::debug(sprintf('Now creating array for transaction %d of %d', $index + 1, $total));
$extra = [];
if (null !== $transaction->getExtra()) {
$extra = $transaction->getExtra()->toArray();
}
$destinationData = $transaction->getOpposingAccountData();
$amount = $transaction->getAmount();
$source = $originalSource;
$destination = $this->mapper->map(null, $amount, $destinationData);
$notes = (string)trans('import.imported_from_account', ['account' => $spectreAccount->getName()]) . ' ' . "\n";
$foreignAmount = null;
$foreignCurrencyCode = null;
$currencyCode = $transaction->getCurrencyCode();
$type = 'withdrawal';
// switch source and destination if amount is greater than zero.
if (bccomp($amount, '0') === 1) {
[$source, $destination] = [$destination, $source];
$type = 'deposit';
}
Log::debug(sprintf('Mapped destination to #%d ("%s")', $destination->id, $destination->name));
Log::debug(sprintf('Set source to #%d ("%s")', $source->id, $source->name));
// put some data in tags:
$tags = [];
$tags[] = $transaction->getMode();
$tags[] = $transaction->getStatus();
if ($transaction->isDuplicated()) {
$tags[] = 'possibly-duplicated';
}
// get extra fields:
foreach ($extra as $key => $value) {
if ('' === (string)$value) {
continue;
}
switch ($key) {
case 'original_category':
case 'original_subcategory':
case 'customer_category_code':
case 'customer_category_name':
$tags[] = $value;
break;
case 'original_amount':
$foreignAmount = $value;
break;
case 'original_currency_code':
$foreignCurrencyCode = $value;
break;
default:
$notes .= $key . ': ' . $value . ' ' . "\n"; // for newline in Markdown.
}
}
$entry = [
'type' => $type,
'date' => $transaction->getMadeOn()->format('Y-m-d'),
'tags' => $tags,
'user' => $this->importJob->user_id,
'notes' => $notes,
// all custom fields:
'external_id' => (string)$transaction->getId(),
// journal data:
'description' => $transaction->getDescription(),
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'bill_id' => null,
'bill_name' => null,
// transaction data:
'transactions' => [
[
'currency_id' => null,
'currency_code' => $currencyCode,
'description' => null,
'amount' => $amount,
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
'category_name' => $transaction->getCategory(),
'source_id' => $source->id,
'source_name' => null,
'destination_id' => $destination->id,
'destination_name' => null,
'foreign_currency_id' => null,
'foreign_currency_code' => $foreignCurrencyCode,
'foreign_amount' => $foreignAmount,
'reconciled' => false,
'identifier' => 0,
],
],
];
$array[] = $entry;
}
Log::debug(sprintf('Return %d entries', \count($array)));
return $array;
}
/**
* @param int $accountId
*
* @return LocalAccount
* @throws FireflyException
*/
private function getLocalAccount(int $accountId): LocalAccount
{
$account = $this->accountRepository->findNull($accountId);
if (null === $account) {
throw new FireflyException(sprintf('Cannot find Firefly III asset account with ID #%d. Job must stop now.', $accountId));
}
if ($account->accountType->type !== AccountType::ASSET) {
throw new FireflyException(sprintf('Account with ID #%d is not an asset account. Job must stop now.', $accountId));
}
return $account;
}
/**
* @param int $accountId
*
* @return SpectreAccount
* @throws FireflyException
*/
private function getSpectreAccount(int $accountId): SpectreAccount
{
$config = $this->importJob->configuration;
$accounts = $config['accounts'] ?? [];
foreach ($accounts as $account) {
$spectreId = (int)($account['id'] ?? 0.0);
if ($spectreId === $accountId) {
return new SpectreAccount($account);
}
}
throw new FireflyException(sprintf('Cannot find Spectre account with ID #%d in configuration. Job will exit.', $accountId));
}
/**
* @param SpectreAccount $spectreAccount
* @param LocalAccount $localAccount
*
* @return array
* @throws FireflyException
*/
private function getTransactions(SpectreAccount $spectreAccount, LocalAccount $localAccount): array
{
// grab all transactions
$request = new ListTransactionsRequest($this->importJob->user);
$request->setAccount($spectreAccount);
$request->call();
$transactions = $request->getTransactions();
return $this->convertToArray($transactions, $spectreAccount, $localAccount);
}
}

View File

@@ -33,101 +33,11 @@ use FireflyIII\Services\Spectre\Request\ListLoginsRequest;
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
use Log;
/**
* Class ManageLoginsHandler
*/
class ManageLoginsHandler
{
public $countLogins = 0;
/** @var ImportJob */
private $importJob;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* Tasks for this stage:
*
* - List all of the users logins.
* - If zero, return to "get-token" stage and make user make a login. That stage redirects here.
* - If one or more, list and let user select.
*
* @throws FireflyException
*/
public function run(): void
{
$customer = $this->getCustomer();
$request = new ListLoginsRequest($this->importJob->user);
$request->setCustomer($customer);
$request->call();
$list = $request->getLogins();
// count is zero?
$this->countLogins = \count($list);
if ($this->countLogins > 0) {
$store = [];
/** @var Login $login */
foreach ($list as $login) {
$store[] = $login->toArray();
}
$config = $this->repository->getConfiguration($this->importJob);
$config['all-logins'] = $store;
$this->repository->setConfiguration($this->importJob, $config);
}
}
/**
* @param ImportJob $importJob
*/
public function setImportJob(ImportJob $importJob): void
{
$this->importJob = $importJob;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->repository->setUser($importJob->user);
}
/**
* @return Customer
* @throws FireflyException
*/
private function getCustomer(): Customer
{
Log::debug('Now in manageLoginsHandler::getCustomer()');
$customer = $this->getExistingCustomer();
if (null === $customer) {
Log::debug('The customer is NULL, will fire a newCustomerRequest.');
$newCustomerRequest = new NewCustomerRequest($this->importJob->user);
$customer = $newCustomerRequest->getCustomer();
}
Log::debug('The customer is not null.');
return $customer;
}
/**
* @return Customer|null
* @throws FireflyException
*/
private function getExistingCustomer(): ?Customer
{
Log::debug('Now in getExistingCustomer()');
$customer = null;
$getCustomerRequest = new ListCustomersRequest($this->importJob->user);
$getCustomerRequest->call();
$customers = $getCustomerRequest->getCustomers();
Log::debug(sprintf('Found %d customer(s)', \count($customers)));
/** @var Customer $current */
foreach ($customers as $current) {
if ('default_ff3_customer' === $current->getIdentifier()) {
$customer = $current;
Log::debug('Found the correct customer.');
break;
}
}
return $customer;
}
}

View File

@@ -27,9 +27,17 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Services\Spectre\Object\Account;
use FireflyIII\Services\Spectre\Object\Customer;
use FireflyIII\Services\Spectre\Object\Login;
use FireflyIII\Services\Spectre\Request\ListAccountsRequest;
use FireflyIII\Services\Spectre\Request\ListCustomersRequest;
use FireflyIII\Services\Spectre\Request\ListLoginsRequest;
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
use Log;
/**
* Class StageAuthenticatedHandler
*/
class StageAuthenticatedHandler
{
/** @var ImportJob */
@@ -43,24 +51,31 @@ class StageAuthenticatedHandler
*
* @throws FireflyException
*/
public function run()
public function run(): void
{
Log::debug('Now in StageAuthenticatedHandler::run()');
// grab a list of logins.
$config = $this->importJob->configuration;
$logins = $config['all-logins'] ?? [];
Log::debug(sprintf('%d logins in config', \count($logins)));
if (\count($logins) === 0) {
throw new FireflyException('StageAuthenticatedHandler expects more than 0 logins. Apologies, the import has stopped.');
// get logins from Spectre.
$logins = $this->getLogins();
$config['all-logins'] = $logins;
}
$selectedLogin = $config['selected-login'];
$selectedLogin = $config['selected-login'] ?? 0;
$login = null;
Log::debug(sprintf('$selectedLogin is %d', $selectedLogin));
foreach ($logins as $loginArray) {
$loginId = $loginArray['id'] ?? -1;
if ($loginId === $selectedLogin) {
Log::debug('Selected login is in the array with logins.');
$login = new Login($loginArray);
}
}
if (null === $login) {
Log::debug('Login is null, simply use the first one from the array.');
$login = new Login($logins[0]);
}
@@ -93,13 +108,88 @@ class StageAuthenticatedHandler
*/
private function getAccounts(Login $login): array
{
Log::debug(sprintf('Now in StageAuthenticatedHandler::getAccounts() for login #%d', $login->getId()));
$request = new ListAccountsRequest($this->importJob->user);
$request->setLogin($login);
$request->call();
$accounts = $request->getAccounts();
Log::debug(sprintf('Found %d accounts using login', \count($accounts)));
return $accounts;
}
/**
* @return Customer
* @throws FireflyException
*/
private function getCustomer(): Customer
{
Log::debug('Now in stageNewHandler::getCustomer()');
$customer = $this->getExistingCustomer();
if (null === $customer) {
Log::debug('The customer is NULL, will fire a newCustomerRequest.');
$newCustomerRequest = new NewCustomerRequest($this->importJob->user);
$customer = $newCustomerRequest->getCustomer();
}
Log::debug('The customer is not null.');
return $customer;
}
/**
* @return Customer|null
* @throws FireflyException
*/
private function getExistingCustomer(): ?Customer
{
Log::debug('Now in ChooseLoginHandler::getExistingCustomer()');
$preference = app('preferences')->getForUser($this->importJob->user, 'spectre_customer');
if (null !== $preference) {
Log::debug('Customer is in user configuration');
$customer = new Customer($preference->data);
return $customer;
}
Log::debug('Customer is not in user config');
$customer = null;
$getCustomerRequest = new ListCustomersRequest($this->importJob->user);
$getCustomerRequest->call();
$customers = $getCustomerRequest->getCustomers();
Log::debug(sprintf('Found %d customer(s)', \count($customers)));
/** @var Customer $current */
foreach ($customers as $current) {
if ('default_ff3_customer' === $current->getIdentifier()) {
$customer = $current;
Log::debug('Found the correct customer.');
app('preferences')->setForUser($this->importJob->user, 'spectre_customer', $customer->toArray());
break;
}
}
return $customer;
}
/**
* @return array
* @throws FireflyException
*/
private function getLogins(): array
{
$customer = $this->getCustomer();
$request = new ListLoginsRequest($this->importJob->user);
$request->setCustomer($customer);
$request->call();
$logins = $request->getLogins();
$return = [];
/** @var Login $login */
foreach ($logins as $login) {
$return[] = $login->toArray();
}
return $return;
}
}

View File

@@ -27,9 +27,9 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Services\Spectre\Object\Customer;
use FireflyIII\Services\Spectre\Object\Token;
use FireflyIII\Services\Spectre\Request\CreateTokenRequest;
use FireflyIII\Services\Spectre\Object\Login;
use FireflyIII\Services\Spectre\Request\ListCustomersRequest;
use FireflyIII\Services\Spectre\Request\ListLoginsRequest;
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
use Log;
@@ -40,35 +40,49 @@ use Log;
*/
class StageNewHandler
{
/** @var int */
public $countLogins = 0;
/** @var ImportJob */
private $importJob;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* Tasks for this stage:
*
* - Get the user's customer from Spectre.
* - Create a new customer if it does not exist.
* - Store it in the job either way.
* - Use it to grab a token.
* - Store the token in the job.
* - List all of the users logins.
* - If zero, return to "get-token" stage and make user make a login. That stage redirects here.
* - If one or more, list and let user select.
*
* @throws FireflyException
*/
public function run(): void
{
Log::debug('Now in stageNewHandler::run()');
$customer = $this->getCustomer();
Log::debug('Now in ManageLoginsHandler::run()');
$customer = $this->getCustomer();
$config = $this->repository->getConfiguration($this->importJob);
// get token using customer.
app('preferences')->setForUser($this->importJob->user, 'spectre_customer', $customer->toArray());
Log::debug('Going to get a list of logins.');
$request = new ListLoginsRequest($this->importJob->user);
$request->setCustomer($customer);
$request->call();
// store token in the job.
$config = $this->repository->getConfiguration($this->importJob);
$list = $request->getLogins();
$this->repository->setConfiguration($this->importJob, $config);
// count is zero?
$this->countLogins = \count($list);
Log::debug(sprintf('Number of logins is %d', $this->countLogins));
if ($this->countLogins > 0) {
$store = [];
/** @var Login $login */
foreach ($list as $login) {
$store[] = $login->toArray();
}
$config['all-logins'] = $store;
$this->repository->setConfiguration($this->importJob, $config);
Log::debug('Stored all logins in configuration.');
}
}
/**
@@ -87,7 +101,7 @@ class StageNewHandler
*/
private function getCustomer(): Customer
{
Log::debug('Now in stageNewHandler::getCustomer()');
Log::debug('Now in manageLoginsHandler::getCustomer()');
$customer = $this->getExistingCustomer();
if (null === $customer) {
Log::debug('The customer is NULL, will fire a newCustomerRequest.');
@@ -106,7 +120,7 @@ class StageNewHandler
*/
private function getExistingCustomer(): ?Customer
{
Log::debug('Now in getExistingCustomer()');
Log::debug('Now in manageLoginsHandler::getExistingCustomer()');
$customer = null;
$getCustomerRequest = new ListCustomersRequest($this->importJob->user);
$getCustomerRequest->call();
@@ -120,11 +134,9 @@ class StageNewHandler
Log::debug('Found the correct customer.');
break;
}
Log::debug(sprintf('Skip customer with name "%s"', $current->getIdentifier()));
}
return $customer;
}
}