From 66ee382da0267960f1865528651313801c9d7d63 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 16 Dec 2017 17:19:10 +0100 Subject: [PATCH] Lots of new code for the import routine part 2 --- .../Import/ConfigurationController.php | 37 ++++- .../Controllers/Import/FileController.php | 9 +- .../Controllers/Import/IndexController.php | 1 + .../Controllers/Import/StatusController.php | 15 +++ app/Http/breadcrumbs.php | 21 +-- app/Import/Mapper/AssetAccountIbans.php | 2 +- app/Import/Mapper/AssetAccounts.php | 2 +- app/Import/Mapper/Bills.php | 2 +- app/Import/Mapper/Budgets.php | 2 +- app/Import/Mapper/Categories.php | 2 +- app/Import/Mapper/OpposingAccountIbans.php | 2 +- app/Import/Mapper/OpposingAccounts.php | 2 +- app/Import/Mapper/Tags.php | 2 +- app/Import/Mapper/TransactionCurrencies.php | 2 +- .../ImportJob/ImportJobRepository.php | 20 ++- .../ImportJobRepositoryInterface.php | 8 +- app/Support/Import/Configuration/File/Map.php | 13 +- .../Import/Configuration/File/Roles.php | 28 ++-- .../Import/Configuration/File/Upload.php | 40 +++++- config/import.php | 6 + resources/lang/en_US/bank.php | 14 -- resources/lang/en_US/csv.php | 75 ----------- resources/lang/en_US/firefly.php | 18 +-- resources/lang/en_US/import.php | 126 ++++++++++++++++++ .../views/import/{file => _old}/finished.twig | 0 .../views/import/{file => _old}/index.twig | 0 .../views/import/{file => _old}/status.twig | 0 resources/views/import/file/initial.twig | 24 ++-- resources/views/import/file/map.twig | 12 +- resources/views/import/file/roles.twig | 20 +-- resources/views/import/file/upload.twig | 70 +++++----- routes/web.php | 2 + 32 files changed, 351 insertions(+), 226 deletions(-) create mode 100644 app/Http/Controllers/Import/StatusController.php create mode 100644 resources/lang/en_US/import.php rename resources/views/import/{file => _old}/finished.twig (100%) rename resources/views/import/{file => _old}/index.twig (100%) rename resources/views/import/{file => _old}/status.twig (100%) diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 79ea3ae368..428ffc8c9e 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -9,6 +9,8 @@ use FireflyIII\Http\Controllers\Controller; use FireflyIII\Import\Configuration\ConfiguratorInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Request; +use Log; /** * Class ConfigurationController @@ -53,8 +55,9 @@ class ConfigurationController extends Controller if ($configurator->isJobConfigured()) { $this->repository->updateStatus($job, 'configured'); - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } + $this->repository->updateStatus($job, 'configuring'); $view = $configurator->getNextView(); $data = $configurator->getNextData(); $subTitle = trans('firefly.import_config_bread_crumb'); @@ -63,6 +66,36 @@ class ConfigurationController extends Controller return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } + /** + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function post(Request $request, ImportJob $job) + { + Log::debug('Now in postConfigure()', ['job' => $job->key]); + $configurator = $this->makeConfigurator($job); + + // is the job already configured? + if ($configurator->isJobConfigured()) { + return redirect(route('import.status', [$job->key])); + } + $data = $request->all(); + $configurator->configureJob($data); + + // get possible warning from configurator: + $warning = $configurator->getWarningMessage(); + + if (strlen($warning) > 0) { + $request->session()->flash('warning', $warning); + } + + // return to configure + return redirect(route('import.configure', [$job->key])); + } + /** * @param ImportJob $job * @@ -76,7 +109,7 @@ class ConfigurationController extends Controller $key = sprintf('import.configuration.%s', $type); $className = config($key); if (null === $className || !class_exists($className)) { - throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".',$type)); // @codeCoverageIgnore + throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".', $type)); // @codeCoverageIgnore } /** @var ConfiguratorInterface $configurator */ $configurator = app($className); diff --git a/app/Http/Controllers/Import/FileController.php b/app/Http/Controllers/Import/FileController.php index 575c47ae1f..5e13cc9ed3 100644 --- a/app/Http/Controllers/Import/FileController.php +++ b/app/Http/Controllers/Import/FileController.php @@ -81,7 +81,7 @@ class FileController extends Controller if ($configurator->isJobConfigured()) { $this->repository->updateStatus($job, 'configured'); - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } $view = $configurator->getNextView(); $data = $configurator->getNextData(); @@ -227,6 +227,7 @@ class FileController extends Controller * @param ImportJob $job * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException */ public function postConfigure(Request $request, ImportJob $job) { @@ -235,7 +236,7 @@ class FileController extends Controller // is the job already configured? if ($configurator->isJobConfigured()) { - return redirect(route('import.file.status', [$job->key])); + return redirect(route('import.status', [$job->key])); } $data = $request->all(); $configurator->configureJob($data); @@ -248,7 +249,7 @@ class FileController extends Controller } // return to configure - return redirect(route('import.file.configure', [$job->key])); + return redirect(route('import.configure', [$job->key])); } /** @@ -285,7 +286,7 @@ class FileController extends Controller $subTitle = trans('firefly.import_status_sub_title'); $subTitleIcon = 'fa-star'; - return view('import.file.status', compact('job', 'subTitle', 'subTitleIcon')); + return view('import.status', compact('job', 'subTitle', 'subTitleIcon')); } /** diff --git a/app/Http/Controllers/Import/IndexController.php b/app/Http/Controllers/Import/IndexController.php index efaf516f35..866f0b1557 100644 --- a/app/Http/Controllers/Import/IndexController.php +++ b/app/Http/Controllers/Import/IndexController.php @@ -40,6 +40,7 @@ class IndexController extends Controller * * @param string $bank * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector * @throws FireflyException */ public function create(string $bank) diff --git a/app/Http/Controllers/Import/StatusController.php b/app/Http/Controllers/Import/StatusController.php new file mode 100644 index 0000000000..f4f11d9fda --- /dev/null +++ b/app/Http/Controllers/Import/StatusController.php @@ -0,0 +1,15 @@ +parent('import.index'); - $breadcrumbs->push(trans('firefly.import_file'), route('import.file.index')); - } -); - -Breadcrumbs::register( - 'import.file.configure', - function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { - $breadcrumbs->parent('import.file.index'); - $breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.file.configure', [$job->key])); + $breadcrumbs->push(trans('firefly.import_config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key])); } ); Breadcrumbs::register( - 'import.file.status', + 'import.status', function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) { - $breadcrumbs->parent('import.file.index'); - $breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.file.status', [$job->key])); + $breadcrumbs->parent('import.index'); + $breadcrumbs->push(trans('firefly.import_status_bread_crumb', ['key' => $job->key]), route('import.status', [$job->key])); } ); diff --git a/app/Import/Mapper/AssetAccountIbans.php b/app/Import/Mapper/AssetAccountIbans.php index 6a53db8941..9e2c78f277 100644 --- a/app/Import/Mapper/AssetAccountIbans.php +++ b/app/Import/Mapper/AssetAccountIbans.php @@ -56,7 +56,7 @@ class AssetAccountIbans implements MapperInterface asort($list); $list = $topList + $list; - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/AssetAccounts.php b/app/Import/Mapper/AssetAccounts.php index 43188a6833..1df9b2dca7 100644 --- a/app/Import/Mapper/AssetAccounts.php +++ b/app/Import/Mapper/AssetAccounts.php @@ -53,7 +53,7 @@ class AssetAccounts implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Bills.php b/app/Import/Mapper/Bills.php index b758b52dd1..90e22e2558 100644 --- a/app/Import/Mapper/Bills.php +++ b/app/Import/Mapper/Bills.php @@ -46,7 +46,7 @@ class Bills implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Budgets.php b/app/Import/Mapper/Budgets.php index eb1b73c9d8..286a6e3879 100644 --- a/app/Import/Mapper/Budgets.php +++ b/app/Import/Mapper/Budgets.php @@ -46,7 +46,7 @@ class Budgets implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Categories.php b/app/Import/Mapper/Categories.php index d6e0bd3d0f..10155d77a6 100644 --- a/app/Import/Mapper/Categories.php +++ b/app/Import/Mapper/Categories.php @@ -46,7 +46,7 @@ class Categories implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/OpposingAccountIbans.php b/app/Import/Mapper/OpposingAccountIbans.php index 894ff7f509..35d4178a88 100644 --- a/app/Import/Mapper/OpposingAccountIbans.php +++ b/app/Import/Mapper/OpposingAccountIbans.php @@ -62,7 +62,7 @@ class OpposingAccountIbans implements MapperInterface asort($list); $list = $topList + $list; - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/OpposingAccounts.php b/app/Import/Mapper/OpposingAccounts.php index c80c1753ab..7269fca8d9 100644 --- a/app/Import/Mapper/OpposingAccounts.php +++ b/app/Import/Mapper/OpposingAccounts.php @@ -59,7 +59,7 @@ class OpposingAccounts implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/Tags.php b/app/Import/Mapper/Tags.php index 8a696ae082..0c2532b1c9 100644 --- a/app/Import/Mapper/Tags.php +++ b/app/Import/Mapper/Tags.php @@ -46,7 +46,7 @@ class Tags implements MapperInterface } asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Import/Mapper/TransactionCurrencies.php b/app/Import/Mapper/TransactionCurrencies.php index 0a52533aee..fc2ee8d395 100644 --- a/app/Import/Mapper/TransactionCurrencies.php +++ b/app/Import/Mapper/TransactionCurrencies.php @@ -42,7 +42,7 @@ class TransactionCurrencies implements MapperInterface asort($list); - $list = [0 => trans('csv.map_do_not_map')] + $list; + $list = [0 => trans('import.map_do_not_map')] + $list; return $list; } diff --git a/app/Repositories/ImportJob/ImportJobRepository.php b/app/Repositories/ImportJob/ImportJobRepository.php index c2e3bd81d6..e40e541410 100644 --- a/app/Repositories/ImportJob/ImportJobRepository.php +++ b/app/Repositories/ImportJob/ImportJobRepository.php @@ -125,18 +125,22 @@ class ImportJobRepository implements ImportJobRepositoryInterface } /** - * @param ImportJob $job - * @param UploadedFile $file + * @param ImportJob $job + * @param null|UploadedFile $file * - * @return mixed + * @return bool + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException */ - public function processFile(ImportJob $job, UploadedFile $file): bool + public function processFile(ImportJob $job, ?UploadedFile $file): bool { + if (is_null($file)) { + return false; + } /** @var UserRepositoryInterface $repository */ $repository = app(UserRepositoryInterface::class); $newName = sprintf('%s.upload', $job->key); $uploaded = new SplFileObject($file->getRealPath()); - $content = $uploaded->fread($uploaded->getSize()); + $content = trim($uploaded->fread($uploaded->getSize())); $contentEncrypted = Crypt::encrypt($content); $disk = Storage::disk('upload'); @@ -171,8 +175,12 @@ class ImportJobRepository implements ImportJobRepositoryInterface */ public function setConfiguration(ImportJob $job, array $configuration): ImportJob { - $job->configuration = $configuration; + Log::debug(sprintf('Incoming config for job "%s" is: ', $job->key), $configuration); + $currentConfig = $job->configuration; + $newConfig = array_merge($currentConfig, $configuration); + $job->configuration = $newConfig; $job->save(); + Log::debug(sprintf('Set config of job "%s" to: ', $job->key), $newConfig); return $job; } diff --git a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php index a2a9869eaf..31067db0e6 100644 --- a/app/Repositories/ImportJob/ImportJobRepositoryInterface.php +++ b/app/Repositories/ImportJob/ImportJobRepositoryInterface.php @@ -54,12 +54,12 @@ interface ImportJobRepositoryInterface public function processConfiguration(ImportJob $job, UploadedFile $file): bool; /** - * @param ImportJob $job - * @param UploadedFile $file + * @param ImportJob $job + * @param null|UploadedFile $file * - * @return mixed + * @return bool */ - public function processFile(ImportJob $job, UploadedFile $file): bool; + public function processFile(ImportJob $job, ?UploadedFile $file): bool; /** * @param ImportJob $job diff --git a/app/Support/Import/Configuration/File/Map.php b/app/Support/Import/Configuration/File/Map.php index fa7b7428e2..20d84bdfb7 100644 --- a/app/Support/Import/Configuration/File/Map.php +++ b/app/Support/Import/Configuration/File/Map.php @@ -29,6 +29,7 @@ use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; use League\Csv\Reader; +use League\Csv\Statement; use Log; /** @@ -49,6 +50,7 @@ class Map implements ConfigurationInterface * @return array * * @throws FireflyException + * @throws \League\Csv\Exception */ public function getData(): array { @@ -57,13 +59,15 @@ class Map implements ConfigurationInterface // in order to actually map we also need all possible values from the CSV file. $content = $this->job->uploadFileContents(); + $offset = 0; /** @var Reader $reader */ $reader = Reader::createFromString($content); $reader->setDelimiter($this->configuration['delimiter']); - if($this->configuration['has-headers']) { - $reader->setHeaderOffset(0); + if ($this->configuration['has-headers']) { + $offset = 1; } - $results = $reader->getRecords(); + $stmt = (new Statement)->offset($offset); + $results = $stmt->process($reader); $this->validSpecifics = array_keys(config('csv.import_specifics')); $indexes = array_keys($this->data); $rowIndex = 0; @@ -71,13 +75,12 @@ class Map implements ConfigurationInterface $row = $this->runSpecifics($row); //do something here - foreach ($indexes as $index) { // this is simply 1, 2, 3, etc. if (!isset($row[$index])) { // don't really know how to handle this. Just skip, for now. continue; } - $value = $row[$index]; + $value = trim($row[$index]); if (strlen($value) > 0) { // we can do some preprocessing here, // which is exclusively to fix the tags: diff --git a/app/Support/Import/Configuration/File/Roles.php b/app/Support/Import/Configuration/File/Roles.php index f39f698d2b..a06785757c 100644 --- a/app/Support/Import/Configuration/File/Roles.php +++ b/app/Support/Import/Configuration/File/Roles.php @@ -45,19 +45,28 @@ class Roles implements ConfigurationInterface * Get the data necessary to show the configuration screen. * * @return array + * @throws \League\Csv\Exception */ public function getData(): array { - $config = $this->job->configuration; - $content = $this->job->uploadFileContents(); - + $config = $this->job->configuration; + $content = $this->job->uploadFileContents(); + $config['has-headers'] = true; + $headers = []; + $offset = 0; // create CSV reader. $reader = Reader::createFromString($content); $reader->setDelimiter($config['delimiter']); + if ($config['has-headers']) { - $reader->setHeaderOffset(0); + $offset = 1; + // get headers: + $stmt = (new Statement)->limit(1)->offset(0); + $records = $stmt->process($reader); + $headers = $records->fetchOne(); } - $stmt = (new Statement)->limit(intval(config('csv.example_rows', 5))); + // example rows: + $stmt = (new Statement)->limit(intval(config('csv.example_rows', 5)))->offset($offset); // set data: $roles = $this->getRoles(); asort($roles); @@ -65,10 +74,9 @@ class Roles implements ConfigurationInterface 'examples' => [], 'roles' => $roles, 'total' => 0, - 'headers' => $config['has-headers'] ? $reader->fetchOne(0) : [], + 'headers' => $headers, ]; - $records = $stmt->process($reader); foreach ($records as $row) { $row = $this->processSpecifics($row); @@ -142,7 +150,7 @@ class Roles implements ConfigurationInterface { $roles = []; foreach (array_keys(config('csv.import_roles')) as $role) { - $roles[$role] = trans('csv.column_' . $role); + $roles[$role] = trans('import.column_' . $role); } return $roles; @@ -236,7 +244,7 @@ class Roles implements ConfigurationInterface */ private function processSpecifics(array $row): array { - $names = array_keys($this->job->configuration['specifics']); + $names = array_keys($this->job->configuration['specifics'] ?? []); foreach ($names as $name) { /** @var SpecificInterface $specific */ $specific = app('FireflyIII\Import\Specifics\\' . $name); @@ -271,7 +279,7 @@ class Roles implements ConfigurationInterface $this->warning = ''; } if (0 === $assigned || !$hasAmount) { - $this->warning = strval(trans('csv.roles_warning')); + $this->warning = strval(trans('import.roles_warning')); } return true; diff --git a/app/Support/Import/Configuration/File/Upload.php b/app/Support/Import/Configuration/File/Upload.php index b31e2915b9..133e356eb9 100644 --- a/app/Support/Import/Configuration/File/Upload.php +++ b/app/Support/Import/Configuration/File/Upload.php @@ -22,11 +22,9 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\Configuration\File; -use FireflyIII\Import\Specifics\SpecificInterface; use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Configuration\ConfigurationInterface; -use League\Csv\Reader; -use League\Csv\Statement; use Log; /** @@ -47,7 +45,17 @@ class Upload implements ConfigurationInterface */ public function getData(): array { - return []; + $importFileTypes = []; + $defaultImportType = config('import.options.file.default_import_format'); + + foreach (config('import.options.file.import_formats') as $type) { + $importFileTypes[$type] = trans('import.import_file_type_' . $type); + } + + return [ + 'default_type' => $defaultImportType, + 'file_types' => $importFileTypes, + ]; } /** @@ -81,8 +89,28 @@ class Upload implements ConfigurationInterface */ public function storeConfiguration(array $data): bool { - echo 'do something with data.'; - exit; + Log::debug('Now in storeConfiguration for file Upload.'); + /** @var ImportJobRepositoryInterface $repository */ + $repository = app(ImportJobRepositoryInterface::class); + $type = $data['import_file_type'] ?? 'unknown'; + $config = $this->job->configuration; + $config['file-type'] = in_array($type, config('import.options.file.import_formats')) ? $type : 'unknown'; + $repository->setConfiguration($this->job, $config); + $uploaded = $repository->processFile($this->job, $data['import_file'] ?? null); + $this->job->save(); + Log::debug(sprintf('Result of upload is %s', var_export($uploaded, true))); + // process config, if present: + if (isset($data['configuration_file'])) { + $repository->processConfiguration($this->job, $data['configuration_file']); + } + $config = $this->job->configuration; + $config['has-file-upload'] = $uploaded; + $repository->setConfiguration($this->job, $config); + + if ($uploaded === false) { + $this->warning = 'No valid upload.'; + } + return true; } diff --git a/config/import.php b/config/import.php index ad8a9d1ff3..4181666ea5 100644 --- a/config/import.php +++ b/config/import.php @@ -21,6 +21,12 @@ return [ 'spectre' => 'FireflyIII\Import\Configuration\SpectreConfigurator', 'plaid' => 'FireflyIII\Import\Configuration\PlaidConfigurator', ], + 'options' => [ + 'file' => [ + 'import_formats' => ['csv'], // mt940 + 'default_import_format' => 'csv', + ], + ], 'default_config' => [ 'file' => [ 'has-config-file' => true, diff --git a/resources/lang/en_US/bank.php b/resources/lang/en_US/bank.php index b9187230d5..ee3504e6f3 100644 --- a/resources/lang/en_US/bank.php +++ b/resources/lang/en_US/bank.php @@ -23,19 +23,5 @@ declare(strict_types=1); return [ - 'bunq_prerequisites_title' => 'Prerequisites for an import from bunq', - 'bunq_prerequisites_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app.', - // Spectre: - 'spectre_title' => 'Import using Spectre', - 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', - 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', - 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', - 'spectre_select_country_title' => 'Select a country', - 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', - 'spectre_select_provider_title' => 'Select a bank', - 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', - 'spectre_input_fields_title' => 'Input mandatory fields', - 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', - 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', ]; diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index 8889ca3aab..724ca562c1 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -23,79 +23,4 @@ declare(strict_types=1); return [ - // initial config - 'initial_title' => 'Import setup (1/3) - Basic CSV import setup', - 'initial_text' => 'To be able to import your file correctly, please validate the options below.', - 'initial_box' => 'Basic CSV import setup', - 'initial_box_title' => 'Basic CSV import setup options', - 'initial_header_help' => 'Check this box if the first row of your CSV file are the column titles.', - 'initial_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', - 'initial_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', - 'initial_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', - 'initial_submit' => 'Continue with step 2/3', - - // new options: - 'apply_rules_title' => 'Apply rules', - 'apply_rules_description' => 'Apply your rules. Note that this slows the import significantly.', - 'match_bills_title' => 'Match bills', - 'match_bills_description' => 'Match your bills to newly created withdrawals. Note that this slows the import significantly.', - - // roles config - 'roles_title' => 'Import setup (2/3) - Define each column\'s role', - 'roles_text' => 'Each column in your CSV file contains certain data. Please indicate what kind of data the importer should expect. The option to "map" data means that you will link each entry found in the column to a value in your database. An often mapped column is the column that contains the IBAN of the opposing account. That can be easily matched to IBAN\'s present in your database already.', - 'roles_table' => 'Table', - 'roles_column_name' => 'Name of column', - 'roles_column_example' => 'Column example data', - 'roles_column_role' => 'Column data meaning', - 'roles_do_map_value' => 'Map these values', - 'roles_column' => 'Column', - 'roles_no_example_data' => 'No example data available', - 'roles_submit' => 'Continue with step 3/3', - 'roles_warning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', - - // map data - 'map_title' => 'Import setup (3/3) - Connect import data to Firefly III data', - 'map_text' => 'In the following tables, the left value shows you information found in your uploaded CSV file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', - 'map_field_value' => 'Field value', - 'map_field_mapped_to' => 'Mapped to', - 'map_do_not_map' => '(do not map)', - 'map_submit' => 'Start the import', - - // map things. - 'column__ignore' => '(ignore this column)', - 'column_account-iban' => 'Asset account (IBAN)', - 'column_account-id' => 'Asset account ID (matching Firefly)', - 'column_account-name' => 'Asset account (name)', - 'column_amount' => 'Amount', - 'column_amount_debet' => 'Amount (debet column)', - 'column_amount_credit' => 'Amount (credit column)', - 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', - 'column_bill-id' => 'Bill ID (matching Firefly)', - 'column_bill-name' => 'Bill name', - 'column_budget-id' => 'Budget ID (matching Firefly)', - 'column_budget-name' => 'Budget name', - 'column_category-id' => 'Category ID (matching Firefly)', - 'column_category-name' => 'Category name', - 'column_currency-code' => 'Currency code (ISO 4217)', - 'column_currency-id' => 'Currency ID (matching Firefly)', - 'column_currency-name' => 'Currency name (matching Firefly)', - 'column_currency-symbol' => 'Currency symbol (matching Firefly)', - 'column_date-interest' => 'Interest calculation date', - 'column_date-book' => 'Transaction booking date', - 'column_date-process' => 'Transaction process date', - 'column_date-transaction' => 'Date', - 'column_description' => 'Description', - 'column_opposing-iban' => 'Opposing account (IBAN)', - 'column_opposing-id' => 'Opposing account ID (matching Firefly)', - 'column_external-id' => 'External ID', - 'column_opposing-name' => 'Opposing account (name)', - 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', - 'column_ing-debet-credit' => 'ING specific debet/credit indicator', - 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', - 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', - 'column_sepa-db' => 'SEPA Direct Debet', - 'column_tags-comma' => 'Tags (comma separated)', - 'column_tags-space' => 'Tags (space separated)', - 'column_account-number' => 'Asset account (account number)', - 'column_opposing-number' => 'Opposing account (account number)', ]; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 2e0cda0d22..9c4d3e3a49 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1070,29 +1070,17 @@ return [ // import bread crumbs and titles: 'import' => 'Import', 'import_data' => 'Import data', + 'import_general_index_file' => 'Import a file', 'import_from_bunq' => 'Import from bunq', 'import_using_spectre' => 'Import using Spectre', 'import_using_plaid' => 'Import using Plaid', - + 'import_config_sub_title' => 'Set up your import', + 'import_config_bread_crumb' => 'Set up your import', // import index page: 'import_index_title' => 'Import data into Firefly III', 'import_index_sub_title' => 'Index', 'import_general_index_intro' => 'Welcome to Firefly\'s import routine. There are a few ways of importing data into Firefly III, displayed here as buttons.', - 'import_general_index_file' => 'Import a file', - 'import_index_intro' => 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', - 'import_index_file' => 'Select your file', - 'import_index_config' => '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 configuration file.', - 'import_index_type' => 'Select the type of file you will upload', - 'import_index_start' => 'Start importing', - 'import_file' => 'Import a file', - - // supported file types: - 'import_file_type_csv' => 'CSV (comma separated values)', - - // import configuration routine: - 'import_config_sub_title' => 'Set up your import file', - 'import_config_bread_crumb' => 'Set up your import file', // import status page: 'import_status_bread_crumb' => 'Import status', diff --git a/resources/lang/en_US/import.php b/resources/lang/en_US/import.php new file mode 100644 index 0000000000..d1135a4198 --- /dev/null +++ b/resources/lang/en_US/import.php @@ -0,0 +1,126 @@ + 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', + // 'import_index_file' => 'Select your file', + // 'import_index_config' => '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 configuration file.', + // + // 'import_index_start' => 'Start importing', + // 'import_file' => 'Import a file', + // + // // supported file types: + // + // + // // import configuration routine: + + + // file: upload something: + 'file_upload_title' => 'Import setup (1/4) - Upload your file', + 'file_upload_text' => 'This routine will help you import files from your bank into Firefly III. Please check out the help pages in the top right corner.', + 'file_upload_fields' => 'Fields', + 'file_upload_help' => 'Select your file', + '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 configuration file', + 'file_upload_type_help' => 'Select the type of file you will upload', + 'file_upload_submit' => 'Upload files', + + // file: upload types + 'import_file_type_csv' => 'CSV (comma separated values)', + + // file: initial config for CSV + 'csv_initial_title' => 'Import setup (2/4) - Basic CSV import setup', + 'csv_initial_text' => 'To be able to import your file correctly, please validate the options below.', + 'csv_initial_box' => 'Basic CSV import setup', + 'csv_initial_box_title' => 'Basic CSV import setup options', + 'csv_initial_header_help' => 'Check this box if the first row of your CSV file are the column titles.', + 'csv_initial_date_help' => 'Date time format in your CSV. Follow the format like this page indicates. The default value will parse dates that look like this: :dateExample.', + 'csv_initial_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', + 'csv_initial_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', + 'csv_initial_submit' => 'Continue with step 3/4', + + // file: new options: + 'file_apply_rules_title' => 'Apply rules', + 'file_apply_rules_description' => 'Apply your rules. Note that this slows the import significantly.', + 'file_match_bills_title' => 'Match bills', + 'file_match_bills_description' => 'Match your bills to newly created withdrawals. Note that this slows the import significantly.', + + // file: roles config + 'csv_roles_title' => 'Import setup (3/4) - Define each column\'s role', + 'csv_roles_text' => 'Each column in your CSV file contains certain data. Please indicate what kind of data the importer should expect. The option to "map" data means that you will link each entry found in the column to a value in your database. An often mapped column is the column that contains the IBAN of the opposing account. That can be easily matched to IBAN\'s present in your database already.', + 'csv_roles_table' => 'Table', + 'csv_roles_column_name' => 'Name of column', + 'csv_roles_column_example' => 'Column example data', + 'csv_roles_column_role' => 'Column data meaning', + 'csv_roles_do_map_value' => 'Map these values', + 'csv_roles_column' => 'Column', + 'csv_roles_no_example_data' => 'No example data available', + 'csv_roles_submit' => 'Continue with step 4/4', + 'csv_roles_warning' => 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', + + + // map data + 'file_map_title' => 'Import setup (4/4) - Connect import data to Firefly III data', + 'file_map_text' => 'In the following tables, the left value shows you information found in your uploaded file. It is your task to map this value, if possible, to a value already present in your database. Firefly will stick to this mapping. If there is no value to map to, or you do not wish to map the specific value, select nothing.', + 'file_map_field_value' => 'Field value', + 'file_map_field_mapped_to' => 'Mapped to', + 'map_do_not_map' => '(do not map)', + 'file_map_submit' => 'Start the import', + + // map things. + 'column__ignore' => '(ignore this column)', + 'column_account-iban' => 'Asset account (IBAN)', + 'column_account-id' => 'Asset account ID (matching Firefly)', + 'column_account-name' => 'Asset account (name)', + 'column_amount' => 'Amount', + 'column_amount_debet' => 'Amount (debet column)', + 'column_amount_credit' => 'Amount (credit column)', + 'column_amount-comma-separated' => 'Amount (comma as decimal separator)', + 'column_bill-id' => 'Bill ID (matching Firefly)', + 'column_bill-name' => 'Bill name', + 'column_budget-id' => 'Budget ID (matching Firefly)', + 'column_budget-name' => 'Budget name', + 'column_category-id' => 'Category ID (matching Firefly)', + 'column_category-name' => 'Category name', + 'column_currency-code' => 'Currency code (ISO 4217)', + 'column_currency-id' => 'Currency ID (matching Firefly)', + 'column_currency-name' => 'Currency name (matching Firefly)', + 'column_currency-symbol' => 'Currency symbol (matching Firefly)', + 'column_date-interest' => 'Interest calculation date', + 'column_date-book' => 'Transaction booking date', + 'column_date-process' => 'Transaction process date', + 'column_date-transaction' => 'Date', + 'column_description' => 'Description', + 'column_opposing-iban' => 'Opposing account (IBAN)', + 'column_opposing-id' => 'Opposing account ID (matching Firefly)', + 'column_external-id' => 'External ID', + 'column_opposing-name' => 'Opposing account (name)', + 'column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator', + 'column_ing-debet-credit' => 'ING specific debet/credit indicator', + 'column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID', + 'column_sepa-ct-op' => 'SEPA Credit Transfer opposing account', + 'column_sepa-db' => 'SEPA Direct Debet', + 'column_tags-comma' => 'Tags (comma separated)', + 'column_tags-space' => 'Tags (space separated)', + 'column_account-number' => 'Asset account (account number)', + 'column_opposing-number' => 'Opposing account (account number)', + + // bunq + 'bunq_prerequisites_title' => 'Prerequisites for an import from bunq', + 'bunq_prerequisites_text' => 'In order to import from bunq, you need to obtain an API key. You can do this through the app.', + + // Spectre: + 'spectre_title' => 'Import using Spectre', + 'spectre_prerequisites_title' => 'Prerequisites for an import using Spectre', + 'spectre_prerequisites_text' => 'In order to import data using the Spectre API, you need to prove some secrets. They can be found on the secrets page.', + 'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your security page.', + 'spectre_select_country_title' => 'Select a country', + 'spectre_select_country_text' => 'Firefly III has a large selection of banks and sites from which Spectre can download transactional data. These banks are sorted by country. Please not that there is a "Fake Country" for when you wish to test something. If you wish to import from other financial tools, please use the imaginary country called "Other financial applications". By default, Spectre only allows you to download data from fake banks. Make sure your status is "Live" on your Dashboard if you wish to download from real banks.', + 'spectre_select_provider_title' => 'Select a bank', + 'spectre_select_provider_text' => 'Spectre supports the following banks or financial services grouped under :country. Please pick the one you wish to import from.', + 'spectre_input_fields_title' => 'Input mandatory fields', + 'spectre_input_fields_text' => 'The following fields are mandated by ":provider" (from :country).', + 'spectre_instructions_english' => 'These instructions are provided by Spectre for your convencience. They are in English:', +]; \ No newline at end of file diff --git a/resources/views/import/file/finished.twig b/resources/views/import/_old/finished.twig similarity index 100% rename from resources/views/import/file/finished.twig rename to resources/views/import/_old/finished.twig diff --git a/resources/views/import/file/index.twig b/resources/views/import/_old/index.twig similarity index 100% rename from resources/views/import/file/index.twig rename to resources/views/import/_old/index.twig diff --git a/resources/views/import/file/status.twig b/resources/views/import/_old/status.twig similarity index 100% rename from resources/views/import/file/status.twig rename to resources/views/import/_old/status.twig diff --git a/resources/views/import/file/initial.twig b/resources/views/import/file/initial.twig index fbefb1b37c..bf7a5e2ea6 100644 --- a/resources/views/import/file/initial.twig +++ b/resources/views/import/file/initial.twig @@ -10,11 +10,11 @@
-

{{ trans('csv.initial_title') }}

+

{{ trans('import.csv_initial_title') }}

- {{ trans('csv.initial_text') }} + {{ trans('import.csv_initial_text') }}

@@ -29,42 +29,42 @@
-

{{ trans('csv.initial_box_title') }}

+

{{ trans('import.csv_initial_box_title') }}

{{ 'mandatoryFields'|_ }}

- {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('csv.initial_header_help')}) }} - {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('csv.initial_date_help', {dateExample: phpdate('Ymd')}) }) }} - {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('csv.initial_delimiter_help') } ) }} - {{ ExpandedForm.select('csv_import_account', data.accounts, job.configuration['import-account'], {helpText: trans('csv.initial_import_account_help')} ) }} + {{ ExpandedForm.checkbox('has_headers',1,job.configuration['has-headers'],{helpText: trans('import.csv_initial_header_help')}) }} + {{ ExpandedForm.text('date_format',job.configuration['date-format'],{helpText: trans('import.csv_initial_date_help', {dateExample: phpdate('Ymd')}) }) }} + {{ ExpandedForm.select('csv_delimiter', data.delimiters, job.configuration['delimiter'], {helpText: trans('import.csv_initial_delimiter_help') } ) }} + {{ ExpandedForm.select('csv_import_account', data.accounts, job.configuration['import-account'], {helpText: trans('import.csv_initial_import_account_help')} ) }}

