From 0e615c6e4fe0f0e4fcd11e5f77987eef09a37013 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 27 Jun 2025 08:18:28 +0200 Subject: [PATCH 01/21] Expand readme and workflow. --- .github/workflows/release.yml | 24 +++++++++++++++++++++++- readme.md | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac136229..78fc5d4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -230,6 +230,12 @@ jobs: echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt echo "" >> output.txt echo ":warning: Please be careful with this pre-release, as it may not work as expected." >> output.txt + + # donations! + echo '' >> output.txt + echo '### Support Firefly III' >> output.txt + echo 'Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. For more information, please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information.' >> output.txt + echo '' >> output.txt fi # describe a branch release if [[ "$version" == branch* ]]; then @@ -250,16 +256,32 @@ jobs: if [[ "develop" != "$version" ]] && [[ "$version" != branch* ]] && [[ "$version" != *alpha* ]] && [[ "$version" != *beta* ]]; then echo 'Describe the latest release' sudo chown -R runner:docker output.txt + + # the changelog is in output.txt + mv output.txt output2.txt + touch output.txt echo '' >> output.txt - echo "Welcome to release $version of the Firefly III Data Importer. It contains the the latest fixes, translations and features. Docker users can find this release under the \`latest\` tag." >> output.txt + echo "Welcome to release $version of the Firefly III Data Importer. This release contains the the latest fixes, translations and features. Docker users can find this release under the \`latest\` tag." >> output.txt echo '' >> output.txt + + # add changelog to file. + cat output2.txt >> output.txt + echo '' >> output.txt + rm -f output2.txt + echo '### Instructions' >> output.txt echo '' >> output.txt echo "* Installation instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/installation/docker/), [Portainer](https://docs.firefly-iii.org/how-to/firefly-iii/installation/portainer/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/installation/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/installation/self-managed/)" >> output.txt echo "* Or read the upgrade instructions for [Docker](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/docker/), [Kubernetes](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/kubernetes/) or [self-managed servers](https://docs.firefly-iii.org/how-to/firefly-iii/upgrade/self-managed/)" >> output.txt echo "* The releases are signed, and you can verify them using the [Firefly III releases PGP key](https://docs.firefly-iii.org/explanation/more-information/signatures/)." >> output.txt + # donations! + echo '' >> output.txt + echo '### Support Firefly III' >> output.txt + echo 'Did you know you can support the development of Firefly III? You can donate in many ways, like GitHub Sponsors or Patreon. For more information, please [follow this link](https://bit.ly/donate-to-Firefly-III) for more information.' >> output.txt + echo '' >> output.txt + fi # describe alpha release diff --git a/readme.md b/readme.md index b461c215..04968517 100644 --- a/readme.md +++ b/readme.md @@ -34,7 +34,7 @@ The data importer does not connect to your bank directly. Instead, it uses [GoCardless](https://gocardless.com/) and [SaltEdge](https://www.saltedge.com/products/spectre/countries) to connect to over 6000 banks worldwide. These services are free for Firefly III users, but require registration. Keep in mind these services have their own privacy and data usage policies. -The data importer can import CSV files you've downloaded from your bank. +The data importer can also connect to your bank using `SimpleFIN`, and it can import CSV files you've downloaded from your bank. You can run the data importer once, for a bulk import. You can also run it regularly to keep up with new transactions. From b28c6c98aa028381886f691e8903929e9aa6e46c Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 27 Jun 2025 10:53:46 +0200 Subject: [PATCH 02/21] Catch parse exception when handling invalid dates. --- .../Routine/GenerateTransactions.php | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php index 446f82cd..ea864a01 100644 --- a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php +++ b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php @@ -90,20 +90,20 @@ class GenerateTransactions */ public function collectNordigenAccounts(): void { - $url = config('nordigen.url'); - $accessToken = TokenManager::getAccessToken(); - $info = []; + $url = config('nordigen.url'); + $accessToken = TokenManager::getAccessToken(); + $info = []; Log::debug('Going to collect account information from Nordigen.'); /** * @var string $nordigenIdentifier - * @var int $account + * @var int $account */ foreach ($this->accounts as $nordigenIdentifier => $account) { Log::debug(sprintf('Now at #%d => %s', $account, $nordigenIdentifier)); - $set = []; + $set = []; // get account details - $request = new GetAccountInformationRequest($url, $accessToken, $nordigenIdentifier); + $request = new GetAccountInformationRequest($url, $accessToken, $nordigenIdentifier); $request->setTimeOut(config('importer.connection.timeout')); // @var ArrayResponse $response @@ -161,14 +161,14 @@ class GenerateTransactions /** * @var string $accountId - * @var array $entries + * @var array $entries */ foreach ($transactions as $accountId => $entries) { $total = count($entries); Log::debug(sprintf('Going to parse account %s with %d transaction(s).', $accountId, $total)); /** - * @var int $index + * @var int $index * @var Transaction $entry */ foreach ($entries as $index => $entry) { @@ -192,13 +192,13 @@ class GenerateTransactions { Log::debug(sprintf('Nordigen transaction: "%s" with amount %s %s', $entry->getDescription(), $entry->currencyCode, $entry->transactionAmount)); - $return = [ + $return = [ 'apply_rules' => $this->configuration->isRules(), 'error_if_duplicate_hash' => $this->configuration->isIgnoreDuplicateTransactions(), 'transactions' => [], ]; - $valueDate = $entry->getValueDate(); - $transaction = [ + $valueDate = $entry->getValueDate(); + $transaction = [ 'type' => 'withdrawal', 'date' => $entry->getDate()->toW3cString(), 'datetime' => $entry->getDate()->toW3cString(), @@ -238,15 +238,17 @@ class GenerateTransactions } // #9533 add entry reference as tag or as booking date. if ('' !== $entry->entryReference) { - if (false === Carbon::parse($entry->entryReference)->getTimestamp()) { + $parsed = null; + try { + $parsed = Carbon::parse($entry->entryReference)->getTimestamp(); + } catch (InvalidFormatException $e) { + Log::debug(sprintf('Cannot parse entry reference "%s" as date, but that\'s OK.', $entry->entryReference)); + } + if (null === $parsed) { $transaction['tags'][] = $entry->entryReference; } - if (false !== Carbon::parse($entry->entryReference)->getTimestamp()) { - try { - $transaction['booking_date'] = Carbon::parse($entry->entryReference)->toW3cString(); - } catch (InvalidFormatException) { - // ignore error. - } + if (null !== $parsed) { + $transaction['booking_date'] = Carbon::parse($entry->entryReference)->toW3cString(); } } @@ -264,18 +266,18 @@ class GenerateTransactions private function appendPositiveAmountInfo(string $accountId, array $transaction, Transaction $entry): array { // amount is positive: deposit or transfer. Nordigen account is the destination - $transaction['type'] = 'deposit'; - $transaction['amount'] = $entry->transactionAmount; + $transaction['type'] = 'deposit'; + $transaction['amount'] = $entry->transactionAmount; // destination is a Nordigen account (has to be!) - $transaction['destination_id'] = (int) $this->accounts[$accountId]; + $transaction['destination_id'] = (int)$this->accounts[$accountId]; Log::debug(sprintf('Destination ID is now #%d, which should be a Firefly III asset account.', $transaction['destination_id'])); // append source iban and number (if present) - $transaction = $this->appendAccountFields($transaction, $entry, 'source'); + $transaction = $this->appendAccountFields($transaction, $entry, 'source'); // TODO clean up mapping - $mappedId = null; + $mappedId = null; if (isset($transaction['source_name'])) { Log::debug(sprintf('Check if "%s" is mapped to an account by the user.', $transaction['source_name'])); $mappedId = $this->getMappedAccountId($transaction['source_name']); @@ -302,7 +304,7 @@ class GenerateTransactions } } - $transaction = $this->positiveTransactionSafetyCatch($transaction, (string) $entry->getSourceName(), (string) $entry->getSourceIban()); + $transaction = $this->positiveTransactionSafetyCatch($transaction, (string)$entry->getSourceName(), (string)$entry->getSourceIban()); Log::debug(sprintf('destination_id = %d, source_name = "%s", source_iban = "%s", source_id = "%s"', $transaction['destination_id'] ?? '', $transaction['source_name'] ?? '', $transaction['source_iban'] ?? '', $transaction['source_id'] ?? '')); @@ -341,7 +343,7 @@ class GenerateTransactions break; } // temp measure to make sure it's a string: - $iban = (string) $iban; + $iban = (string)$iban; Log::debug('Done collecting account numbers and names.'); if ('' !== $number) { @@ -355,7 +357,7 @@ class GenerateTransactions } // The data importer determines the account type based on the IBAN. - $accountType = (string) ($this->targetTypes[$iban] ?? 'unknown'); + $accountType = (string)($this->targetTypes[$iban] ?? 'unknown'); // If the IBAN is a known target account, but it's not a liability OR revenue OR expense, the data importer knows for sure this is a transfer. // it will save the ID and nothing else. @@ -389,7 +391,7 @@ class GenerateTransactions // If the account number is a known target account, but it's not a liability, the data importer knows for sure this is a transfer. // it will save the ID and nothing else. - $accountType = (string) ($this->targetTypes[$number] ?? 'unknown'); + $accountType = (string)($this->targetTypes[$number] ?? 'unknown'); if ($this->isAssetAccount($accountType, $number) && sprintf(self::NUMBER_FORMAT, '') !== $number) { Log::debug(sprintf('Recognized "%s" (number) as a Firefly III asset account so this is a transfer.', $number)); $transaction[$idKey] = $this->targetAccounts[$number]; @@ -415,7 +417,7 @@ class GenerateTransactions private function getMappedAccountId(string $name): ?int { if (isset($this->configuration->getMapping()['accounts'][$name])) { - return (int) $this->configuration->getMapping()['accounts'][$name]; + return (int)$this->configuration->getMapping()['accounts'][$name]; } return null; @@ -450,8 +452,8 @@ class GenerateTransactions */ private function getAccountType(int $accountId): string { - $token = SecretManager::getAccessToken(); - $url = SecretManager::getBaseUrl(); + $token = SecretManager::getAccessToken(); + $url = SecretManager::getBaseUrl(); Log::debug(sprintf('Going to download account #%d', $accountId)); $request = new GetAccountRequest($url, $token); $request->setTimeOut(config('importer.connection.timeout')); @@ -463,7 +465,7 @@ class GenerateTransactions } catch (ApiHttpException $e) { throw new ImporterHttpException($e->getMessage(), 0, $e); } - $type = $result->getAccount()->type; + $type = $result->getAccount()->type; Log::debug(sprintf('Discovered that account #%d is of type "%s"', $accountId, $type)); @@ -502,12 +504,12 @@ class GenerateTransactions private function appendNegativeAmountInfo(string $accountId, array $transaction, Transaction $entry): array { $transaction['amount'] = bcmul($entry->transactionAmount, '-1'); - $transaction['source_id'] = (int) $this->accounts[$accountId]; // TODO entry may not exist, then what? + $transaction['source_id'] = (int)$this->accounts[$accountId]; // TODO entry may not exist, then what? // append source iban and number (if present) - $transaction = $this->appendAccountFields($transaction, $entry, 'destination'); + $transaction = $this->appendAccountFields($transaction, $entry, 'destination'); - $mappedId = null; + $mappedId = null; if (isset($transaction['destination_name'])) { Log::debug(sprintf('Check if "%s" is mapped to an account by the user.', $transaction['destination_name'])); $mappedId = $this->getMappedAccountId($transaction['destination_name']); @@ -518,7 +520,7 @@ class GenerateTransactions if (null !== $mappedId && 0 !== $mappedId) { Log::debug(sprintf('Account name "%s" is mapped to Firefly III account ID "%d"', $transaction['destination_name'], $mappedId)); - $mappedType = $this->getMappedAccountType($mappedId); + $mappedType = $this->getMappedAccountType($mappedId); $originalDestName = $transaction['destination_name']; $transaction['destination_id'] = $mappedId; @@ -535,7 +537,7 @@ class GenerateTransactions } } - $transaction = $this->negativeTransactionSafetyCatch($transaction, (string) $entry->getDestinationName(), (string) $entry->getDestinationIban()); + $transaction = $this->negativeTransactionSafetyCatch($transaction, (string)$entry->getDestinationName(), (string)$entry->getDestinationIban()); Log::debug(sprintf('source_id = %d, destination_id = "%s", destination_name = "%s", destination_iban = "%s"', $transaction['source_id'], $transaction['destination_id'] ?? '', $transaction['destination_name'] ?? '', $transaction['destination_iban'] ?? '')); From 4f8c8401d12fad239b987bd1307e78bb918766f4 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 27 Jun 2025 10:55:42 +0200 Subject: [PATCH 03/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Routine/GenerateTransactions.php | 49 ++++++++++--------- composer.lock | 12 ++--- config/importer.php | 2 +- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php index ea864a01..87506207 100644 --- a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php +++ b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php @@ -90,20 +90,20 @@ class GenerateTransactions */ public function collectNordigenAccounts(): void { - $url = config('nordigen.url'); - $accessToken = TokenManager::getAccessToken(); - $info = []; + $url = config('nordigen.url'); + $accessToken = TokenManager::getAccessToken(); + $info = []; Log::debug('Going to collect account information from Nordigen.'); /** * @var string $nordigenIdentifier - * @var int $account + * @var int $account */ foreach ($this->accounts as $nordigenIdentifier => $account) { Log::debug(sprintf('Now at #%d => %s', $account, $nordigenIdentifier)); - $set = []; + $set = []; // get account details - $request = new GetAccountInformationRequest($url, $accessToken, $nordigenIdentifier); + $request = new GetAccountInformationRequest($url, $accessToken, $nordigenIdentifier); $request->setTimeOut(config('importer.connection.timeout')); // @var ArrayResponse $response @@ -161,14 +161,14 @@ class GenerateTransactions /** * @var string $accountId - * @var array $entries + * @var array $entries */ foreach ($transactions as $accountId => $entries) { $total = count($entries); Log::debug(sprintf('Going to parse account %s with %d transaction(s).', $accountId, $total)); /** - * @var int $index + * @var int $index * @var Transaction $entry */ foreach ($entries as $index => $entry) { @@ -192,13 +192,13 @@ class GenerateTransactions { Log::debug(sprintf('Nordigen transaction: "%s" with amount %s %s', $entry->getDescription(), $entry->currencyCode, $entry->transactionAmount)); - $return = [ + $return = [ 'apply_rules' => $this->configuration->isRules(), 'error_if_duplicate_hash' => $this->configuration->isIgnoreDuplicateTransactions(), 'transactions' => [], ]; - $valueDate = $entry->getValueDate(); - $transaction = [ + $valueDate = $entry->getValueDate(); + $transaction = [ 'type' => 'withdrawal', 'date' => $entry->getDate()->toW3cString(), 'datetime' => $entry->getDate()->toW3cString(), @@ -239,6 +239,7 @@ class GenerateTransactions // #9533 add entry reference as tag or as booking date. if ('' !== $entry->entryReference) { $parsed = null; + try { $parsed = Carbon::parse($entry->entryReference)->getTimestamp(); } catch (InvalidFormatException $e) { @@ -266,18 +267,18 @@ class GenerateTransactions private function appendPositiveAmountInfo(string $accountId, array $transaction, Transaction $entry): array { // amount is positive: deposit or transfer. Nordigen account is the destination - $transaction['type'] = 'deposit'; - $transaction['amount'] = $entry->transactionAmount; + $transaction['type'] = 'deposit'; + $transaction['amount'] = $entry->transactionAmount; // destination is a Nordigen account (has to be!) $transaction['destination_id'] = (int)$this->accounts[$accountId]; Log::debug(sprintf('Destination ID is now #%d, which should be a Firefly III asset account.', $transaction['destination_id'])); // append source iban and number (if present) - $transaction = $this->appendAccountFields($transaction, $entry, 'source'); + $transaction = $this->appendAccountFields($transaction, $entry, 'source'); // TODO clean up mapping - $mappedId = null; + $mappedId = null; if (isset($transaction['source_name'])) { Log::debug(sprintf('Check if "%s" is mapped to an account by the user.', $transaction['source_name'])); $mappedId = $this->getMappedAccountId($transaction['source_name']); @@ -304,7 +305,7 @@ class GenerateTransactions } } - $transaction = $this->positiveTransactionSafetyCatch($transaction, (string)$entry->getSourceName(), (string)$entry->getSourceIban()); + $transaction = $this->positiveTransactionSafetyCatch($transaction, (string)$entry->getSourceName(), (string)$entry->getSourceIban()); Log::debug(sprintf('destination_id = %d, source_name = "%s", source_iban = "%s", source_id = "%s"', $transaction['destination_id'] ?? '', $transaction['source_name'] ?? '', $transaction['source_iban'] ?? '', $transaction['source_id'] ?? '')); @@ -343,7 +344,7 @@ class GenerateTransactions break; } // temp measure to make sure it's a string: - $iban = (string)$iban; + $iban = (string)$iban; Log::debug('Done collecting account numbers and names.'); if ('' !== $number) { @@ -452,8 +453,8 @@ class GenerateTransactions */ private function getAccountType(int $accountId): string { - $token = SecretManager::getAccessToken(); - $url = SecretManager::getBaseUrl(); + $token = SecretManager::getAccessToken(); + $url = SecretManager::getBaseUrl(); Log::debug(sprintf('Going to download account #%d', $accountId)); $request = new GetAccountRequest($url, $token); $request->setTimeOut(config('importer.connection.timeout')); @@ -465,7 +466,7 @@ class GenerateTransactions } catch (ApiHttpException $e) { throw new ImporterHttpException($e->getMessage(), 0, $e); } - $type = $result->getAccount()->type; + $type = $result->getAccount()->type; Log::debug(sprintf('Discovered that account #%d is of type "%s"', $accountId, $type)); @@ -507,9 +508,9 @@ class GenerateTransactions $transaction['source_id'] = (int)$this->accounts[$accountId]; // TODO entry may not exist, then what? // append source iban and number (if present) - $transaction = $this->appendAccountFields($transaction, $entry, 'destination'); + $transaction = $this->appendAccountFields($transaction, $entry, 'destination'); - $mappedId = null; + $mappedId = null; if (isset($transaction['destination_name'])) { Log::debug(sprintf('Check if "%s" is mapped to an account by the user.', $transaction['destination_name'])); $mappedId = $this->getMappedAccountId($transaction['destination_name']); @@ -520,7 +521,7 @@ class GenerateTransactions if (null !== $mappedId && 0 !== $mappedId) { Log::debug(sprintf('Account name "%s" is mapped to Firefly III account ID "%d"', $transaction['destination_name'], $mappedId)); - $mappedType = $this->getMappedAccountType($mappedId); + $mappedType = $this->getMappedAccountType($mappedId); $originalDestName = $transaction['destination_name']; $transaction['destination_id'] = $mappedId; @@ -537,7 +538,7 @@ class GenerateTransactions } } - $transaction = $this->negativeTransactionSafetyCatch($transaction, (string)$entry->getDestinationName(), (string)$entry->getDestinationIban()); + $transaction = $this->negativeTransactionSafetyCatch($transaction, (string)$entry->getDestinationName(), (string)$entry->getDestinationIban()); Log::debug(sprintf('source_id = %d, destination_id = "%s", destination_name = "%s", destination_iban = "%s"', $transaction['source_id'], $transaction['destination_id'] ?? '', $transaction['destination_name'] ?? '', $transaction['destination_iban'] ?? '')); diff --git a/composer.lock b/composer.lock index 3f327f7e..e36f2bd3 100644 --- a/composer.lock +++ b/composer.lock @@ -8247,16 +8247,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.2.3", + "version": "12.2.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "60a8ea2d8b2f070000051b56778009e11576e7d1" + "reference": "b71849b29f7a8d7574e4401873cb8b539896613f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60a8ea2d8b2f070000051b56778009e11576e7d1", - "reference": "60a8ea2d8b2f070000051b56778009e11576e7d1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b71849b29f7a8d7574e4401873cb8b539896613f", + "reference": "b71849b29f7a8d7574e4401873cb8b539896613f", "shasum": "" }, "require": { @@ -8324,7 +8324,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.5" }, "funding": [ { @@ -8348,7 +8348,7 @@ "type": "tidelift" } ], - "time": "2025-06-20T11:33:06+00:00" + "time": "2025-06-27T04:37:55+00:00" }, { "name": "rector/rector", diff --git a/config/importer.php b/config/importer.php index 159608cc..af41f50c 100644 --- a/config/importer.php +++ b/config/importer.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ - 'version' => '1.7.0', + 'version' => 'develop/2025-06-27', 'flows' => ['nordigen', 'spectre', 'file', 'simplefin'], 'enabled_flows' => [ 'nordigen' => true, From 6a37fb3a6f249d7cc60053434a420e439b95d700 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 27 Jun 2025 11:18:21 +0200 Subject: [PATCH 04/21] Remove info as tags, add in notes instead. --- .../Nordigen/Conversion/Routine/GenerateTransactions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php index 87506207..a4392959 100644 --- a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php +++ b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php @@ -246,7 +246,7 @@ class GenerateTransactions Log::debug(sprintf('Cannot parse entry reference "%s" as date, but that\'s OK.', $entry->entryReference)); } if (null === $parsed) { - $transaction['tags'][] = $entry->entryReference; + $transaction['notes'] = trim(sprintf("%s\n\nEntry reference: %s",$transaction['notes'], $entry->entryReference)); } if (null !== $parsed) { $transaction['booking_date'] = Carbon::parse($entry->entryReference)->toW3cString(); @@ -376,7 +376,7 @@ class GenerateTransactions $accountName = $this->getRevenueOrExpenseName($iban, $accountType); if ($bonusTag !== $accountName) { Log::debug(sprintf('Add account name "%s" as extra tag because the recognized account is called "%s".', $bonusTag, $accountName)); - $transaction['bonus_tags'][] = $bonusTag; + $transaction['notes'] = sprintf("%s\n\nOriginal account name: %s",$transaction['notes'], $bonusTag); } } From d12d1427a6dd38e5cb8006c081037c28864e1d95 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 27 Jun 2025 11:21:24 +0200 Subject: [PATCH 05/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Nordigen/Conversion/Routine/GenerateTransactions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php index a4392959..907cf80e 100644 --- a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php +++ b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php @@ -246,7 +246,7 @@ class GenerateTransactions Log::debug(sprintf('Cannot parse entry reference "%s" as date, but that\'s OK.', $entry->entryReference)); } if (null === $parsed) { - $transaction['notes'] = trim(sprintf("%s\n\nEntry reference: %s",$transaction['notes'], $entry->entryReference)); + $transaction['notes'] = trim(sprintf("%s\n\nEntry reference: %s", $transaction['notes'], $entry->entryReference)); } if (null !== $parsed) { $transaction['booking_date'] = Carbon::parse($entry->entryReference)->toW3cString(); @@ -376,7 +376,7 @@ class GenerateTransactions $accountName = $this->getRevenueOrExpenseName($iban, $accountType); if ($bonusTag !== $accountName) { Log::debug(sprintf('Add account name "%s" as extra tag because the recognized account is called "%s".', $bonusTag, $accountName)); - $transaction['notes'] = sprintf("%s\n\nOriginal account name: %s",$transaction['notes'], $bonusTag); + $transaction['notes'] = sprintf("%s\n\nOriginal account name: %s", $transaction['notes'], $bonusTag); } } From 059ea095ef1bbef0ad90db1edaeae51de1487276 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 27 Jun 2025 19:40:27 +0200 Subject: [PATCH 06/21] Fix variable reference. --- .../Nordigen/Conversion/Routine/GenerateTransactions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php index a4392959..123f9060 100644 --- a/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php +++ b/app/Services/Nordigen/Conversion/Routine/GenerateTransactions.php @@ -460,7 +460,7 @@ class GenerateTransactions $request->setTimeOut(config('importer.connection.timeout')); $request->setId($accountId); - // @var GetAccountResponse $result + /** @var GetAccountResponse $result */ try { $result = $request->get(); } catch (ApiHttpException $e) { From aca337cc4dd3dc141ff545f76e79711c8a94877e Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 27 Jun 2025 19:40:57 +0200 Subject: [PATCH 07/21] Add some counters, add warning instead of error. --- app/Console/AutoImports.php | 1 - app/Http/Controllers/Import/ConversionController.php | 5 +++-- .../Conversion/Routine/PseudoTransactionProcessor.php | 3 ++- .../Conversion/Routine/TransactionProcessor.php | 11 ++++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/Console/AutoImports.php b/app/Console/AutoImports.php index 429a4846..c4be753e 100644 --- a/app/Console/AutoImports.php +++ b/app/Console/AutoImports.php @@ -174,7 +174,6 @@ trait AutoImports { $exitCodes = []; - // @var string $file foreach ($files as $jsonFile => $importableFile) { try { $exitCodes[$importableFile] = $this->importFile($jsonFile, $importableFile); diff --git a/app/Http/Controllers/Import/ConversionController.php b/app/Http/Controllers/Import/ConversionController.php index a9202e86..7f540d3f 100644 --- a/app/Http/Controllers/Import/ConversionController.php +++ b/app/Http/Controllers/Import/ConversionController.php @@ -33,6 +33,7 @@ use App\Services\CSV\Conversion\RoutineManager as CSVRoutineManager; use App\Services\Nordigen\Conversion\RoutineManager as NordigenRoutineManager; 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\SimpleFIN\Conversion\RoutineManager as SimpleFINRoutineManager; use App\Services\SimpleFIN\Validation\ConfigurationContractValidator; @@ -117,7 +118,7 @@ class ConversionController extends Controller if (!in_array($flow, config('importer.flows'), true)) { throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); } - // @var RoutineManagerInterface $routine + /** @var RoutineManagerInterface $routine */ if ('file' === $flow) { $contentType = $configuration->getContentType(); if ('unknown' === $contentType || 'csv' === $contentType) { @@ -247,7 +248,7 @@ class ConversionController extends Controller if (!in_array($flow, config('importer.flows'), true)) { throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); } - // @var RoutineManagerInterface $routine + /** @var RoutineManagerInterface $routine */ if ('file' === $flow) { $contentType = $configuration->getContentType(); if ('unknown' === $contentType || 'csv' === $contentType) { diff --git a/app/Services/CSV/Conversion/Routine/PseudoTransactionProcessor.php b/app/Services/CSV/Conversion/Routine/PseudoTransactionProcessor.php index 141c9883..b6c27eb5 100644 --- a/app/Services/CSV/Conversion/Routine/PseudoTransactionProcessor.php +++ b/app/Services/CSV/Conversion/Routine/PseudoTransactionProcessor.php @@ -35,6 +35,7 @@ use GrumpyDictator\FFIIIApiSupport\Model\Account; use GrumpyDictator\FFIIIApiSupport\Model\TransactionCurrency; use GrumpyDictator\FFIIIApiSupport\Request\GetAccountRequest; use GrumpyDictator\FFIIIApiSupport\Request\GetCurrencyRequest; +use GrumpyDictator\FFIIIApiSupport\Response\GetAccountResponse; use GrumpyDictator\FFIIIApiSupport\Response\GetCurrencyResponse; use Illuminate\Support\Facades\Log; @@ -84,7 +85,7 @@ class PseudoTransactionProcessor $accountRequest->setTimeOut(config('importer.connection.timeout')); $accountRequest->setId($accountId); - // @var GetAccountResponse $result + /** @var GetAccountResponse $result */ try { $result = $accountRequest->get(); } catch (ApiHttpException $e) { diff --git a/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php b/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php index 6092ef00..f71b35db 100644 --- a/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php +++ b/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php @@ -74,9 +74,10 @@ class TransactionProcessor $return = []; Log::debug(sprintf('Found %d accounts to download from.', count($accounts))); + $total = count($accounts); foreach ($accounts as $key => $account) { $account = (string) $account; - Log::debug(sprintf('Going to download transactions for account #%d "%s"', $key, $account)); + Log::debug(sprintf('[%d/%d] Going to download transactions for account #%d "%s"', $key+1, $total, $key+1, $account)); $object = new Account(); $object->setIdentifier($account); $fullInfo = null; @@ -113,13 +114,13 @@ class TransactionProcessor $request = new GetTransactionsRequest($url, $accessToken, $account); $request->setTimeOut(config('importer.connection.timeout')); - // @var GetTransactionsResponse $transactions + /** @var GetTransactionsResponse $transactions */ try { $transactions = $request->get(); Log::debug(sprintf('GetTransactionsResponse: count %d transaction(s)', count($transactions))); } catch (ImporterHttpException|RateLimitException $e) { Log::debug(sprintf('Ran into %s instead of GetTransactionsResponse', $e::class)); - $this->addError(0, $e->getMessage()); + $this->addWarning(0, $e->getMessage()); $return[$account] = []; // save the rate limits: @@ -148,9 +149,9 @@ class TransactionProcessor ]; $return[$account] = $this->filterTransactions($transactions); - Log::debug(sprintf('Done downloading transactions for account %s "%s"', $key, $account)); + Log::debug(sprintf('[%d/%d] Done downloading transactions for account #%d "%s"', $key+1, $total, $key+1, $account)); } - Log::debug('Done with download'); + Log::debug('Done with download of transactions.'); return $return; } From dc328c35b1e6bd2e513e2e4afdbaf3136273ea44 Mon Sep 17 00:00:00 2001 From: JC5 Date: Fri, 27 Jun 2025 19:42:38 +0200 Subject: [PATCH 08/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Import/ConversionController.php | 2 ++ .../Nordigen/Conversion/Routine/TransactionProcessor.php | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Import/ConversionController.php b/app/Http/Controllers/Import/ConversionController.php index 7f540d3f..771ad439 100644 --- a/app/Http/Controllers/Import/ConversionController.php +++ b/app/Http/Controllers/Import/ConversionController.php @@ -118,6 +118,7 @@ class ConversionController extends Controller if (!in_array($flow, config('importer.flows'), true)) { throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); } + /** @var RoutineManagerInterface $routine */ if ('file' === $flow) { $contentType = $configuration->getContentType(); @@ -248,6 +249,7 @@ class ConversionController extends Controller if (!in_array($flow, config('importer.flows'), true)) { throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); } + /** @var RoutineManagerInterface $routine */ if ('file' === $flow) { $contentType = $configuration->getContentType(); diff --git a/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php b/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php index f71b35db..ce9e1d26 100644 --- a/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php +++ b/app/Services/Nordigen/Conversion/Routine/TransactionProcessor.php @@ -74,10 +74,10 @@ class TransactionProcessor $return = []; Log::debug(sprintf('Found %d accounts to download from.', count($accounts))); - $total = count($accounts); + $total = count($accounts); foreach ($accounts as $key => $account) { $account = (string) $account; - Log::debug(sprintf('[%d/%d] Going to download transactions for account #%d "%s"', $key+1, $total, $key+1, $account)); + Log::debug(sprintf('[%d/%d] Going to download transactions for account #%d "%s"', $key + 1, $total, $key + 1, $account)); $object = new Account(); $object->setIdentifier($account); $fullInfo = null; @@ -149,7 +149,7 @@ class TransactionProcessor ]; $return[$account] = $this->filterTransactions($transactions); - Log::debug(sprintf('[%d/%d] Done downloading transactions for account #%d "%s"', $key+1, $total, $key+1, $account)); + Log::debug(sprintf('[%d/%d] Done downloading transactions for account #%d "%s"', $key + 1, $total, $key + 1, $account)); } Log::debug('Done with download of transactions.'); From d0dfdd64ac37316a01fa0f0d17f9b104dcc68a72 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 29 Jun 2025 17:28:05 +0200 Subject: [PATCH 09/21] Fix import if not simplefin. --- .../Import/ConfigurationController.php | 4 +- app/Http/Controllers/IndexController.php | 2 +- app/Http/Controllers/TokenController.php | 2 +- .../Services/AccountInformationCollector.php | 4 + app/Support/Internal/CollectsAccounts.php | 5 +- app/Support/Internal/MergesAccountLists.php | 6 +- composer.lock | 142 +++++++++--------- .../firefly-iii-account-generic.blade.php | 2 + .../importer-account-title.blade.php | 3 +- .../v2/components/importer-account.blade.php | 2 +- 10 files changed, 90 insertions(+), 82 deletions(-) diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 84865abc..4107e22b 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -99,7 +99,6 @@ class ConfigurationController extends Controller // collect Firefly III accounts $fireflyIIIaccounts = $this->getFireflyIIIAccounts(); - // possibilities for duplicate detection (unique columns) // also get the nordigen / spectre accounts @@ -146,8 +145,7 @@ class ConfigurationController extends Controller $fileType = $detector->detectContentTypeFromContent($content); $configuration->setContentType($fileType); } - - // Get currency data for SimpleFIN account creation widget + // Get currency data for account creation widget $currencies = $this->getCurrencies(); return view('import.004-configure.index', compact('mainTitle', 'subTitle', 'fireflyIIIaccounts', 'configuration', 'flow', 'importerAccounts', 'uniqueColumns', 'currencies')); diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index 26b335bc..fa26a45c 100644 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -84,7 +84,7 @@ class IndexController extends Controller Log::debug('IndexController authentication detection', [ 'client_id' => $clientId, 'url' => $url, - 'access_token_config' => $accessTokenConfig, + 'access_token_config' => substr($accessTokenConfig, 0, 25) . '...', 'access_token_empty' => '' === $accessTokenConfig, ]); diff --git a/app/Http/Controllers/TokenController.php b/app/Http/Controllers/TokenController.php index 2617f8bc..b29af928 100644 --- a/app/Http/Controllers/TokenController.php +++ b/app/Http/Controllers/TokenController.php @@ -162,7 +162,6 @@ class TokenController extends Controller $infoRequest->setVerify(config('importer.connection.verify')); $infoRequest->setTimeOut(config('importer.connection.timeout')); - Log::debug(sprintf('Now trying to authenticate with Firefly III at %s', $url)); try { @@ -171,6 +170,7 @@ class TokenController extends Controller } catch (ApiHttpException $e) { Log::notice(sprintf('Could NOT authenticate with Firefly III at %s', $url)); Log::error(sprintf('Could not connect to Firefly III: %s', $e->getMessage())); + Log::debug(sprintf('Using access token "%s" (limited to 25 chars if present)', substr($token, 0, 25))); return response()->json(['result' => 'NOK', 'message' => $e->getMessage()]); } diff --git a/app/Services/Nordigen/Services/AccountInformationCollector.php b/app/Services/Nordigen/Services/AccountInformationCollector.php index 4678919b..ea4c8ee3 100644 --- a/app/Services/Nordigen/Services/AccountInformationCollector.php +++ b/app/Services/Nordigen/Services/AccountInformationCollector.php @@ -207,6 +207,10 @@ class AccountInformationCollector Log::debug('Set new IBAN from basic details.'); $account->setIban($array['iban']); } + if(array_key_exists('owner_name', $array) && '' !== $array['owner_name'] && '' === $account->getOwnerName()) { + Log::debug('Set new owner name from basic details.'); + $account->setOwnerName($array['owner_name']); + } return $account; } diff --git a/app/Support/Internal/CollectsAccounts.php b/app/Support/Internal/CollectsAccounts.php index 7a8700fb..72310d5b 100644 --- a/app/Support/Internal/CollectsAccounts.php +++ b/app/Support/Internal/CollectsAccounts.php @@ -58,7 +58,6 @@ trait CollectsAccounts Constants::LIABILITIES => [], ]; $url = null; - $token = null; try { $url = SecretManager::getBaseUrl(); @@ -140,9 +139,11 @@ trait CollectsAccounts Log::debug(sprintf('Now in %s', __METHOD__)); $requisitions = $configuration->getNordigenRequisitions(); $identifier = array_shift($requisitions); + $inCache = Cache::has($identifier) && config('importer.use_cache'); + $inCache = false; // if cached, return it. - if (Cache::has($identifier) && config('importer.use_cache')) { + if ($inCache) { $result = Cache::get($identifier); $return = []; foreach ($result as $arr) { diff --git a/app/Support/Internal/MergesAccountLists.php b/app/Support/Internal/MergesAccountLists.php index 53a34ff0..5bd241af 100644 --- a/app/Support/Internal/MergesAccountLists.php +++ b/app/Support/Internal/MergesAccountLists.php @@ -46,7 +46,7 @@ trait MergesAccountLists /** @var ImportServiceAccount $account */ foreach ($generic as $account) { - Log::debug(sprintf('Working on generic account "%s": "%s" ("%s", "%s")', $account->name, $account->id, $account->iban, $account->bban)); + Log::debug(sprintf('Working on generic account name: "%s": id:"%s" (iban:"%s", number:"%s")', $account->name, $account->id, $account->iban, $account->bban)); $iban = $account->iban; $number = $account->bban; @@ -75,7 +75,8 @@ trait MergesAccountLists continue; } Log::debug('No special filtering on the Firefly III account list.'); - $entry['firefly_iii_accounts'] = array_merge($fireflyIII[Constants::ASSET_ACCOUNTS], $fireflyIII[Constants::LIABILITIES]); + // remove array_merge because SimpleFIN does not do this so it broke all the other importer routines. + $entry['firefly_iii_accounts'] = $fireflyIII; $return[] = $entry; } @@ -84,6 +85,7 @@ trait MergesAccountLists protected function filterByAccountNumber(array $firefly, string $iban, string $number): array { + Log::debug(sprintf('Now filtering Firefly III accounts by IBAN "%s" or number "%s".', $iban, $number)); // FIXME this check should also check the number of the account. if ('' === $iban) { return []; diff --git a/composer.lock b/composer.lock index e36f2bd3..327887eb 100644 --- a/composer.lock +++ b/composer.lock @@ -516,12 +516,12 @@ "source": { "type": "git", "url": "https://github.com/JC5/api-support-classes.git", - "reference": "83127041fcf522c9548f8016569fe5d4403f2d83" + "reference": "c70158bc66bb3da01c9b7547539f1331ea2282ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/JC5/api-support-classes/zipball/83127041fcf522c9548f8016569fe5d4403f2d83", - "reference": "83127041fcf522c9548f8016569fe5d4403f2d83", + "url": "https://api.github.com/repos/JC5/api-support-classes/zipball/c70158bc66bb3da01c9b7547539f1331ea2282ce", + "reference": "c70158bc66bb3da01c9b7547539f1331ea2282ce", "shasum": "" }, "require": { @@ -615,7 +615,7 @@ "type": "patreon" } ], - "time": "2025-05-02T03:42:46+00:00" + "time": "2025-06-29T14:43:41+00:00" }, { "name": "fruitcake/php-cors", @@ -3611,16 +3611,16 @@ }, { "name": "symfony/console", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", "shasum": "" }, "require": { @@ -3685,7 +3685,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.0" + "source": "https://github.com/symfony/console/tree/v7.3.1" }, "funding": [ { @@ -3701,7 +3701,7 @@ "type": "tidelift" } ], - "time": "2025-05-24T10:34:04+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/css-selector", @@ -3837,16 +3837,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83" + "reference": "35b55b166f6752d6aaf21aa042fc5ed280fce235" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/cf68d225bc43629de4ff54778029aee6dc191b83", - "reference": "cf68d225bc43629de4ff54778029aee6dc191b83", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/35b55b166f6752d6aaf21aa042fc5ed280fce235", + "reference": "35b55b166f6752d6aaf21aa042fc5ed280fce235", "shasum": "" }, "require": { @@ -3894,7 +3894,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.3.0" + "source": "https://github.com/symfony/error-handler/tree/v7.3.1" }, "funding": [ { @@ -3910,7 +3910,7 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-06-13T07:48:40+00:00" }, { "name": "symfony/event-dispatcher", @@ -4134,16 +4134,16 @@ }, { "name": "symfony/http-client", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9" + "reference": "4403d87a2c16f33345dca93407a8714ee8c05a64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9", - "reference": "57e4fb86314015a695a750ace358d07a7e37b8a9", + "url": "https://api.github.com/repos/symfony/http-client/zipball/4403d87a2c16f33345dca93407a8714ee8c05a64", + "reference": "4403d87a2c16f33345dca93407a8714ee8c05a64", "shasum": "" }, "require": { @@ -4155,6 +4155,7 @@ }, "conflict": { "amphp/amp": "<2.5", + "amphp/socket": "<1.1", "php-http/discovery": "<1.15", "symfony/http-foundation": "<6.4" }, @@ -4167,7 +4168,6 @@ "require-dev": { "amphp/http-client": "^4.2.1|^5.0", "amphp/http-tunnel": "^1.0|^2.0", - "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", @@ -4209,7 +4209,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v7.3.0" + "source": "https://github.com/symfony/http-client/tree/v7.3.1" }, "funding": [ { @@ -4225,7 +4225,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T08:23:16+00:00" + "time": "2025-06-28T07:58:39+00:00" }, { "name": "symfony/http-client-contracts", @@ -4307,16 +4307,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "4236baf01609667d53b20371486228231eb135fd" + "reference": "23dd60256610c86a3414575b70c596e5deff6ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4236baf01609667d53b20371486228231eb135fd", - "reference": "4236baf01609667d53b20371486228231eb135fd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/23dd60256610c86a3414575b70c596e5deff6ed9", + "reference": "23dd60256610c86a3414575b70c596e5deff6ed9", "shasum": "" }, "require": { @@ -4366,7 +4366,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.3.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.1" }, "funding": [ { @@ -4382,20 +4382,20 @@ "type": "tidelift" } ], - "time": "2025-05-12T14:48:23+00:00" + "time": "2025-06-23T15:07:14+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f" + "reference": "1644879a66e4aa29c36fe33dfa6c54b450ce1831" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/ac7b8e163e8c83dce3abcc055a502d4486051a9f", - "reference": "ac7b8e163e8c83dce3abcc055a502d4486051a9f", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1644879a66e4aa29c36fe33dfa6c54b450ce1831", + "reference": "1644879a66e4aa29c36fe33dfa6c54b450ce1831", "shasum": "" }, "require": { @@ -4480,7 +4480,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.3.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.1" }, "funding": [ { @@ -4496,20 +4496,20 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:47:32+00:00" + "time": "2025-06-28T08:24:55+00:00" }, { "name": "symfony/mailer", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c" + "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/0f375bbbde96ae8c78e4aa3e63aabd486e33364c", - "reference": "0f375bbbde96ae8c78e4aa3e63aabd486e33364c", + "url": "https://api.github.com/repos/symfony/mailer/zipball/b5db5105b290bdbea5ab27b89c69effcf1cb3368", + "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368", "shasum": "" }, "require": { @@ -4560,7 +4560,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.3.0" + "source": "https://github.com/symfony/mailer/tree/v7.3.1" }, "funding": [ { @@ -4576,20 +4576,20 @@ "type": "tidelift" } ], - "time": "2025-04-04T09:51:09+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/mailgun-mailer", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/mailgun-mailer.git", - "reference": "3c1dfd9ff0a487a4116baec42d11ae21a061e3f1" + "reference": "8c18f2bff4e70ed5669ab8228302edd2fecd689b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/3c1dfd9ff0a487a4116baec42d11ae21a061e3f1", - "reference": "3c1dfd9ff0a487a4116baec42d11ae21a061e3f1", + "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/8c18f2bff4e70ed5669ab8228302edd2fecd689b", + "reference": "8c18f2bff4e70ed5669ab8228302edd2fecd689b", "shasum": "" }, "require": { @@ -4629,7 +4629,7 @@ "description": "Symfony Mailgun Mailer Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailgun-mailer/tree/v7.3.0" + "source": "https://github.com/symfony/mailgun-mailer/tree/v7.3.1" }, "funding": [ { @@ -4645,7 +4645,7 @@ "type": "tidelift" } ], - "time": "2024-09-28T08:24:38+00:00" + "time": "2025-06-20T16:15:52+00:00" }, { "name": "symfony/mime", @@ -5749,16 +5749,16 @@ }, { "name": "symfony/translation", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667" + "reference": "241d5ac4910d256660238a7ecf250deba4c73063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/4aba29076a29a3aa667e09b791e5f868973a8667", - "reference": "4aba29076a29a3aa667e09b791e5f868973a8667", + "url": "https://api.github.com/repos/symfony/translation/zipball/241d5ac4910d256660238a7ecf250deba4c73063", + "reference": "241d5ac4910d256660238a7ecf250deba4c73063", "shasum": "" }, "require": { @@ -5825,7 +5825,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.3.0" + "source": "https://github.com/symfony/translation/tree/v7.3.1" }, "funding": [ { @@ -5841,7 +5841,7 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:19:49+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/translation-contracts", @@ -5923,16 +5923,16 @@ }, { "name": "symfony/uid", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/7beeb2b885cd584cd01e126c5777206ae4c3c6a3", - "reference": "7beeb2b885cd584cd01e126c5777206ae4c3c6a3", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { @@ -5977,7 +5977,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.3.0" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -5993,20 +5993,20 @@ "type": "tidelift" } ], - "time": "2025-05-24T14:28:13+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e" + "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/548f6760c54197b1084e1e5c71f6d9d523f2f78e", - "reference": "548f6760c54197b1084e1e5c71f6d9d523f2f78e", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", + "reference": "6e209fbe5f5a7b6043baba46fe5735a4b85d0d42", "shasum": "" }, "require": { @@ -6061,7 +6061,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.1" }, "funding": [ { @@ -6077,7 +6077,7 @@ "type": "tidelift" } ], - "time": "2025-04-27T18:39:23+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", @@ -8416,12 +8416,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "b79895bbbfcb549006a8ee8efae7f77edffe5329" + "reference": "d4ca0cc4c49ba3437778e201d35844715d9b1bd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/b79895bbbfcb549006a8ee8efae7f77edffe5329", - "reference": "b79895bbbfcb549006a8ee8efae7f77edffe5329", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d4ca0cc4c49ba3437778e201d35844715d9b1bd9", + "reference": "d4ca0cc4c49ba3437778e201d35844715d9b1bd9", "shasum": "" }, "conflict": { @@ -8491,7 +8491,7 @@ "bedita/bedita": "<4", "bednee/cooluri": "<1.0.30", "bigfork/silverstripe-form-capture": ">=3,<3.1.1", - "billz/raspap-webgui": "<=3.1.4", + "billz/raspap-webgui": "<3.3.6", "bk2k/bootstrap-package": ">=7.1,<7.1.2|>=8,<8.0.8|>=9,<9.0.4|>=9.1,<9.1.3|>=10,<10.0.10|>=11,<11.0.3", "blueimp/jquery-file-upload": "==6.4.4", "bmarshall511/wordpress_zero_spam": "<5.2.13", @@ -9100,7 +9100,7 @@ "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1", "ssddanbrown/bookstack": "<24.05.1", "starcitizentools/citizen-skin": ">=2.4.2,<3.3.1", - "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2", + "starcitizentools/tabber-neue": ">=1.9.1,<2.7.2|>=3,<3.1.1", "statamic/cms": "<=5.16", "stormpath/sdk": "<9.9.99", "studio-42/elfinder": "<=2.1.64", @@ -9361,7 +9361,7 @@ "type": "tidelift" } ], - "time": "2025-06-24T22:05:35+00:00" + "time": "2025-06-27T21:05:09+00:00" }, { "name": "sebastian/cli-parser", diff --git a/resources/views/v2/components/firefly-iii-account-generic.blade.php b/resources/views/v2/components/firefly-iii-account-generic.blade.php index 49604550..822699fd 100644 --- a/resources/views/v2/components/firefly-iii-account-generic.blade.php +++ b/resources/views/v2/components/firefly-iii-account-generic.blade.php @@ -6,6 +6,7 @@ id="account-select-{{ $account['import_account']->id }}"> + @if('simplefin' === $flow) + @endif @foreach($account['firefly_iii_accounts'] as $accountGroupKey => $accountGroup) diff --git a/resources/views/v2/components/importer-account-title.blade.php b/resources/views/v2/components/importer-account-title.blade.php index 7a27b44a..6d560964 100644 --- a/resources/views/v2/components/importer-account-title.blade.php +++ b/resources/views/v2/components/importer-account-title.blade.php @@ -43,7 +43,7 @@ @if('' !== $account['import_account']->iban) title="IBAN: {{ $account['import_account']->iban }}" @endif >
- {{ $account['import_account']->name ?? 'Unnamed SimpleFIN Account' }} + {{ $account['import_account']->name ?? 'Unnamed account' }}
@if(isset($account['import_account']->org) && is_array($account['import_account']->org) && !empty($account['import_account']->org['name']))
@@ -87,6 +87,7 @@ @endif
+ {{-- Display 'mapped_to' if available --}} {{-- Display 'extra' fields if any --}} @php $extraData = (array)($account['import_account']->extra ?? []); @endphp @if(count($extraData) > 0) diff --git a/resources/views/v2/components/importer-account.blade.php b/resources/views/v2/components/importer-account.blade.php index 1d0fd7ba..5c702f0f 100644 --- a/resources/views/v2/components/importer-account.blade.php +++ b/resources/views/v2/components/importer-account.blade.php @@ -17,7 +17,7 @@ There are no Firefly III accounts to import into @endif @if( $flow === 'simplefin' || (isset($account['firefly_iii_accounts']['assets']) && count($account['firefly_iii_accounts']['assets']) > 0) || (isset($account['firefly_iii_accounts']['liabilities']) && count($account['firefly_iii_accounts']['liabilities']) > 0) ) - + @endif From 5db957c5c32ee7299d1e7a60fec972b021efb236 Mon Sep 17 00:00:00 2001 From: JC5 Date: Sun, 29 Jun 2025 17:30:38 +0200 Subject: [PATCH 10/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-29?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 12 ++++++------ app/Http/Controllers/IndexController.php | 2 +- .../Services/AccountInformationCollector.php | 2 +- app/Support/Internal/CollectsAccounts.php | 4 ++-- config/importer.php | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 4967d366..4ad923fd 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -1256,16 +1256,16 @@ }, { "name": "symfony/console", - "version": "v7.3.0", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", - "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", + "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", "shasum": "" }, "require": { @@ -1330,7 +1330,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.0" + "source": "https://github.com/symfony/console/tree/v7.3.1" }, "funding": [ { @@ -1346,7 +1346,7 @@ "type": "tidelift" } ], - "time": "2025-05-24T10:34:04+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/deprecation-contracts", diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index fa26a45c..074520d2 100644 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -84,7 +84,7 @@ class IndexController extends Controller Log::debug('IndexController authentication detection', [ 'client_id' => $clientId, 'url' => $url, - 'access_token_config' => substr($accessTokenConfig, 0, 25) . '...', + 'access_token_config' => substr($accessTokenConfig, 0, 25).'...', 'access_token_empty' => '' === $accessTokenConfig, ]); diff --git a/app/Services/Nordigen/Services/AccountInformationCollector.php b/app/Services/Nordigen/Services/AccountInformationCollector.php index ea4c8ee3..0d6d0ee7 100644 --- a/app/Services/Nordigen/Services/AccountInformationCollector.php +++ b/app/Services/Nordigen/Services/AccountInformationCollector.php @@ -207,7 +207,7 @@ class AccountInformationCollector Log::debug('Set new IBAN from basic details.'); $account->setIban($array['iban']); } - if(array_key_exists('owner_name', $array) && '' !== $array['owner_name'] && '' === $account->getOwnerName()) { + if (array_key_exists('owner_name', $array) && '' !== $array['owner_name'] && '' === $account->getOwnerName()) { Log::debug('Set new owner name from basic details.'); $account->setOwnerName($array['owner_name']); } diff --git a/app/Support/Internal/CollectsAccounts.php b/app/Support/Internal/CollectsAccounts.php index 72310d5b..ac431166 100644 --- a/app/Support/Internal/CollectsAccounts.php +++ b/app/Support/Internal/CollectsAccounts.php @@ -139,8 +139,8 @@ trait CollectsAccounts Log::debug(sprintf('Now in %s', __METHOD__)); $requisitions = $configuration->getNordigenRequisitions(); $identifier = array_shift($requisitions); - $inCache = Cache::has($identifier) && config('importer.use_cache'); - $inCache = false; + $inCache = Cache::has($identifier) && config('importer.use_cache'); + $inCache = false; // if cached, return it. if ($inCache) { diff --git a/config/importer.php b/config/importer.php index af41f50c..8d9d9c96 100644 --- a/config/importer.php +++ b/config/importer.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ - 'version' => 'develop/2025-06-27', + 'version' => 'develop/2025-06-29', 'flows' => ['nordigen', 'spectre', 'file', 'simplefin'], 'enabled_flows' => [ 'nordigen' => true, From f8c71193d509e7b383da9c329f2572f2abec81b7 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 30 Jun 2025 20:24:04 +0200 Subject: [PATCH 11/21] Add validation. --- .../Nordigen/Conversion/RoutineManager.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/Services/Nordigen/Conversion/RoutineManager.php b/app/Services/Nordigen/Conversion/RoutineManager.php index b4dca2dc..2f0355e2 100644 --- a/app/Services/Nordigen/Conversion/RoutineManager.php +++ b/app/Services/Nordigen/Conversion/RoutineManager.php @@ -106,6 +106,9 @@ class RoutineManager implements RoutineManagerInterface Log::debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('The GoCardless API URL is %s', config('nordigen.url'))); + // Step 0: configuration validation. + $this->validateAccounts(); + // Step 1: get transactions from GoCardless $this->downloadFromGoCardless(); @@ -357,4 +360,18 @@ class RoutineManager implements RoutineManagerInterface throw new ImporterErrorException($e->getMessage(), 0, $e); } } + + /** + * @throws ImporterErrorException + */ + private function validateAccounts(): void + { + Log::debug('Validating accounts in configuration.'); + $accounts = $this->configuration->getAccounts(); + foreach($accounts as $key => $accountId) { + if(0 === (int)$accountId) { + throw new ImporterErrorException(sprintf('Cannot import GoCardless account "%s" into Firefly III account #%d. Recreate your configuration file.',$key, $accountId)); + } + } + } } From 60b294cbd1b9ab8ec2b100a504b9efa8ab0974d9 Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 30 Jun 2025 20:31:57 +0200 Subject: [PATCH 12/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ci/php-cs-fixer/composer.lock | 53 ++++++++++--------- app/Http/Controllers/Controller.php | 10 ++-- .../Nordigen/Conversion/RoutineManager.php | 6 +-- config/importer.php | 2 +- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/.ci/php-cs-fixer/composer.lock b/.ci/php-cs-fixer/composer.lock index 4ad923fd..da18bed2 100644 --- a/.ci/php-cs-fixer/composer.lock +++ b/.ci/php-cs-fixer/composer.lock @@ -406,58 +406,59 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.75.0", + "version": "v3.76.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" + "reference": "0e3c484cef0ae9314b0f85986a36296087432c40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", - "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/0e3c484cef0ae9314b0f85986a36296087432c40", + "reference": "0e3c484cef0ae9314b0f85986a36296087432c40", "shasum": "" }, "require": { "clue/ndjson-react": "^1.0", "composer/semver": "^3.4", - "composer/xdebug-handler": "^3.0.3", + "composer/xdebug-handler": "^3.0.5", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", "fidry/cpu-core-counter": "^1.2", "php": "^7.4 || ^8.0", - "react/child-process": "^0.6.5", + "react/child-process": "^0.6.6", "react/event-loop": "^1.0", - "react/promise": "^2.0 || ^3.0", + "react/promise": "^2.11 || ^3.0", "react/socket": "^1.0", "react/stream": "^1.0", - "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", - "symfony/console": "^5.4 || ^6.4 || ^7.0", - "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", - "symfony/finder": "^5.4 || ^6.4 || ^7.0", - "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0", - "symfony/polyfill-mbstring": "^1.31", - "symfony/polyfill-php80": "^1.31", - "symfony/polyfill-php81": "^1.31", - "symfony/process": "^5.4 || ^6.4 || ^7.2", - "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", + "symfony/console": "^5.4.45 || ^6.4.13 || ^7.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", + "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", + "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", + "symfony/polyfill-mbstring": "^1.32", + "symfony/polyfill-php80": "^1.32", + "symfony/polyfill-php81": "^1.32", + "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", + "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" }, "require-dev": { "facile-it/paraunit": "^1.3.1 || ^2.6", "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.2", - "keradus/cli-executor": "^2.1", + "justinrainbow/json-schema": "^5.3 || ^6.4", + "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.7", + "php-coveralls/php-coveralls": "^2.8", "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", - "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", - "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" + "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", + "symfony/polyfill-php84": "^1.32", + "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", + "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -498,7 +499,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.76.0" }, "funding": [ { @@ -506,7 +507,7 @@ "type": "github" } ], - "time": "2025-03-31T18:40:42+00:00" + "time": "2025-06-30T14:15:06+00:00" }, { "name": "psr/container", diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 28289e3b..abe7314e 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -48,13 +48,13 @@ class Controller extends BaseController public function __construct() { // validate some env vars (skip over config) -// $accessToken = (string) env('FIREFLY_III_ACCESS_TOKEN', ''); -// $clientId = (string) env('FIREFLY_III_CLIENT_ID', ''); -// $baseUrl = (string) env('FIREFLY_III_URL', ''); -// $vanityUrl = (string) env('VANITY_URL', ''); + // $accessToken = (string) env('FIREFLY_III_ACCESS_TOKEN', ''); + // $clientId = (string) env('FIREFLY_III_CLIENT_ID', ''); + // $baseUrl = (string) env('FIREFLY_III_URL', ''); + // $vanityUrl = (string) env('VANITY_URL', ''); // experimental. Use config instead - $accessToken = (string) config('importer.access_token',''); + $accessToken = (string) config('importer.access_token', ''); $clientId = (string) config('importer.client_id', ''); $baseUrl = (string) config('importer.url', ''); $vanityUrl = (string) config('importer.vanity_url', ''); diff --git a/app/Services/Nordigen/Conversion/RoutineManager.php b/app/Services/Nordigen/Conversion/RoutineManager.php index 2f0355e2..f450d6f8 100644 --- a/app/Services/Nordigen/Conversion/RoutineManager.php +++ b/app/Services/Nordigen/Conversion/RoutineManager.php @@ -368,9 +368,9 @@ class RoutineManager implements RoutineManagerInterface { Log::debug('Validating accounts in configuration.'); $accounts = $this->configuration->getAccounts(); - foreach($accounts as $key => $accountId) { - if(0 === (int)$accountId) { - throw new ImporterErrorException(sprintf('Cannot import GoCardless account "%s" into Firefly III account #%d. Recreate your configuration file.',$key, $accountId)); + foreach ($accounts as $key => $accountId) { + if (0 === (int)$accountId) { + throw new ImporterErrorException(sprintf('Cannot import GoCardless account "%s" into Firefly III account #%d. Recreate your configuration file.', $key, $accountId)); } } } diff --git a/config/importer.php b/config/importer.php index 8d9d9c96..b0d4532a 100644 --- a/config/importer.php +++ b/config/importer.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ - 'version' => 'develop/2025-06-29', + 'version' => 'develop/2025-06-30', 'flows' => ['nordigen', 'spectre', 'file', 'simplefin'], 'enabled_flows' => [ 'nordigen' => true, From 9c8615bb6d97322f486bac76d2f94bf38cac2079 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 30 Jun 2025 20:39:53 +0200 Subject: [PATCH 13/21] Move validation. --- app/Services/Nordigen/Conversion/RoutineManager.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Services/Nordigen/Conversion/RoutineManager.php b/app/Services/Nordigen/Conversion/RoutineManager.php index 2f0355e2..bce1cf4f 100644 --- a/app/Services/Nordigen/Conversion/RoutineManager.php +++ b/app/Services/Nordigen/Conversion/RoutineManager.php @@ -83,11 +83,17 @@ class RoutineManager implements RoutineManagerInterface return $this->transactionProcessor->getAccounts(); } + /** + * @throws ImporterErrorException + */ public function setConfiguration(Configuration $configuration): void { // save config $this->configuration = $configuration; + // Step 0: configuration validation. + $this->validateAccounts(); + // share config $this->transactionProcessor->setConfiguration($configuration); $this->transactionGenerator->setConfiguration($configuration); @@ -106,9 +112,6 @@ class RoutineManager implements RoutineManagerInterface Log::debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('The GoCardless API URL is %s', config('nordigen.url'))); - // Step 0: configuration validation. - $this->validateAccounts(); - // Step 1: get transactions from GoCardless $this->downloadFromGoCardless(); From 7efe9148a5e9893db1b0951fa402b83384770569 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 30 Jun 2025 20:42:55 +0200 Subject: [PATCH 14/21] Add some spaces --- app/Services/Nordigen/Conversion/RoutineManager.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Services/Nordigen/Conversion/RoutineManager.php b/app/Services/Nordigen/Conversion/RoutineManager.php index c93d85a6..4f8230f2 100644 --- a/app/Services/Nordigen/Conversion/RoutineManager.php +++ b/app/Services/Nordigen/Conversion/RoutineManager.php @@ -369,9 +369,9 @@ class RoutineManager implements RoutineManagerInterface */ private function validateAccounts(): void { - Log::debug('Validating accounts in configuration.'); - $accounts = $this->configuration->getAccounts(); - foreach ($accounts as $key => $accountId) { + Log:: debug('Validating accounts in configuration.'); + $accounts = $this->configuration->getAccounts(); + foreach ($accounts as $key => $accountId) { if (0 === (int)$accountId) { throw new ImporterErrorException(sprintf('Cannot import GoCardless account "%s" into Firefly III account #%d. Recreate your configuration file.', $key, $accountId)); } From ed995292c08b4a99f05fde82b3afe8af9a31b8a6 Mon Sep 17 00:00:00 2001 From: JC5 Date: Mon, 30 Jun 2025 20:44:40 +0200 Subject: [PATCH 15/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-06-30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Services/Nordigen/Conversion/RoutineManager.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Services/Nordigen/Conversion/RoutineManager.php b/app/Services/Nordigen/Conversion/RoutineManager.php index 4f8230f2..c93d85a6 100644 --- a/app/Services/Nordigen/Conversion/RoutineManager.php +++ b/app/Services/Nordigen/Conversion/RoutineManager.php @@ -369,9 +369,9 @@ class RoutineManager implements RoutineManagerInterface */ private function validateAccounts(): void { - Log:: debug('Validating accounts in configuration.'); - $accounts = $this->configuration->getAccounts(); - foreach ($accounts as $key => $accountId) { + Log::debug('Validating accounts in configuration.'); + $accounts = $this->configuration->getAccounts(); + foreach ($accounts as $key => $accountId) { if (0 === (int)$accountId) { throw new ImporterErrorException(sprintf('Cannot import GoCardless account "%s" into Firefly III account #%d. Recreate your configuration file.', $key, $accountId)); } From 61f9e2dbc78c48b64246ceea869563c762cd3242 Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 1 Jul 2025 05:58:19 +0200 Subject: [PATCH 16/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-07-01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/importer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/importer.php b/config/importer.php index b0d4532a..8ca09628 100644 --- a/config/importer.php +++ b/config/importer.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ - 'version' => 'develop/2025-06-30', + 'version' => 'develop/2025-07-01', 'flows' => ['nordigen', 'spectre', 'file', 'simplefin'], 'enabled_flows' => [ 'nordigen' => true, From 7bc2610c54689e331b1a0055d0241c6e558a1f4d Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 1 Jul 2025 19:56:08 +0200 Subject: [PATCH 17/21] Fix https://github.com/firefly-iii/firefly-iii/issues/10508 --- .../Import/ConfigurationController.php | 1 + app/Support/Internal/MergesAccountLists.php | 86 ++++++++++++------- .../v2/components/importer-account.blade.php | 10 ++- 3 files changed, 63 insertions(+), 34 deletions(-) diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 4107e22b..2e2cf276 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -98,6 +98,7 @@ class ConfigurationController extends Controller } // collect Firefly III accounts + // this function returns an array with keys 'assets' and 'liabilities', each containing an array of Firefly III accounts. $fireflyIIIaccounts = $this->getFireflyIIIAccounts(); // possibilities for duplicate detection (unique columns) diff --git a/app/Support/Internal/MergesAccountLists.php b/app/Support/Internal/MergesAccountLists.php index 5bd241af..ff057690 100644 --- a/app/Support/Internal/MergesAccountLists.php +++ b/app/Support/Internal/MergesAccountLists.php @@ -53,26 +53,39 @@ trait MergesAccountLists $currency = $account->currencyCode; $entry = [ 'import_account' => $account, + 'firefly_iii_accounts' => [ + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], + ], ]; $filteredByNumber = $this->filterByAccountNumber($fireflyIII, $iban, $number); $filteredByCurrency = $this->filterByCurrency($fireflyIII, $currency); - - if (1 === count($filteredByNumber)) { - Log::debug(sprintf('Generic account ("%s", "%s") has a single FF3 counter part (#%d, "%s")', $iban, $number, $filteredByNumber[0]->id, $filteredByNumber[0]->name)); - $entry['firefly_iii_accounts'] = array_unique(array_merge($filteredByNumber, $filteredByCurrency), SORT_REGULAR); - $return[] = $entry; - - continue; + Log::debug('Filtered by number', $filteredByNumber); + Log::debug('Filtered by currency', $filteredByCurrency); + $count = 0; + foreach([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { + if (1 === count($filteredByNumber[$key])) { + Log::debug(sprintf('Generic account ("%s", "%s") has a single FF3 %s counter part (#%d, "%s")', $iban, $number, $key, $filteredByNumber[$key][0]->id, $filteredByNumber[$key][0]->name)); + $entry['firefly_iii_accounts'][$key] = array_unique(array_merge( + $filteredByNumber[$key], + $filteredByCurrency[$key]), SORT_REGULAR); + $return[] = $entry; + $count++; + continue 2; + } } - Log::debug(sprintf('Found %d FF3 accounts with the same IBAN or number ("%s")', count($filteredByNumber), $iban)); - if (count($filteredByCurrency) > 0) { - Log::debug(sprintf('Generic account ("%s") has %d Firefly III counter part(s) with the same currency %s.', $account->name, count($filteredByCurrency), $currency)); - $entry['firefly_iii_accounts'] = $filteredByCurrency; - $return[] = $entry; + Log::debug(sprintf('Found %d FF3 accounts with the same IBAN or number ("%s")', $count, $iban)); + unset($count); - continue; + foreach([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { + if (count($filteredByCurrency[$key]) > 0) { + Log::debug(sprintf('Generic account ("%s") has %d Firefly III %s counter part(s) with the same currency %s.', $account->name, $key, count($filteredByCurrency), $currency)); + $entry['firefly_iii_accounts'][$key] = $filteredByCurrency[$key]; + $return[] = $entry; + continue 2; + } } Log::debug('No special filtering on the Firefly III account list.'); // remove array_merge because SimpleFIN does not do this so it broke all the other importer routines. @@ -83,21 +96,26 @@ trait MergesAccountLists return $return; } - protected function filterByAccountNumber(array $firefly, string $iban, string $number): array + protected function filterByAccountNumber(array $fireflyIII, string $iban, string $number): array { Log::debug(sprintf('Now filtering Firefly III accounts by IBAN "%s" or number "%s".', $iban, $number)); // FIXME this check should also check the number of the account. if ('' === $iban) { - return []; + return [ + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], + ]; } - $result = []; - // TODO check if this the correct merge type. - $all = array_merge($firefly[Constants::ASSET_ACCOUNTS] ?? [], $firefly[Constants::LIABILITIES] ?? []); - - /** @var Account $account */ - foreach ($all as $account) { - if ($iban === $account->iban || $number === $account->number || $iban === $account->number || $number === $account->iban) { - $result[] = $account; + $result = [ + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], + ]; + + foreach($fireflyIII as $key => $accounts) { + foreach ($accounts as $account) { + if ($iban === $account->iban || $number === $account->number || $iban === $account->number || $number === $account->iban) { + $result[$key][] = $account; + } } } @@ -107,15 +125,21 @@ trait MergesAccountLists protected function filterByCurrency(array $fireflyIII, string $currency): array { if ('' === $currency) { - return []; + return [ + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], + ]; } - $result = []; - $all = array_merge($fireflyIII[Constants::ASSET_ACCOUNTS] ?? [], $fireflyIII[Constants::LIABILITIES] ?? []); - - /** @var Account $account */ - foreach ($all as $account) { - if ($currency === $account->currencyCode) { - $result[] = $account; + $result = [ + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], + ]; + + foreach($fireflyIII as $key => $accounts) { + foreach ($accounts as $account) { + if ($currency === $account->currencyCode) { + $result[$key][] = $account; + } } } diff --git a/resources/views/v2/components/importer-account.blade.php b/resources/views/v2/components/importer-account.blade.php index 5c702f0f..9bbbddd2 100644 --- a/resources/views/v2/components/importer-account.blade.php +++ b/resources/views/v2/components/importer-account.blade.php @@ -13,8 +13,12 @@
- @if( $flow !== 'simplefin' && (!isset($account['firefly_iii_accounts']['assets']) || count($account['firefly_iii_accounts']['assets']) === 0) && (!isset($account['firefly_iii_accounts']['liabilities']) || count($account['firefly_iii_accounts']['liabilities']) === 0) ) - There are no Firefly III accounts to import into + @if( + // flow is not simplefin. + $flow !== 'simplefin' && + ((!isset($account['firefly_iii_accounts']['assets']) || count($account['firefly_iii_accounts']['assets']) === 0) && (!isset($account['firefly_iii_accounts']['liabilities']) || count($account['firefly_iii_accounts']['liabilities']) === 0) ) + ) + X There are no Firefly III accounts to import into @endif @if( $flow === 'simplefin' || (isset($account['firefly_iii_accounts']['assets']) && count($account['firefly_iii_accounts']['assets']) > 0) || (isset($account['firefly_iii_accounts']['liabilities']) && count($account['firefly_iii_accounts']['liabilities']) > 0) ) @@ -24,7 +28,7 @@ From 0f43789ccb4068601a198608f16fee6817c965a7 Mon Sep 17 00:00:00 2001 From: JC5 Date: Tue, 1 Jul 2025 19:58:36 +0200 Subject: [PATCH 18/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'develop'=20on=202025-07-01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Support/Internal/MergesAccountLists.php | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/app/Support/Internal/MergesAccountLists.php b/app/Support/Internal/MergesAccountLists.php index ff057690..5e0eb0bd 100644 --- a/app/Support/Internal/MergesAccountLists.php +++ b/app/Support/Internal/MergesAccountLists.php @@ -52,7 +52,7 @@ trait MergesAccountLists $number = $account->bban; $currency = $account->currencyCode; $entry = [ - 'import_account' => $account, + 'import_account' => $account, 'firefly_iii_accounts' => [ Constants::ASSET_ACCOUNTS => [], Constants::LIABILITIES => [], @@ -63,15 +63,17 @@ trait MergesAccountLists $filteredByCurrency = $this->filterByCurrency($fireflyIII, $currency); Log::debug('Filtered by number', $filteredByNumber); Log::debug('Filtered by currency', $filteredByCurrency); - $count = 0; - foreach([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { + $count = 0; + foreach ([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { if (1 === count($filteredByNumber[$key])) { Log::debug(sprintf('Generic account ("%s", "%s") has a single FF3 %s counter part (#%d, "%s")', $iban, $number, $key, $filteredByNumber[$key][0]->id, $filteredByNumber[$key][0]->name)); $entry['firefly_iii_accounts'][$key] = array_unique(array_merge( $filteredByNumber[$key], - $filteredByCurrency[$key]), SORT_REGULAR); - $return[] = $entry; - $count++; + $filteredByCurrency[$key] + ), SORT_REGULAR); + $return[] = $entry; + ++$count; + continue 2; } } @@ -79,11 +81,12 @@ trait MergesAccountLists Log::debug(sprintf('Found %d FF3 accounts with the same IBAN or number ("%s")', $count, $iban)); unset($count); - foreach([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { + foreach ([Constants::ASSET_ACCOUNTS, Constants::LIABILITIES] as $key) { if (count($filteredByCurrency[$key]) > 0) { Log::debug(sprintf('Generic account ("%s") has %d Firefly III %s counter part(s) with the same currency %s.', $account->name, $key, count($filteredByCurrency), $currency)); $entry['firefly_iii_accounts'][$key] = $filteredByCurrency[$key]; - $return[] = $entry; + $return[] = $entry; + continue 2; } } @@ -102,8 +105,8 @@ trait MergesAccountLists // FIXME this check should also check the number of the account. if ('' === $iban) { return [ - Constants::ASSET_ACCOUNTS => [], - Constants::LIABILITIES => [], + Constants::ASSET_ACCOUNTS => [], + Constants::LIABILITIES => [], ]; } $result = [ @@ -111,7 +114,7 @@ trait MergesAccountLists Constants::LIABILITIES => [], ]; - foreach($fireflyIII as $key => $accounts) { + foreach ($fireflyIII as $key => $accounts) { foreach ($accounts as $account) { if ($iban === $account->iban || $number === $account->number || $iban === $account->number || $number === $account->iban) { $result[$key][] = $account; @@ -135,7 +138,7 @@ trait MergesAccountLists Constants::LIABILITIES => [], ]; - foreach($fireflyIII as $key => $accounts) { + foreach ($fireflyIII as $key => $accounts) { foreach ($accounts as $account) { if ($currency === $account->currencyCode) { $result[$key][] = $account; From 7722cefe042c946b25d5a2a31ac6dc50929eb1ef Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Jul 2025 06:02:57 +0200 Subject: [PATCH 19/21] Update changelog, push new version. --- changelog.md | 25 +++++++++++++++++++++++++ config/importer.php | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 3e613a2d..255e39d1 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,31 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## v1.7.1 - 2025-07-02 + +> ⚠️ Some changes in this release may unexpectedly lead to duplicate transactions. This is caused by changes in the data handling routines. This is unfortunate, but a result of new insights, changed APIs and other minor fixes. My apologies for any inconvenience. I try to avoid these kinds of changes, but it can't always be helped. + +### Added +- Initial release. + +### Changed +- For GoCardless imports: opposing account names and other reference data is no longer added as tags, but as text in the notes. This may lead to duplicates. +- GoCardless: better handling of rate limits. + +### Deprecated +- Initial release. + +### Removed +- Initial release. + +### Fixed +- #10508 +- Improved parsing of dates. + +### Security +- Initial release. + + ## v1.7.0 - 2025-06-26 ### Added diff --git a/config/importer.php b/config/importer.php index 8ca09628..2ef5fb6a 100644 --- a/config/importer.php +++ b/config/importer.php @@ -68,7 +68,7 @@ return [ 'ignore_not_found_transactions' => env('IGNORE_NOT_FOUND_TRANSACTIONS', false), 'namespace' => 'c40dcba2-411d-11ec-973a-0242ac130003', 'use_cache' => env('USE_CACHE', false), - 'minimum_version' => '6.2.16', + 'minimum_version' => '6.2.20', 'cache_api_calls' => false, 'ignored_files' => ['.gitignore'], 'tracker_site_id' => env('TRACKER_SITE_ID', ''), From 8499ae18d6f728a19ded7cea427973b82f7678f5 Mon Sep 17 00:00:00 2001 From: James Cole Date: Wed, 2 Jul 2025 06:03:11 +0200 Subject: [PATCH 20/21] Cleanup changelog. --- changelog.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/changelog.md b/changelog.md index 255e39d1..91d95a6a 100644 --- a/changelog.md +++ b/changelog.md @@ -6,27 +6,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). > ⚠️ Some changes in this release may unexpectedly lead to duplicate transactions. This is caused by changes in the data handling routines. This is unfortunate, but a result of new insights, changed APIs and other minor fixes. My apologies for any inconvenience. I try to avoid these kinds of changes, but it can't always be helped. -### Added -- Initial release. - ### Changed - For GoCardless imports: opposing account names and other reference data is no longer added as tags, but as text in the notes. This may lead to duplicates. - GoCardless: better handling of rate limits. -### Deprecated -- Initial release. - -### Removed -- Initial release. - ### Fixed - #10508 - Improved parsing of dates. -### Security -- Initial release. - - ## v1.7.0 - 2025-06-26 ### Added From 432de8b810bd62f5d5a09478f3bdcf7461f44bbb Mon Sep 17 00:00:00 2001 From: JC5 Date: Wed, 2 Jul 2025 06:06:00 +0200 Subject: [PATCH 21/21] =?UTF-8?q?=F0=9F=A4=96=20Auto=20commit=20for=20rele?= =?UTF-8?q?ase=20'v1.7.1'=20on=202025-07-02?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 2 +- config/importer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 91d95a6a..9fc858fc 100644 --- a/changelog.md +++ b/changelog.md @@ -11,7 +11,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - GoCardless: better handling of rate limits. ### Fixed -- #10508 +- [Issue 10508](https://github.com/firefly-iii/firefly-iii/issues/10508) (Firefly Version 1.7.0 uses SimpleFIN for GoCardless and crashes when importing) reported by @L3tum - Improved parsing of dates. ## v1.7.0 - 2025-06-26 diff --git a/config/importer.php b/config/importer.php index 2ef5fb6a..4b238df5 100644 --- a/config/importer.php +++ b/config/importer.php @@ -24,7 +24,7 @@ declare(strict_types=1); return [ - 'version' => 'develop/2025-07-01', + 'version' => '1.7.1', 'flows' => ['nordigen', 'spectre', 'file', 'simplefin'], 'enabled_flows' => [ 'nordigen' => true,