First basic import #145

This commit is contained in:
James Cole 2018-07-30 20:39:19 +02:00
parent dfd9cf0874
commit 2add644706
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
8 changed files with 794 additions and 117 deletions

View File

@ -27,7 +27,8 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Import\JobConfiguration\Ynab\NewYnabJobHandler; use FireflyIII\Support\Import\JobConfiguration\Ynab\NewYnabJobHandler;
use FireflyIII\Support\Import\JobConfiguration\Ynab\SelectBudgetsHandler; use FireflyIII\Support\Import\JobConfiguration\Ynab\SelectAccountsHandler;
use FireflyIII\Support\Import\JobConfiguration\Ynab\SelectBudgetHandler;
use FireflyIII\Support\Import\JobConfiguration\Ynab\YnabJobConfigurationInterface; use FireflyIII\Support\Import\JobConfiguration\Ynab\YnabJobConfigurationInterface;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use Log; use Log;
@ -121,8 +122,12 @@ class YnabJobConfiguration implements JobConfigurationInterface
$handler->setImportJob($this->importJob); $handler->setImportJob($this->importJob);
break; break;
case 'select_budgets': case 'select_budgets':
/** @var SelectBudgetsHandler $handler */ /** @var SelectBudgetHandler $handler */
$handler = app(SelectBudgetsHandler::class); $handler = app(SelectBudgetHandler::class);
$handler->setImportJob($this->importJob);
break;
case 'select_accounts':
$handler = app(SelectAccountsHandler::class);
$handler->setImportJob($this->importJob); $handler->setImportJob($this->importJob);
break; break;
default: default:

View File

@ -27,6 +27,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Support\Import\Routine\Ynab\GetAccountsHandler; use FireflyIII\Support\Import\Routine\Ynab\GetAccountsHandler;
use FireflyIII\Support\Import\Routine\Ynab\ImportDataHandler;
use FireflyIII\Support\Import\Routine\Ynab\StageGetAccessHandler; use FireflyIII\Support\Import\Routine\Ynab\StageGetAccessHandler;
use FireflyIII\Support\Import\Routine\Ynab\StageGetBudgetsHandler; use FireflyIII\Support\Import\Routine\Ynab\StageGetBudgetsHandler;
use Log; use Log;
@ -104,7 +105,18 @@ class YnabRoutine implements RoutineInterface
$this->repository->setStage($this->importJob, 'select_accounts'); $this->repository->setStage($this->importJob, 'select_accounts');
$this->repository->setStatus($this->importJob, 'need_job_config'); $this->repository->setStatus($this->importJob, 'need_job_config');
return;
}
if('go-for-import' === $this->importJob->stage) {
$this->repository->setStatus($this->importJob, 'running');
$this->repository->setStage($this->importJob, 'do_import');
/** @var ImportDataHandler $handler */
$handler = app(ImportDataHandler::class);
$handler->setImportJob($this->importJob);
$handler->run();
$this->repository->setStatus($this->importJob, 'provider_finished');
$this->repository->setStage($this->importJob, 'final');
return;
} }
// if ('match_accounts' === $this->importJob->stage) { // if ('match_accounts' === $this->importJob->stage) {

View File

@ -0,0 +1,57 @@
<?php
/**
* GetTransactionsRequest.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\Ynab\Request;
use Log;
/**
* Class GetTransactionsRequest
*/
class GetTransactionsRequest extends YnabRequest
{
/** @var string */
public $accountId;
/** @var array */
public $accounts;
/** @var string */
public $budgetId;
/** @var array */
public $transactions;
/**
*
*/
public function call(): void
{
Log::debug('Now in GetTransactionsRequest::call()');
$uri = $this->api . sprintf('/budgets/%s/accounts/%s/transactions', $this->budgetId, $this->accountId);
Log::debug(sprintf('URI is %s', $uri));
$result = $this->authenticatedGetRequest($uri, []);
// expect data in [data][transactions]
$this->transactions = $result['data']['transactions'] ?? [];
}
}

View File

