Browse Source

Clean up code.

pull/17/head
James Cole 4 years ago
parent
commit
8267f0e730
No known key found for this signature in database GPG Key ID: BDE6667570EADBD5
  1. 80
      app/Console/AutoImports.php
  2. 2
      app/Console/Commands/AutoImport.php
  3. 3
      app/Console/Commands/Import.php
  4. 2
      app/Exceptions/ImporterErrorException.php
  5. 2
      app/Http/Controllers/AutoUploadController.php
  6. 39
      app/Http/Controllers/DebugController.php
  7. 2
      app/Http/Controllers/Import/CSV/RoleController.php
  8. 232
      app/Http/Controllers/Import/ConfigurationController.php
  9. 2
      app/Http/Controllers/Import/ConversionController.php
  10. 155
      app/Http/Controllers/Import/MapController.php
  11. 6
      app/Http/Controllers/Import/Nordigen/LinkController.php
  12. 1
      app/Http/Controllers/Import/Nordigen/SelectionController.php
  13. 14
      app/Http/Controllers/Import/SubmitController.php
  14. 3
      app/Http/Controllers/ServiceController.php
  15. 6
      app/Http/Controllers/TokenController.php
  16. 264
      app/Http/Middleware/IsReadyForStep.php
  17. 89
      app/Services/CSV/Configuration/Configuration.php
  18. 2
      app/Services/CSV/Conversion/RoutineManager.php
  19. 4
      app/Services/CSV/Conversion/Task/Accounts.php
  20. 6
      app/Services/CSV/File/FileReader.php
  21. 1
      app/Services/CSV/Mapper/MapperService.php
  22. 2
      app/Services/CSV/Roles/RoleService.php
  23. 2
      app/Services/CSV/Specifics/SpecificService.php
  24. 1
      app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php
  25. 6
      app/Services/Nordigen/Conversion/RoutineManager.php
  26. 152
      app/Services/Nordigen/Model/Account.php
  27. 28
      app/Services/Nordigen/Model/Balance.php
  28. 129
      app/Services/Nordigen/Model/Transaction.php
  29. 4
      app/Services/Nordigen/Request/ListBanksRequest.php
  30. 2
      app/Services/Nordigen/Request/PostNewRequisitionRequest.php
  31. 82
      app/Services/Nordigen/Request/Request.php
  32. 6
      app/Services/Nordigen/Response/GetTransactionsResponse.php
  33. 1
      app/Services/Nordigen/Response/ListAccountsResponse.php
  34. 25
      app/Services/Nordigen/Response/ListBanksResponse.php
  35. 78
      app/Services/Nordigen/TokenManager.php
  36. 46
      app/Services/Session/Constants.php
  37. 16
      app/Services/Shared/Conversion/GeneratesIdentifier.php
  38. 84
      app/Services/Shared/Import/Routine/ApiSubmitter.php
  39. 14
      app/Services/Shared/Import/Routine/RoutineManager.php
  40. 4
      app/Services/Spectre/AuthenticationValidator.php
  41. 2
      app/Services/Spectre/Conversion/Routine/FilterTransactions.php
  42. 4
      app/Services/Spectre/Model/Account.php
  43. 26
      app/Services/Spectre/Model/Connection.php
  44. 2
      app/Services/Spectre/Model/Customer.php
  45. 34
      app/Services/Spectre/Model/Transaction.php
  46. 6
      app/Services/Spectre/Model/TransactionExtra.php
  47. 2
      app/Services/Spectre/Request/GetAccountsRequest.php
  48. 2
      app/Services/Spectre/Request/GetTransactionsRequest.php
  49. 2
      app/Services/Spectre/Request/ListConnectionsRequest.php
  50. 2
      app/Services/Spectre/Request/ListCustomersRequest.php
  51. 2
      app/Services/Spectre/Request/PostConnectSessionsRequest.php
  52. 2
      app/Services/Spectre/Response/ListCustomersResponse.php
  53. 2
      app/Services/Spectre/Response/PostConnectSessionResponse.php
  54. 8
      config/app.php
  55. 10
      config/auth.php
  56. 14
      config/filesystems.php
  57. 42
      config/ide-helper.php
  58. 8
      config/nordigen.php
  59. 2
      config/transaction_types.php
  60. 26
      config/twigbridge.php
  61. 82
      resources/views/emails/import/finished.blade.php
  62. 9
      resources/views/import/002-authenticate/index.twig
  63. 7
      resources/views/import/003-upload/index.twig
  64. 34
      resources/views/import/004-configure/index.twig
  65. 6
      resources/views/import/005-roles/index.twig
  66. 3
      resources/views/import/006-mapping/index.twig
  67. 9
      resources/views/import/009-selection/index.twig
  68. 18
      resources/views/token/client_id.twig
  69. 9
      resources/views/token/index.twig
  70. 2
      routes/channels.php
  71. 4
      routes/web.php

80
app/Console/AutoImports.php

@ -87,22 +87,6 @@ trait AutoImports
return $return;
}
/**
* @param string $directory
* @param array $files
*
* @throws ImporterErrorException
*/
protected function importFiles(string $directory, array $files): void
{
/** @var string $file */
foreach ($files as $file) {
$this->importFile($directory, $file);
}
}
/**
* @param string $file
*
@ -156,6 +140,20 @@ trait AutoImports
return true;
}
/**
* @param string $directory
* @param array $files
*
* @throws ImporterErrorException
*/
protected function importFiles(string $directory, array $files): void
{
/** @var string $file */
foreach ($files as $file) {
$this->importFile($directory, $file);
}
}
/**
* @param string $file
* @param string $directory
@ -297,31 +295,6 @@ trait AutoImports
}
}
/**
*
*/
private function reportImport(): void
{
$list = [
'info' => $this->importMessages,
'warn' => $this->importWarnings,
'error' => $this->importErrors,
];
foreach ($list as $func => $set) {
/**
* @var int $index
* @var array $messages
*/
foreach ($set as $index => $messages) {
if (count($messages) > 0) {
foreach ($messages as $message) {
$this->$func(sprintf('Import index %d: %s', $index, $message));
}
}
}
}
}
/**
* @param Configuration $configuration
* @param array $transactions
@ -389,4 +362,29 @@ trait AutoImports
// TODO make event handler and send email message
}
/**
*
*/
private function reportImport(): void
{
$list = [
'info' => $this->importMessages,
'warn' => $this->importWarnings,
'error' => $this->importErrors,
];
foreach ($list as $func => $set) {
/**
* @var int $index
* @var array $messages
*/
foreach ($set as $index => $messages) {
if (count($messages) > 0) {
foreach ($messages as $message) {
$this->$func(sprintf('Import index %d: %s', $index, $message));
}
}
}
}
}
}

2
app/Console/Commands/AutoImport.php

@ -65,7 +65,7 @@ class AutoImport extends Command
return 1;
}
$argument = (string) ($this->argument('directory') ?? './');
$argument = (string) ($this->argument('directory') ?? './');
$directory = realpath($argument);
$this->line(sprintf('Going to automatically import everything found in %s (%s)', $directory, $argument));

3
app/Console/Commands/Import.php

