diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 7a592442a4..3b1f2fc704 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -14,6 +14,7 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; use FireflyIII\Crud\Account\AccountCrud; +use FireflyIII\Import\Mapper\MapperInterface; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; use Illuminate\Http\Request; @@ -111,54 +112,18 @@ class CsvImporter implements ImporterInterface */ public function getDataForSettings(): array { - $config = $this->job->configuration; - $data = [ - 'columns' => [], - 'columnCount' => 0, - ]; if ($this->doColumnRoles()) { - - // show user column role configuration. - $content = $this->job->uploadFileContents(); - - // create CSV reader. - $reader = Reader::createFromString($content); - $start = $config['has-headers'] ? 1 : 0; - $end = $start + self::EXAMPLE_ROWS; // first X rows - - // collect example data in $data['columns'] - while ($start < $end) { - $row = $reader->fetchOne($start); - foreach ($row as $index => $value) { - $value = trim($value); - if (strlen($value) > 0) { - $data['columns'][$index][] = $value; - } - } - $start++; - $data['columnCount'] = count($row); - } - - // make unique example data - foreach ($data['columns'] as $index => $values) { - $data['columns'][$index] = array_unique($values); - } - - $data['set_roles'] = []; - // collect possible column roles: - $data['available_roles'] = []; - foreach (array_keys(config('csv.import_roles')) as $role) { - $data['available_roles'][$role] = trans('csv.column_' . $role); - } - - $config['column-count'] = $data['columnCount']; - $this->job->configuration = $config; - $this->job->save(); + $data = $this->getDataForColumnRoles(); + + return $data; + } + + if ($this->doColumnMapping()) { + $data = $this->getDataForColumnMapping(); return $data; } - echo 'no settings to do.'; exit; @@ -176,6 +141,11 @@ class CsvImporter implements ImporterInterface if ($this->doColumnRoles()) { return 'import.csv.roles'; } + + if ($this->doColumnMapping()) { + return 'import.csv.map'; + } + echo 'no view for settings'; exit; } @@ -269,6 +239,14 @@ class CsvImporter implements ImporterInterface } } + /** + * @return bool + */ + private function doColumnMapping(): bool + { + return $this->job->configuration['column-mapping-complete'] === false; + } + /** * @return bool */ @@ -276,4 +254,94 @@ class CsvImporter implements ImporterInterface { return $this->job->configuration['column-roles-complete'] === false; } + + /** + * @return array + */ + private function getDataForColumnMapping(): array + { + $config = $this->job->configuration; + $data = []; + + foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { + if ($mustBeMapped) { + $column = $config['column-roles'][$index] ?? '_ignore'; + $canBeMapped = config('csv.import_roles.' . $column . '.mappable'); + if ($canBeMapped) { + $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); + /** @var MapperInterface $mapper */ + $mapper = new $mapperName; + $data[$index] = [ + 'name' => $column, + 'mapper' => $mapperName, + 'options' => $mapper->getMap(), + 'values' => [], + ]; + } + } + } + + + echo '
'; + var_dump($data); + var_dump($config); + + + exit; + + + } + + /** + * @return array + */ + private function getDataForColumnRoles():array + { + $config = $this->job->configuration; + $data = [ + 'columns' => [], + 'columnCount' => 0, + ]; + + // show user column role configuration. + $content = $this->job->uploadFileContents(); + + // create CSV reader. + $reader = Reader::createFromString($content); + $start = $config['has-headers'] ? 1 : 0; + $end = $start + self::EXAMPLE_ROWS; // first X rows + + // collect example data in $data['columns'] + while ($start < $end) { + $row = $reader->fetchOne($start); + foreach ($row as $index => $value) { + $value = trim($value); + if (strlen($value) > 0) { + $data['columns'][$index][] = $value; + } + } + $start++; + $data['columnCount'] = count($row); + } + + // make unique example data + foreach ($data['columns'] as $index => $values) { + $data['columns'][$index] = array_unique($values); + } + + $data['set_roles'] = []; + // collect possible column roles: + $data['available_roles'] = []; + foreach (array_keys(config('csv.import_roles')) as $role) { + $data['available_roles'][$role] = trans('csv.column_' . $role); + } + + $config['column-count'] = $data['columnCount']; + $this->job->configuration = $config; + $this->job->save(); + + return $data; + + + } } \ No newline at end of file diff --git a/app/Import/Mapper/AssetAccounts.php b/app/Import/Mapper/AssetAccounts.php new file mode 100644 index 0000000000..fdf0a64daa --- /dev/null +++ b/app/Import/Mapper/AssetAccounts.php @@ -0,0 +1,53 @@ +getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]); + $list = []; + + /** @var Account $account */ + foreach ($set as $account) { + $name = $account->name; + $iban = $account->iban ?? ''; + if (strlen($iban) > 0) { + $name .= ' (' . $account->iban . ')'; + } + $list[$account->id] = $name; + } + + asort($list); + + $list = [0 => trans('csv.do_not_map')] + $list; + + return $list; + + } +} \ No newline at end of file diff --git a/app/Import/Mapper/MapperInterface.php b/app/Import/Mapper/MapperInterface.php new file mode 100644 index 0000000000..c285c9b31f --- /dev/null +++ b/app/Import/Mapper/MapperInterface.php @@ -0,0 +1,26 @@ +getAccountsByType( + [ + AccountType::DEFAULT, AccountType::ASSET, + AccountType::EXPENSE, AccountType::BENEFICIARY, + AccountType::REVENUE + ]); + $list = []; + + /** @var Account $account */ + foreach ($set as $account) { + $name = $account->name; + $iban = $account->iban ?? ''; + if (strlen($iban) > 0) { + $name .= ' (' . $account->iban . ')'; + } + $list[$account->id] = $name; + } + + asort($list); + + $list = [0 => trans('csv.do_not_map')] + $list; + + return $list; + } +} \ No newline at end of file diff --git a/app/Import/Mapper/TransactionCurrencies.php b/app/Import/Mapper/TransactionCurrencies.php new file mode 100644 index 0000000000..6f21ebb355 --- /dev/null +++ b/app/Import/Mapper/TransactionCurrencies.php @@ -0,0 +1,42 @@ +id] = $currency->name . ' (' . $currency->code . ')'; + } + + asort($list); + + $list = [0 => trans('csv.do_not_map')] + $list; + + return $list; + + } +} \ No newline at end of file diff --git a/app/Models/Account.php b/app/Models/Account.php index 9070be24e4..fdb7b3c2e9 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -13,6 +13,8 @@ namespace FireflyIII\Models; use Auth; use Crypt; +use FireflyIII\Exceptions\FireflyException; +use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -183,10 +185,14 @@ class Account extends Model */ public function getIbanAttribute($value): string { - if (is_null($value)) { + if (is_null($value) || strlen(strval($value)) === 0) { return ''; } - $result = Crypt::decrypt($value); + try { + $result = Crypt::decrypt($value); + } catch (DecryptException $e) { + throw new FireflyException('Cannot decrypt value "' . $value . '" for account #' . $this->id); + } if (is_null($result)) { return ''; } diff --git a/config/csv.php b/config/csv.php index e235133f32..6e0a7edd6f 100644 --- a/config/csv.php +++ b/config/csv.php @@ -25,37 +25,37 @@ return [ 'mappable' => false, 'field' => 'bill', 'converter' => 'BillId', - 'mapper' => 'Bill', + 'mapper' => 'Bills', ], 'bill-name' => [ 'mappable' => true, 'converter' => 'BillName', 'field' => 'bill', - 'mapper' => 'Bill', + 'mapper' => 'Bills', ], 'currency-id' => [ 'mappable' => true, 'converter' => 'CurrencyId', 'field' => 'currency', - 'mapper' => 'TransactionCurrency' + 'mapper' => 'TransactionCurrencies' ], 'currency-name' => [ 'mappable' => true, 'converter' => 'CurrencyName', 'field' => 'currency', - 'mapper' => 'TransactionCurrency' + 'mapper' => 'TransactionCurrencies' ], 'currency-code' => [ 'mappable' => true, 'converter' => 'CurrencyCode', 'field' => 'currency', - 'mapper' => 'TransactionCurrency' + 'mapper' => 'TransactionCurrencies' ], 'currency-symbol' => [ 'mappable' => true, 'converter' => 'CurrencySymbol', 'field' => 'currency', - 'mapper' => 'TransactionCurrency' + 'mapper' => 'TransactionCurrencies' ], 'description' => [ 'mappable' => false, @@ -76,13 +76,13 @@ return [ 'mappable' => true, 'converter' => 'BudgetId', 'field' => 'budget', - 'mapper' => 'Budget', + 'mapper' => 'Budgets', ], 'budget-name' => [ 'mappable' => true, 'converter' => 'BudgetName', 'field' => 'budget', - 'mapper' => 'Budget', + 'mapper' => 'Budgets', ], 'rabo-debet-credit' => [ 'mappable' => false, @@ -98,73 +98,73 @@ return [ 'mappable' => true, 'converter' => 'CategoryId', 'field' => 'category', - 'mapper' => 'Category', + 'mapper' => 'Categories', ], 'category-name' => [ 'mappable' => true, 'converter' => 'CategoryName', 'field' => 'category', - 'mapper' => 'Category', + 'mapper' => 'Categories', ], 'tags-comma' => [ 'mappable' => true, 'field' => 'tags', 'converter' => 'TagsComma', - 'mapper' => 'Tag', + 'mapper' => 'Tags', ], 'tags-space' => [ 'mappable' => true, 'field' => 'tags', 'converter' => 'TagsSpace', - 'mapper' => 'Tag', + 'mapper' => 'Tags', ], 'account-id' => [ 'mappable' => true, - 'mapper' => 'AssetAccount', + 'mapper' => 'AssetAccountId', 'field' => 'asset-account-id', - 'converter' => 'AccountId' + 'converter' => 'AssetAccounts' ], 'account-name' => [ 'mappable' => true, - 'mapper' => 'AssetAccount', + 'mapper' => 'AssetAccountName', 'field' => 'asset-account-name', - 'converter' => 'AssetAccountName' + 'converter' => 'AssetAccounts' ], 'account-iban' => [ 'mappable' => true, 'converter' => 'AssetAccountIban', 'field' => 'asset-account-iban', - 'mapper' => 'AssetAccount' + 'mapper' => 'AssetAccounts' ], 'account-number' => [ 'mappable' => true, 'converter' => 'AssetAccountNumber', 'field' => 'asset-account-number', - 'mapper' => 'AssetAccount' + 'mapper' => 'AssetAccounts' ], 'opposing-id' => [ 'mappable' => true, 'field' => 'opposing-account-id', 'converter' => 'OpposingAccountId', - 'mapper' => 'AnyAccount', + 'mapper' => 'OpposingAccounts', ], 'opposing-name' => [ 'mappable' => true, 'field' => 'opposing-account-name', 'converter' => 'OpposingAccountName', - 'mapper' => 'AnyAccount', + 'mapper' => 'OpposingAccounts', ], 'opposing-iban' => [ 'mappable' => true, 'field' => 'opposing-account-iban', 'converter' => 'OpposingAccountIban', - 'mapper' => 'AnyAccount', + 'mapper' => 'OpposingAccounts', ], 'opposing-number' => [ 'mappable' => true, 'field' => 'opposing-account-number', 'converter' => 'OpposingAccountNumber', - 'mapper' => 'AnyAccount', + 'mapper' => 'OpposingAccounts', ], 'amount' => [ 'mappable' => false, diff --git a/resources/lang/en_US/csv.php b/resources/lang/en_US/csv.php index c27e945dce..3f2003ff22 100644 --- a/resources/lang/en_US/csv.php +++ b/resources/lang/en_US/csv.php @@ -32,6 +32,7 @@ return [ 'column' => 'Column', 'no_example_data' => 'No example data available', 'store_column_roles' => 'Continue import', + 'do_not_map' => '(do not map)', 'column__ignore' => '(ignore this column)', 'column_account-iban' => 'Asset account (IBAN)', diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig new file mode 100644 index 0000000000..bc6a5cea31 --- /dev/null +++ b/resources/views/import/csv/map.twig @@ -0,0 +1,84 @@ +{% extends "./layout/default.twig" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }} +{% endblock %} + +{% block content %} + + ++ + +{% endblock %}++++++ +++{{ trans('csv.map_title') }}
++++ {{ trans('csv.map_text') }} +
+