Lots of new code for the import routine part 2

This commit is contained in:
James Cole 2017-12-16 17:19:10 +01:00
parent 84b6708260
commit 66ee382da0
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
32 changed files with 351 additions and 226 deletions

View File

@ -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);

View File

@ -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'));
}
/**

View File

@ -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)

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Http\Controllers\Controller;
/**
* Class StatusController
*/
class StatusController extends Controller
{
}

View File

@ -548,27 +548,18 @@ Breadcrumbs::register(
}
);
// FILE IMPORT
Breadcrumbs::register(
'import.file.index',
function (BreadCrumbGenerator $breadcrumbs) {
'import.configure',
function (BreadCrumbGenerator $breadcrumbs, ImportJob $job) {
$breadcrumbs->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]));
}
);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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,

View File

@ -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 <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.',
'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your <a href="https://www.saltedge.com/clients/security/edit">security page</a>.',
'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 <a href="https://www.saltedge.com/clients/dashboard">Dashboard</a> 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 <em>:country</em>. 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:',
];

View File

@ -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 <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.',
'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)',
];

View File

@ -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 <a href="https://github.com/firefly-iii/import-configurations/wiki">configuration file</a>.',
'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',

View File

@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
return [
// general strings for file upload
// '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 <a href="https://github.com/firefly-iii/import-configurations/wiki">configuration file</a>.',
//
// '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 <a href="https://github.com/firefly-iii/import-configurations/wiki">configuration file</a>',
'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 <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.',
'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 <a href="https://www.saltedge.com/clients/profile/secrets">secrets page</a>.',
'spectre_enter_pub_key' => 'The import will only work when you enter this public key on your <a href="https://www.saltedge.com/clients/security/edit">security page</a>.',
'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 <a href="https://www.saltedge.com/clients/dashboard">Dashboard</a> 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 <em>:country</em>. 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:',
];

View File