@ -27,6 +27,7 @@ namespace App\Console\Commands;
use App\Console\AutoImports;
use App\Console\HaveAccess;
use App\Console\VerifyJSON;
use App\Exceptions\ImporterErrorException;
use App\Services\CSV\Configuration\Configuration;
use Illuminate\Console\Command;
use Log;
@ -58,7 +59,7 @@ class Import extends Command
* Execute the console command.
*
* @return int
* @throws \App\Exceptions\ImporterErrorException
* @throws ImporterErrorException
*/
public function handle(): int
{

2
app/Exceptions/ImporterErrorException.php

@ -33,5 +33,5 @@ use Exception;
class ImporterErrorException extends Exception
{
public array $json;
public int $statusCode = 0;
public int $statusCode = 0;
}

2
app/Http/Controllers/AutoUploadController.php

@ -45,7 +45,7 @@ class AutoUploadController extends Controller
*/
public function index(AutoUploadRequest $request)
{
die('todo'. __METHOD__);
die('todo' . __METHOD__);
$access = $this->haveAccess();
if (false === $access) {
throw new ImporterErrorException('Could not connect to your local Firefly III instance.');

39
app/Http/Controllers/DebugController.php

@ -34,14 +34,10 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Routing\Route;
use Illuminate\View\View;
use Log;
use Monolog\Handler\RotatingFileHandler;
use Route as RouteFacade;
class DebugController extends Controller
@ -59,22 +55,22 @@ class DebugController extends Controller
$search = ['~', '#'];
$replace = ['\~', '# '];
$now = Carbon::now()->format('Y-m-d H:i:s e');
$phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI;
$userAgent = $request->header('user-agent');
$trustedProxies = config('importer.trusted_proxies');
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int)ini_get('error_reporting'));
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$cacheDriver = config('cache.default');
$bcscale = bcscale();
$tz = env('TZ');
$isDocker = env('IS_DOCKER', false);
$now = Carbon::now()->format('Y-m-d H:i:s e');
$phpVersion = str_replace($search, $replace, PHP_VERSION);
$phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI;
$userAgent = $request->header('user-agent');
$trustedProxies = config('importer.trusted_proxies');
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int) ini_get('error_reporting'));
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$cacheDriver = config('cache.default');
$bcscale = bcscale();
$tz = env('TZ');
$isDocker = env('IS_DOCKER', false);
// get latest log file:
$logger = Log::driver();
@ -122,6 +118,7 @@ class DebugController extends Controller
)
);
}
/**
* Some common combinations.
*
@ -141,6 +138,6 @@ class DebugController extends Controller
E_COMPILE_ERROR | E_RECOVERABLE_ERROR | E_ERROR | E_CORE_ERROR => 'E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR',
];
return $array[$value] ?? (string)$value;
return $array[$value] ?? (string) $value;
}
}

2
app/Http/Controllers/Import/CSV/RoleController.php

@ -82,7 +82,7 @@ class RoleController extends Controller
}
// get columns from file
$content = StorageService::getContent(session()->get(Constants::UPLOAD_CSV_FILE),$configuration->isConversion());
$content = StorageService::getContent(session()->get(Constants::UPLOAD_CSV_FILE), $configuration->isConversion());
$columns = RoleService::getColumns($content, $configuration);
$examples = RoleService::getExampleData($content, $configuration);

232
app/Http/Controllers/Import/ConfigurationController.php

@ -153,73 +153,6 @@ class ConfigurationController extends Controller
);
}
/**
* @param Request $request
*
* @return JsonResponse
*/
public function phpDate(Request $request): JsonResponse
{
Log::debug(sprintf('Method %s', __METHOD__));
$dateObj = new Date;
[$locale, $format] = $dateObj->splitLocaleFormat((string) $request->get('format'));
$date = Carbon::make('1984-09-17')->locale($locale);
return response()->json(['result' => $date->translatedFormat($format)]);
}
/**
* @param ConfigurationPostRequest $request
*
* @return RedirectResponse
* @throws ImporterErrorException
*/
public function postIndex(ConfigurationPostRequest $request): RedirectResponse
{
Log::debug(sprintf('Now running %s', __METHOD__));
// store config on drive.
$fromRequest = $request->getAll();
$configuration = Configuration::fromRequest($fromRequest);
$configuration->setFlow($request->cookie(Constants::FLOW_COOKIE));
// TODO are all fields actually in the config?
// loop accounts:
$accounts = [];
foreach (array_keys($fromRequest['do_import']) as $identifier) {
if (isset($fromRequest['accounts'][$identifier])) {
$accounts[$identifier] = (int) $fromRequest['accounts'][$identifier];
}
}
$configuration->setAccounts($accounts);
$configuration->updateDateRange();
$json = '{}';
try {
$json = json_encode($configuration->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
} catch (JsonException $e) {
Log::error($e->getMessage());
throw new ImporterErrorException($e->getMessage(), 0, $e);
}
StorageService::storeContent($json);
session()->put(Constants::CONFIGURATION, $configuration->toSessionArray());
Log::debug(sprintf('Configuration debug: Connection ID is "%s"', $configuration->getConnection()));
// set config as complete.
session()->put(Constants::CONFIG_COMPLETE_INDICATOR, true);
if ('nordigen' === $configuration->getFlow() || 'spectre' === $configuration->getFlow()) {
// at this point, nordigen is ready for data conversion.
session()->put(Constants::READY_FOR_CONVERSION, true);
}
// always redirect to roles, even if this isn't the step yet
// for nordigen and spectre, roles will be skipped right away.
return redirect(route('005-roles.index'));
}
/**
* List Nordigen accounts with account details, balances, and 2 transactions (if present)
* @return array
@ -264,56 +197,6 @@ class ConfigurationController extends Controller
return $return;
}
/**
* @param SpectreGetAccountsResponse $spectre
* @param array $firefly
*
* TODO should be a helper
*/
private function mergeSpectreAccountLists(SpectreGetAccountsResponse $spectre, array $firefly): array
{
$return = [];
Log::debug('Now creating Spectre account lists.');
/** @var SpectreAccount $spectreAccount */
foreach ($spectre as $spectreAccount) {
Log::debug(sprintf('Now working on Spectre account "%s": "%s"', $spectreAccount->name, $spectreAccount->id));
$iban = $spectreAccount->iban;
$currency = $spectreAccount->currencyCode;
$entry = [
'import_service' => $spectreAccount,
'firefly' => [],
];
// only iban?
$filteredByIban = $this->filterByIban($firefly, $iban);
if (1 === count($filteredByIban)) {
Log::debug(sprintf('This account (%s) has a single Firefly III counter part (#%d, "%s", same IBAN), so will use that one.', $iban, $filteredByIban[0]->id, $filteredByIban[0]->name));
$entry['firefly'] = $filteredByIban;
$return[] = $entry;
continue;
}
Log::debug(sprintf('Found %d accounts with the same IBAN ("%s")', count($filteredByIban), $iban));
// only currency?
$filteredByCurrency = $this->filterByCurrency($firefly, $currency);
if (count($filteredByCurrency) > 0) {
Log::debug(sprintf('This account (%s) has some Firefly III counter parts with the same currency so will only use those.', $currency));
$entry['firefly'] = $filteredByCurrency;
$return[] = $entry;
continue;
}
Log::debug('No special filtering on the Firefly III account list.');
$entry['firefly'] = $firefly;
$return[] = $entry;
}
return $return;
}
/**
* @param array $nordigen
* @param array $firefly
@ -406,5 +289,120 @@ class ConfigurationController extends Controller
return $result;
}
/**
* @param SpectreGetAccountsResponse $spectre
* @param array $firefly
*
* TODO should be a helper
*/
private function mergeSpectreAccountLists(SpectreGetAccountsResponse $spectre, array $firefly): array
{
$return = [];
Log::debug('Now creating Spectre account lists.');
/** @var SpectreAccount $spectreAccount */
foreach ($spectre as $spectreAccount) {
Log::debug(sprintf('Now working on Spectre account "%s": "%s"', $spectreAccount->name, $spectreAccount->id));
$iban = $spectreAccount->iban;
$currency = $spectreAccount->currencyCode;
$entry = [
'import_service' => $spectreAccount,
'firefly' => [],
];
// only iban?
$filteredByIban = $this->filterByIban($firefly, $iban);
if (1 === count($filteredByIban)) {
Log::debug(sprintf('This account (%s) has a single Firefly III counter part (#%d, "%s", same IBAN), so will use that one.', $iban, $filteredByIban[0]->id, $filteredByIban[0]->name));
$entry['firefly'] = $filteredByIban;
$return[] = $entry;
continue;
}
Log::debug(sprintf('Found %d accounts with the same IBAN ("%s")', count($filteredByIban), $iban));
// only currency?
$filteredByCurrency = $this->filterByCurrency($firefly, $currency);
if (count($filteredByCurrency) > 0) {
Log::debug(sprintf('This account (%s) has some Firefly III counter parts with the same currency so will only use those.', $currency));
$entry['firefly'] = $filteredByCurrency;
$return[] = $entry;
continue;
}
Log::debug('No special filtering on the Firefly III account list.');
$entry['firefly'] = $firefly;
$return[] = $entry;
}
return $return;
}
/**
* @param Request $request
*
* @return JsonResponse
*/
public function phpDate(Request $request): JsonResponse
{
Log::debug(sprintf('Method %s', __METHOD__));
$dateObj = new Date;
[$locale, $format] = $dateObj->splitLocaleFormat((string) $request->get('format'));
$date = Carbon::make('1984-09-17')->locale($locale);
return response()->json(['result' => $date->translatedFormat($format)]);
}
/**
* @param ConfigurationPostRequest $request
*
* @return RedirectResponse
* @throws ImporterErrorException
*/
public function postIndex(ConfigurationPostRequest $request): RedirectResponse
{
Log::debug(sprintf('Now running %s', __METHOD__));
// store config on drive.
$fromRequest = $request->getAll();
$configuration = Configuration::fromRequest($fromRequest);
$configuration->setFlow($request->cookie(Constants::FLOW_COOKIE));
// TODO are all fields actually in the config?
// loop accounts:
$accounts = [];
foreach (array_keys($fromRequest['do_import']) as $identifier) {
if (isset($fromRequest['accounts'][$identifier])) {
$accounts[$identifier] = (int) $fromRequest['accounts'][$identifier];
}
}
$configuration->setAccounts($accounts);
$configuration->updateDateRange();
$json = '{}';
try {
$json = json_encode($configuration->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
} catch (JsonException $e) {
Log::error($e->getMessage());
throw new ImporterErrorException($e->getMessage(), 0, $e);
}
StorageService::storeContent($json);
session()->put(Constants::CONFIGURATION, $configuration->toSessionArray());
Log::debug(sprintf('Configuration debug: Connection ID is "%s"', $configuration->getConnection()));
// set config as complete.
session()->put(Constants::CONFIG_COMPLETE_INDICATOR, true);
if ('nordigen' === $configuration->getFlow() || 'spectre' === $configuration->getFlow()) {
// at this point, nordigen is ready for data conversion.
session()->put(Constants::READY_FOR_CONVERSION, true);
}
// always redirect to roles, even if this isn't the step yet
// for nordigen and spectre, roles will be skipped right away.
return redirect(route('005-roles.index'));
}
}

2
app/Http/Controllers/Import/ConversionController.php

@ -31,11 +31,11 @@ use App\Http\Middleware\ConversionControllerMiddleware;
use App\Services\CSV\Configuration\Configuration;
use App\Services\CSV\Conversion\RoutineManager as CSVRoutineManager;
use App\Services\Nordigen\Conversion\RoutineManager as NordigenRoutineManager;
use App\Services\Spectre\Conversion\RoutineManager as SpectreRoutineManager;
use App\Services\Session\Constants;
use App\Services\Shared\Conversion\ConversionStatus;
use App\Services\Shared\Conversion\RoutineManagerInterface;
use App\Services\Shared\Conversion\RoutineStatusManager;
use App\Services\Spectre\Conversion\RoutineManager as SpectreRoutineManager;
use App\Services\Storage\StorageService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

155
app/Http/Controllers/Import/MapController.php

@ -43,6 +43,8 @@ use InvalidArgumentException;
use JsonException;
use League\Csv\Exception;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class MapController
@ -172,7 +174,7 @@ class MapController extends Controller
// index 1: category (TODO)
// index 0, category name:
$index = 1;
$index = 1;
$category = config('csv.import_roles.category-name') ?? null;
$category['role'] = 'category-name';
$category['values'] = $this->getCategories();
@ -202,6 +204,80 @@ class MapController extends Controller
return view('import.006-mapping.index', compact('mainTitle', 'subTitle', 'roles', 'data'));
}
/**
* @return array
* @throws FileNotFoundException
* @throws ImporterErrorException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*
* TODO move to helper or something
*/
private function getOpposingAccounts(): array
{
Log::debug(sprintf('Now in %s', __METHOD__));
$downloadIdentifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER);
$disk = Storage::disk(self::DISK_NAME);
$json = $disk->get(sprintf('%s.json', $downloadIdentifier));
try {
$array = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new ImporterErrorException(sprintf('Could not decode download: %s', $e->getMessage()), 0, $e);
}
$opposing = [];
$total = count($array);
/** @var array $transaction */
foreach ($array as $index => $transaction) {
Log::debug(sprintf('[%s/%s] Parsing transaction', ($index + 1), $total));
/** @var array $row */
foreach ($transaction['transactions'] as $row) {
$opposing[] = (string) array_key_exists('destination_name', $row) ? $row['destination_name'] : '';
$opposing[] = (string) array_key_exists('source_name', $row) ? $row['source_name'] : '';
}
}
$filtered = array_filter(
$opposing,
static function (string $value) {
return '' !== $value;
}
);
return array_unique($filtered);
}
private function getCategories(): array
{
Log::debug(sprintf('Now in %s', __METHOD__));
$downloadIdentifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER);
$disk = Storage::disk(self::DISK_NAME);
$json = $disk->get(sprintf('%s.json', $downloadIdentifier));
try {
$array = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new ImporterErrorException(sprintf('Could not decode download: %s', $e->getMessage()), 0, $e);
}
$categories = [];
$total = count($array);
/** @var array $transaction */
foreach ($array as $index => $transaction) {
Log::debug(sprintf('[%s/%s] Parsing transaction', ($index + 1), $total));
/** @var array $row */
foreach ($transaction['transactions'] as $row) {
$categories[] = (string) array_key_exists('category_name', $row) ? $row['category_name'] : '';
}
}
$filtered = array_filter(
$categories,
static function (?string $value) {
return '' !== (string) $value;
}
);
return array_unique($filtered);
}
/**
* @param Request $request
*
@ -277,7 +353,7 @@ class MapController extends Controller
// set map config as complete.
session()->put(Constants::MAPPING_COMPLETE_INDICATOR, true);
session()->put(Constants::READY_FOR_CONVERSION, true);
if('nordigen' === $configuration->getFlow() || 'spectre' === $configuration->getFlow()) {
if ('nordigen' === $configuration->getFlow() || 'spectre' === $configuration->getFlow()) {
// if nordigen, now ready for submission!
session()->put(Constants::READY_FOR_SUBMISSION, true);
}
@ -308,79 +384,4 @@ class MapController extends Controller
// original has been updated:
return $original;
}
/**
* @return array
* @throws FileNotFoundException
* @throws ImporterErrorException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*
* TODO move to helper or something
*/
private function getOpposingAccounts(): array
{
Log::debug(sprintf('Now in %s', __METHOD__));
$downloadIdentifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER);
$disk = Storage::disk(self::DISK_NAME);
$json = $disk->get(sprintf('%s.json', $downloadIdentifier));
try {
$array = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new ImporterErrorException(sprintf('Could not decode download: %s', $e->getMessage()), 0, $e);
}
$opposing = [];
$total = count($array);
/** @var array $transaction */
foreach ($array as $index => $transaction) {
Log::debug(sprintf('[%s/%s] Parsing transaction', ($index + 1), $total));
/** @var array $row */
foreach ($transaction['transactions'] as $row) {
$opposing[] = (string) array_key_exists('destination_name', $row) ? $row['destination_name'] : '';
$opposing[] = (string) array_key_exists('source_name', $row) ? $row['source_name'] : '';
}
}
$filtered = array_filter(
$opposing,
static function (string $value) {
return '' !== $value;
}
);
return array_unique($filtered);
}
private function getCategories(): array
{
Log::debug(sprintf('Now in %s', __METHOD__));
$downloadIdentifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER);
$disk = Storage::disk(self::DISK_NAME);
$json = $disk->get(sprintf('%s.json', $downloadIdentifier));
try {
$array = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new ImporterErrorException(sprintf('Could not decode download: %s', $e->getMessage()), 0, $e);
}
$categories = [];
$total = count($array);
/** @var array $transaction */
foreach ($array as $index => $transaction) {
Log::debug(sprintf('[%s/%s] Parsing transaction', ($index + 1), $total));
/** @var array $row */
foreach ($transaction['transactions'] as $row) {
$categories[] = (string) array_key_exists('category_name', $row) ? $row['category_name'] : '';
}
}
$filtered = array_filter(
$categories,
static function (?string $value) {
return '' !== (string)$value;
}
);
return array_unique($filtered);
}
}

6
app/Http/Controllers/Import/Nordigen/LinkController.php

@ -35,6 +35,8 @@ use App\Services\Nordigen\TokenManager;
use App\Services\Session\Constants;
use Illuminate\Http\Request;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Ramsey\Uuid\Uuid;
/**
@ -97,8 +99,8 @@ class LinkController extends Controller
/**
* @param Request $request
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function callback(Request $request)
{

1
app/Http/Controllers/Import/Nordigen/SelectionController.php

@ -22,6 +22,7 @@
*/
declare(strict_types=1);
namespace App\Http\Controllers\Import\Nordigen;
use App\Exceptions\ImporterErrorException;

14
app/Http/Controllers/Import/SubmitController.php

