Repository: portfolio-performance/portfolio (2025-07-12T05:40:23Z)

Issue:
[PDF] Saxo Deposit in different currency
### Affected Bank

Saxo

### Extracted Text from PDF

```shell
PDFBox Version: 3.0.5
Portfolio Performance Version: 0.77.3
System: macosx | aarch64 | 21.0.5+11-LTS | Azul Systems, Inc.
-----------------------------------------
Dirk Ackermann - 20305991
Cash Transfer Detail Report
Reporting period: 16-Jun-2025 - 16-Jun-2025
Generated at: 02-Jul-2025 06:16:23 18:16:23 (GMT+01)
fqQP LXOlrrMji
JkTelXnk 31k 96
17-478 eLBuWQDz
Poland
Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark Dirk Ackermann Reporting period
/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: PLN 16-Jun-2025 - 16-Jun-2025
Page 1 of 2 Account(s): PLN 373796fZBG Generated at: 02-Jul-2025
Cash Transfer Details, PLN
Reporting period
16-Jun-2025 to 16-Jun-2025
Cash Transfer
LGPy DE1210011001XXXXXXX
wcZj ObCOj ocNj Kbdb ZQWrMGQwC
Amount Booked Amount
Event Booking amount ID Trade Date Value Date Conversion Rate Conversion cost
EUR PLN
Deposit 44951434298 16-Jun-2025 16-Jun-2025 20.000,00 4,260382 -213,55 85.207,65
Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark QjzL TTOYCdGNw Reporting period
/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: PLN 16-Jun-2025 - 16-Jun-2025
Page 2 of 2 Account(s): PLN 264388nmEb Generated at: 02-Jul-2025
```

### Issue Description

Importer failed... likely because of variance between transferred and credited currency


Discussion:
Hello @dirkackermann 
Thank you for your explanation. Unfortunately, this does not help us in practice, as we do not have an account or custody account with every bank worldwide.

What should be booked here?
Please describe the details of the transaction and add them here.
(Step by step)

Regards
Alex
It should book a deposit transaction with the booked amount. In this case PLN - column Booked Amount PLN. Value in this case is 85.207,65.
Hello @dirkackermann 
I checked this transaction... but I didn't see the problem?
<img width="1211" height="617" alt="Image" src="https://github.com/user-attachments/assets/fefe8366-5b6a-454e-bcb5-795613aff8ab" />



Patch:
diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/SaxoBankPDFExtractor.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/SaxoBankPDFExtractor.java
index b4774cfdae..befb682f5c 100644
--- a/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/SaxoBankPDFExtractor.java
+++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/datatransfer/pdf/SaxoBankPDFExtractor.java
@@ -15,6 +15,7 @@
 import name.abuchen.portfolio.model.Client;
 import name.abuchen.portfolio.model.PortfolioTransaction;
 import name.abuchen.portfolio.money.Money;
+import name.abuchen.portfolio.money.Values;
 
 @SuppressWarnings("nls")
 public class SaxoBankPDFExtractor extends AbstractPDFExtractor
