diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index c3c1763bc9..589d7a1a5a 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -33,6 +33,27 @@ class ImportController extends Controller View::share('title', trans('firefly.import_data')); } + /** + * This is the last step before the import starts. + * + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function complete(ImportJob $job) + { + Log::debug('Now in complete()', ['job' => $job->key]); + if (!$this->jobInCorrectStep($job, 'complete')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + $subTitle = trans('firefy.import_complete'); + $subTitleIcon = 'fa-star'; + + return view('import.complete', compact('job', 'subTitle', 'subTitleIcon')); + } + /** * This is step 3. * This is the first step in configuring the job. It can only be executed @@ -45,7 +66,10 @@ class ImportController extends Controller */ public function configure(ImportJob $job) { + Log::debug('Now at start of configure()'); if (!$this->jobInCorrectStep($job, 'configure')) { + Log::debug('Job is not in correct state for configure()', ['status' => $job->status]); + return $this->redirectToCorrectStep($job); } @@ -59,6 +83,35 @@ class ImportController extends Controller return view('import.' . $job->file_type . '.configure', compact('data', 'job', 'subTitle', 'subTitleIcon')); + } + + /** + * Generate a JSON file of the job's config and send it to the user. + * + * @param ImportJob $job + * + * @return mixed + */ + public function download(ImportJob $job) + { + Log::debug('Now in download()', ['job' => $job->key]); + $config = $job->configuration; + $config['column-roles-complete'] = false; + $config['column-mapping-complete'] = false; + $result = json_encode($config, JSON_PRETTY_PRINT); + $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\')); + + return response($result, 200) + ->header('Content-disposition', 'attachment; filename=' . $name) + ->header('Content-Type', 'application/json') + ->header('Content-Description', 'File Transfer') + ->header('Connection', 'Keep-Alive') + ->header('Expires', '0') + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', strlen($result)); + + } /** @@ -68,6 +121,7 @@ class ImportController extends Controller */ public function index() { + Log::debug('Now at index'); $subTitle = trans('firefly.import_data_index'); $subTitleIcon = 'fa-home'; $importFileTypes = []; @@ -91,9 +145,11 @@ class ImportController extends Controller */ public function postConfigure(Request $request, ImportJob $job) { + Log::debug('Now in postConfigure()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'process')) { return $this->redirectToCorrectStep($job); } + Log::debug('Continue postConfigure()', ['job' => $job->key]); // actual code $importer = $this->makeImporter($job); @@ -122,6 +178,7 @@ class ImportController extends Controller */ public function postSettings(Request $request, ImportJob $job) { + Log::debug('Now in postSettings()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'store-settings')) { return $this->redirectToCorrectStep($job); } @@ -143,29 +200,32 @@ class ImportController extends Controller */ public function settings(ImportJob $job) { + Log::debug('Now in settings()', ['job' => $job->key]); if (!$this->jobInCorrectStep($job, 'settings')) { + Log::debug('Job should not be in settings()'); + return $this->redirectToCorrectStep($job); } + Log::debug('Continue in settings()'); $importer = $this->makeImporter($job); $subTitle = trans('firefy.settings_for_import'); $subTitleIcon = 'fa-wrench'; // now show settings screen to user. if ($importer->requireUserSettings()) { + Log::debug('Job requires user config.'); $data = $importer->getDataForSettings(); $view = $importer->getViewForSettings(); return view($view, compact('data', 'job', 'subTitle', 'subTitleIcon')); } + Log::debug('Job does NOT require user config.'); + + $job->status = 'settings_complete'; + $job->save(); // if no more settings, save job and continue to process thing. - - - echo 'now in settings (done)'; - exit; - - // actual code - + return redirect(route('import.complete', [$job->key])); // ask the importer for the requested action. // for example pick columns or map data. @@ -182,6 +242,7 @@ class ImportController extends Controller */ public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository) { + Log::debug('Now in upload()'); // create import job: $type = $request->get('import_file_type'); $job = $repository->create($type); @@ -232,6 +293,7 @@ class ImportController extends Controller */ private function jobInCorrectStep(ImportJob $job, string $method): bool { + Log::debug('Now in jobInCorrectStep()', ['job' => $job->key, 'method' => $method]); switch ($method) { case 'configure': case 'process': @@ -241,6 +303,9 @@ class ImportController extends Controller case 'store-settings': return $job->status === 'import_configuration_saved'; break; + case 'complete': + return $job->status === 'settings_complete'; + break; } return false; @@ -272,13 +337,23 @@ class ImportController extends Controller */ private function redirectToCorrectStep(ImportJob $job) { + Log::debug('Now in redirectToCorrectStep()', ['job' => $job->key]); switch ($job->status) { case 'import_status_never_started': + Log::debug('Will redirect to configure()'); + return redirect(route('import.configure', [$job->key])); break; case 'import_configuration_saved': + Log::debug('Will redirect to settings()'); + return redirect(route('import.settings', [$job->key])); break; + case 'settings_complete': + Log::debug('Will redirect to complete()'); + + return redirect(route('import.complete', [$job->key])); + break; } throw new FireflyException('Cannot redirect for job state ' . $job->status); diff --git a/app/Http/routes.php b/app/Http/routes.php index 662fafce7e..b2496d42cd 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -229,6 +229,8 @@ Route::group( Route::post('/import/configure/{importJob}', ['uses' => 'ImportController@postConfigure', 'as' => 'import.process_configuration']); Route::get('/import/settings/{importJob}', ['uses' => 'ImportController@settings', 'as' => 'import.settings']); Route::post('/import/settings/{importJob}', ['uses' => 'ImportController@postSettings', 'as' => 'import.postSettings']); + Route::get('/import/complete/{importJob}', ['uses' => 'ImportController@complete', 'as' => 'import.complete']); + Route::get('/import/download/{importJob}', ['uses' => 'ImportController@download', 'as' => 'import.download']); /** diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index 3b1f2fc704..cccfcdde74 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -158,13 +158,16 @@ class CsvImporter implements ImporterInterface */ public function requireUserSettings(): bool { - // does the job have both a 'map' array and a 'columns' array. - $config = $this->job->configuration; - if (isset($config['map']) && isset($config['columns'])) { - return false; - } + Log::debug('doColumnMapping is ' . ($this->doColumnMapping() ? 'true' : 'false')); + Log::debug('doColumnRoles is ' . ($this->doColumnRoles() ? 'true' : 'false')); + if ($this->doColumnMapping() || $this->doColumnRoles()) { + Log::debug('Return true'); - return true; + return true; + } + Log::debug('Return false'); + + return false; } /** @@ -216,25 +219,52 @@ class CsvImporter implements ImporterInterface */ public function storeSettings(Request $request) { - $config = $this->job->configuration; - $count = $config['column-count']; - $all = $request->all(); - $roleSet = 0; - for ($i = 0; $i < $count; $i++) { - $selectedRole = $all['role'][$i] ?? '_ignore'; - $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; - if ($selectedRole == '_ignore' && $doMapping === true) { - $doMapping = false; // cannot map ignored columns. + $config = $this->job->configuration; + $all = $request->all(); + if ($request->get('settings') == 'roles') { + $count = $config['column-count']; + + $roleSet = 0; // how many roles have been defined + $mapSet = 0; // how many columns must be mapped + for ($i = 0; $i < $count; $i++) { + $selectedRole = $all['role'][$i] ?? '_ignore'; + $doMapping = isset($all['map'][$i]) && $all['map'][$i] == '1' ? true : false; + if ($selectedRole == '_ignore' && $doMapping === true) { + $doMapping = false; // cannot map ignored columns. + } + if ($selectedRole != '_ignore') { + $roleSet++; + } + if ($doMapping === true) { + $mapSet++; + } + $config['column-roles'][$i] = $selectedRole; + $config['column-do-mapping'][$i] = $doMapping; } - if ($selectedRole != '_ignore') { - $roleSet++; + if ($roleSet > 0) { + $config['column-roles-complete'] = true; + $this->job->configuration = $config; + $this->job->save(); + } + if ($mapSet === 0) { + // skip setting of map: + $config['column-mapping-complete'] = true; } - $config['column-roles'][$i] = $selectedRole; - $config['column-do-mapping'][$i] = $doMapping; } - if ($roleSet > 0) { - $config['column-roles-complete'] = true; - $this->job->configuration = $config; + if ($request->get('settings') == 'map') { + foreach ($all['mapping'] as $index => $data) { + $config['column-mapping-config'][$index] = []; + foreach ($data as $value => $mapId) { + $mapId = intval($mapId); + if ($mapId !== 0) { + $config['column-mapping-config'][$index][$value] = intval($mapId); + } + } + } + + // set thing to be completed. + $config['column-mapping-complete'] = true; + $this->job->configuration = $config; $this->job->save(); } } @@ -260,8 +290,9 @@ class CsvImporter implements ImporterInterface */ private function getDataForColumnMapping(): array { - $config = $this->job->configuration; - $data = []; + $config = $this->job->configuration; + $data = []; + $indexes = []; foreach ($config['column-do-mapping'] as $index => $mustBeMapped) { if ($mustBeMapped) { @@ -271,9 +302,11 @@ class CsvImporter implements ImporterInterface $mapperName = '\FireflyIII\Import\Mapper\\' . config('csv.import_roles.' . $column . '.mapper'); /** @var MapperInterface $mapper */ $mapper = new $mapperName; + $indexes[] = $index; $data[$index] = [ 'name' => $column, 'mapper' => $mapperName, + 'index' => $index, 'options' => $mapper->getMap(), 'values' => [], ]; @@ -281,15 +314,25 @@ class CsvImporter implements ImporterInterface } } + // in order to actually map we also need all possible values from the CSV file. + $content = $this->job->uploadFileContents(); + $reader = Reader::createFromString($content); + $results = $reader->fetch(); - echo '
';
-        var_dump($data);
-        var_dump($config);
-
-
-        exit;
-
+        foreach ($results as $row) {
+            //do something here
+            foreach ($indexes as $index) {
+                $value = $row[$index];
+                if (strlen($value) > 0) {
+                    $data[$index]['values'][] = $row[$index];
+                }
+            }
+        }
+        foreach ($data as $index => $entry) {
+            $data[$index]['values'] = array_unique($data[$index]['values']);
+        }
 
+        return $data;
     }
 
     /**
diff --git a/app/Import/Mapper/Bills.php b/app/Import/Mapper/Bills.php
new file mode 100644
index 0000000000..0dcdc06f10
--- /dev/null
+++ b/app/Import/Mapper/Bills.php
@@ -0,0 +1,46 @@
+getBills();
+        $list       = [];
+
+        /** @var Bill $bill */
+        foreach ($result as $bill) {
+            $list[$bill->id] = $bill->name . ' [' . $bill->match . ']';
+        }
+        asort($list);
+
+        $list = [0 => trans('csv.do_not_map')] + $list;
+
+        return $list;
+
+    }
+}
\ No newline at end of file
diff --git a/app/Import/Mapper/Budgets.php b/app/Import/Mapper/Budgets.php
new file mode 100644
index 0000000000..a1b9c2edd8
--- /dev/null
+++ b/app/Import/Mapper/Budgets.php
@@ -0,0 +1,47 @@
+getBudgets();
+        $list       = [];
+
+        /** @var Budget $budget */
+        foreach ($result as $budget) {
+            $list[$budget->id] = $budget->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/Categories.php b/app/Import/Mapper/Categories.php
new file mode 100644
index 0000000000..0f85392c8f
--- /dev/null
+++ b/app/Import/Mapper/Categories.php
@@ -0,0 +1,47 @@
+getCategories();
+        $list       = [];
+
+        /** @var Category $category */
+        foreach ($result as $category) {
+            $list[$category->id] = $category->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/Tags.php b/app/Import/Mapper/Tags.php
new file mode 100644
index 0000000000..4f608f2146
--- /dev/null
+++ b/app/Import/Mapper/Tags.php
@@ -0,0 +1,46 @@
+get();
+        $list       = [];
+
+        /** @var Tag $tag */
+        foreach ($result as $tag) {
+            $list[$tag->id] = $tag->tag;
+        }
+        asort($list);
+
+        $list = [0 => trans('csv.do_not_map')] + $list;
+
+        return $list;
+
+    }
+}
\ No newline at end of file
diff --git a/config/csv.php b/config/csv.php
index 6e0a7edd6f..3481ca2769 100644
--- a/config/csv.php
+++ b/config/csv.php
@@ -15,11 +15,12 @@ return [
     /*
      * Configuration for possible column roles.
      */
-    'import_roles' => [
+    'import_roles'     => [
         '_ignore'           => [
             'mappable'  => false,
-            'converter' => 'Ignore',
             'field'     => 'ignored',
+            'converter' => 'Ignore',
+
         ],
         'bill-id'           => [
             'mappable'  => false,
@@ -29,33 +30,33 @@ return [
         ],
         'bill-name'         => [
             'mappable'  => true,
-            'converter' => 'BillName',
             'field'     => 'bill',
+            'converter' => 'BillName',
             'mapper'    => 'Bills',
         ],
         'currency-id'       => [
             'mappable'  => true,
-            'converter' => 'CurrencyId',
             'field'     => 'currency',
-            'mapper'    => 'TransactionCurrencies'
+            'converter' => 'CurrencyId',
+            'mapper'    => 'TransactionCurrencies',
         ],
         'currency-name'     => [
             'mappable'  => true,
             'converter' => 'CurrencyName',
             'field'     => 'currency',
-            'mapper'    => 'TransactionCurrencies'
+            'mapper'    => 'TransactionCurrencies',
         ],
         'currency-code'     => [
             'mappable'  => true,
             'converter' => 'CurrencyCode',
             'field'     => 'currency',
-            'mapper'    => 'TransactionCurrencies'
+            'mapper'    => 'TransactionCurrencies',
         ],
         'currency-symbol'   => [
             'mappable'  => true,
             'converter' => 'CurrencySymbol',
             'field'     => 'currency',
-            'mapper'    => 'TransactionCurrencies'
+            'mapper'    => 'TransactionCurrencies',
         ],
         'description'       => [
             'mappable'  => false,
@@ -89,7 +90,7 @@ return [
             'converter' => 'RabobankDebetCredit',
             'field'     => 'amount-modifier',
         ],
-        'ing-debet-credit' => [
+        'ing-debet-credit'  => [
             'mappable'  => false,
             'converter' => 'INGDebetCredit',
             'field'     => 'amount-modifier',
@@ -120,27 +121,28 @@ return [
         ],
         'account-id'        => [
             'mappable'  => true,
-            'mapper'    => 'AssetAccountId',
             'field'     => 'asset-account-id',
-            'converter' => 'AssetAccounts'
+            'converter' => 'AssetAccountId',
+            'mapper'    => 'AssetAccounts',
         ],
         'account-name'      => [
             'mappable'  => true,
-            'mapper'    => 'AssetAccountName',
             'field'     => 'asset-account-name',
-            'converter' => 'AssetAccounts'
+            'converter' => 'AssetAccountName',
+            'mapper'    => 'AssetAccounts',
         ],
         'account-iban'      => [
             'mappable'  => true,
-            'converter' => 'AssetAccountIban',
             'field'     => 'asset-account-iban',
-            'mapper'    => 'AssetAccounts'
+            'converter' => 'AssetAccountIban',
+            'mapper'    => 'AssetAccounts',
+
         ],
-        'account-number'      => [
+        'account-number'    => [
             'mappable'  => true,
-            'converter' => 'AssetAccountNumber',
             'field'     => 'asset-account-number',
-            'mapper'    => 'AssetAccounts'
+            'converter' => 'AssetAccountNumber',
+            'mapper'    => 'AssetAccounts',
         ],
         'opposing-id'       => [
             'mappable'  => true,
@@ -160,7 +162,7 @@ return [
             'converter' => 'OpposingAccountIban',
             'mapper'    => 'OpposingAccounts',
         ],
-        'opposing-number'     => [
+        'opposing-number'   => [
             'mappable'  => true,
             'field'     => 'opposing-account-number',
             'converter' => 'OpposingAccountNumber',
@@ -171,11 +173,6 @@ return [
             'converter' => 'Amount',
             'field'     => 'amount',
         ],
-//        'amount-comma-separated' => [
-//            'mappable'  => false,
-//            'converter' => 'AmountComma',
-//            'field'     => 'amount',
-//        ],
         'sepa-ct-id'        => [
             'mappable'  => false,
             'converter' => 'Description',
@@ -194,12 +191,6 @@ return [
     ],
 
 
-
-
-
-
-
-
     /*
 
 
diff --git a/resources/views/import/complete.twig b/resources/views/import/complete.twig
new file mode 100644
index 0000000000..0a7aaaff7b
--- /dev/null
+++ b/resources/views/import/complete.twig
@@ -0,0 +1,33 @@
+{% extends "./layout/default.twig" %}
+
+{% block breadcrumbs %}
+    {{ Breadcrumbs.renderIfExists }}
+{% endblock %}
+{% block content %}
+    
+
+
+
+

{{ 'import_complete'|_ }}

+
+
+

+ {{ 'import_complete_text'|_ }} +

+ +
+
+
+
+{% endblock %} +{% block scripts %} +{% endblock %} +{% block styles %} +{% endblock %} diff --git a/resources/views/import/csv/map.twig b/resources/views/import/csv/map.twig index bc6a5cea31..7119e382aa 100644 --- a/resources/views/import/csv/map.twig +++ b/resources/views/import/csv/map.twig @@ -24,6 +24,44 @@
+ + + {% for field in data %} +
+
+
+
+

{{ field.name }}

+
+
+ + + + + + + + + {% for option in field.values %} + + + + + {% endfor %} + +
{{ trans('csv.field_value') }}{{ trans('csv.field_mapped_to') }}
+ {{ option }} + + {{ Form.select('mapping['~field.index~']['~option~']', + field.options, + job.configuration['column-mapping-config'][field.index][option], {class: 'form-control'}) }} +
+
+
+
+
+ {% endfor %} + {# diff --git a/resources/views/import/csv/roles.twig b/resources/views/import/csv/roles.twig index c1f317cbbf..e73f172e43 100644 --- a/resources/views/import/csv/roles.twig +++ b/resources/views/import/csv/roles.twig @@ -54,10 +54,16 @@ {% endif %} - {{ Form.select(('role['~loop.index0~']'), data.available_roles,data.set_roles[index],{class: 'form-control'}) }} + {{ Form.select(('role['~loop.index0~']'), + data.available_roles, + job.configuration['column-roles'][loop.index0], + {class: 'form-control'}) }} - {{ Form.checkbox(('map['~loop.index0~']'),1,map[index]) }} + {{ Form.checkbox(('map['~loop.index0~']'),1, + job.configuration['column-do-mapping'][loop.index0] + + ) }} diff --git a/resources/views/import/index.twig b/resources/views/import/index.twig index b69d953026..b378cfe026 100644 --- a/resources/views/import/index.twig +++ b/resources/views/import/index.twig @@ -6,7 +6,6 @@ {% block content %}
-

{{ 'import'|_ }}