@ -39,6 +39,8 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use JsonException;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Storage;
/**
@ -54,14 +56,14 @@ class SubmitController extends Controller
public function __construct()
{
parent::__construct();
view()->share('pageTitle','Submit data to Firefly III');
view()->share('pageTitle', 'Submit data to Firefly III');
$this->middleware(SubmitControllerMiddleware::class);
}
/**
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws FileNotFoundException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function index()
{
@ -155,10 +157,10 @@ class SubmitController extends Controller
}
try {
$json = $disk->get($fileName);
$json = $disk->get($fileName);
$transactions = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
Log::debug(sprintf('Found %d transactions on the drive.', count($transactions)));
} catch (FileNotFoundException|JsonException $e) {
} catch (FileNotFoundException | JsonException $e) {
// TODO error in logs
SubmissionStatusManager::setSubmissionStatus(SubmissionStatus::SUBMISSION_ERRORED);
return response()->json($importJobStatus->toArray());

3
app/Http/Controllers/ServiceController.php

@ -26,8 +26,8 @@ namespace App\Http\Controllers;
use App\Http\Middleware\ServiceControllerMiddleware;
use App\Services\Enums\AuthenticationStatus;
use App\Services\Spectre\AuthenticationValidator as SpectreValidator;
use App\Services\Nordigen\AuthenticationValidator as NordigenValidator;
use App\Services\Spectre\AuthenticationValidator as SpectreValidator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@ -46,6 +46,7 @@ class ServiceController extends Controller
app('view')->share('pageTitle', 'Importing data...');
$this->middleware(ServiceControllerMiddleware::class);
}
/**
* @param Request $request
* @return JsonResponse

6
app/Http/Controllers/TokenController.php

@ -29,6 +29,7 @@ use GrumpyDictator\FFIIIApiSupport\Exceptions\ApiHttpException;
use GrumpyDictator\FFIIIApiSupport\Request\SystemInformationRequest;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\JsonResponse;
@ -40,6 +41,7 @@ use InvalidArgumentException;
use JsonException;
use Log;
use Str;
use Throwable;
/**
* Class TokenController
@ -244,8 +246,8 @@ class TokenController extends Controller
* @param Request $request
* @return Application|Factory|\Illuminate\Contracts\View\View|RedirectResponse|Redirector
* @throws ImporterErrorException
* @throws \GuzzleHttp\Exception\GuzzleException
* @throws \Throwable
* @throws GuzzleException
* @throws Throwable
*/
public function callback(Request $request)
{

264
app/Http/Middleware/IsReadyForStep.php

@ -88,80 +88,44 @@ trait IsReadyForStep
* @return bool
* @throws ImporterErrorException
*/
private function isReadyForBasicStep(): bool
{
Log::debug(sprintf('isReadyForBasicStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('isReadyForBasicStep: Cannot handle basic step "%s"', self::STEP));
case 'service-validation':
return true;
}
}
/**
* @return bool
* @throws ImporterErrorException
*/
private function isReadyForSpectreStep(): bool
private function isReadyForCSVStep(): bool
{
Log::debug(sprintf('isReadyForSpectreStep("%s")', self::STEP));
Log::debug(sprintf('isReadyForCSVStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('isReadyForSpectreStep: Cannot handle Spectre step "%s"', self::STEP));
throw new ImporterErrorException(sprintf('isReadyForCSVStep: Cannot handle CSV step "%s"', self::STEP));
case 'service-validation':
case 'authenticate':
return true;
case 'conversion':
if (session()->has(Constants::READY_FOR_SUBMISSION) && true === session()->get(Constants::READY_FOR_SUBMISSION)) {
Log::debug('Spectre: Return false, ready for submission.');
return false;
}
// if/else is in reverse!
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
return true;
}
// will probably never return false, but OK.
return false;
case 'upload-files':
if (session()->has(Constants::HAS_UPLOAD) && true === session()->get(Constants::HAS_UPLOAD)) {
return false;
}
return true;
case 'select-connection':
if (session()->has(Constants::HAS_UPLOAD) && true === session()->get(Constants::HAS_UPLOAD)) {
return true;
}
case 'authenticate':
// for CSV this is always false.
return false;
case 'define-roles':
if (session()->has(Constants::ROLES_COMPLETE_INDICATOR) && true === session()->get(Constants::ROLES_COMPLETE_INDICATOR)) {
return false;
}
return true;
case 'configuration':
if (session()->has(Constants::CONNECTION_SELECTED_INDICATOR) && true === session()->get(Constants::CONNECTION_SELECTED_INDICATOR)) {
return true;
if (session()->has(Constants::CONFIG_COMPLETE_INDICATOR) && true === session()->get(Constants::CONFIG_COMPLETE_INDICATOR)) {
return false;
}
return false;
case 'define-roles':
return false;
return true;
case 'map':
// mapping must be complete, or not ready for this step.
if (session()->has(Constants::MAPPING_COMPLETE_INDICATOR) && true === session()->get(Constants::MAPPING_COMPLETE_INDICATOR)) {
Log::debug('Spectre: Return false, not ready for step [1].');
return false;
}
// conversion complete?
if (session()->has(Constants::CONVERSION_COMPLETE_INDICATOR) && true === session()->get(Constants::CONVERSION_COMPLETE_INDICATOR)) {
Log::debug('Spectre: Return true, ready for step [4].');
return true;
}
// must already have the conversion, or not ready for this step:
return true;
case 'conversion':
// if/else is in reverse!
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
Log::debug('Spectre: Return false, not yet ready for step [2].');
return false;
return true;
}
// otherwise return false.
Log::debug('Spectre: Return true, ready for step [3].');
return true;
// will probably never return false, but OK.
return false;
case 'submit':
// if/else is in reverse!
if (session()->has(Constants::CONVERSION_COMPLETE_INDICATOR) && true === session()->get(Constants::CONVERSION_COMPLETE_INDICATOR)) {
@ -254,44 +218,65 @@ trait IsReadyForStep
* @return bool
* @throws ImporterErrorException
*/
private function isReadyForCSVStep(): bool
private function isReadyForSpectreStep(): bool
{
Log::debug(sprintf('isReadyForCSVStep("%s")', self::STEP));
Log::debug(sprintf('isReadyForSpectreStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('isReadyForCSVStep: Cannot handle CSV step "%s"', self::STEP));
throw new ImporterErrorException(sprintf('isReadyForSpectreStep: Cannot handle Spectre step "%s"', self::STEP));
case 'service-validation':
case 'authenticate':
return true;
case 'upload-files':
if (session()->has(Constants::HAS_UPLOAD) && true === session()->get(Constants::HAS_UPLOAD)) {
case 'conversion':
if (session()->has(Constants::READY_FOR_SUBMISSION) && true === session()->get(Constants::READY_FOR_SUBMISSION)) {
Log::debug('Spectre: Return false, ready for submission.');
return false;
}
return true;
case 'authenticate':
// for CSV this is always false.
// if/else is in reverse!
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
return true;
}
// will probably never return false, but OK.
return false;
case 'define-roles':
if (session()->has(Constants::ROLES_COMPLETE_INDICATOR) && true === session()->get(Constants::ROLES_COMPLETE_INDICATOR)) {
case 'upload-files':
if (session()->has(Constants::HAS_UPLOAD) && true === session()->get(Constants::HAS_UPLOAD)) {
return false;
}
return true;
case 'select-connection':
if (session()->has(Constants::HAS_UPLOAD) && true === session()->get(Constants::HAS_UPLOAD)) {
return true;
}
return false;
case 'configuration':
if (session()->has(Constants::CONFIG_COMPLETE_INDICATOR) && true === session()->get(Constants::CONFIG_COMPLETE_INDICATOR)) {
return false;
if (session()->has(Constants::CONNECTION_SELECTED_INDICATOR) && true === session()->get(Constants::CONNECTION_SELECTED_INDICATOR)) {
return true;
}
return true;
return false;
case 'define-roles':
return false;
case 'map':
// mapping must be complete, or not ready for this step.
if (session()->has(Constants::MAPPING_COMPLETE_INDICATOR) && true === session()->get(Constants::MAPPING_COMPLETE_INDICATOR)) {
Log::debug('Spectre: Return false, not ready for step [1].');
return false;
}
return true;
case 'conversion':
// if/else is in reverse!
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
// conversion complete?
if (session()->has(Constants::CONVERSION_COMPLETE_INDICATOR) && true === session()->get(Constants::CONVERSION_COMPLETE_INDICATOR)) {
Log::debug('Spectre: Return true, ready for step [4].');
return true;
}
// will probably never return false, but OK.
return false;
// must already have the conversion, or not ready for this step:
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
Log::debug('Spectre: Return false, not yet ready for step [2].');
return false;
}
// otherwise return false.
Log::debug('Spectre: Return true, ready for step [3].');
return true;
case 'submit':
// if/else is in reverse!
if (session()->has(Constants::CONVERSION_COMPLETE_INDICATOR) && true === session()->get(Constants::CONVERSION_COMPLETE_INDICATOR)) {
@ -301,6 +286,21 @@ trait IsReadyForStep
}
}
/**
* @return bool
* @throws ImporterErrorException
*/
private function isReadyForBasicStep(): bool
{
Log::debug(sprintf('isReadyForBasicStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('isReadyForBasicStep: Cannot handle basic step "%s"', self::STEP));
case 'service-validation':
return true;
}
}
/**
* @param Request $request
* @return RedirectResponse|null
@ -329,60 +329,38 @@ trait IsReadyForStep
* @return RedirectResponse
* @throws ImporterErrorException
*/
private function redirectToCorrectSpectreStep(): RedirectResponse
private function redirectToCorrectCSVStep(): RedirectResponse
{
Log::debug(sprintf('redirectToCorrectSpectreStep("%s")', self::STEP));
Log::debug(sprintf('redirectToCorrectCSVStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('redirectToCorrectSpectreStep: Cannot handle basic step "%s"', self::STEP));
throw new ImporterErrorException(sprintf('redirectToCorrectCSVStep: Cannot handle CSV step "%s"', self::STEP));
case 'upload-files':
// assume files are uploaded, go to step 11 (connection selection)
// back to selection
$route = route('011-connections.index');
$route = route('004-configure.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'define-roles':
// will always push to mapping, and mapping will send them to
// the right step.
$route = route('006-mapping.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'configuration':
$route = route('005-roles.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'map':
// if no conversion yet, go there first
// must already have the conversion, or not ready for this step:
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
Log::debug('Spectre: Is ready for conversion, so send to conversion.');
$route = route('007-convert.index');
Log::debug(sprintf('Spectre: Return redirect to "%s"', $route));
return redirect($route);
}
Log::debug('Spectre: Is ready for submit.');
// otherwise go to import right away
$route = route('008-submit.index');
Log::debug(sprintf('Spectre: Return redirect to "%s"', $route));
$route = route('007-convert.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'conversion':
if (session()->has(Constants::READY_FOR_SUBMISSION) && true === session()->get(Constants::READY_FOR_SUBMISSION)) {
$route = route('008-submit.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
}
}
}
/**
* @return RedirectResponse
* @throws ImporterErrorException
*/
private function redirectToBasicStep(): RedirectResponse
{
Log::debug(sprintf('redirectToBasicStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('redirectToBasicStep: Cannot handle basic step "%s"', self::STEP));
// redirect to mapping
$route = route('006-mapping.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'authenticate':
$route = route('003-upload.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
}
}
/**
@ -441,38 +419,60 @@ trait IsReadyForStep
* @return RedirectResponse
* @throws ImporterErrorException
*/
private function redirectToCorrectCSVStep(): RedirectResponse
private function redirectToCorrectSpectreStep(): RedirectResponse
{
Log::debug(sprintf('redirectToCorrectCSVStep("%s")', self::STEP));
Log::debug(sprintf('redirectToCorrectSpectreStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('redirectToCorrectCSVStep: Cannot handle CSV step "%s"', self::STEP));
throw new ImporterErrorException(sprintf('redirectToCorrectSpectreStep: Cannot handle basic step "%s"', self::STEP));
case 'upload-files':
$route = route('004-configure.index');
// assume files are uploaded, go to step 11 (connection selection)
// back to selection
$route = route('011-connections.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'define-roles':
// will always push to mapping, and mapping will send them to
// the right step.
$route = route('006-mapping.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'configuration':
$route = route('005-roles.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'map':
$route = route('007-convert.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
// if no conversion yet, go there first
// must already have the conversion, or not ready for this step:
if (session()->has(Constants::READY_FOR_CONVERSION) && true === session()->get(Constants::READY_FOR_CONVERSION)) {
Log::debug('Spectre: Is ready for conversion, so send to conversion.');
$route = route('007-convert.index');
Log::debug(sprintf('Spectre: Return redirect to "%s"', $route));
return redirect($route);
}
Log::debug('Spectre: Is ready for submit.');
// otherwise go to import right away
$route = route('008-submit.index');
Log::debug(sprintf('Spectre: Return redirect to "%s"', $route));
return redirect($route);
case 'conversion':
// redirect to mapping
$route = route('006-mapping.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
case 'authenticate':
$route = route('003-upload.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
if (session()->has(Constants::READY_FOR_SUBMISSION) && true === session()->get(Constants::READY_FOR_SUBMISSION)) {
$route = route('008-submit.index');
Log::debug(sprintf('Return redirect to "%s"', $route));
return redirect($route);
}
}
}
/**
* @return RedirectResponse
* @throws ImporterErrorException
*/
private function redirectToBasicStep(): RedirectResponse
{
Log::debug(sprintf('redirectToBasicStep("%s")', self::STEP));
switch (self::STEP) {
default:
throw new ImporterErrorException(sprintf('redirectToBasicStep: Cannot handle basic step "%s"', self::STEP));
}
}
}

89
app/Services/CSV/Configuration/Configuration.php

@ -368,18 +368,6 @@ class Configuration
return self::fromArray($data);
}
/**
* @param array $data
*
* @return static
*/
private static function fromVersionThree(array $data): self
{
$object = self::fromArray($data);
$object->specifics = [];
return $object;
}
/**
* @param array $array
*
@ -454,6 +442,18 @@ class Configuration
return $object;
}
/**
* @param array $data
*
* @return static
*/
private static function fromVersionThree(array $data): self
{
$object = self::fromArray($data);
$object->specifics = [];
return $object;
}
/**
* @return bool
*/
@ -523,15 +523,6 @@ class Configuration
return $array;
}
/**
* @param string $identifier
*/
public function setIdentifier(string $identifier): void
{
$this->identifier = $identifier;
}
/**
* @return array
*/
@ -641,6 +632,14 @@ class Configuration
return $this->connection;
}
/**
* @param string $connection
*/
public function setConnection(string $connection): void
{
$this->connection = $connection;
}
/**
* @return string
*/
@ -650,11 +649,11 @@ class Configuration
}
/**
* @param string $connection
* @param string $identifier
*/
public function setConnection(string $connection): void
public function setIdentifier(string $identifier): void
{
$this->connection = $connection;
$this->identifier = $identifier;
}
/**
@ -674,20 +673,19 @@ class Configuration
}
/**
* @return bool
* @param array $doMapping
*/
public function isMapAllData(): bool
public function setDoMapping(array $doMapping): void
{
return $this->mapAllData;
$this->doMapping = $doMapping;
}
/**
* @param array $doMapping
* @return bool
*/
public function setDoMapping(array $doMapping): void
public function isMapAllData(): bool
{
$this->doMapping = $doMapping;
return $this->mapAllData;
}
/**
@ -760,27 +758,19 @@ class Configuration
}
/**
* @param string $nordigenCountry
*/
public function setNordigenCountry(string $nordigenCountry): void
{
$this->nordigenCountry = $nordigenCountry;
}
/**
* @param string $nordigenBank
* @return string
*/
public function setNordigenBank(string $nordigenBank): void
public function getNordigenCountry(): string
{
$this->nordigenBank = $nordigenBank;
return $this->nordigenCountry;
}
/**
* @return string
* @param string $nordigenCountry
*/
public function getNordigenCountry(): string
public function setNordigenCountry(string $nordigenCountry): void
{
return $this->nordigenCountry;
$this->nordigenCountry = $nordigenCountry;
}
/**
@ -791,7 +781,6 @@ class Configuration
return $this->conversion;
}
/**
* @return string
*/
@ -800,6 +789,14 @@ class Configuration
return $this->nordigenBank;
}
/**
* @param string $nordigenBank
*/
public function setNordigenBank(string $nordigenBank): void
{
$this->nordigenBank = $nordigenBank;
}
/**
* @param string $identifier
*/

2
app/Services/CSV/Conversion/RoutineManager.php

@ -114,7 +114,7 @@ class RoutineManager implements RoutineManagerInterface
if (!$this->isCli()) {
try {
$this->csvFileProcessor->setReader(FileReader::getReaderFromSession($this->configuration->isConversion()));
} catch (ContainerExceptionInterface|NotFoundExceptionInterface|FileNotFoundException $e) {
} catch (ContainerExceptionInterface | NotFoundExceptionInterface | FileNotFoundException $e) {
throw new ImporterErrorException($e->getMessage(), 0, $e);
}
}

4
app/Services/CSV/Conversion/Task/Accounts.php

@ -257,14 +257,14 @@ class Accounts extends AbstractTask
$array['bic'] = $array['bic'] ?? null;
// Return ID or name if not null
if (null !== $array['id'] || '' !== (string)$array['name']) {
if (null !== $array['id'] || '' !== (string) $array['name']) {
Log::debug('Array with account has some name info, return that.', $array);
return $array;
}
// Return ID or IBAN if not null
if (null !== $array['id'] || '' !== (string)$array['iban']) {
if (null !== $array['id'] || '' !== (string) $array['iban']) {
Log::debug('Array with account has some IBAN info, return that.', $array);
return $array;

6
app/Services/CSV/File/FileReader.php

@ -30,6 +30,8 @@ use App\Services\Storage\StorageService;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use League\Csv\Reader;
use Log;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class FileReader
@ -42,8 +44,8 @@ class FileReader
* @param bool $convert
* @return Reader
* @throws FileNotFoundException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public static function getReaderFromSession(bool $convert = false): Reader
{

1
app/Services/CSV/Mapper/MapperService.php

@ -26,7 +26,6 @@ namespace App\Services\CSV\Mapper;
use App\Exceptions\ImporterErrorException;
use App\Services\CSV\Specifics\SpecificService;
use League\Csv\Exception;
use League\Csv\Reader;
use League\Csv\Statement;

2
app/Services/CSV/Roles/RoleService.php

@ -166,7 +166,7 @@ class RoleService
$examples[$index] = array_unique($examples[$index]);
}
}
foreach($examples as $line => $entries) {
foreach ($examples as $line => $entries) {
asort($entries);
$examples[$line] = $entries;
}

2
app/Services/CSV/Specifics/SpecificService.php

@ -44,8 +44,8 @@ class SpecificService
/**
* @param array $row
* @param array $specifics
* @deprecated
* @return array
* @deprecated
*/
public static function runSpecifics(array $row, array $specifics): array
{

1
app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php

@ -27,7 +27,6 @@ namespace App\Services\Nordigen\Conversion\Routine;
use App\Exceptions\ImporterErrorException;
use App\Exceptions\ImporterHttpException;
use App\Services\CSV\Configuration\Configuration;
use App\Services\Nordigen\Model\Transaction;
use App\Services\Nordigen\Request\GetTransactionsRequest;
use App\Services\Nordigen\Response\GetTransactionsResponse;
use App\Services\Nordigen\TokenManager;

6
app/Services/Nordigen/Conversion/RoutineManager.php

@ -41,9 +41,9 @@ use Log;
*/
class RoutineManager implements RoutineManagerInterface
{
private array $allMessages;
private array $allWarnings;
private array $allErrors;
private array $allMessages;
private array $allWarnings;
private array $allErrors;
use IsRunningCli, GeneratesIdentifier;
private Configuration $configuration;

152
app/Services/Nordigen/Model/Account.php

@ -83,6 +83,37 @@ class Account
return $self;
}
/**
* @param array $array
* @return static
*/
public static function fromLocalArray(array $array): self
{
$object = new self;
$object->identifier = $array['identifier'];
$object->bban = $array['bban'];
$object->bic = $array['bic'];
$object->cashAccountType = $array['cash_account_type'];
$object->currency = $array['currency'];
$object->details = $array['details'];
$object->displayName = $array['display_name'];
$object->iban = $array['iban'];
$object->linkedAccounts = $array['linked_accounts'];
$object->msisdn = $array['msisdn'];
$object->name = $array['name'];
$object->ownerAddressUnstructured = $array['owner_address_unstructured'];
$object->ownerName = $array['owner_name'];
$object->product = $array['product'];
$object->resourceId = $array['resource_id'];
$object->status = $array['status'];
$object->usage = $array['usage'];
$object->balances = [];
foreach ($array['balances'] as $arr) {
$object->balances[] = Balance::fromLocalArray($arr);
}
return $object;
}
/**
* @param Balance $balance
*/
@ -187,38 +218,6 @@ class Account
$this->details = $details;
}
/**
* @return string
*/
public function getDisplayName(): string
{
return $this->displayName;
}
/**
* @param string $displayName
*/
public function setDisplayName(string $displayName): void
{
$this->displayName = $displayName;
}
/**
* @return string
*/
public function getIban(): string
{
return $this->iban;
}
/**
* @param string $iban
*/
public function setIban(string $iban): void
{
$this->iban = $iban;
}
/**
* @return string
*/
@ -251,14 +250,6 @@ class Account
$this->msisdn = $msisdn;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return string
*/
@ -292,6 +283,14 @@ class Account
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*/
@ -303,17 +302,17 @@ class Account
/**
* @return string
*/
public function getOwnerAddressUnstructured(): string
public function getDisplayName(): string
{
return $this->ownerAddressUnstructured;
return $this->displayName;
}
/**
* @param string $ownerAddressUnstructured
* @param string $displayName
*/
public function setOwnerAddressUnstructured(string $ownerAddressUnstructured): void
public function setDisplayName(string $displayName): void
{
$this->ownerAddressUnstructured = $ownerAddressUnstructured;
$this->displayName = $displayName;
}
/**
@ -332,6 +331,38 @@ class Account
$this->ownerName = $ownerName;
}
/**
* @return string
*/
public function getIban(): string
{
return $this->iban;
}
/**
* @param string $iban
*/
public function setIban(string $iban): void
{
$this->iban = $iban;
}
/**
* @return string
*/
public function getOwnerAddressUnstructured(): string
{
return $this->ownerAddressUnstructured;
}
/**
* @param string $ownerAddressUnstructured
*/
public function setOwnerAddressUnstructured(string $ownerAddressUnstructured): void
{
$this->ownerAddressUnstructured = $ownerAddressUnstructured;
}
/**
* @return string
*/
@ -430,36 +461,5 @@ class Account
return $array;
}
/**
* @param array $array
* @return static
*/
public static function fromLocalArray(array $array): self
{
$object = new self;
$object->identifier = $array['identifier'];
$object->bban = $array['bban'];
$object->bic = $array['bic'];
$object->cashAccountType = $array['cash_account_type'];
$object->currency = $array['currency'];
$object->details = $array['details'];
$object->displayName = $array['display_name'];
$object->iban = $array['iban'];
$object->linkedAccounts = $array['linked_accounts'];
$object->msisdn = $array['msisdn'];
$object->name = $array['name'];
$object->ownerAddressUnstructured = $array['owner_address_unstructured'];
$object->ownerName = $array['owner_name'];
$object->product = $array['product'];
$object->resourceId = $array['resource_id'];
$object->status = $array['status'];
$object->usage = $array['usage'];
$object->balances = [];
foreach ($array['balances'] as $arr) {
$object->balances[] = Balance::fromLocalArray($arr);
}
return $object;
}
}

28
app/Services/Nordigen/Model/Balance.php

@ -57,20 +57,6 @@ class Balance
return $self;
}
/**
* @return array
*/
public function toLocalArray(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
'type' => $this->type,
'date' => $this->date,
'last_change_date_time' => $this->lastChangeDateTime,
];
}
/**
* @return $this
*/
@ -84,4 +70,18 @@ class Balance
$object->lastChangeDateTime = $array['last_change_date_time'];
return $object;
}
/**
* @return array
*/
public function toLocalArray(): array
{
return [
'amount' => $this->amount,
'currency' => $this->currency,
'type' => $this->type,
'date' => $this->date,
'last_change_date_time' => $this->lastChangeDateTime,
];
}
}

129
app/Services/Nordigen/Model/Transaction.php

@ -149,6 +149,69 @@ class Transaction
return $object;
}
/**
* @param array $array
* @return static
*/
public static function fromLocalArray(array $array): self
{
$object = new self;
$object->additionalInformation = $array['additional_information'];
$object->additionalInformationStructured = $array['additional_information_structured'];
$object->balanceAfterTransaction = $array['balance_after_transaction'];
$object->bankTransactionCode = $array['bank_transaction_code'];
$object->bookingDate = Carbon::createFromFormat(DateTimeInterface::W3C, $array['booking_date']);
$object->checkId = $array['check_id'];
$object->creditorAgent = $array['creditor_agent'];
$object->creditorId = $array['creditor_id'];
$object->creditorName = $array['creditor_name'];
$object->currencyExchange = $array['currency_exchange'];
$object->debtorAgent = $array['debtor_agent'];
$object->debtorName = $array['debtor_name'];
$object->entryReference = $array['entry_reference'];
$object->key = $array['key'];
$object->mandateId = $array['mandate_id'];
$object->proprietaryBank = $array['proprietary_bank'];
$object->purposeCode = $array['purpose_code'];
$object->remittanceInformationStructured = $array['remittance_information_structured'];
$object->remittanceInformationStructuredArray = $array['remittance_information_structured_array'];
$object->remittanceInformationUnstructured = $array['remittance_information_unstructured'];
$object->remittanceInformationUnstructuredArray = $array['remittance_information_unstructured_array'];
$object->transactionId = $array['transaction_id'];
$object->ultimateCreditor = $array['ultimate_creditor'];
$object->ultimateDebtor = $array['ultimate_debtor'];
$object->valueDate = Carbon::createFromFormat(DateTimeInterface::W3C, $array['value_date']);
$object->transactionAmount = $array['transaction_amount']['amount'];
$object->currencyCode = $array['transaction_amount']['currency'];
$object->accountIdentifier = $array['account_identifier'];
// undocumented values:
$object->endToEndId = $array['end_to_end_id'];
// TODO copy paste code.
$object->debtorAccountIban = array_key_exists('iban', $array['debtor_account']) ? $array['debtor_account']['iban'] : '';
$object->creditorAccountIban = array_key_exists('iban', $array['creditor_account']) ? $array['creditor_account']['iban'] : '';
$object->debtorAccountCurrency = array_key_exists('currency', $array['debtor_account']) ? $array['debtor_account']['currency'] : '';
$object->creditorAccountCurrency = array_key_exists('currency', $array['creditor_account']) ? $array['creditor_account']['currency'] : '';
//$object-> = $array[''];
// generate transactionID if empty:
if ('' === $object->transactionId) {
$hash = hash('sha256', (string) microtime());
try {
$hash = hash('sha256', json_encode($array, JSON_THROW_ON_ERROR));
} catch (JsonException $e) {
Log::error(sprintf('Could not parse array into JSON: %s', $e->getMessage()));
}
$object->transactionId = (string) Uuid::uuid5(config('importer.namespace'), $hash);
}
return $object;
}
/**
* @return Carbon
*/
@ -181,7 +244,7 @@ class Transaction
$description = implode(' ', $this->remittanceInformationUnstructuredArray);
Log::debug('Description is now remittanceInformationUnstructuredArray');
}
if('' === $description) {
if ('' === $description) {
Log::debug('Description is now remittanceInformationStructured');
$description = $this->remittanceInformationStructured;
}
@ -225,7 +288,6 @@ class Transaction
return null;
}
/**
* Return name of the source account. Depends also on the amount
*
@ -312,67 +374,4 @@ class Transaction
return $return;
}
/**
* @param array $array
* @return static
*/
public static function fromLocalArray(array $array): self
{
$object = new self;
$object->additionalInformation = $array['additional_information'];
$object->additionalInformationStructured = $array['additional_information_structured'];
$object->balanceAfterTransaction = $array['balance_after_transaction'];
$object->bankTransactionCode = $array['bank_transaction_code'];
$object->bookingDate = Carbon::createFromFormat(DateTimeInterface::W3C, $array['booking_date']);
$object->checkId = $array['check_id'];
$object->creditorAgent = $array['creditor_agent'];
$object->creditorId = $array['creditor_id'];
$object->creditorName = $array['creditor_name'];
$object->currencyExchange = $array['currency_exchange'];
$object->debtorAgent = $array['debtor_agent'];
$object->debtorName = $array['debtor_name'];
$object->entryReference = $array['entry_reference'];
$object->key = $array['key'];
$object->mandateId = $array['mandate_id'];
$object->proprietaryBank = $array['proprietary_bank'];
$object->purposeCode = $array['purpose_code'];
$object->remittanceInformationStructured = $array['remittance_information_structured'];
$object->remittanceInformationStructuredArray = $array['remittance_information_structured_array'];
$object->remittanceInformationUnstructured = $array['remittance_information_unstructured'];
$object->remittanceInformationUnstructuredArray = $array['remittance_information_unstructured_array'];
$object->transactionId = $array['transaction_id'];
$object->ultimateCreditor = $array['ultimate_creditor'];
$object->ultimateDebtor = $array['ultimate_debtor'];
$object->valueDate = Carbon::createFromFormat(DateTimeInterface::W3C, $array['value_date']);
$object->transactionAmount = $array['transaction_amount']['amount'];
$object->currencyCode = $array['transaction_amount']['currency'];
$object->accountIdentifier = $array['account_identifier'];
// undocumented values:
$object->endToEndId = $array['end_to_end_id'];
// TODO copy paste code.
$object->debtorAccountIban = array_key_exists('iban', $array['debtor_account']) ? $array['debtor_account']['iban'] : '';
$object->creditorAccountIban = array_key_exists('iban', $array['creditor_account']) ? $array['creditor_account']['iban'] : '';
$object->debtorAccountCurrency = array_key_exists('currency', $array['debtor_account']) ? $array['debtor_account']['currency'] : '';
$object->creditorAccountCurrency = array_key_exists('currency', $array['creditor_account']) ? $array['creditor_account']['currency'] : '';
//$object-> = $array[''];
// generate transactionID if empty:
if ('' === $object->transactionId) {
$hash = hash('sha256', (string) microtime());
try {
$hash = hash('sha256', json_encode($array, JSON_THROW_ON_ERROR));
} catch (JsonException $e) {
Log::error(sprintf('Could not parse array into JSON: %s', $e->getMessage()));
}
$object->transactionId = (string) Uuid::uuid5(config('importer.namespace'), $hash);
}
return $object;
}
}

4
app/Services/Nordigen/Request/ListBanksRequest.php

@ -60,8 +60,8 @@ class ListBanksRequest extends Request
} catch (ImporterErrorException $e) {
$error = [
'error' => [
'message' =>$e->getMessage()
]
'message' => $e->getMessage(),
],
];
return new ErrorResponse($error);
} catch (ImporterHttpException $e) {

2
app/Services/Nordigen/Request/PostNewRequisitionRequest.php

@ -80,7 +80,7 @@ class PostNewRequisitionRequest extends Request
[
'redirect' => route('010-build-link.callback'),
'institution_id' => $this->bank,
'reference' => $this->reference,
'reference' => $this->reference,
];
$result = $this->authenticatedJsonPost($array);

82
app/Services/Nordigen/Request/Request.php

@ -133,7 +133,7 @@ abstract class Request
//throw new ImporterHttpException(sprintf('Could not decode JSON: %s', $e->getMessage()), 0, $e);
}
// if status code is 503, the account does not exist.
$exception = new ImporterErrorException(sprintf('%s: %s', get_class($e), $e->getMessage()), 0, $e);
$exception = new ImporterErrorException(sprintf('%s: %s', get_class($e), $e->getMessage()), 0, $e);
$exception->json = $json;
throw $exception;
}
@ -167,46 +167,6 @@ abstract class Request
return $json;
}
/**
* @return array
* @throws ImporterHttpException
*/
protected function authenticatedJsonPost(array $json): array
{
Log::debug(sprintf('Now at %s', __METHOD__));
$fullUrl = sprintf('%s/%s', $this->getBase(), $this->getUrl());
if (null !== $this->parameters) {
$fullUrl = sprintf('%s?%s', $fullUrl, http_build_query($this->parameters));
}
$client = $this->getClient();
try {
$res = $client->request(
'POST', $fullUrl, [
'json' => $json,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => sprintf('Bearer %s', $this->getToken()),
],
]
);
} catch (ClientException $e) {
// TODO error response, not an exception.
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost: %s', $e->getMessage()), 0, $e);
}
$body = (string) $res->getBody();
try {
$json = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
// TODO error response, not an exception.
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost JSON: %s', $e->getMessage()), 0, $e);
}
return $json;
}
/**
* @return string
*/
@ -269,5 +229,45 @@ abstract class Request
$this->token = $token;
}
/**
* @return array
* @throws ImporterHttpException
*/
protected function authenticatedJsonPost(array $json): array
{
Log::debug(sprintf('Now at %s', __METHOD__));
$fullUrl = sprintf('%s/%s', $this->getBase(), $this->getUrl());
if (null !== $this->parameters) {
$fullUrl = sprintf('%s?%s', $fullUrl, http_build_query($this->parameters));
}
$client = $this->getClient();
try {
$res = $client->request(
'POST', $fullUrl, [
'json' => $json,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => sprintf('Bearer %s', $this->getToken()),
],
]
);
} catch (ClientException $e) {
// TODO error response, not an exception.
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost: %s', $e->getMessage()), 0, $e);
}
$body = (string) $res->getBody();
try {
$json = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
// TODO error response, not an exception.
throw new ImporterHttpException(sprintf('AuthenticatedJsonPost JSON: %s', $e->getMessage()), 0, $e);
}
return $json;
}
}

6
app/Services/Nordigen/Response/GetTransactionsResponse.php

@ -34,7 +34,7 @@ use Iterator;
/**
* Class GetTransactionsResponse
*/
class GetTransactionsResponse extends Response implements Iterator, Countable
class GetTransactionsResponse extends Response implements Iterator, Countable
{
private Collection $collection;
private int $position = 0;
@ -44,9 +44,9 @@ class GetTransactionsResponse extends Response implements Iterator, Countable
*/
public function __construct(array $data)
{
$this->collection= new Collection;
$this->collection = new Collection;
/** @var array $array */
foreach($data as $array) {
foreach ($data as $array) {
$this->collection->push(Transaction::fromArray($array));
}
}

1
app/Services/Nordigen/Response/ListAccountsResponse.php

@ -53,6 +53,7 @@ class ListAccountsResponse extends Response implements Iterator, Countable
}
$this->collection = new Collection($this->accounts);
}
/**
* Count elements of an object.
*

25
app/Services/Nordigen/Response/ListBanksResponse.php

@ -27,10 +27,10 @@ namespace App\Services\Nordigen\Response;
use App\Services\Nordigen\Model\Bank;
use App\Services\Nordigen\Model\Country;
use App\Services\Shared\Response\Response;
use Countable;
use Illuminate\Support\Collection;
use Iterator;
use App\Services\Shared\Response\Response;
/**
* Class ListBanksResponse
@ -64,18 +64,6 @@ class ListBanksResponse extends Response implements Iterator, Countable
$this->collection = new Collection(array_values($this->countries));
}
/**
* @param Bank $object
* @param array $countries
*/
private function addToCountries(Bank $object, array $countries): void
{
/** @var string $code */
foreach ($countries as $code) {
$this->countries[$code]->addBank($object);
}
}
/**
* @param array $bank
*/
@ -88,6 +76,17 @@ class ListBanksResponse extends Response implements Iterator, Countable
}
}
/**
* @param Bank $object
* @param array $countries
*/
private function addToCountries(Bank $object, array $countries): void
{
/** @var string $code */
foreach ($countries as $code) {
$this->countries[$code]->addBank($object);
}
}
/**
* Count elements of an object.

78
app/Services/Nordigen/TokenManager.php

@ -39,25 +39,19 @@ use Psr\Container\NotFoundExceptionInterface;
class TokenManager
{
/**
* @return bool
* @return string
* @throws ImporterErrorException
*/
public static function hasValidAccessToken(): bool
public static function getAccessToken(): string
{
Log::debug(sprintf('Now at %s', __METHOD__));
$hasAccessToken = session()->has(Constants::NORDIGEN_ACCESS_TOKEN);
if (false === $hasAccessToken) {
Log::debug('No Nordigen token is present, so no valid access token');
return false;
}
$tokenValidity = session()->get(Constants::NORDIGEN_ACCESS_EXPIRY_TIME) ?? 0;
Log::debug(sprintf('Nordigen token is valid until %s', date('Y-m-d H:i:s', $tokenValidity)));
$result = time() < $tokenValidity;
if (false === $result) {
Log::debug('Nordigen token is no longer valid');
return false;
self::validateAllTokens();
try {
$token = session()->get(Constants::NORDIGEN_ACCESS_TOKEN);
} catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) {
throw new ImporterErrorException($e->getMessage(), 0, $e);
}
Log::debug('Nordigen token is valid.');
return true;
return $token;
}
/**
@ -102,6 +96,28 @@ class TokenManager
return time() < $tokenValidity;
}
/**
* @return bool
*/
public static function hasValidAccessToken(): bool
{
Log::debug(sprintf('Now at %s', __METHOD__));
$hasAccessToken = session()->has(Constants::NORDIGEN_ACCESS_TOKEN);
if (false === $hasAccessToken) {
Log::debug('No Nordigen token is present, so no valid access token');
return false;
}
$tokenValidity = session()->get(Constants::NORDIGEN_ACCESS_EXPIRY_TIME) ?? 0;
Log::debug(sprintf('Nordigen token is valid until %s', date('Y-m-d H:i:s', $tokenValidity)));
$result = time() < $tokenValidity;
if (false === $result) {
Log::debug('Nordigen token is no longer valid');
return false;
}
Log::debug('Nordigen token is valid.');
return true;
}
/**
* @return bool
*/
@ -116,6 +132,14 @@ class TokenManager
die(__METHOD__);
}
/**
*
*/
public static function getFreshAccessToken(): void
{
die(__METHOD__);
}
/**
* get new token set and store in session
* @throws ImporterHttpException
@ -135,28 +159,4 @@ class TokenManager
session()->put(Constants::NORDIGEN_REFRESH_EXPIRY_TIME, $result->refreshExpires);
}
/**
* @return string
* @throws ImporterErrorException
*/
public static function getAccessToken(): string
{
Log::debug(sprintf('Now at %s', __METHOD__));
self::validateAllTokens();
try {
$token = session()->get(Constants::NORDIGEN_ACCESS_TOKEN);
} catch (NotFoundExceptionInterface | ContainerExceptionInterface $e) {
throw new ImporterErrorException($e->getMessage(), 0, $e);
}
return $token;
}
/**
*
*/
public static function getFreshAccessToken(): void
{
die(__METHOD__);
}
}