@@ -68,6 +69,15 @@ private void addBuySellTransaction()
 
                         .oneOf( //
                                         // @formatter:off
+                                        // Instrument Republic of France 3.75% 25 May 2056, EUR Trade time 20-Jun-2025 08:45:38
+                                        // ISIN FR001400XJJ3 Value Date 24-Jun-2025
+                                        // @formatter:on
+                                        section -> section //
+                                                        .attributes("name", "currency", "isin") //
+                                                        .match("^Instrument (?<name>.*), (?<currency>[A-Z]{3}) Trade time.*$") //
+                                                        .match("^ISIN (?<isin>[A-Z]{2}[A-Z0-9]{9}[0-9]) Value.*$") //
+                                                        .assign((t, v) -> t.setSecurity(getOrCreateSecurity(v))),
+                                        // @formatter:off
                                         // Instrument iShares Core MSCI World UCITS ETF Handelszeit 05-Dez-2024 11:21:27
                                         // ISIN IE00B4L5Y983 Valuta 09-Dez-2024
                                         // Symbol SWDA:xswx Order-ID 5236807355
@@ -115,6 +125,21 @@ private void addBuySellTransaction()
                                                         .match("^K\\/V Kauf Menge (?<shares>[\\.,\\d]+)$") //
                                                         .assign((t, v) -> t.setShares(asShares(v.get("shares")))),
                                         // @formatter:off
+                                        // Order Type Limit Order Quantity 10.000,00
+                                        // Bond Traded Value
+                                        // @formatter:on
+                                        section -> section //
+                                                        .attributes("shares") //
+                                                        .match("^.*Quantity (?<shares>[\\.,\\d]+)$") //
+                                                        .find("Bond Traded Value") //
+                                                        .assign((t, v) -> {
+                                                            // @formatter:off
+                                                            // Percentage quotation, workaround for bonds
+                                                            // @formatter:on
+                                                            var shares = asBigDecimal(v.get("shares"));
+                                                            t.setShares(Values.Share.factorize(shares.doubleValue() / 100));
+                                                        }),
+                                        // @formatter:off
                                         // B/S Buy Quantity 49,00
                                         // Order Type Limit Order Quantity 49,00
                                         // @formatter:on
@@ -209,6 +234,23 @@ private void addBuySellTransaction()
                                                         .match("^.*(?<note>Trade ID [\\d]+).*$") //
                                                         .assign((t, v) -> t.setNote(concatenate(t.getNote(), trim(v.get("note")), " | "))))
 
+                        .optionalOneOf( //
+                                        // @formatter:off
+                                        // Bond Accrued
+                                        // 45126758574 20-Jun-2025 24-Jun-2025 -30,82 1,000000 0,00 -30,82
+                                        // Interest
+                                        // @formatter:on
+                                        section -> section //
+                                                        .attributes("note1", "note2") //
+                                                        .match("^(?<note1>Bond Accrued).*$") //
+                                                        .match("[\\d]+ [\\d]{2}\\-[\\w]+\\-[\\d]{4} [\\d]{2}\\-[\\w]+\\-[\\d]{4} \\-(?<note2>[\\.,\\d]+) [\\.,\\d]+ [\\.,\\d]+ \\-[\\.,\\d]+$") //
+                                                        .find("Interest.*") //
+                                                        .assign((t, v) -> {
+
+                                                            var note = v.get("note1") + " " + trim(v.get("note2")) + " " + t.getPortfolioTransaction().getCurrencyCode();
+                                                            t.setNote(concatenate(t.getNote(), note, " | "));
+                                                        }))
+
                         .conclude(ExtractorUtils.fixGrossValueBuySell())
 
                         .wrap(BuySellEntryItem::new);
@@ -499,6 +541,18 @@ private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction
                             checkAndSetFee(fees, t, type.getCurrentContext());
                         })
 
+                        // @formatter:off
+                        // Bond Accrued
+                        // 45126758574 20-Jun-2025 24-Jun-2025 -30,82 1,000000 0,00 -30,82
+                        // Interest
+                        // @formatter:on
+                        .section("fee").optional() //
+                        .documentContext("currency") //
+                        .find("Bond Accrued.*") //
+                        .match("^[\\d]+ [\\d]{2}\\-[\\w]+\\-[\\d]{4} [\\d]{2}\\-[\\w]+\\-[\\d]{4} \\-(?<fee>[\\.,\\d]+) [\\.,\\d]+ [\\.,\\d]+ \\-[\\.,\\d]+$") //
+                        .find("Interest.*") //
+                        .assign((t, v) -> processFeeEntries(t, v, type))
+
                         // @formatter:off
                         // Saxo is counterparty No Total Trading Costs -1,00 USD
                         // @formatter:on


