diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php index 45524c1714..4bd6173903 100644 --- a/app/Http/Controllers/ImportController.php +++ b/app/Http/Controllers/ImportController.php @@ -3,11 +3,13 @@ namespace FireflyIII\Http\Controllers; use Crypt; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Requests; use FireflyIII\Http\Requests\ImportUploadRequest; use FireflyIII\Import\Importer\ImporterInterface; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use Illuminate\Http\Request; use SplFileObject; use Storage; use View; @@ -30,26 +32,34 @@ class ImportController extends Controller } /** + * This is step 3. + * This is the first step in configuring the job. It can only be executed + * when the job is set to "import_status_never_started". + * * @param ImportJob $job * * @return View + * @throws FireflyException */ public function configure(ImportJob $job) { - // create proper importer (depends on job) - $type = $job->file_type; - /** @var ImporterInterface $importer */ - $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); - $importer->setJob($job); + if (!$this->jobInCorrectStep($job, 'configure')) { + return $this->redirectToCorrectStep($job); + } + + // actual code + $importer = $this->makeImporter($job); $importer->configure(); $data = $importer->getConfigurationData(); - return view('import.' . $type . '.configure', compact('data', 'job')); + return view('import.' . $job->file_type . '.configure', compact('data', 'job')); } /** + * This is step 1. Upload a file. + * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function index() @@ -67,6 +77,68 @@ class ImportController extends Controller } /** + * Step 4. Save the configuration. + * + * @param Request $request + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function process(Request $request, ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'process')) { + return $this->redirectToCorrectStep($job); + } + + // actual code + $importer = $this->makeImporter($job); + $data = $request->all(); + $importer->saveImportConfiguration($data); + + // update job: + $job->status = 'import_configuration_saved'; + $job->save(); + + // return redirect to settings. + // this could loop until the user is done. + return redirect(route('import.settings', $job->key)); + } + + /** + * Step 5. Depending on the importer, this will show the user settings to + * fill in. + * + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + public function settings(ImportJob $job) + { + if (!$this->jobInCorrectStep($job, 'settings')) { + return $this->redirectToCorrectStep($job); + } + $importer = $this->makeImporter($job); + + // now + + + + echo 'now in settings'; + exit; + + // actual code + + + // ask the importer for the requested action. + // for example pick columns or map data. + // depends of course on the data in the job. + } + + /** + * This is step 2. It creates an Import Job. Stores the import. + * * @param ImportUploadRequest $request * @param ImportJobRepositoryInterface $repository * @@ -88,4 +160,64 @@ class ImportController extends Controller return redirect(route('import.configure', [$job->key])); } + + /** + * @param ImportJob $job + * @param string $method + * + * @return bool + */ + private function jobInCorrectStep(ImportJob $job, string $method): bool + { + switch ($method) { + case 'configure': + case 'process': + return $job->status === 'import_status_never_started'; + break; + case 'settings': + return $job->status === 'import_configuration_saved'; + break; + } + + return false; + + } + + /** + * @param ImportJob $job + * + * @return ImporterInterface + */ + private function makeImporter(ImportJob $job): ImporterInterface + { + // create proper importer (depends on job) + $type = $job->file_type; + /** @var ImporterInterface $importer */ + $importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer'); + $importer->setJob($job); + + return $importer; + + } + + /** + * @param ImportJob $job + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws FireflyException + */ + private function redirectToCorrectStep(ImportJob $job) + { + switch ($job->status) { + case 'import_status_never_started': + return redirect(route('import.configure', [$job->key])); + break; + case 'import_configuration_saved': + return redirect(route('import.settings', [$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 27e95d11ff..29f99c3f4f 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -223,9 +223,12 @@ Route::group( /** * IMPORT CONTROLLER */ - Route::get('/import', ['uses' => 'ImportController@index','as' => 'import.index']); - Route::post('/import/upload', ['uses' => 'ImportController@upload','as' => 'import.upload']); - Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure','as' => 'import.configure']); + Route::get('/import', ['uses' => 'ImportController@index', 'as' => 'import.index']); + Route::post('/import/upload', ['uses' => 'ImportController@upload', 'as' => 'import.upload']); + Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure', 'as' => 'import.configure']); + Route::post('/import/process/{importJob}', ['uses' => 'ImportController@process', 'as' => 'import.process_configuration']); + Route::get('/import/settings/{importJob}', ['uses' => 'ImportController@settings', 'as' => 'import.settings']); + /** * Help Controller diff --git a/app/Import/Importer/CsvImporter.php b/app/Import/Importer/CsvImporter.php index abd18cb913..82b83e1dc8 100644 --- a/app/Import/Importer/CsvImporter.php +++ b/app/Import/Importer/CsvImporter.php @@ -13,6 +13,7 @@ namespace FireflyIII\Import\Importer; use ExpandedForm; +use FireflyIII\Crud\Account\AccountCrud; use FireflyIII\Import\Role\Map; use FireflyIII\Models\AccountType; use FireflyIII\Models\ImportJob; @@ -50,12 +51,23 @@ class CsvImporter implements ImporterInterface 'tab' => trans('form.csv_tab'), ]; + $specifics = []; + + // collect specifics. + foreach (config('firefly.csv_import_specifics') as $name => $className) { + $specifics[$name] = [ + 'name' => $className::getName(), + 'description' => $className::getDescription(), + ]; + } + $data = [ 'accounts' => ExpandedForm::makeSelectList($accounts), 'specifix' => [], 'delimiters' => $delimiters, 'upload_path' => storage_path('upload'), 'is_upload_possible' => is_writable(storage_path('upload')), + 'specifics' => $specifics, ]; return $data; @@ -73,6 +85,40 @@ class CsvImporter implements ImporterInterface exit; } + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data): bool + { + /** @var AccountCrud $repository */ + $repository = app(AccountCrud::class); + $account = $repository->find(intval($data['csv_import_account'])); + $configuration = [ + 'date_format' => $data['date_format'], + 'csv_delimiter' => $data['csv_delimiter'], + 'csv_import_account' => 0, + 'specifics' => [], + ]; + + if (!is_null($account->id)) { + $configuration['csv_import_account'] = $account->id; + } + // loop specifics. + if (is_array($data['specifics'])) { + foreach ($data['specifics'] as $name => $enabled) { + $configuration['specifics'][] = $name; + } + } + $this->job->configuration = $configuration; + $this->job->save(); + + return true; + + + } + /** * @param ImportJob $job */ diff --git a/app/Import/Importer/ImporterInterface.php b/app/Import/Importer/ImporterInterface.php index 4df74f85ba..8116f252a7 100644 --- a/app/Import/Importer/ImporterInterface.php +++ b/app/Import/Importer/ImporterInterface.php @@ -36,6 +36,13 @@ interface ImporterInterface */ public function getConfigurationData(): array; + /** + * @param array $data + * + * @return bool + */ + public function saveImportConfiguration(array $data): bool; + /** * Returns a Map thing used to allow the user to * define roles for each entry. diff --git a/app/Import/Specifics/AbnAmroDescription.php b/app/Import/Specifics/AbnAmroDescription.php new file mode 100644 index 0000000000..a3d3f058fa --- /dev/null +++ b/app/Import/Specifics/AbnAmroDescription.php @@ -0,0 +1,37 @@ +belongsTo('FireflyIII\User'); } + + + /** + * @param $value + * + * @return mixed + */ + public function getConfigurationAttribute($value) + { + if (strlen($value) == 0) { + return []; + } + + return json_decode($value); + } + + /** + * @param $value + */ + public function setConfigurationAttribute($value) + { + $this->attributes['configuration'] = json_encode($value); + } } diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php index 6b96e32322..355d4a135c 100644 --- a/app/Models/PiggyBank.php +++ b/app/Models/PiggyBank.php @@ -51,6 +51,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereDeletedAt($value) * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereEncrypted($value) * @mixin \Eloquent + * @property boolean $active + * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\PiggyBank whereActive($value) */ class PiggyBank extends Model { diff --git a/app/User.php b/app/User.php index fb5305f80f..0c51395c41 100644 --- a/app/User.php +++ b/app/User.php @@ -55,6 +55,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlockedCode($value) * @mixin \Eloquent * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\ImportJob[] $importjobs + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks + * @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Transaction[] $transactions */ class User extends Authenticatable { diff --git a/config/firefly.php b/config/firefly.php index e19fd5d75b..0e93925850 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -11,11 +11,15 @@ return [ 'resend_confirmation' => 3600, 'confirmation_age' => 14400, // four hours - 'export_formats' => [ + 'export_formats' => [ 'csv' => 'FireflyIII\Export\Exporter\CsvExporter', // mt940 FireflyIII Export Exporter MtExporter ], - 'import_formats' => [ + 'csv_import_specifics' => [ + 'RabobankDescription' => 'FireflyIII\Import\Specifics\RabobankDescription', + 'AbnAmroDescription' => 'FireflyIII\Import\Specifics\AbnAmroDescription', + ], + 'import_formats' => [ 'csv' => 'FireflyIII\Import\Importer\CsvImporter', // mt940 FireflyIII Import Importer MtImporter ], diff --git a/database/migrations/2016_05_23_173524_changes_for_v391.php b/database/migrations/2016_05_23_173524_changes_for_v391.php deleted file mode 100644 index b8efd50b4d..0000000000 --- a/database/migrations/2016_05_23_173524_changes_for_v391.php +++ /dev/null @@ -1,44 +0,0 @@ -increments('id'); - $table->timestamps(); - $table->integer('user_id')->unsigned(); - $table->string('key', 12)->unique(); - $table->string('file_type', 12); - $table->string('status', 45); - - // connect rule groups to users - $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); - - } - ); - } -} diff --git a/database/migrations/2016_06_16_000002_create_main_tables.php b/database/migrations/2016_06_16_000002_create_main_tables.php index 720c2c220d..b46f633232 100644 --- a/database/migrations/2016_06_16_000002_create_main_tables.php +++ b/database/migrations/2016_06_16_000002_create_main_tables.php @@ -13,13 +13,13 @@ class CreateMainTables extends Migration */ public function down() { - // Schema::drop('account_meta'); Schema::drop('piggy_bank_repetitions'); Schema::drop('attachments'); Schema::drop('limit_repetitions'); Schema::drop('budget_limits'); Schema::drop('export_jobs'); + Schema::drop('import_jobs'); Schema::drop('preferences'); Schema::drop('role_user'); Schema::drop('rule_actions'); @@ -109,6 +109,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createAttachmentsTable() { @@ -139,6 +142,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createBillsTable() { if (!Schema::hasTable('bills')) { @@ -167,6 +173,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createBudgetTables() { @@ -224,6 +233,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createCategoriesTable() { if (!Schema::hasTable('categories')) { @@ -244,6 +256,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createExportJobsTable() { if (!Schema::hasTable('export_jobs')) { @@ -254,16 +269,31 @@ class CreateMainTables extends Migration $table->integer('user_id', false, true); $table->string('key', 12); $table->string('status', 255); - - // link user id to users table $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); } ); } + if (!Schema::hasTable('import_jobs')) { + Schema::create( + 'import_jobs', function (Blueprint $table) { + $table->increments('id'); + $table->timestamps(); + $table->integer('user_id')->unsigned(); + $table->string('key', 12)->unique(); + $table->string('file_type', 12); + $table->string('status', 45); + $table->text('configuration'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + } + ); + } } + /** + * + */ private function createPiggyBanksTable() { if (!Schema::hasTable('piggy_banks')) { @@ -305,6 +335,9 @@ class CreateMainTables extends Migration } + /** + * + */ private function createPreferencesTable() { if (!Schema::hasTable('preferences')) { @@ -322,6 +355,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createRoleTable() { @@ -342,6 +378,9 @@ class CreateMainTables extends Migration } + /** + * + */ private function createRuleTables() { if (!Schema::hasTable('rule_groups')) { @@ -454,6 +493,9 @@ class CreateMainTables extends Migration } } + /** + * + */ private function createTransactionTables() { @@ -607,6 +649,4 @@ class CreateMainTables extends Migration ); } } - - } diff --git a/resources/views/import/csv/configure.twig b/resources/views/import/csv/configure.twig index d9645cd7d1..64adea984a 100644 --- a/resources/views/import/csv/configure.twig +++ b/resources/views/import/csv/configure.twig @@ -22,7 +22,7 @@ -