46
app/Services/Session/Constants.php

@ -30,49 +30,49 @@ namespace App\Services\Session;
class Constants
{
// constants to remember Nordigen access token, refresh token and validity:
public const NORDIGEN_ACCESS_TOKEN = 'nordigen_access_token';
public const NORDIGEN_REFRESH_TOKEN = 'nordigen_refresh_token';
public const NORDIGEN_ACCESS_EXPIRY_TIME = 'nordigen_access_expiry_time';
public const NORDIGEN_REFRESH_EXPIRY_TIME = 'nordigen_refresh_expiry_time';
public const CONFIGURATION = 'configuration';
public const CONFIG_COMPLETE_INDICATOR = 'config_complete';
public const CONNECTION_SELECTED_INDICATOR = 'connection_selected_ind';
public const CONVERSION_COMPLETE_INDICATOR = 'conversion_complete';
// session value constants:
public const SESSION_SPECTRE_APP_ID = 'spectre_app_id';
public const SESSION_SPECTRE_SECRET = 'spectre_secret';
public const SESSION_NORDIGEN_ID = 'nordigen_id';
public const SESSION_NORDIGEN_KEY = 'nordigen_key';
public const CONVERSION_JOB_IDENTIFIER = 'conversion_job_id';
public const FLOW_COOKIE = 'flow';
public const HAS_UPLOAD = 'has_uploaded_file';
public const IMPORT_JOB_IDENTIFIER = 'import_job_id';
// upload config values:
public const UPLOAD_CSV_FILE = 'csv_file_path';
public const UPLOAD_CONFIG_FILE = 'config_file_path';
public const MAPPING_COMPLETE_INDICATOR = 'mapping_config_complete';
public const NORDIGEN_ACCESS_EXPIRY_TIME = 'nordigen_access_expiry_time';
// cookie name to remember the flow:
public const FLOW_COOKIE = 'flow';
public const NORDIGEN_ACCESS_TOKEN = 'nordigen_access_token';
// stores the configuration array
public const CONFIGURATION = 'configuration';
public const NORDIGEN_REFRESH_EXPIRY_TIME = 'nordigen_refresh_expiry_time';
// if the user is done with specific steps:
public const HAS_UPLOAD = 'has_uploaded_file';
public const CONFIG_COMPLETE_INDICATOR = 'config_complete';
public const NORDIGEN_REFRESH_TOKEN = 'nordigen_refresh_token';
public const READY_FOR_CONVERSION = 'ready_for_conversion';
public const ROLES_COMPLETE_INDICATOR = 'role_config_complete';
public const MAPPING_COMPLETE_INDICATOR = 'mapping_config_complete';
public const CONVERSION_COMPLETE_INDICATOR = 'conversion_complete';
public const SUBMISSION_COMPLETE_INDICATOR = 'submission_complete';
public const READY_FOR_SUBMISSION = 'ready_for_submission';
public const REQUISITION_REFERENCE = 'requisition_reference';
public const ROLES_COMPLETE_INDICATOR = 'role_config_complete';
public const SELECTED_BANK_COUNTRY = 'selected_bank_country';
public const SESSION_NORDIGEN_ID = 'nordigen_id';
public const SESSION_NORDIGEN_KEY = 'nordigen_key';
// spectre specific steps:
public const CONNECTION_SELECTED_INDICATOR = 'connection_selected_ind';
public const SESSION_SPECTRE_APP_ID = 'spectre_app_id';
// nordigen specific steps
public const SELECTED_BANK_COUNTRY = 'selected_bank_country';
public const SESSION_SPECTRE_SECRET = 'spectre_secret';
// nordigen specific constants
public const REQUISITION_REFERENCE = 'requisition_reference';
public const SUBMISSION_COMPLETE_INDICATOR = 'submission_complete';
// constants for data conversion job:
public const CONVERSION_JOB_IDENTIFIER = 'conversion_job_id';
public const IMPORT_JOB_IDENTIFIER = 'import_job_id';
public const UPLOAD_CONFIG_FILE = 'config_file_path';
public const UPLOAD_CSV_FILE = 'csv_file_path';
// /** @var string */

16
app/Services/Shared/Conversion/GeneratesIdentifier.php

@ -37,6 +37,14 @@ trait GeneratesIdentifier
protected string $identifier;
private string $diskName = 'conversion-routines';
/**
* @inheritDoc
*/
public function getIdentifier(): string
{
return $this->identifier;
}
/**
*
*/
@ -54,12 +62,4 @@ trait GeneratesIdentifier
Log::info(sprintf('Job identifier is "%s"', $generatedId));
}
/**
* @inheritDoc
*/
public function getIdentifier(): string
{
return $this->identifier;
}
}