@ -10,11 +10,11 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.initial_title') }}</h3>
<h3 class="box-title">{{ trans('import.csv_initial_title') }}</h3>
</div>
<div class="box-body">
<p>
{{ trans('csv.initial_text') }}
{{ trans('import.csv_initial_text') }}
</p>
</div>
</div>
@ -29,42 +29,42 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.initial_box_title') }}</h3>
<h3 class="box-title">{{ trans('import.csv_initial_box_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-6">
<h4>{{ 'mandatoryFields'|_ }}</h4>
{{ 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')} ) }}
<h4>{{ 'optionalFields'|_ }}</h4>
<div class="form-group">
<label for="apply_rules_label" class="col-sm-4 control-label">
{{ trans('csv.apply_rules_title') }}
{{ trans('import.file_apply_rules_title') }}
</label>
<div class="col-sm-8">
<div class="radio"><label>
{{ Form.checkbox('apply_rules', '1',
job.configuration.apply_rules == '1', {'id': 'apply_rules_label'}) }}
{{ trans('csv.apply_rules_description') }}
{{ trans('import.file_apply_rules_description') }}
</label>
</div>
</div>
</div>
<div class="form-group">
<label for="match_bills_label" class="col-sm-4 control-label">
{{ trans('csv.match_bills_title') }}
{{ trans('import.file_match_bills_title') }}
</label>
<div class="col-sm-8">
<div class="radio"><label>
{{ Form.checkbox('match_bills', '1',
job.configuration.match_bills == '1', {'id': 'match_bills_label'}) }}
{{ trans('csv.match_bills_description') }}
{{ trans('import.file_match_bills_description') }}
</label>
</div>
</div>
@ -97,7 +97,7 @@
<div class="box">
<div class="box-body">
<button type="submit" class="pull-right btn btn-success">
{{ trans('csv.initial_submit') }}
{{ trans('import.csv_initial_submit') }}
</button>
</div>
</div>

View File

@ -11,11 +11,11 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.map_title') }}</h3>
<h3 class="box-title">{{ trans('import.file_map_title') }}</h3>
</div>
<div class="box-body">
<p>
{{ trans('csv.map_text') }}
{{ trans('import.file_map_text') }}
</p>
</div>
</div>
@ -30,14 +30,14 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.column_'~field.name) }}</h3>
<h3 class="box-title">{{ trans('import.column_'~field.name) }}</h3>
</div>
<div class="box-body no-padding">
<table class="table table-hover">
<thead>
<tr>
<th style="width:50%;">{{ trans('csv.map_field_value') }}</th>
<th>{{ trans('csv.map_field_mapped_to') }}</th>
<th style="width:50%;">{{ trans('import.file_map_field_value') }}</th>
<th>{{ trans('import.file_map_field_mapped_to') }}</th>
</tr>
</thead>
<tbody>
@ -67,7 +67,7 @@
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
{{ trans('csv.map_submit') }} <i class="fa fa-arrow-right"></i>
{{ trans('import.file_map_submit') }} <i class="fa fa-arrow-right"></i>
</button>
</div>
</div>

View File

@ -10,11 +10,11 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.roles_title') }}</h3>
<h3 class="box-title">{{ trans('import.csv_roles_title') }}</h3>
</div>
<div class="box-body">
<p>
{{ trans('csv.roles_text') }}
{{ trans('import.csv_roles_text') }}
</p>
</div>
</div>
@ -29,17 +29,17 @@
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('csv.roles_table') }}</h3>
<h3 class="box-title">{{ trans('import.csv_roles_table') }}</h3>
</div>
<div class="box-body">
<table class="table">
<thead>
<tr>
<th style="width:20%;">{{ trans('csv.roles_column_name') }}</th>
<th style="width:40%;">{{ trans('csv.roles_column_example') }}</th>
<th style="width:30%;">{{ trans('csv.roles_column_role') }}</th>
<th style="width:10%;">{{ trans('csv.roles_do_map_value') }}</th>
<th style="width:20%;">{{ trans('import.csv_roles_column_name') }}</th>
<th style="width:40%;">{{ trans('import.csv_roles_column_example') }}</th>
<th style="width:30%;">{{ trans('import.csv_roles_column_role') }}</th>
<th style="width:10%;">{{ trans('import.csv_roles_do_map_value') }}</th>
</tr>
</thead>
{% for i in 0..(data.total -1) %}
@ -47,14 +47,14 @@
<tr>
<td>
{% if data.headers[i] == '' %}
{{ trans('csv.roles_column') }} #{{ loop.index }}
{{ trans('import.csv_roles_column') }} #{{ loop.index }}
{% else %}
{{ data.headers[i] }}
{% endif %}
</td>
<td>
{% if data.examples[i]|length == 0 %}
<em>{{ trans('csv.roles_no_example_data') }}</em>
<em>{{ trans('import.csv_roles_no_example_data') }}</em>
{% else %}
{% for example in data.examples[i] %}
<code>{{ example }}</code><br/>
@ -91,7 +91,7 @@
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
{{ trans('csv.roles_submit') }} <i class="fa fa-arrow-right"></i>
{{ trans('import.csv_roles_submit') }} <i class="fa fa-arrow-right"></i>
</button>
</div>
</div>

View File

@ -5,47 +5,51 @@
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12">
<div class="box box-default">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'import_index_title'|_ }}</h3>
<h3 class="box-title">{{ trans('import.file_upload_title') }}</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-lg-8">
<p>
{{ 'import_index_intro'|_ }}
</p>
</div>
<p>
{{ trans('import.file_upload_text') }}
</p>
</div>
</div>
</div>
</div>
<form method="POST" action="{{ route('import.configure.post', job.key) }}" accept-charset="UTF-8" class="form-horizontal" enctype="multipart/form-data">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<input type="hidden" name="settings" value="upload"/>
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('import.file_upload_fields') }}</h3>
</div>
<div class="row">
<form method="POST" action="{{ route('import.configure.post', job.key) }}" accept-charset="UTF-8" class="form-horizontal"
enctype="multipart/form-data">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="col-lg-8 col-md-8 col-sm-12 col-xs-12">
{{ 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'|_}) }}
<div class="form-group" id="import_file_holder">
<label for="ffInput_submit" class="col-sm-4 control-label">&nbsp;</label>
<div class="col-sm-8">
<button type="submit" class="btn pull-right btn-success">
{{ ('import_index_start')|_ }}
</button>
</div>
</div>
</div>
</form>
<div class="box-body">
{{ 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')}) }}
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
{{ trans('import.file_upload_submit') }} <i class="fa fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block scripts %}
{% endblock %}

View File

@ -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']);