@ -0,0 +1,237 @@
<?php
/**
* SelectAccountsHandler.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\JobConfiguration\Ynab;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Support\MessageBag;
use Log;
/**
* Class SelectAccountsHandler
*/
class SelectAccountsHandler implements YnabJobConfigurationInterface
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var CurrencyRepositoryInterface */
private $currencyRepository;
/** @var ImportJob */
private $importJob;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* Return true when this stage is complete.
*
* @return bool
*/
public function configurationComplete(): bool
{
Log::debug('Now in SelectAccountsHandler::configurationComplete()');
$config = $this->importJob->configuration;
$mapping = $config['mapping'] ?? [];
if (\count($mapping) > 0) {
// mapping is complete.
Log::debug('Looks like user has mapped YNAB accounts to Firefly III accounts', $mapping);
$this->repository->setStage($this->importJob, 'go-for-import');
return true;
}
return false;
}
/**
* Store the job configuration.
*
* @param array $data
*
* @return MessageBag
*/
public function configureJob(array $data): MessageBag
{
Log::debug('Now in SelectAccountsHandler::configureJob()', $data);
$config = $this->importJob->configuration;
$mapping = $data['account_mapping'] ?? [];
$final = [];
$applyRules = 1 === (int)($data['apply_rules'] ?? 0);
foreach ($mapping as $ynabId => $localId) {
// validate each
$ynabId = $this->validYnabAccount($ynabId);
$accountId = $this->validLocalAccount((int)$localId);
$final[$ynabId] = $accountId;
}
Log::debug('Final mapping is:', $final);
$messages = new MessageBag;
$config['mapping'] = $final;
$config['apply-rules'] = $applyRules;
$this->repository->setConfiguration($this->importJob, $config);
if ($final === ['' => 0] || 0 === \count($final)) {
$messages->add('count', (string)trans('import.ynab_no_mapping'));
}
return $messages;
}
/**
* Get data for config view.
*
* @return array
* @throws FireflyException
*/
public function getNextData(): array
{
Log::debug('Now in ChooseAccountsHandler::getnextData()');
$config = $this->importJob->configuration;
$ynabAccounts = $config['accounts'] ?? [];
$budget = $this->getSelectedBudget();
if (0 === \count($ynabAccounts)) {
throw new FireflyException('It seems you have no accounts with this budget. The import cannot continue.'); // @codeCoverageIgnore
}
// list the users accounts:
$ffAccounts = $this->accountRepository->getAccountsByType([AccountType::ASSET]);
$array = [];
/** @var Account $account */
foreach ($ffAccounts as $account) {
$accountId = $account->id;
$currencyId = (int)$this->accountRepository->getMetaValue($account, 'currency_id');
$currency = $this->getCurrency($currencyId);
$array[$accountId] = [
'name' => $account->name,
'iban' => $account->iban,
'code' => $currency->code,
];
}
return [
'budget' => $budget,
'ynab_accounts' => $ynabAccounts,
'ff_accounts' => $array,
];
}
/**
* Get the view for this stage.
*
* @return string
*/
public function getNextView(): string
{
return 'import.ynab.accounts';
}
/**
* @codeCoverageIgnore
* Set the import job.
*
* @param ImportJob $importJob
*/
public function setImportJob(ImportJob $importJob): void
{
$this->importJob = $importJob;
$this->repository = app(ImportJobRepositoryInterface::class);
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
$this->repository->setUser($importJob->user);
$this->currencyRepository->setUser($importJob->user);
$this->accountRepository->setUser($importJob->user);
}
/**
* @param int $currencyId
*
* @return TransactionCurrency
*/
private function getCurrency(int $currencyId): TransactionCurrency
{
$currency = $this->currencyRepository->findNull($currencyId);
if (null === $currency) {
return app('amount')->getDefaultCurrencyByUser($this->importJob->user);
}
return $currency;
}
/**
* @return array
*/
private function getSelectedBudget(): array
{
$config = $this->repository->getConfiguration($this->importJob);
$budgets = $config['budgets'] ?? [];
$selected = $config['selected_budget'] ?? '';
foreach ($budgets as $budget) {
if ($budget['id'] === $selected) {
return $budget;
}
}
return $budgets[0] ?? [];
}
/**
* @param int $accountId
*
* @return int
*/
private function validLocalAccount(int $accountId): int
{
$account = $this->accountRepository->findNull($accountId);
if (null === $account) {
return 0;
}
return $accountId;
}
/**
* @param int $accountId
*
* @return string
*/
private function validYnabAccount(string $accountId): string
{
$config = $this->importJob->configuration;
$accounts = $config['accounts'] ?? [];
foreach ($accounts as $account) {
if ($account['id'] === $accountId) {
return $accountId;
}
}
return '';
}
}

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* SelectBudgetsHandler.php * SelectBudgetHandler.php
* Copyright (c) 2018 thegrumpydictator@gmail.com * Copyright (c) 2018 thegrumpydictator@gmail.com
* *
* This file is part of Firefly III. * This file is part of Firefly III.
@ -29,9 +29,9 @@ use Illuminate\Support\MessageBag;
use Log; use Log;
/** /**
* Class SelectBudgetsHandler * Class SelectBudgetHandler
*/ */
class SelectBudgetsHandler implements YnabJobConfigurationInterface class SelectBudgetHandler implements YnabJobConfigurationInterface
{ {
/** @var ImportJob */ /** @var ImportJob */
private $importJob; private $importJob;
@ -45,7 +45,7 @@ class SelectBudgetsHandler implements YnabJobConfigurationInterface
*/ */
public function configurationComplete(): bool public function configurationComplete(): bool
{ {
Log::debug('Now in SelectBudgetsHandler::configComplete'); Log::debug('Now in SelectBudgetHandler::configComplete');
$configuration = $this->repository->getConfiguration($this->importJob); $configuration = $this->repository->getConfiguration($this->importJob);
$selectedBudget = $configuration['selected_budget'] ?? ''; $selectedBudget = $configuration['selected_budget'] ?? '';
if ($selectedBudget !== '') { if ($selectedBudget !== '') {
@ -67,7 +67,7 @@ class SelectBudgetsHandler implements YnabJobConfigurationInterface
*/ */
public function configureJob(array $data): MessageBag public function configureJob(array $data): MessageBag
{ {
Log::debug('Now in SelectBudgetsHandler::configureJob'); Log::debug('Now in SelectBudgetHandler::configureJob');
$configuration = $this->repository->getConfiguration($this->importJob); $configuration = $this->repository->getConfiguration($this->importJob);
$configuration['selected_budget'] = $data['budget_id']; $configuration['selected_budget'] = $data['budget_id'];
@ -87,7 +87,7 @@ class SelectBudgetsHandler implements YnabJobConfigurationInterface
*/ */
public function getNextData(): array public function getNextData(): array
{ {
Log::debug('Now in SelectBudgetsHandler::getNextData'); Log::debug('Now in SelectBudgetHandler::getNextData');
$configuration = $this->repository->getConfiguration($this->importJob); $configuration = $this->repository->getConfiguration($this->importJob);
$budgets = $configuration['budgets'] ?? []; $budgets = $configuration['budgets'] ?? [];
$return = []; $return = [];
@ -107,7 +107,7 @@ class SelectBudgetsHandler implements YnabJobConfigurationInterface
*/ */
public function getNextView(): string public function getNextView(): string
{ {
Log::debug('Now in SelectBudgetsHandler::getNextView'); Log::debug('Now in SelectBudgetHandler::getNextView');
return 'import.ynab.select-budgets'; return 'import.ynab.select-budgets';
} }

View File

@ -0,0 +1,248 @@
<?php
/**
* ImportDataHandler.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\Routine\Ynab;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Services\Ynab\Request\GetTransactionsRequest;
use FireflyIII\Support\Import\Routine\File\OpposingAccountMapper;
use Log;
/**
* Class ImportDataHandler
*/
class ImportDataHandler
{
/** @var AccountRepositoryInterface */
private $accountRepository;
/** @var ImportJob */
private $importJob;
/** @var OpposingAccountMapper */
private $mapper;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* Get list of accounts for the selected budget.
*
* @throws FireflyException
*/
public function run(): void
{
$config = $this->repository->getConfiguration($this->importJob);
$token = $config['access_token'];
// make request for each mapping:
$mapping = $config['mapping'] ?? [];
$total = [[]];
/**
* @var string $ynabId
* @var int $localId
*/
foreach ($mapping as $ynabId => $localId) {
$localAccount = $this->getLocalAccount((int)$localId);
$transactions = $this->getTransactions($token, $ynabId);
$converted = $this->convertToArray($transactions, $localAccount);
$total[] = $converted;
}
$totalSet = array_merge(...$total);
Log::debug(sprintf('Found %d transactions in total.', \count($totalSet)));
$this->repository->setTransactions($this->importJob, $totalSet);
}
/**
* @param ImportJob $importJob
*/
public function setImportJob(ImportJob $importJob): void
{
$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 Account $localAccount
*
* @return array
* @throws FireflyException
*/
private function convertToArray(array $transactions, Account $localAccount): array
{
$array = [];
$total = \count($transactions);
$budget = $this->getSelectedBudget();
Log::debug(sprintf('Now in StageImportDataHandler::convertToArray() with count %d', \count($transactions)));
/** @var array $transaction */
foreach ($transactions as $index => $transaction) {
Log::debug(sprintf('Now creating array for transaction %d of %d', $index + 1, $total));
$amount = (string)($transaction['amount'] ?? 0);
if ('0' === $amount) {
continue;
}
$source = $localAccount;
$type = 'withdrawal';
$tags = [
$transaction['cleared'] ?? '',
$transaction['approved'] ? 'approved' : 'not-approved',
$transaction['flag_color'] ?? '',
];
$destinationData = [
'name' => $transaction['payee_name'],
'iban' => null,
'number' => $transaction['payee_id'],
'bic' => null,
];
$destination = $this->mapper->map(null, $amount, $destinationData);
if (1 === bccomp($amount, '0')) {
[$source, $destination] = [$destination, $source];
$type = 'deposit';
}
$entry = [
'type' => $type,
'date' => $transaction['date'] ?? date('Y-m-d'),
'tags' => $tags, // TODO
'user' => $this->importJob->user_id,
'notes' => null, // TODO
// all custom fields:
'external_id' => $transaction['id'] ?? '',
// journal data:
'description' => $transaction['memo'] ?? '(empty)',
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'bill_id' => null,
'bill_name' => null,
// transaction data:
'transactions' => [
[
'currency_id' => null,
'currency_code' => $budget['currency_code'],
'description' => null,
'amount' => bcdiv((string)$transaction['amount'], '1000'),
'budget_id' => null,
'budget_name' => null,
'category_id' => null,
'category_name' => $transaction['category_name'],
'source_id' => $source->id,
'source_name' => null,
'destination_id' => $destination->id,
'destination_name' => null,
'foreign_currency_id' => null,
'foreign_currency_code' => null,
'foreign_amount' => null,
'reconciled' => false,
'identifier' => 0,
],
],
];
$array[] = $entry;
}
return $array;
}
/**
* @param int $accountId
*
* @return Account
* @throws FireflyException
*/
private function getLocalAccount(int $accountId): Account
{
$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)); // @codeCoverageIgnore
}
if ($account->accountType->type !== AccountType::ASSET) {
throw new FireflyException(sprintf('Account with ID #%d is not an asset account. Job must stop now.', $accountId)); // @codeCoverageIgnore
}
return $account;
}
/**
* @return array
* @throws FireflyException
*/
private function getSelectedBudget(): array
{
$config = $this->repository->getConfiguration($this->importJob);
$budgets = $config['budgets'] ?? [];
$selected = $config['selected_budget'] ?? '';
if ('' === $selected) {
$firstBudget = $config['budgets'][0] ?? false;
if (false === $firstBudget) {
throw new FireflyException('The configuration contains no budget. Erroring out.');
}
$selected = $firstBudget['id'];
}
foreach ($budgets as $budget) {
if ($budget['id'] === $selected) {
return $budget;
}
}
return $budgets[0] ?? [];
}
/**
* @param string $token
* @param string $budget
* @param string $account
*
* @return array
* @throws FireflyException
*/
private function getTransactions(string $token, string $account): array
{
$budget = $this->getSelectedBudget();
$request = new GetTransactionsRequest;
$request->budgetId = $budget['id'];
$request->accountId = $account;
// todo grab latest date for $ynabId
$request->setAccessToken($token);
$request->call();
return $request->transactions;
}
}

View File

@ -24,132 +24,150 @@ declare(strict_types=1);
return [ return [
// ALL breadcrumbs and subtitles: // ALL breadcrumbs and subtitles:
'index_breadcrumb' => 'Import data into Firefly III', 'index_breadcrumb' => 'Import data into Firefly III',
'prerequisites_breadcrumb_fake' => 'Prerequisites for the fake import provider', 'prerequisites_breadcrumb_fake' => 'Prerequisites for the fake import provider',
'prerequisites_breadcrumb_spectre' => 'Prerequisites for Spectre', 'prerequisites_breadcrumb_spectre' => 'Prerequisites for Spectre',
'prerequisites_breadcrumb_bunq' => 'Prerequisites for bunq', 'prerequisites_breadcrumb_bunq' => 'Prerequisites for bunq',
'prerequisites_breadcrumb_ynab' => 'Prerequisites for YNAB', 'prerequisites_breadcrumb_ynab' => 'Prerequisites for YNAB',
'job_configuration_breadcrumb' => 'Configuration for ":key"', 'job_configuration_breadcrumb' => 'Configuration for ":key"',
'job_status_breadcrumb' => 'Import status for ":key"', 'job_status_breadcrumb' => 'Import status for ":key"',
'cannot_create_for_provider' => 'Firefly III cannot create a job for the ":provider"-provider.', 'cannot_create_for_provider' => 'Firefly III cannot create a job for the ":provider"-provider.',
'disabled_for_demo_user' => 'disabled in demo', 'disabled_for_demo_user' => 'disabled in demo',
// index page: // index page:
'general_index_title' => 'Import a file', 'general_index_title' => 'Import a file',
'general_index_intro' => 'Welcome to Firefly III\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', 'general_index_intro' => 'Welcome to Firefly III\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.',
// import provider strings (index): // import provider strings (index):
'button_fake' => 'Fake an import', 'button_fake' => 'Fake an import',
'button_file' => 'Import a file', 'button_file' => 'Import a file',
'button_bunq' => 'Import from bunq', 'button_bunq' => 'Import from bunq',
'button_spectre' => 'Import using Spectre', 'button_spectre' => 'Import using Spectre',
'button_plaid' => 'Import using Plaid', 'button_plaid' => 'Import using Plaid',
'button_yodlee' => 'Import using Yodlee', 'button_yodlee' => 'Import using Yodlee',
'button_quovo' => 'Import using Quovo', 'button_quovo' => 'Import using Quovo',
'button_ynab' => 'Import from You Need A Budget', 'button_ynab' => 'Import from You Need A Budget',
// global config box (index) // global config box (index)
'global_config_title' => 'Global import configuration', 'global_config_title' => 'Global import configuration',
'global_config_text' => 'In the future, this box will feature preferences that apply to ALL import providers above.', 'global_config_text' => 'In the future, this box will feature preferences that apply to ALL import providers above.',
// prerequisites box (index) // prerequisites box (index)
'need_prereq_title' => 'Import prerequisites', 'need_prereq_title' => 'Import prerequisites',
'need_prereq_intro' => 'Some import methods need your attention before they can be used. For example, they might require special API keys or application secrets. You can configure them here. The icon indicates if these prerequisites have been met.', 'need_prereq_intro' => 'Some import methods need your attention before they can be used. For example, they might require special API keys or application secrets. You can configure them here. The icon indicates if these prerequisites have been met.',
'do_prereq_fake' => 'Prerequisites for the fake provider', 'do_prereq_fake' => 'Prerequisites for the fake provider',
'do_prereq_file' => 'Prerequisites for file imports', 'do_prereq_file' => 'Prerequisites for file imports',
'do_prereq_bunq' => 'Prerequisites for imports from bunq', 'do_prereq_bunq' => 'Prerequisites for imports from bunq',
'do_prereq_spectre' => 'Prerequisites for imports using Spectre', 'do_prereq_spectre' => 'Prerequisites for imports using Spectre',
'do_prereq_plaid' => 'Prerequisites for imports using Plaid', 'do_prereq_plaid' => 'Prerequisites for imports using Plaid',
'do_prereq_yodlee' => 'Prerequisites for imports using Yodlee', 'do_prereq_yodlee' => 'Prerequisites for imports using Yodlee',
'do_prereq_quovo' => 'Prerequisites for imports using Quovo', 'do_prereq_quovo' => 'Prerequisites for imports using Quovo',
'do_prereq_ynab' => 'Prerequisites for imports from YNAB', 'do_prereq_ynab' => 'Prerequisites for imports from YNAB',
// provider config box (index) // provider config box (index)
'can_config_title' => 'Import configuration', 'can_config_title' => 'Import configuration',
'can_config_intro' => 'Some import methods can be configured to your liking. They have extra settings you can tweak.', 'can_config_intro' => 'Some import methods can be configured to your liking. They have extra settings you can tweak.',
'do_config_fake' => 'Configuration for the fake provider', 'do_config_fake' => 'Configuration for the fake provider',
'do_config_file' => 'Configuration for file imports', 'do_config_file' => 'Configuration for file imports',
'do_config_bunq' => 'Configuration for bunq imports', 'do_config_bunq' => 'Configuration for bunq imports',
'do_config_spectre' => 'Configuration for imports from Spectre', 'do_config_spectre' => 'Configuration for imports from Spectre',
'do_config_plaid' => 'Configuration for imports from Plaid', 'do_config_plaid' => 'Configuration for imports from Plaid',
'do_config_yodlee' => 'Configuration for imports from Yodlee', 'do_config_yodlee' => 'Configuration for imports from Yodlee',
'do_config_quovo' => 'Configuration for imports from Quovo', 'do_config_quovo' => 'Configuration for imports from Quovo',
// prerequisites: // prerequisites:
'prereq_fake_title' => 'Prerequisites for an import from the fake import provider', 'prereq_fake_title' => 'Prerequisites for an import from the fake import provider',
'prereq_fake_text' => 'This fake provider requires a fake API key. It must be 32 characters long. You can use this one: 123456789012345678901234567890AA', 'prereq_fake_text' => 'This fake provider requires a fake API key. It must be 32 characters long. You can use this one: 123456789012345678901234567890AA',
'prereq_spectre_title' => 'Prerequisites for an import using the Spectre API', 'prereq_spectre_title' => 'Prerequisites for an import using the Spectre API',
'prereq_spectre_text' => 'In order to import data using the Spectre API (v4), you must provide Firefly III with two secret values. They can be found on the <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.', 'prereq_spectre_text' => 'In order to import data using the Spectre API (v4), you must provide Firefly III with two secret values. They can be found on the <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.',
'prereq_spectre_pub' => 'Likewise, the Spectre API needs to know the public key you see below. Without it, it will not recognize you. Please enter this public key on your <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.', 'prereq_spectre_pub' => 'Likewise, the Spectre API needs to know the public key you see below. Without it, it will not recognize you. Please enter this public key on your <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.',
'prereq_bunq_title' => 'Prerequisites for an import from bunq', 'prereq_bunq_title' => 'Prerequisites for an import from bunq',
'prereq_bunq_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app. Please note that the import function for bunq is in BETA. It has only been tested against the sandbox API.', 'prereq_bunq_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app. Please note that the import function for bunq is in BETA. It has only been tested against the sandbox API.',
'prereq_bunq_ip' => 'bunq requires your externally facing IP address. Firefly III has tried to fill this in using <a href="https://www.ipify.org/">the ipify service</a>. Make sure this IP address is correct, or the import will fail.', 'prereq_bunq_ip' => 'bunq requires your externally facing IP address. Firefly III has tried to fill this in using <a href="https://www.ipify.org/">the ipify service</a>. Make sure this IP address is correct, or the import will fail.',
'prereq_ynab_title' => 'Prerequisites for an import from YNAB', 'prereq_ynab_title' => 'Prerequisites for an import from YNAB',
'prereq_ynab_text' => 'In order to be able to download transactions from YNAB, please create a new application on your <a href="https://app.youneedabudget.com/settings/developer">Developer Settings Page</a> and enter the client ID and secret on this page.', 'prereq_ynab_text' => 'In order to be able to download transactions from YNAB, please create a new application on your <a href="https://app.youneedabudget.com/settings/developer">Developer Settings Page</a> and enter the client ID and secret on this page.',
'prereq_ynab_redirect' => 'To complete the configuration, enter the following URL at the <a href="https://app.youneedabudget.com/settings/developer">Developer Settings Page</a> under the "Redirect URI(s)".', 'prereq_ynab_redirect' => 'To complete the configuration, enter the following URL at the <a href="https://app.youneedabudget.com/settings/developer">Developer Settings Page</a> under the "Redirect URI(s)".',
// prerequisites success messages: // prerequisites success messages:
'prerequisites_saved_for_fake' => 'Fake API key stored successfully!', 'prerequisites_saved_for_fake' => 'Fake API key stored successfully!',
'prerequisites_saved_for_spectre' => 'App ID and secret stored!', 'prerequisites_saved_for_spectre' => 'App ID and secret stored!',
'prerequisites_saved_for_bunq' => 'API key and IP stored!', 'prerequisites_saved_for_bunq' => 'API key and IP stored!',
'prerequisites_saved_for_ynab' => 'YNAB client ID and secret stored!', 'prerequisites_saved_for_ynab' => 'YNAB client ID and secret stored!',
// job configuration: // job configuration:
'job_config_apply_rules_title' => 'Job configuration - apply your rules?', 'job_config_apply_rules_title' => 'Job configuration - apply your rules?',
'job_config_apply_rules_text' => 'Once the fake provider has run, your rules can be applied to the transactions. This adds time to the import.', 'job_config_apply_rules_text' => 'Once the fake provider has run, your rules can be applied to the transactions. This adds time to the import.',
'job_config_input' => 'Your input', 'job_config_input' => 'Your input',
// job configuration for the fake provider: // job configuration for the fake provider:
'job_config_fake_artist_title' => 'Enter album name', 'job_config_fake_artist_title' => 'Enter album name',
'job_config_fake_artist_text' => 'Many import routines have a few configuration steps you must go through. In the case of the fake import provider, you must answer some weird questions. In this case, enter "David Bowie" to continue.', 'job_config_fake_artist_text' => 'Many import routines have a few configuration steps you must go through. In the case of the fake import provider, you must answer some weird questions. In this case, enter "David Bowie" to continue.',
'job_config_fake_song_title' => 'Enter song name', 'job_config_fake_song_title' => 'Enter song name',
'job_config_fake_song_text' => 'Mention the song "Golden years" to continue with the fake import.', 'job_config_fake_song_text' => 'Mention the song "Golden years" to continue with the fake import.',
'job_config_fake_album_title' => 'Enter album name', 'job_config_fake_album_title' => 'Enter album name',
'job_config_fake_album_text' => 'Some import routines require extra data halfway through the import. In the case of the fake import provider, you must answer some weird questions. Enter "Station to station" to continue.', 'job_config_fake_album_text' => 'Some import routines require extra data halfway through the import. In the case of the fake import provider, you must answer some weird questions. Enter "Station to station" to continue.',
// job configuration form the file provider // job configuration form the file provider
'job_config_file_upload_title' => 'Import setup (1/4) - Upload your file', 'job_config_file_upload_title' => 'Import setup (1/4) - Upload your file',
'job_config_file_upload_text' => 'This routine will help you import files from your bank into Firefly III. ', 'job_config_file_upload_text' => 'This routine will help you import files from your bank into Firefly III. ',
'job_config_file_upload_help' => 'Select your file. Please make sure the file is UTF-8 encoded.', 'job_config_file_upload_help' => 'Select your file. Please make sure the file is UTF-8 encoded.',
'job_config_file_upload_config_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their <a href="https://github.com/firefly-iii/import-configurations/wiki">configuration file</a>', 'job_config_file_upload_config_help' => 'If you have previously imported data into Firefly III, you may have a configuration file, which will pre-set configuration values for you. For some banks, other users have kindly provided their <a href="https://github.com/firefly-iii/import-configurations/wiki">configuration file</a>',
'job_config_file_upload_type_help' => 'Select the type of file you will upload', 'job_config_file_upload_type_help' => 'Select the type of file you will upload',
'job_config_file_upload_submit' => 'Upload files', 'job_config_file_upload_submit' => 'Upload files',
'import_file_type_csv' => 'CSV (comma separated values)', 'import_file_type_csv' => 'CSV (comma separated values)',
'file_not_utf8' => 'The file you have uploaded is not encoded as UTF-8 or ASCII. Firefly III cannot handle such files. Please use Notepad++ or Sublime to convert your file to UTF-8.', 'file_not_utf8' => 'The file you have uploaded is not encoded as UTF-8 or ASCII. Firefly III cannot handle such files. Please use Notepad++ or Sublime to convert your file to UTF-8.',
'job_config_uc_title' => 'Import setup (2/4) - Basic file setup', 'job_config_uc_title' => 'Import setup (2/4) - Basic file setup',
'job_config_uc_text' => 'To be able to import your file correctly, please validate the options below.', 'job_config_uc_text' => 'To be able to import your file correctly, please validate the options below.',
'job_config_uc_header_help' => 'Check this box if the first row of your CSV file are the column titles.', 'job_config_uc_header_help' => 'Check this box if the first row of your CSV file are the column titles.',
'job_config_uc_date_help' => 'Date time format in your file. Follow the format as <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> indicates. The default value will parse dates that look like this: :dateExample.', 'job_config_uc_date_help' => 'Date time format in your file. Follow the format as <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> indicates. The default value will parse dates that look like this: :dateExample.',
'job_config_uc_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', 'job_config_uc_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.',
'job_config_uc_account_help' => 'If your file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the file belong to.', 'job_config_uc_account_help' => 'If your file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the file belong to.',
'job_config_uc_apply_rules_title' => 'Apply rules', 'job_config_uc_apply_rules_title' => 'Apply rules',
'job_config_uc_apply_rules_text' => 'Applies your rules to every imported transaction. Note that this slows the import significantly.', 'job_config_uc_apply_rules_text' => 'Applies your rules to every imported transaction. Note that this slows the import significantly.',
'job_config_uc_specifics_title' => 'Bank-specific options', 'job_config_uc_specifics_title' => 'Bank-specific options',
'job_config_uc_specifics_txt' => 'Some banks deliver badly formatted files. Firefly III can fix those automatically. If your bank delivers such files but it\'s not listed here, please open an issue on GitHub.', 'job_config_uc_specifics_txt' => 'Some banks deliver badly formatted files. Firefly III can fix those automatically. If your bank delivers such files but it\'s not listed here, please open an issue on GitHub.',
'job_config_uc_submit' => 'Continue', 'job_config_uc_submit' => 'Continue',
'invalid_import_account' => 'You have selected an invalid account to import into.', 'invalid_import_account' => 'You have selected an invalid account to import into.',
// job configuration for Spectre: // job configuration for Spectre:
'job_config_spectre_login_title' => 'Choose your login', 'job_config_spectre_login_title' => 'Choose your login',
'job_config_spectre_login_text' => 'Firefly III has found :count existing login(s) in your Spectre account. Which one would you like to use to import from?', 'job_config_spectre_login_text' => 'Firefly III has found :count existing login(s) in your Spectre account. Which one would you like to use to import from?',
'spectre_login_status_active' => 'Active', 'spectre_login_status_active' => 'Active',
'spectre_login_status_inactive' => 'Inactive', 'spectre_login_status_inactive' => 'Inactive',
'spectre_login_status_disabled' => 'Disabled', 'spectre_login_status_disabled' => 'Disabled',
'spectre_login_new_login' => 'Login with another bank, or one of these banks with different credentials.', 'spectre_login_new_login' => 'Login with another bank, or one of these banks with different credentials.',
'job_config_spectre_accounts_title' => 'Select accounts to import from', 'job_config_spectre_accounts_title' => 'Select accounts to import from',
'job_config_spectre_accounts_text' => 'You have selected ":name" (:country). You have :count account(s) available from this provider. Please select the Firefly III asset account(s) where the transactions from these accounts should be stored. Remember, in order to import data both the Firefly III account and the ":name"-account must have the same currency.', 'job_config_spectre_accounts_text' => 'You have selected ":name" (:country). You have :count account(s) available from this provider. Please select the Firefly III asset account(s) where the transactions from these accounts should be stored. Remember, in order to import data both the Firefly III account and the ":name"-account must have the same currency.',
'spectre_no_supported_accounts' => 'You cannot import from this account due to a currency mismatch.', 'spectre_no_supported_accounts' => 'You cannot import from this account due to a currency mismatch.',
'spectre_do_not_import' => '(do not import)', 'spectre_do_not_import' => '(do not import)',
'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.', 'spectre_no_mapping' => 'It seems you have not selected any accounts to import from.',
'imported_from_account' => 'Imported from ":account"', 'imported_from_account' => 'Imported from ":account"',
'spectre_account_with_number' => 'Account :number', 'spectre_account_with_number' => 'Account :number',
'job_config_spectre_apply_rules' => 'Apply rules', 'job_config_spectre_apply_rules' => 'Apply rules',
'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', 'job_config_spectre_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.',
// job configuration for bunq: // job configuration for bunq:
'job_config_bunq_accounts_title' => 'bunq accounts', 'job_config_bunq_accounts_title' => 'bunq accounts',
'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.', 'job_config_bunq_accounts_text' => 'These are the accounts associated with your bunq account. Please select the accounts from which you want to import, and in which account the transactions must be imported.',
'bunq_no_mapping' => 'It seems you have not selected any accounts.', 'bunq_no_mapping' => 'It seems you have not selected any accounts.',
'should_download_config' => 'You should download <a href=":route">the configuration file</a> for this job. This will make future imports way easier.', 'should_download_config' => 'You should download <a href=":route">the configuration file</a> for this job. This will make future imports way easier.',
'share_config_file' => 'If you have imported data from a public bank, you should <a href="https://github.com/firefly-iii/import-configurations/wiki">share your configuration file</a> so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.', 'share_config_file' => 'If you have imported data from a public bank, you should <a href="https://github.com/firefly-iii/import-configurations/wiki">share your configuration file</a> so it will be easy for other users to import their data. Sharing your configuration file will not expose your financial details.',
'job_config_bunq_apply_rules' => 'Apply rules', 'job_config_bunq_apply_rules' => 'Apply rules',
'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.', 'job_config_bunq_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.',
'ynab_account_closed' => 'Account is closed!',
'ynab_account_deleted' => 'Account is deleted!',
'ynab_account_type_savings' => 'savings account',
'ynab_account_type_checking' => 'checking account',
'ynab_account_type_cash' => 'cash account',
'ynab_account_type_creditCard' => 'credit card',
'ynab_account_type_lineOfCredit' => 'line of credit',
'ynab_account_type_otherAsset' => 'other asset account',
'ynab_account_type_otherLiability' => 'other liabilities',
'ynab_account_type_payPal' => 'Paypal',
'ynab_account_type_merchantAccount' => 'merchant account',
'ynab_account_type_investmentAccount' => 'investment account',
'ynab_account_type_mortgage' => 'mortgage',
'ynab_do_not_import' => '(do not import)',
'job_config_ynab_apply_rules' => 'Apply rules',
'job_config_ynab_apply_rules_text' => 'By default, your rules will be applied to the transactions created during this import routine. If you do not want this to happen, deselect this checkbox.',
// job configuration for YNAB: // job configuration for YNAB:
'job_config_select_budgets' => 'Select your budget', 'job_config_select_budgets' => 'Select your budget',
'job_config_spectre_select_budgets_text' => 'You have :count budgets stored at YNAB. Please select the one from which Firefly III will import the transactions.', 'job_config_spectre_select_budgets_text' => 'You have :count budgets stored at YNAB. Please select the one from which Firefly III will import the transactions.',
'ynab_no_mapping' => 'It seems you have not selected any accounts to import from.',
// keys from "extra" array: // keys from "extra" array:
'spectre_extra_key_iban' => 'IBAN', 'spectre_extra_key_iban' => 'IBAN',

View File

@ -0,0 +1,100 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.render }}
{% endblock %}
{% block content %}
<div class="row">
<form class="form-horizontal" action="{{ route('import.job.configuration.post',[importJob.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.job_config_ynab_apply_rules') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-6">
<p>
{{ trans('import.job_config_ynab_apply_rules_text') }}
</p>
{{ ExpandedForm.checkbox('apply_rules', 1, true) }}
</div>
</div>
</div>
</div>
</div>
<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.job_config_ynab_accounts_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ trans('import.job_config_ynab_accounts_text', {count: data.accounts|length}) }}
</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_ynab') }}</th>
<th>{{ trans('list.account') }}</th>
</tr>
</thead>
<tbody>
{% for account in data.ynab_accounts %}
<tr>
<td>
{{ account.name }} ({{ trans('import.ynab_account_type_'~account.type) }})
{% if account.closed %}
<br/><span class="text-warning">{{ trans('import.ynab_account_closed') }}</span>
{% endif %}
{% if account.deleted %}
<br/><span class="text-danger">{{ trans('import.ynab_account_deleted') }}</span>
{% endif %}
</td>
<td>
<select class="form-control" name="account_mapping[{{ account.id }}]">
<option value="0" label="{{ trans('import.ynab_do_not_import') }}">
{{ trans('import.ynab_do_not_import') }}
</option>
{% for ffId, ffAccount in data.ff_accounts %}
{% if ffAccount.code == data.budget.currency_code %}
<option value="{{ ffId }}"{% if currentIban != '' and currentIban == ffAccount.iban %} selected{% endif %}>
{{ ffAccount.name }}{% if ffAccount.iban !='' %} ({{ ffAccount.iban }}){% endif %}
</option>
{% endif %}
{% endfor %}
</select>
</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 %}