84
app/Services/Shared/Import/Routine/ApiSubmitter.php

@ -299,6 +299,45 @@ class ApiSubmitter
return $return;
}
/**
* @param array $line
* @return array
*/
private function replaceMappings(array $line): array
{
Log::debug('Going to map data for this line.');
if (array_key_exists(0, $this->mapping)) {
Log::debug('Configuration has mapping for opposing account name!');
/**
* @var int $index
* @var array $transaction
*/
foreach ($line['transactions'] as $index => $transaction) {
if ('withdrawal' === $transaction['type']) {
// replace destination_name with destination_id
$destination = $transaction['destination_name'] ?? '';
if (array_key_exists($destination, $this->mapping[0])) {
unset($line['transactions'][$index]['destination_name']);
unset($line['transactions'][$index]['destination_iban']);
$line['transactions'][$index]['destination_id'] = $this->mapping[0][$destination];
Log::debug(sprintf('Replaced destination name "%s" with a reference to account id #%d', $destination, $this->mapping[0][$destination]));
}
}
if ('deposit' === $transaction['type']) {
// replace source_name with source_id
$source = $transaction['source_name'] ?? '';
if (array_key_exists($source, $this->mapping[0])) {
unset($line['transactions'][$index]['source_name']);
unset($line['transactions'][$index]['source_iban']);
$line['transactions'][$index]['source_id'] = $this->mapping[0][$source];
Log::debug(sprintf('Replaced source name "%s" with a reference to account id #%d', $source, $this->mapping[0][$source]));
}
}
}
}
return $line;
}
/**
* @param string $key
* @param array $transaction
@ -417,14 +456,6 @@ class ApiSubmitter
$this->setMapping($configuration->getMapping());
}
/**
* @param array $mapping
*/
public function setMapping(array $mapping): void
{
$this->mapping = $mapping;
}
/**
* @param bool $addTag
*/
@ -434,41 +465,10 @@ class ApiSubmitter
}
/**
* @param array $line
* @return array
* @param array $mapping
*/
private function replaceMappings(array $line): array
public function setMapping(array $mapping): void
{
Log::debug('Going to map data for this line.');
if (array_key_exists(0, $this->mapping)) {
Log::debug('Configuration has mapping for opposing account name!');
/**
* @var int $index
* @var array $transaction
*/
foreach ($line['transactions'] as $index => $transaction) {
if ('withdrawal' === $transaction['type']) {
// replace destination_name with destination_id
$destination = $transaction['destination_name'] ?? '';
if (array_key_exists($destination, $this->mapping[0])) {
unset($line['transactions'][$index]['destination_name']);
unset($line['transactions'][$index]['destination_iban']);
$line['transactions'][$index]['destination_id'] = $this->mapping[0][$destination];
Log::debug(sprintf('Replaced destination name "%s" with a reference to account id #%d', $destination, $this->mapping[0][$destination]));
}
}
if ('deposit' === $transaction['type']) {
// replace source_name with source_id
$source = $transaction['source_name'] ?? '';
if (array_key_exists($source, $this->mapping[0])) {
unset($line['transactions'][$index]['source_name']);
unset($line['transactions'][$index]['source_iban']);
$line['transactions'][$index]['source_id'] = $this->mapping[0][$source];
Log::debug(sprintf('Replaced source name "%s" with a reference to account id #%d', $source, $this->mapping[0][$source]));
}
}
}
}
return $line;
$this->mapping = $mapping;
}
}