{{ 'optionalFields'|_ }}

@@ -97,7 +97,7 @@
diff --git a/resources/views/import/file/map.twig b/resources/views/import/file/map.twig index edc4b4b5bb..31d309d7f2 100644 --- a/resources/views/import/file/map.twig +++ b/resources/views/import/file/map.twig @@ -11,11 +11,11 @@
-

{{ trans('csv.map_title') }}

+

{{ trans('import.file_map_title') }}

- {{ trans('csv.map_text') }} + {{ trans('import.file_map_text') }}

@@ -30,14 +30,14 @@
-

{{ trans('csv.column_'~field.name) }}

+

{{ trans('import.column_'~field.name) }}

- - + + @@ -67,7 +67,7 @@
diff --git a/resources/views/import/file/roles.twig b/resources/views/import/file/roles.twig index f35b92bfe4..e5861329e2 100644 --- a/resources/views/import/file/roles.twig +++ b/resources/views/import/file/roles.twig @@ -10,11 +10,11 @@
-

{{ trans('csv.roles_title') }}

+

{{ trans('import.csv_roles_title') }}

- {{ trans('csv.roles_text') }} + {{ trans('import.csv_roles_text') }}

@@ -29,17 +29,17 @@
-

{{ trans('csv.roles_table') }}

+