Test patch:
diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/Buy03.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/Buy03.txt
new file mode 100644
index 0000000000..c7ec9f2b6d
--- /dev/null
+++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/Buy03.txt
@@ -0,0 +1,54 @@
+PDFBox Version: 3.0.5
+Portfolio Performance Version: 0.77.3
+System: macosx | aarch64 | 21.0.5+11-LTS | Azul Systems, Inc.
+-----------------------------------------
+Dirk Ackermann - 20305991
+Trade Details Report
+Reporting period: 20-Jun-2025 - 20-Jun-2025
+Generated at: 01-Jul-2025 07:23:37 19:23:37 (GMT+01)
+zuVr SKNMkYimz
+SYMtbKPq 14a 21
+03-525 JXaKIEOv
+Poland
+Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark Dirk Ackermann Reporting period
+/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: EUR 20-Jun-2025 - 20-Jun-2025
+Page 1 of 3 Account(s): EUR 923975INET Generated at: 01-Jul-2025
+Trade details, EUR
+Reporting period
+20-Jun-2025 to 20-Jun-2025
+Bond
+Instrument Republic of France 3.75% 25 May 2056, EUR Trade time 20-Jun-2025 08:45:38
+ISIN FR001400XJJ3 Value Date 24-Jun-2025
+Symbol FRANCE-3.75-25MAY56 Order ID 5298178479
+BLOOMBERG TRADING FACILITY B.V.
+Exchange Description Trade ID 6310729230
+(BTFE)
+Primary Exchange Bonds Europe Government Traded Value -9.564,92 EUR
+Venue Exchange Price 95,341 EUR
+Order Type Limit Order Quantity 10.000,00
+B/S Buy Dirty price 95,65 EUR
+Open/Close OPEN Spread Costs 0,00 EUR
+Saxo is counterparty No Total Trading Costs -20,00 EUR
+Booking amount Amount Conversion cost Booked Amount
+Trade Date Value Date Conversion Rate
+ID EUR EUR EUR
+Bond Accrued
+45126758574 20-Jun-2025 24-Jun-2025 -30,82 1,000000 0,00 -30,82
+Interest
+Bond Traded Value
+45126556841 20-Jun-2025 24-Jun-2025 -9.534,10 1,000000 0,00 -9.534,10
+Amount
+Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark QjXq qpfHsJPEQ Reporting period
+/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: EUR 20-Jun-2025 - 20-Jun-2025
+Page 2 of 3 Account(s): EUR 923975INET Generated at: 01-Jul-2025
+Trade details, EUR
+Reporting period
+20-Jun-2025 to 20-Jun-2025
+Booking amount Amount Conversion cost Booked Amount
+Trade Date Value Date Conversion Rate
+ID EUR EUR EUR
+Commission 45119243492 20-Jun-2025 24-Jun-2025 -20,00 1,000000 0,00 -20,00
+Net Amount - - - - - 0,00 -9.584,92
+Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark DeuX PFKXHWnHY Reporting period
+/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: EUR 20-Jun-2025 - 20-Jun-2025
+Page 3 of 3 Account(s): EUR 923975INET Generated at: 01-Jul-2025
\ No newline at end of file
diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/CashTransfer03.txt b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/CashTransfer03.txt
new file mode 100644
index 0000000000..905ebb0dd0
--- /dev/null
+++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/CashTransfer03.txt
@@ -0,0 +1,28 @@
+PDFBox Version: 3.0.5
+Portfolio Performance Version: 0.77.3
+System: macosx | aarch64 | 21.0.5+11-LTS | Azul Systems, Inc.
+-----------------------------------------
+Dirk Ackermann - 20305991
+Cash Transfer Detail Report
+Reporting period: 16-Jun-2025 - 16-Jun-2025
+Generated at: 02-Jul-2025 06:16:23 18:16:23 (GMT+01)
+fqQP LXOlrrMji
+JkTelXnk 31k 96
+17-478 eLBuWQDz
+Poland
+Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark Dirk Ackermann Reporting period
+/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: PLN 16-Jun-2025 - 16-Jun-2025
+Page 1 of 2 Account(s): PLN 373796fZBG Generated at: 02-Jul-2025
+Cash Transfer Details, PLN
+Reporting period
+16-Jun-2025 to 16-Jun-2025
+Cash Transfer
+LGPy DE1210011001XXXXXXX
+wcZj ObCOj ocNj Kbdb ZQWrMGQwC
+Amount Booked Amount
+Event Booking amount ID Trade Date Value Date Conversion Rate Conversion cost
+EUR PLN
+Deposit 44951434298 16-Jun-2025 16-Jun-2025 20.000,00 4,260382 -213,55 85.207,65
+Saxo Bank / Philip Heymans Alle 15 / 2900 Hellerup / Denmark QjzL TTOYCdGNw Reporting period
+/ Phone No.: +45 39 77 40 00 / Fax No.: +45 39 77 42 00 / Email: info@saxobank.com Currency: PLN 16-Jun-2025 - 16-Jun-2025
+Page 2 of 2 Account(s): PLN 264388nmEb Generated at: 02-Jul-2025
\ No newline at end of file
diff --git a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/SaxoBankPDFExtractorTest.java b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/SaxoBankPDFExtractorTest.java
index 839dc93bd5..e4b400cf8e 100644
--- a/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/SaxoBankPDFExtractorTest.java
+++ b/name.abuchen.portfolio.tests/src/name/abuchen/portfolio/datatransfer/pdf/saxobank/SaxoBankPDFExtractorTest.java
@@ -415,6 +415,37 @@ public void testSecurityBuy02()
                         hasTaxes("USD", 0.00), hasFees("USD", 1.00))));
     }
 