14
app/Services/Shared/Import/Routine/RoutineManager.php

@ -34,12 +34,12 @@ use Log;
*/
class RoutineManager
{
private ApiSubmitter $apiSubmitter;
private array $transactions;
private string $identifier;
private array $allMessages;
private array $allWarnings;
private array $allErrors;
private ApiSubmitter $apiSubmitter;
private array $transactions;
private string $identifier;
private array $allMessages;
private array $allWarnings;
private array $allErrors;
/**
* @param string $identifier
@ -64,7 +64,7 @@ class RoutineManager
*/
public function setConfiguration(Configuration $configuration): void
{
$this->apiSubmitter = new ApiSubmitter;
$this->apiSubmitter = new ApiSubmitter;
$this->apiSubmitter->setIdentifier($this->identifier);
$this->apiSubmitter->setConfiguration($configuration);
Log::debug('Created APISubmitter in RoutineManager');

4
app/Services/Spectre/AuthenticationValidator.php

@ -51,10 +51,10 @@ class AuthenticationValidator implements AuthenticationValidatorInterface
$secret = config('spectre.secret');
if ('' === $appId && !$this->isCli()) {
$appId = (string)session()->get(Constants::SESSION_SPECTRE_APP_ID);
$appId = (string) session()->get(Constants::SESSION_SPECTRE_APP_ID);
}
if ('' === $secret && !$this->isCli()) {
$secret = (string)session()->get(Constants::SESSION_SPECTRE_SECRET);
$secret = (string) session()->get(Constants::SESSION_SPECTRE_SECRET);
}

2
app/Services/Spectre/Conversion/Routine/FilterTransactions.php

@ -56,7 +56,7 @@ class FilterTransactions
unset($transaction['transactions'][0]['datetime']);
if (0 === (int)($transaction['transactions'][0]['category_id'] ?? 0)) {
if (0 === (int) ($transaction['transactions'][0]['category_id'] ?? 0)) {
//Log::debug('IS NULL');
unset($transaction['transactions'][0]['category_id']);
}

4
app/Services/Spectre/Model/Account.php

@ -61,13 +61,13 @@ class Account
{
$model = new self;
$model->matched = false;
$model->id = (string)$data['id'];
$model->id = (string) $data['id'];
$model->accountName = $data['extra']['account_name'] ?? '';
$model->accountNumber = $data['extra']['account_number'] ?? '';
$model->balance = $data['balance'] ?? 0;
$model->cardType = $data['extra']['card_type'] ?? '';
$model->clientName = $data['extra']['client_name'] ?? '';
$model->connectionId = (string)$data['connection_id'];
$model->connectionId = (string) $data['connection_id'];
$model->currencyCode = $data['currency_code'];
$model->iban = $data['extra']['iban'] ?? '';
$model->name = $data['name'];

26
app/Services/Spectre/Model/Connection.php

@ -32,18 +32,18 @@ use Carbon\Carbon;
*/
class Connection
{
public string $categorization;
public string $countryCode;
public string $customerId;
public string $id;
public Carbon $lastSuccess;
public ?string $nextPossibleRefreshAt;
public string $providerCode;
public string $providerId;
public string $providerName;
public string $secret;
public string $status;
public Carbon $updatedAt;
public string $categorization;
public string $countryCode;
public string $customerId;
public string $id;
public Carbon $lastSuccess;
public ?string $nextPossibleRefreshAt;
public string $providerCode;
public string $providerId;
public string $providerName;
public string $secret;
public string $status;
public Carbon $updatedAt;
/**
* Customer constructor.
@ -60,7 +60,7 @@ class Connection
public static function fromArray(array $data): self
{
$model = new self;
$model->id = (string)$data['id'];
$model->id = (string) $data['id'];
$model->categorization = $data['categorization'];
$model->countryCode = $data['country_code'];
$model->customerId = $data['customer_id'];

2
app/Services/Spectre/Model/Customer.php

@ -52,7 +52,7 @@ class Customer
public static function fromArray(array $data): self
{
$model = new self;
$model->id = (string)$data['id'];
$model->id = (string) $data['id'];
$model->identifier = $data['identifier'];
$model->secret = $data['secret'];

34
app/Services/Spectre/Model/Transaction.php

@ -32,19 +32,19 @@ use Carbon\Carbon;
*/
class Transaction
{
public string $accountId;
public string $amount;
public string $category;
public Carbon $createdAt;
public string $currencyCode;
public string $description;
public bool $duplicated;
public TransactionExtra $extra;
public string $id;
public Carbon $madeOn;
public string $mode;
public string $status;
public Carbon $updatedAt;
public string $accountId;
public string $amount;
public string $category;
public Carbon $createdAt;
public string $currencyCode;
public string $description;
public bool $duplicated;
public TransactionExtra $extra;
public string $id;
public Carbon $madeOn;
public string $mode;
public string $status;
public Carbon $updatedAt;
/**
* Transaction constructor.
@ -61,11 +61,11 @@ class Transaction
public static function fromArray(array $data): self
{
$model = new self;
$model->id = (string)$data['id'];
$model->id = (string) $data['id'];
$model->mode = $data['mode'];
$model->status = $data['status'];
$model->madeOn = new Carbon($data['made_on']);
$model->amount = (string)$data['amount'];
$model->amount = (string) $data['amount'];
$model->currencyCode = $data['currency_code'];
$model->description = $data['description'];
$model->category = $data['category'];
@ -84,7 +84,7 @@ class Transaction
public function toArray(): array
{
return [
'id' => (string)$this->id,
'id' => (string) $this->id,
'account_id' => $this->accountId,
'made_on' => $this->madeOn ? $this->madeOn->toW3cString() : '',
'created_at' => $this->createdAt ? $this->createdAt->toW3cString() : '',
@ -93,7 +93,7 @@ class Transaction
'status' => $this->status,
'amount' => $this->amount,
'currency_code' => $this->currencyCode,
'description' => (string)$this->description,
'description' => (string) $this->description,
'category' => $this->category,
'duplicated' => $this->duplicated,
'extra' => $this->extra->toArray(),

6
app/Services/Spectre/Model/TransactionExtra.php

@ -83,7 +83,7 @@ class TransactionExtra
$model->postingDate = isset($data['posting_date']) ? new Carbon($data['posting_date']) : null;
$model->postingTime = isset($data['posting_time']) ? new Carbon($data['posting_time']) : null;
$model->accountNumber = $data['account_number'] ?? null;
$model->originalAmount = isset($data['original_amount']) ? (string)$data['original_amount'] : null;
$model->originalAmount = isset($data['original_amount']) ? (string) $data['original_amount'] : null;
$model->originalCurrencyCode = $data['original_currency_code'] ?? null;
$model->assetCode = $data['asset_code'] ?? null;
$model->assetAmount = $data['asset_amount'] ?? null;
@ -100,8 +100,8 @@ class TransactionExtra
$model->units = $data['units'] ?? null;
$model->additional = $data['additional'] ?? null;
$model->unitPrice = $data['unit_price'] ?? null;
$model->accountBalanceSnapshot = isset($data['account_balance_snapshot']) ? (string)$data['account_balance_snapshot'] : null;
$model->categorizationConfidence = isset($data['categorization_confidence']) ? (string)$data['categorization_confidence'] : null;
$model->accountBalanceSnapshot = isset($data['account_balance_snapshot']) ? (string) $data['account_balance_snapshot'] : null;
$model->categorizationConfidence = isset($data['categorization_confidence']) ? (string) $data['categorization_confidence'] : null;
return $model;
}

2
app/Services/Spectre/Request/GetAccountsRequest.php

@ -25,10 +25,10 @@ declare(strict_types=1);
namespace App\Services\Spectre\Request;
use App\Exceptions\ImporterErrorException;
use App\Services\Shared\Response\Response;
use App\Services\Spectre\Response\ErrorResponse;
use App\Services\Spectre\Response\GetAccountsResponse;
use Log;
use App\Services\Shared\Response\Response;
/**
* Class GetAccountsRequest

2
app/Services/Spectre/Request/GetTransactionsRequest.php

@ -77,7 +77,7 @@ class GetTransactionsRequest extends Request
// extract next ID
$hasNextPage = false;
if (isset($response['meta']['next_id']) && (int)$response['meta']['next_id'] > $nextId) {
if (isset($response['meta']['next_id']) && (int) $response['meta']['next_id'] > $nextId) {
$hasNextPage = true;
$nextId = $response['meta']['next_id'];
Log::debug(sprintf('Next ID is now %d.', $nextId));

2
app/Services/Spectre/Request/ListConnectionsRequest.php

@ -26,8 +26,8 @@ declare(strict_types=1);
namespace App\Services\Spectre\Request;
use App\Exceptions\ImporterErrorException;
use App\Services\Spectre\Response\ErrorResponse;
use App\Services\Shared\Response\Response;
use App\Services\Spectre\Response\ErrorResponse;
use App\Services\Spectre\Response\ListConnectionsResponse;
use JsonException;
use Log;

2
app/Services/Spectre/Request/ListCustomersRequest.php

@ -26,9 +26,9 @@ declare(strict_types=1);
namespace App\Services\Spectre\Request;
use App\Exceptions\ImporterHttpException;
use App\Services\Shared\Response\Response;
use App\Services\Spectre\Response\ErrorResponse;
use App\Services\Spectre\Response\ListCustomersResponse;
use App\Services\Shared\Response\Response;
/**
* Class ListCustomersRequest

2
app/Services/Spectre/Request/PostConnectSessionsRequest.php

@ -24,8 +24,8 @@ declare(strict_types=1);
namespace App\Services\Spectre\Request;
use App\Services\Spectre\Response\PostConnectSessionResponse;
use App\Services\Shared\Response\Response;
use App\Services\Spectre\Response\PostConnectSessionResponse;
/**
* Class PostConnectSessionsRequest

2
app/Services/Spectre/Response/ListCustomersResponse.php

@ -37,7 +37,7 @@ use Iterator;
class ListCustomersResponse extends Response implements Iterator, Countable
{
private Collection $collection;
private int $position = 0;
private int $position = 0;
/**
* @inheritDoc

2
app/Services/Spectre/Response/PostConnectSessionResponse.php

@ -41,6 +41,6 @@ class PostConnectSessionResponse extends Response
*/
public function __construct(array $data)
{
$this->connect_url = (string)$data['connect_url'];
$this->connect_url = (string) $data['connect_url'];
}
}

8
config/app.php

@ -23,6 +23,8 @@
declare(strict_types=1);
use App\Support\Facades\Steam;
return [
/*
@ -90,7 +92,7 @@ return [
|
*/
'timezone' => env('TZ','Europe/Amsterdam'),
'timezone' => env('TZ', 'Europe/Amsterdam'),
/*
|--------------------------------------------------------------------------
@ -142,7 +144,7 @@ return [
|
*/
// not used by data importer.
'key' => 'PSPGRY5PWJ6D1UMZLBL5BNAZIN4I1QSD',
'key' => 'PSPGRY5PWJ6D1UMZLBL5BNAZIN4I1QSD',
'cipher' => 'AES-256-CBC',
@ -251,7 +253,7 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Twig' => TwigBridge\Facade\Twig::class,
'Steam' => \App\Support\Facades\Steam::class,
'Steam' => Steam::class,
],
];

10
config/auth.php

@ -41,11 +41,11 @@ return [
],
// Al stewart - A small fruit song
'line_a' => 'Said the apple to the orange',
'line_b' => 'Oh I wanted you to come',
'line_c' => 'Close to me and kiss me to the core',
'line_d' => 'Then you might know me like no other orange',
'line_e' => 'Has ever done before',
'line_a' => 'Said the apple to the orange',
'line_b' => 'Oh I wanted you to come',
'line_c' => 'Close to me and kiss me to the core',
'line_d' => 'Then you might know me like no other orange',
'line_e' => 'Has ever done before',
/*
|--------------------------------------------------------------------------

14
config/filesystems.php

@ -65,31 +65,31 @@ return [
'disks' => [
'local' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
],
'uploads' => [
'uploads' => [
'driver' => 'local',
'root' => storage_path('uploads'),
],
'jobs' => [
'jobs' => [
'driver' => 'local',
'root' => storage_path('jobs'),
],
'conversion-routines' => [
'conversion-routines' => [
'driver' => 'local',
'root' => storage_path('conversion-routines'),
],
'submission-routines' => [
'submission-routines' => [
'driver' => 'local',
'root' => storage_path('submission-routines'),
],
'configurations' => [
'configurations' => [
'driver' => 'local',
'root' => envNonEmpty('JSON_CONFIGURATION_DIR', storage_path('configurations')),
],
'public' => [
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',

42
config/ide-helper.php

@ -22,7 +22,7 @@
declare(strict_types=1);
return array(
return [
/*
|--------------------------------------------------------------------------
@ -33,8 +33,8 @@ return array(
|
*/
'filename' => '_ide_helper',
'format' => 'php',
'filename' => '_ide_helper',
'format' => 'php',
'meta_filename' => '.phpstorm.meta.php',
@ -87,9 +87,9 @@ return array(
'include_helpers' => false,
'helper_files' => array(
base_path().'/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
),
'helper_files' => [
base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
],
/*
|--------------------------------------------------------------------------
@ -101,9 +101,9 @@ return array(
|
*/
'model_locations' => array(
'model_locations' => [
'app',
),
],
/*
@ -115,12 +115,12 @@ return array(
|
*/
'extra' => array(
'Eloquent' => array('Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'),
'Session' => array('Illuminate\Session\Store'),
),
'extra' => [
'Eloquent' => ['Illuminate\Database\Eloquent\Builder', 'Illuminate\Database\Query\Builder'],
'Session' => ['Illuminate\Session\Store'],
],
'magic' => array(),
'magic' => [],
/*
|--------------------------------------------------------------------------
@ -132,9 +132,9 @@ return array(
|
*/
'interfaces' => array(
'interfaces' => [
),
],
/*
|--------------------------------------------------------------------------
@ -162,9 +162,9 @@ return array(
| ),
|
*/
'custom_db_types' => array(
'custom_db_types' => [
),
],
/*
|--------------------------------------------------------------------------
@ -200,10 +200,10 @@ return array(
| Cast the given "real type" to the given "type".
|
*/
'type_overrides' => array(
'type_overrides' => [
'integer' => 'int',
'boolean' => 'bool',
),
],
/*
|--------------------------------------------------------------------------
@ -214,6 +214,6 @@ return array(
| magic methods and properties.
|
*/
'include_class_docblocks' => false,
'include_class_docblocks' => false,
);
];

8
config/nordigen.php

@ -23,11 +23,11 @@
declare(strict_types=1);
return [
'id' => env('NORDIGEN_ID', ''),
'key' => env('NORDIGEN_KEY', ''),
'url' => 'https://ob.nordigen.com',
'id' => env('NORDIGEN_ID', ''),
'key' => env('NORDIGEN_KEY', ''),
'url' => 'https://ob.nordigen.com',
'use_sandbox' => env('NORDIGEN_SANDBOX', false),
'countries' => [
'countries' => [
'AF' => 'Afghanistan', 'AX' => 'Åland Islands', 'AL' => 'Albania', 'DZ' => 'Algeria', 'AS' => 'American Samoa', 'AD' => 'Andorra', 'AO' => 'Angola',
'AI' => 'Anguilla', 'AQ' => 'Antarctica', 'AG' => 'Antigua and Barbuda', 'AR' => 'Argentina', 'AM' => 'Armenia', 'AW' => 'Aruba', 'AU' => 'Australia',
'AT' => 'Austria', 'AZ' => 'Azerbaijan', 'BS' => 'Bahamas', 'BH' => 'Bahrain', 'BD' => 'Bangladesh', 'BB' => 'Barbados', 'BY' => 'Belarus',

2
config/transaction_types.php

@ -27,7 +27,7 @@ use GrumpyDictator\FFIIIApiSupport\Model\TransactionType;
return [
// having the source + dest will tell you the transaction type.
'account_to_transaction' => [
'account_to_transaction' => [
AccountType::ASSET => [
AccountType::ASSET => TransactionType::TRANSFER,

26
config/twigbridge.php

@ -36,7 +36,7 @@ return [
| File extension for Twig view files.
|
*/
'extension' => 'twig',
'extension' => 'twig',
/*
|--------------------------------------------------------------------------
@ -51,11 +51,11 @@ return [
// When set to true, the generated templates have a __toString() method
// that you can use to display the generated nodes.
// default: false
'debug' => env('APP_DEBUG', false),
'debug' => env('APP_DEBUG', false),
// The charset used by the templates.
// default: utf-8
'charset' => 'utf-8',
'charset' => 'utf-8',
// The base template class to use for generated templates.
// default: TwigBridge\Twig\Template
@ -64,26 +64,26 @@ return [
// An absolute path where to store the compiled templates, or false to disable caching. If null
// then the cache file path is used.
// default: cache file storage path
'cache' => null,
'cache' => null,
// When developing with Twig, it's useful to recompile the template
// whenever the source code changes. If you don't provide a value
// for the auto_reload option, it will be determined automatically based on the debug value.
'auto_reload' => true,
'auto_reload' => true,
// If set to false, Twig will silently ignore invalid variables
// (variables and or attributes/methods that do not exist) and
// replace them with a null value. When set to true, Twig throws an exception instead.
// default: false
'strict_variables' => false,
'strict_variables' => false,
// If set to true, auto-escaping will be enabled by default for all templates.
// default: 'html'
'autoescape' => 'html',
'autoescape' => 'html',
// A flag that indicates which optimizations to apply
// (default to -1 -- all optimizations are enabled; set it to 0 to disable)
'optimizations' => -1,
'optimizations' => -1,
],
/*
@ -95,7 +95,7 @@ return [
| NOTE: these will be overwritten if you pass data into the view with the same key.
|
*/
'globals' => [],
'globals' => [],
],
'extensions' => [
@ -110,7 +110,7 @@ return [
| `Twig\Extension\DebugExtension` is enabled automatically if twig.debug is TRUE.
|
*/
'enabled' => [
'enabled' => [
'TwigBridge\Extension\Loader\Facades',
'TwigBridge\Extension\Loader\Filters',
'TwigBridge\Extension\Loader\Functions',
@ -157,10 +157,10 @@ return [
| in order to be marked as safe.
|
*/
'facades' => [
'facades' => [
'Form' => [
'is_safe' => ['radio'],
],'Session'
], 'Session',
],
/*
@ -226,7 +226,7 @@ return [
| </code>
|
*/
'filters' => [
'filters' => [
'get' => 'data_get',
],
],

82
resources/views/emails/import/finished.blade.php

@ -1,57 +1,57 @@
@component('mail::message')
# Result of your import on {{ $time }}
# Result of your import on {{ $time }}
<hr>
<hr>
@if(count($errors) > 0)
## Errors
@endif
@if(count($errors) > 0)
## Errors
@endif
@foreach($errors as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {{ $message }}
@endforeach
@endforeach
@foreach($errors as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {{ $message }}
@endforeach
@endforeach
@if(count($warnings) > 0)
## Warnings
@endif
@if(count($warnings) > 0)
## Warnings
@endif
@foreach($warnings as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {{ $message }}
@endforeach
@endforeach
@foreach($warnings as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {{ $message }}
@endforeach
@endforeach
@if(count($messages) > 0)
## Messages
@endif
@if(count($messages) > 0)
## Messages
@endif
@foreach($messages as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {!! $message !!}
@endforeach
@endforeach
@foreach($messages as $index => $objList)
@foreach($objList as $message)
- Line #{{ $index + 1 }}: {!! $message !!}
@endforeach
@endforeach
<hr>
<hr>
@if(0 === count($errors))
_No errors detected_
@endif
@if(0 === count($errors))
_No errors detected_
@endif
@if(0 === count($warnings))
_No warnings detected_
@endif
@if(0 === count($warnings))
_No warnings detected_
@endif
@if(0 === count($messages))
_No messages detected_
@endif
@if(0 === count($messages))
_No messages detected_
@endif
@component('mail::button', ['url' => $url])
Go to Firefly III
@endcomponent
@component('mail::button', ['url' => $url])
Go to Firefly III
@endcomponent
Enjoy!<br>
Firefly III Data Importer, v{{ config('importer.version') }}
Enjoy!<br>
Firefly III Data Importer, v{{ config('importer.version') }}
@endcomponent

9
resources/views/import/002-authenticate/index.twig

@ -13,7 +13,8 @@
{{ subTitle }}
</div>
<div class="card-body">
<p>In order to import using {{ flow|capitalize }}, you must enter the authentication data you received from this provider.
<p>In order to import using {{ flow|capitalize }}, you must enter the authentication data you
received from this provider.
You can read how to get the necessary codes in the
<a target="_blank" href="https://docs.firefly-iii.org/data-importer/install/configure/">documentation</a>
</p>
@ -93,8 +94,10 @@
<div class="card">
<div class="card-body">
<div class="btn-group btn-group-sm">
<a href="{{ route('back.start') }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> Go back to index</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span class="fas fa-redo-alt"></span> Start over</a>
<a href="{{ route('back.start') }}" class="btn btn-secondary"><span
class="fas fa-arrow-left"></span> Go back to index</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span
class="fas fa-redo-alt"></span> Start over</a>
</div>
</div>
</div>

7
resources/views/import/003-upload/index.twig

@ -15,10 +15,13 @@
<div class="card-body">
<p>
Use the form elements below to upload your data.
If you need support, <a target="_blank" href="https://docs.firefly-iii.org/data-importer/usage/upload/">check out the documentation</a>.
If you need support, <a target="_blank"
href="https://docs.firefly-iii.org/data-importer/usage/upload/">check
out the documentation</a>.
</p>
<p>
A configuration file is entirely <strong>optional</strong>. You can use it to pre-configure the import options. In a later
A configuration file is entirely <strong>optional</strong>. You can use it to pre-configure
the import options. In a later
stage you may even use it for automation.
</p>
</div>

34
resources/views/import/004-configure/index.twig

@ -17,13 +17,15 @@
<p>
CSV files come in many shapes and forms. Some of the most important settings are below.
They apply to all lines in the file. If you would like some support, <a
href="https://docs.firefly-iii.org/data-importer/usage/configure/" target="_blank"> check out the documentation for this page.</a>
href="https://docs.firefly-iii.org/data-importer/usage/configure/" target="_blank">
check out the documentation for this page.</a>
</p>
{% endif %}
{% if 'nordigen' == flow or 'spectre' == flow %}
<p>
Your {{ flow|capitalize}} import can be configured and fine-tuned.
<a href="https://docs.firefly-iii.org/data-importer/usage/configure/" target="_blank">Check out the documentation for this page.</a>
Your {{ flow|capitalize }} import can be configured and fine-tuned.
<a href="https://docs.firefly-iii.org/data-importer/usage/configure/" target="_blank">Check
out the documentation for this page.</a>
</p>
{% endif %}
</div>
@ -57,7 +59,8 @@
<input type="hidden" name="connection" value="{{ configuration.getConnection }}"/>
<input type="hidden" name="nordigen_country" value="{{ configuration.getNordigenCountry }}"/>
<input type="hidden" name="nordigen_bank" value="{{ configuration.getNordigenBank }}"/>
<input type="hidden" name="nordigen_requisitions" value="{{ configuration.getNordigenRequisitions|json_encode }}"/>
<input type="hidden" name="nordigen_requisitions"
value="{{ configuration.getNordigenRequisitions|json_encode }}"/>
<div class="row mt-3">
<div class="col-lg-10 offset-lg-1">
<div class="card">
@ -82,7 +85,7 @@
</thead>
<tbody>
{% for information in importerAccounts %}
{# update a variable #}
{# update a variable #}
{% if 'disabled' == information.import_service.status %}
{% set errorAccounts = errorAccounts+1 %}
{% endif %}
@ -187,7 +190,9 @@
</tr>
{% endfor %}
</tbody>
<caption>Select the accounts you want to import into your Firefly III installation.</caption>
<caption>Select the accounts you want to import into your Firefly III
installation.
</caption>
</table>
{# TODO rewrite so Nordigen and Spectre use the same account object #}
@ -207,18 +212,23 @@
<label for="X" class="col-sm-3 col-form-label">Ignore Spectre categories</label>
<div class="col-sm-9">
<div class="form-check">
<input class="form-check-input" {% if configuration.isIgnoreSpectreCategories or null == configuration %}checked{% endif %} type="checkbox" value="1" id="ignore_spectre_categories" name="ignore_spectre_categories" aria-describedby="duplicateSpectre">
<input class="form-check-input"
{% if configuration.isIgnoreSpectreCategories or null == configuration %}checked{% endif %}
type="checkbox" value="1" id="ignore_spectre_categories"
name="ignore_spectre_categories"
aria-describedby="duplicateSpectre">
<label class="form-check-label" for="ignore_spectre_categories">
Ignore Spectre's categories.
</label>
</div>
<small class="form-text text-muted" id="duplicateSpectre">
Spectre adds categories to each transaction. You can choose to ignore them.
Spectre adds categories to each transaction. You can choose to ignore
them.
</small>
</div>
</div>
{% endif %}
{% endif %}
{% endif %}
@ -379,7 +389,8 @@
Yes
</label>
<small id="mapAllDataHelp" class="form-text text-muted">
<br>You get the opportunity to link your data to existing Firefly III data, for a cleaner import.
<br>You get the opportunity to link your data to existing Firefly III
data, for a cleaner import.
</small>
</div>
</div>
@ -475,7 +486,8 @@
Firefly III can automatically detect duplicate transactions. This is pretty
foolproof. In some special cases however,
you want more control over this process. Read more about the options below in <a
href="https://docs.firefly-iii.org/data-importer/usage/configure/" target="_blank">the documentation</a>.
href="https://docs.firefly-iii.org/data-importer/usage/configure/"
target="_blank">the documentation</a>.
</p>
</div>

6
resources/views/import/005-roles/index.twig

@ -130,8 +130,10 @@
<div class="card">
<div class="card-body">
<div class="btn-group btn-group-sm">
<a href="{{ route('back.config') }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> Go back to configuration</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span class="fas fa-redo-alt"></span> Start over</a>
<a href="{{ route('back.config') }}" class="btn btn-secondary"><span
class="fas fa-arrow-left"></span> Go back to configuration</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span
class="fas fa-redo-alt"></span> Start over</a>
</div>
</div>
</div>

3
resources/views/import/006-mapping/index.twig

@ -16,7 +16,8 @@
<p>Map data in your import to your Firefly III instance.</p>
<p>
Entries in your import may already exist in another form in your own Firefly III
instance. Be sure to <a target="_blank" href="https://docs.firefly-iii.org/data-importer/usage/map/">
instance. Be sure to <a target="_blank"
href="https://docs.firefly-iii.org/data-importer/usage/map/">
check out the documentation</a>, because this is where
the magic happens.
</p>

9
resources/views/import/009-selection/index.twig

@ -83,7 +83,8 @@
</div>
<div class="card-body">
<div class="form-group row">
<label for="bank_{{ country.code }}" class="col-sm-3 col-form-label">Bank ({{ country.code }})</label>
<label for="bank_{{ country.code }}" class="col-sm-3 col-form-label">Bank
({{ country.code }})</label>
<div class="col-sm-9">
<select class="form-control bank-selector" name="bank_{{ country.code }}"
id="bank_{{ country.code }}">
@ -137,8 +138,10 @@
<div class="card">
<div class="card-body">
<div class="btn-group btn-group-sm">
<a href="{{ route('back.upload') }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> Go back to upload</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span class="fas fa-redo-alt"></span> Start over</a>
<a href="{{ route('back.upload') }}" class="btn btn-secondary"><span
class="fas fa-arrow-left"></span> Go back to upload</a>
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span
class="fas fa-redo-alt"></span> Start over</a>
</div>
</div>
</div>

18
resources/views/token/client_id.twig

@ -50,16 +50,20 @@
{% if '' == baseURL %}
<div class="form-group mb-3">
<label for="input_base_url">Firefly III URL</label>
<input type="url" placeholder="https://" value="{{ baseURL }}" class="form-control" id="input_base_url" autocomplete="off" name="base_url">
{% if errors.has('base_url') %}<span class="text-danger">{{ errors.first('base_url') }}</span>{% endif %}
{% if Session.has('secure_url') %}<span class="text-danger">{{ Session.get('secure_url') }}</span>{% endif %}
<input type="url" placeholder="https://" value="{{ baseURL }}" class="form-control"
id="input_base_url" autocomplete="off" name="base_url">
{% if errors.has('base_url') %}<span
class="text-danger">{{ errors.first('base_url') }}</span>{% endif %}
{% if Session.has('secure_url') %}<span
class="text-danger">{{ Session.get('secure_url') }}</span>{% endif %}
</div>
{% endif %}
<div class="form-group mb-3">
<label for="input_client_id">Client ID</label>
<input type="number" step="1" min="1" class="form-control" id="input_client_id"
autocomplete="off" name="client_id" value="{{ clientId }}">
{% if errors.has('client_id') %}<span class="text-danger">{{ errors.first('client_id') }}</span>{% endif %}
{% if errors.has('client_id') %}<span
class="text-danger">{{ errors.first('client_id') }}</span>{% endif %}
</div>
<input type="submit" name="submit" value="Submit" class="float-end btn btn-success"/>
</form>
@ -69,8 +73,10 @@
<div class="card mt-3">
<div class="card-body">
<p>
<a class="btn btn-danger btn-sm" href="{{ route('flush') }}" data-bs-toggle="tooltip" data-bs-placement="top" title="This button resets your progress">Start over</a>
<a class="btn btn-danger btn-sm" href="{{ route('reset') }}" data-bs-toggle="tooltip" data-bs-placement="top" title="This button tries to connect to Firefly III again">Reauthenticate</a>
<a class="btn btn-danger btn-sm" href="{{ route('flush') }}" data-bs-toggle="tooltip"
data-bs-placement="top" title="This button resets your progress">Start over</a>
<a class="btn btn-danger btn-sm" href="{{ route('reset') }}" data-bs-toggle="tooltip"
data-bs-placement="top" title="This button tries to connect to Firefly III again">Reauthenticate</a>
</p>
</div>
</div>

9
resources/views/token/index.twig

@ -15,7 +15,7 @@
The Personal Access Token or the Client ID is invalid.
</p>
<p class="card-text">
Either you added the wrong value to the <code>.env</code> file or your Docker
Either you added the wrong value to the <code>.env</code> file or your Docker
parameters (<code>-e</code>) are wrong.
The error returned is:
@ -24,7 +24,8 @@
{{ errorMessage }}
</p>
<p class="card-text">
This error can be a bit cryptic, and you can find common errors on <a href="https://docs.firefly-iii.org/data-importer/errors/token_errors/" target="_blank">
This error can be a bit cryptic, and you can find common errors on <a
href="https://docs.firefly-iii.org/data-importer/errors/token_errors/" target="_blank">
this page in the documentation</a>.
Please refresh the page to validate your Personal Access Token again.
</p>
@ -40,6 +41,6 @@
</div>
</div>
<p>
</p>
<p>
</p>
{% endblock %}

2
routes/channels.php

@ -35,6 +35,6 @@ declare(strict_types=1);
Broadcast::channel(
'App.User.{id}', function ($user, $id) {
return (int)$user->id === (int)$id;
return (int) $user->id === (int) $id;
}
);

4
routes/web.php

@ -38,8 +38,8 @@ Route::get('/validate/spectre', 'ServiceController@validateSpectre')->name('vali
Route::get('/validate/nordigen', 'ServiceController@validateNordigen')->name('validate.nordigen');
// clear session
Route::get('/flush','IndexController@flush')->name('flush');
Route::get('/reset','IndexController@reset')->name('reset');
Route::get('/flush', 'IndexController@flush')->name('flush');
Route::get('/reset', 'IndexController@reset')->name('reset');
// step 2: Authenticate Nordigen / Spectre manually if necessary.
// check : must not be CSV flow. If so redirect to upload.

Loading…
Cancel
Save