{{ trans('import.csv_roles_table') }}

{{ trans('csv.map_field_value') }}{{ trans('csv.map_field_mapped_to') }}{{ trans('import.file_map_field_value') }}{{ trans('import.file_map_field_mapped_to') }}
- - - - + + + + {% for i in 0..(data.total -1) %} @@ -47,14 +47,14 @@
{{ trans('csv.roles_column_name') }}{{ trans('csv.roles_column_example') }}{{ trans('csv.roles_column_role') }}{{ trans('csv.roles_do_map_value') }}{{ trans('import.csv_roles_column_name') }}{{ trans('import.csv_roles_column_example') }}{{ trans('import.csv_roles_column_role') }}{{ trans('import.csv_roles_do_map_value') }}
{% if data.headers[i] == '' %} - {{ trans('csv.roles_column') }} #{{ loop.index }} + {{ trans('import.csv_roles_column') }} #{{ loop.index }} {% else %} {{ data.headers[i] }} {% endif %} {% if data.examples[i]|length == 0 %} - {{ trans('csv.roles_no_example_data') }} + {{ trans('import.csv_roles_no_example_data') }} {% else %} {% for example in data.examples[i] %} {{ example }}
@@ -91,7 +91,7 @@
diff --git a/resources/views/import/file/upload.twig b/resources/views/import/file/upload.twig index 50c184e58d..4f4414db79 100644 --- a/resources/views/import/file/upload.twig +++ b/resources/views/import/file/upload.twig @@ -5,47 +5,51 @@ {% endblock %} {% block content %}
-
-
+
+
-