+    @Test
+    public void testSecurityBuy03()
+    {
+        var extractor = new SaxoBankPDFExtractor(new Client());
+
+        List<Exception> errors = new ArrayList<>();
+
+        var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Buy03.txt"), errors);
+
+        assertThat(errors, empty());
+        assertThat(countSecurities(results), is(1L));
+        assertThat(countBuySell(results), is(1L));
+        assertThat(countAccountTransactions(results), is(0L));
+        assertThat(results.size(), is(2));
+        new AssertImportActions().check(results, "EUR");
+
+        // check security
+        assertThat(results, hasItem(security( //
+                        hasIsin("FR001400XJJ3"), hasWkn(null), hasTicker(null), //
+                        hasName("Republic of France 3.75% 25 May 2056"), //
+                        hasCurrencyCode("EUR"))));
+
+        // check buy sell transaction
+        assertThat(results, hasItem(purchase( //
+                        hasDate("2025-06-20T08:45:38"), hasShares(100.00), //
+                        hasSource("Buy03.txt"), //
+                        hasNote("Order ID 5298178479 | Trade ID 6310729230 | Bond Accrued 30,82 EUR"), //
+                        hasAmount("EUR", 9584.92), hasGrossValue("EUR", 9534.10), //
+                        hasTaxes("EUR", 0.00), hasFees("EUR", 20.00 + 30.82))));
+    }
+
     @Test
     public void testCashTransfer01()
     {
@@ -457,6 +488,27 @@ public void testCashTransfer02()
                         hasSource("CashTransfer02.txt"), hasNote("45108148786"))));
     }
 
+    @Test
+    public void testCashTransfer03()
+    {
+        var extractor = new SaxoBankPDFExtractor(new Client());
+
+        List<Exception> errors = new ArrayList<>();
+
+        var results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "CashTransfer03.txt"), errors);
+
+        assertThat(errors, empty());
+        assertThat(countSecurities(results), is(0L));
+        assertThat(countBuySell(results), is(0L));
+        assertThat(countAccountTransactions(results), is(1L));
+        assertThat(results.size(), is(1));
+        new AssertImportActions().check(results, "PLN");
+
+        // assert transaction
+        assertThat(results, hasItem(deposit(hasDate("2025-06-16"), hasAmount("PLN", 85207.65), //
+                        hasSource("CashTransfer03.txt"), hasNote("44951434298"))));
+    }
+
     @Test
     public void testDividende01()
     {