diff --git a/app/Helpers/Csv/Converter/AmountComma.php b/app/Helpers/Csv/Converter/AmountComma.php new file mode 100644 index 0000000000..2427361148 --- /dev/null +++ b/app/Helpers/Csv/Converter/AmountComma.php @@ -0,0 +1,30 @@ +value ); + + if (is_numeric($value)) { + return floatval($value); + } + + return 0; + } +} diff --git a/app/Helpers/Csv/Specifix/AbnAmroDescription.php b/app/Helpers/Csv/Specifix/AbnAmroDescription.php new file mode 100644 index 0000000000..2b6b3c97d5 --- /dev/null +++ b/app/Helpers/Csv/Specifix/AbnAmroDescription.php @@ -0,0 +1,172 @@ +parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription(); + + // If the description could not be parsed, specify an unknown opposing + // account, as an opposing account is required + if( !$parsed ) { + $this->data[ "opposing-account-name" ] = trans('firefly.unknown'); + } + + return $this->data; + } + + /** + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + } + + /** + * @param array $row + */ + public function setRow($row) + { + $this->row = $row; + } + + /** + * Parses the current description in SEPA format + * @return boolean true if the description is SEPA format, false otherwise + */ + protected function parseSepaDescription() + { + // See if the current description is formatted as a SEPA plain description + if( preg_match( "/^SEPA(.{28})/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as SEPA plain description.'); + $type = trim($matches[1]); + + // SEPA plain descriptions contain several key-value pairs, split by a colon + preg_match_all( "/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s))/", $this->data[ "description" ], $matches, PREG_SET_ORDER ); + + foreach( $matches as $match ) { + $key = $match[1]; + $value = trim($match[2]); + + switch( strtoupper($key) ) { + case 'OMSCHRIJVING': + $this->data['description'] = $value; + break; + case 'NAAM': + $this->data['opposing-account-name'] = $value; + break; + case 'IBAN': + $this->data['opposing-account-iban'] = $value; + break; + default: + // Ignore the rest + } + } + + return true; + } + + return false; + } + + /** + * Parses the current description in TRTP format + * @return boolean true if the description is TRTP format, false otherwise + */ + protected function parseTRTPDescription() + { + // See if the current description is formatted in TRTP format + if( preg_match_all( "!\/([A-Z]{3,4})\/([^/]*)!", $this->data[ "description" ], $matches, PREG_SET_ORDER ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as TRTP format.'); + + foreach( $matches as $match ) { + $key = $match[1]; + $value = trim($match[2]); + + switch( strtoupper($key) ) { + case 'TRTP': + $type = $value; + break; + case 'NAME': + $this->data['opposing-account-name'] = $value; + break; + case 'REMI': + $this->data['description'] = $value; + break; + case 'IBAN': + $this->data['opposing-account-iban'] = $value; + break; + default: + // Ignore the rest + } + } + + return true; + } + + return false; + } + + /** + * Parses the current description in GEA/BEA format + * @return boolean true if the description is GEA/BEAformat, false otherwise + */ + protected function parseGEABEADescription() + { + // See if the current description is formatted in GEA/BEA format + if( preg_match( "/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as GEA or BEA format.'); + + $this->data[ "opposing-account-name" ] = $matches[4]; + $this->data[ "description" ] = $matches[4]; + + return true; + } + + return false; + } + + /** + * Parses the current description with costs from ABN AMRO itself + * @return boolean true if the description is GEA/BEAformat, false otherwise + */ + protected function parseABNAMRODescription() + { + // See if the current description is formatted in ABN AMRO format + if( preg_match( "/ABN AMRO.{24} (.*)/", $this->data[ "description" ], $matches ) ) { + Log::debug('AbnAmroSpecifix: Description is structured as costs from ABN AMRO itself.'); + + $this->data[ "opposing-account-name" ] = "ABN AMRO"; + $this->data[ "description" ] = $matches[1]; + + return true; + } + + return false; + } + +} diff --git a/config/csv.php b/config/csv.php index ba1ae72f47..dfdcc3d26f 100644 --- a/config/csv.php +++ b/config/csv.php @@ -2,6 +2,7 @@ return [ 'specifix' => [ 'RabobankDescription', + 'AbnAmroDescription', 'Dummy' ], 'post_processors' => [ @@ -176,6 +177,12 @@ return [ 'converter' => 'Amount', 'field' => 'amount', ], + 'amount-comma-separated' => [ + 'name' => 'Amount (comma as decimal separator)', + 'mappable' => false, + 'converter' => 'AmountComma', + 'field' => 'amount', + ], 'sepa-ct-id' => [ 'name' => 'SEPA Credit Transfer end-to-end ID', 'mappable' => false, diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index be816a7d26..d30005be04 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -257,6 +257,7 @@ return [ 'csv_column_account-id' => 'Asset account ID (matching Firefly)', 'csv_column_account-name' => 'Asset account (name)', 'csv_column_amount' => 'Amount', + 'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)', 'csv_column_bill-id' => 'Bill ID (matching Firefly)', 'csv_column_bill-name' => 'Bill name', 'csv_column_budget-id' => 'Budget ID (matching Firefly)', @@ -280,6 +281,7 @@ return [ 'csv_column_tags-comma' => 'Tags (comma separated)', 'csv_column_tags-space' => 'Tags (space separated)', 'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.', + 'csv_specifix_AbnAmroDescription' => 'Select this when you\'re importing ABN AMRO CSV export files.', 'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.', 'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.', 'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.', diff --git a/resources/lang/nl_NL/firefly.php b/resources/lang/nl_NL/firefly.php index 3493ef0643..a2fc14f54e 100755 --- a/resources/lang/nl_NL/firefly.php +++ b/resources/lang/nl_NL/firefly.php @@ -257,6 +257,7 @@ return [ 'csv_column_account-id' => 'Betaalrekening (ID gelijk aan Firefly)', 'csv_column_account-name' => 'Betaalrekeningnaam', 'csv_column_amount' => 'Bedrag', + 'csv_column_amount-comma-separated' => 'Bedrag (komma as decimaalscheidingsteken)', 'csv_column_bill-id' => 'Contract (ID gelijk aan Firefly)', 'csv_column_bill-name' => 'Contractnaam', 'csv_column_budget-id' => 'Budget (ID gelijk aan Firefly)', @@ -280,6 +281,7 @@ return [ 'csv_column_tags-comma' => 'Tags (kommagescheiden)', 'csv_column_tags-space' => 'Tags (spatiegescheiden)', 'csv_specifix_RabobankDescription' => 'Vink dit aan als je Rabobank bestanden importeert.', + 'csv_specifix_AbnAmroDescription' => 'Vink dit aan als je ABN AMRO CSV bestanden importeert.', 'csv_specifix_Dummy' => 'Dit vinkje doet niks (dummy).', 'csv_import_account_help' => 'Als jouw CSV bestand geen referenties bevat naar jouw rekening(en), geef dan hier aan om welke rekening het gaat.', 'csv_delimiter_help' => 'Kies het veldscheidingsteken dat in het invoerbestand is gebruikt. Bij twijfel is de komma de veiligste optie.',