From c17a0cfedeac2357ad889eea4de35554867a3483 Mon Sep 17 00:00:00 2001 From: Robert Becker Date: Wed, 2 Jul 2025 17:11:01 +0200 Subject: [PATCH 1/2] Add description line glue parameter to sepa account --- lib/Fhp/Action/GetStatementOfAccount.php | 3 +++ lib/Fhp/MT940/MT940.php | 27 +++++++++++++++---- lib/Fhp/Model/SEPAAccount.php | 26 ++++++++++++++++++ .../DKB/DKBIntegrationTestBase.php | 1 + .../DKB/GetStatementOfAccountTest.php | 2 +- 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/lib/Fhp/Action/GetStatementOfAccount.php b/lib/Fhp/Action/GetStatementOfAccount.php index 8529ec5e..9ce07d68 100644 --- a/lib/Fhp/Action/GetStatementOfAccount.php +++ b/lib/Fhp/Action/GetStatementOfAccount.php @@ -208,6 +208,9 @@ private function parseMt940() } else { $parser = new MT940(); } + if (null !== $this->account->getTransactionDescriptionLineGlue()) { + $parser->setDescriptionLineGlue($this->account->getTransactionDescriptionLineGlue()); + } try { // Note: Some banks encode their MT 940 data as SWIFT/ISO-8859 like it should be according to the diff --git a/lib/Fhp/MT940/MT940.php b/lib/Fhp/MT940/MT940.php index 32c4b034..0802d855 100644 --- a/lib/Fhp/MT940/MT940.php +++ b/lib/Fhp/MT940/MT940.php @@ -13,6 +13,8 @@ class MT940 public const CD_CREDIT = 'credit'; public const CD_DEBIT = 'debit'; + private string $descriptionLineGlue = ''; + /** * @throws MT940Exception */ @@ -143,7 +145,7 @@ public function parse(string $rawData): array if (isset($result[$soaDate])) { #$result[$soaDate] = ['end_balance' => []]; - + $amount = str_replace(',', '.', substr($day[$i], 10, -1)); $cdMark = substr($day[$i], 0, 1); if ($cdMark == 'C') { @@ -180,15 +182,17 @@ protected function parseDescription($descr, $transaction): array preg_match_all('/\?(\d{2})([^\?]+)/', $descr, $matches, PREG_SET_ORDER); $descriptionLines = []; - $description1 = ''; // Legacy, could be removed. - $description2 = ''; // Legacy, could be removed. + $description1 = ''; + $description2 = ''; foreach ($matches as $m) { $index = (int) $m[1]; if ((20 <= $index && $index <= 29) || (60 <= $index && $index <= 63)) { if (20 <= $index && $index <= 29) { + if ($description1 !== '') $description1 .= $this->descriptionLineGlue; $description1 .= $m[2]; } else { + if ($description2 !== '') $description2 .= $this->descriptionLineGlue; $description2 .= $m[2]; } $descriptionLines[] = $m[2]; @@ -222,7 +226,7 @@ protected function extractStructuredDataFromRemittanceLines($descriptionLines, s { $description = []; if (empty($descriptionLines) || strlen($descriptionLines[0]) < 5 || $descriptionLines[0][4] !== '+') { - $description['SVWZ'] = implode('', $descriptionLines); + $description['SVWZ'] = implode($this->descriptionLineGlue, $descriptionLines); } else { $lastType = null; foreach ($descriptionLines as $line) { @@ -233,7 +237,11 @@ protected function extractStructuredDataFromRemittanceLines($descriptionLines, s $lastType = substr($line, 0, 4); $description[$lastType] = substr($line, 5); } else { - $description[$lastType] .= $line; + if ($lastType === 'SVWZ') { + $description[$lastType] .= $this->descriptionLineGlue . $line; + } else { + $description[$lastType] .= $line; + } } if (strlen($line) < 27) { // Usually, lines are 27 characters long. In case characters are missing, then it's either the end @@ -255,4 +263,13 @@ protected function getDate(string $val): string preg_match('/(\d{4})(\d{2})(\d{2})/', $val, $m); return $m[1] . '-' . $m[2] . '-' . $m[3]; } + + public function getDescriptionLineGlue(): string { + return $this->descriptionLineGlue; + } + + public function setDescriptionLineGlue(string $descriptionLineGlue): self { + $this->descriptionLineGlue = $descriptionLineGlue; + return $this; + } } diff --git a/lib/Fhp/Model/SEPAAccount.php b/lib/Fhp/Model/SEPAAccount.php index 251b4466..726fefa6 100644 --- a/lib/Fhp/Model/SEPAAccount.php +++ b/lib/Fhp/Model/SEPAAccount.php @@ -20,6 +20,14 @@ class SEPAAccount /** @var string|null */ protected $blz; + /** + * Determines how single lines in transaction descriptions are joined, see #481. + * Defaults to an empty string for maximum compatibility but some banks implicitly assume line breaks for this. + * This is not provided by the bank and needs to be set manually. + * @var string|null + */ + protected $transactionDescriptionLineGlue; + public function getIban(): ?string { return $this->iban; @@ -94,4 +102,22 @@ public function setBlz(?string $blz) return $this; } + + /** + * @return string|null + */ + public function getTransactionDescriptionLineGlue() + { + return $this->transactionDescriptionLineGlue; + } + + /** + * @param string|null $transactionDescriptionLineGlue + * @return self + */ + public function setTransactionDescriptionLineGlue($transactionDescriptionLineGlue): self + { + $this->transactionDescriptionLineGlue = $transactionDescriptionLineGlue; + return $this; + } } diff --git a/lib/Tests/Fhp/Integration/DKB/DKBIntegrationTestBase.php b/lib/Tests/Fhp/Integration/DKB/DKBIntegrationTestBase.php index 6efe6122..5bf2a6e8 100644 --- a/lib/Tests/Fhp/Integration/DKB/DKBIntegrationTestBase.php +++ b/lib/Tests/Fhp/Integration/DKB/DKBIntegrationTestBase.php @@ -60,6 +60,7 @@ protected function getTestAccount(): SEPAAccount $sepaAccount->setBic('BYLADEM1001'); $sepaAccount->setAccountNumber('1234567890'); $sepaAccount->setBlz('12030000'); + $sepaAccount->setTransactionDescriptionLineGlue(PHP_EOL); return $sepaAccount; } } diff --git a/lib/Tests/Fhp/Integration/DKB/GetStatementOfAccountTest.php b/lib/Tests/Fhp/Integration/DKB/GetStatementOfAccountTest.php index ebd0c176..05afb985 100644 --- a/lib/Tests/Fhp/Integration/DKB/GetStatementOfAccountTest.php +++ b/lib/Tests/Fhp/Integration/DKB/GetStatementOfAccountTest.php @@ -148,7 +148,7 @@ private function checkResult(StatementOfAccount $statement) $this->assertEquals(new \DateTime('2019-09-04'), $transaction1->getBookingDate()); $this->assertEquals(Statement::CD_DEBIT, $transaction1->getCreditDebit()); $this->assertEqualsWithDelta(12.00, $transaction1->getAmount(), 0.01); - $this->assertEquals('32301000-P111111-33333333 DATUM 02.09.2019, 22.19 UHR1.TAN 012345', $transaction1->getMainDescription()); + $this->assertEquals("32301000-P111111-33333\n333 \nDATUM 02.09.2019, 22.19 UHR\n1.TAN 012345", $transaction1->getMainDescription()); $this->assertEquals('HKCCS12345', $transaction1->getStructuredDescription()['KREF']); $this->assertEquals('EMPFAENGER ABCDE', $transaction1->getName()); From c436a4f05f5534496da4a9b84430bcb7f9a85a82 Mon Sep 17 00:00:00 2001 From: Robert Becker Date: Mon, 6 Jan 2025 14:22:22 +0100 Subject: [PATCH 2/2] minor cleanup --- lib/Fhp/Model/SEPAAccount.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/Fhp/Model/SEPAAccount.php b/lib/Fhp/Model/SEPAAccount.php index 726fefa6..962c2fbd 100644 --- a/lib/Fhp/Model/SEPAAccount.php +++ b/lib/Fhp/Model/SEPAAccount.php @@ -103,19 +103,15 @@ public function setBlz(?string $blz) return $this; } - /** - * @return string|null - */ - public function getTransactionDescriptionLineGlue() + public function getTransactionDescriptionLineGlue(): ?string { return $this->transactionDescriptionLineGlue; } /** - * @param string|null $transactionDescriptionLineGlue - * @return self + * @return $this */ - public function setTransactionDescriptionLineGlue($transactionDescriptionLineGlue): self + public function setTransactionDescriptionLineGlue($transactionDescriptionLineGlue) { $this->transactionDescriptionLineGlue = $transactionDescriptionLineGlue; return $this;