{{ 'import_index_title'|_ }}

+

{{ trans('import.file_upload_title') }}

-
-
-

- {{ 'import_index_intro'|_ }} -

-
+

+ {{ trans('import.file_upload_text') }} +

+
+
+ +
+
+ +
+ + + +
+
+
+
+

{{ trans('import.file_upload_fields') }}

- -
- - - - -
- - {{ ExpandedForm.file('import_file', {helpText: 'import_index_file'|_}) }} - {{ ExpandedForm.file('configuration_file', {helpText: 'import_index_config'|_|raw}) }} - {{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_index_type'|_}) }} - -
- -
- -
-
-
- +
+ {{ ExpandedForm.file('import_file', {helpText: trans('import.file_upload_help')}) }} + {{ ExpandedForm.file('configuration_file', {helpText: trans('import.file_upload_config_help')|raw}) }} + {{ ExpandedForm.select('import_file_type', data.file_types, data.default_type, {'helpText' : trans('import.file_upload_type_help')}) }}
-
+
+
+
+
+ +
+
+
+
+ {% endblock %} {% block scripts %} {% endblock %} diff --git a/routes/web.php b/routes/web.php index ee79111ec4..42ca3e5acc 100755 --- a/routes/web.php +++ b/routes/web.php @@ -440,6 +440,8 @@ Route::group( Route::get('configure/{importJob}', ['uses' => 'Import\ConfigurationController@index', 'as' => 'configure']); Route::post('configure/{importJob}', ['uses' => 'Import\ConfigurationController@post', 'as' => 'configure.post']); + // get status of any job: + Route::get('status/{importJob}', ['uses' => 'Import\StatusController@index', 'as' => 'status']); // file import // Route::get('file', ['uses' => 'Import\FileController@index', 'as' => 'file.index']);