Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SelfWealth PDF-Importer #2329

Closed
flywire opened this issue Jul 14, 2021 · 6 comments
Closed

SelfWealth PDF-Importer #2329

flywire opened this issue Jul 14, 2021 · 6 comments

Comments

@flywire
Copy link
Contributor

flywire commented Jul 14, 2021

One transaction per file. What more can I say?

PDF author: ''
PDFBox Version: 1.8.16
-----------------------------------------
SelfWealth Limited ABN: 52 154 324 428 AFSL 421789 W: www.selfwealth.com.au E: support@selfwealth.com.au
This trade was executed and cleared by OpenMarkets Australia Ltd ABN 38 090 472 012,
AFSL 246 705, Market Particpant of ASX, CHI­X and NSX.
Buy Confirmation
MR JOHN DOE Account Number: 1234567
JOHN DOE A/C Reference No: T20210701123456­-1
1 LONG ROAD Trade Date: 1 Jul 2021
SYDNEY NSW Settlement Date: 5 Jul 2021
2000, AUS Market: ASX
WE HAVE BOUGHT ON YOUR ACCOUNT
Quantity Security Code Security Description Price +1 Consideration Currency
25 UMAX BETA S&P500 YIELDMAX 12.40 $312.50 AUD
Brokerage* $9.50 AUD
Adviser Fee* $0.00 AUD
Net Value $322.00 AUD
GST included in this invoice is $0.86
The confirmation is a tax invoice ­ please retain for tax purposes. If this confirmation does not correspond with your records please contact us immediately at
support@selfwealth.com.au
Settlement Instructions All consideration and any information or documents required by OpenMarkets must be provided to OpenMarkets by 9am AEST on the Settlement
Date. This transaction will be settled from your linked cash account or in accordance with your instructions on the Settlement Date.
Contract Comments
Ex Dividend
* Inclusive of GST
+1 Standard Financial Rounding Applied (if applicable)
This confirmation is provided to you by each of SelfWealth and OpenMarkets. The Brokerage and Adviser fees set out in this confirmation are charged by SelfWealth.
OpenMarkets has not charged you any fees for the above transaction(s). The above transaction(s) and this confirmation are issued subject to the directions, decisions
and requirements of the operator of the relevant Market, ASIC Market Integrity Rules, the operating rules of the relevant Market, and, where relevant, the Clearing
Rules of the relevant Clearing Facility and the Settlement Rules of the relevant Settlement Facility, the customs and usages of the relevant Market and the correction of
errors and omissions. If this confirmation relates to multiple transactions, those transactions may have been completed on ASX or CHI­X.”
Generated At: 5 Jul 2021 16:30:01 PM Page: 1 of 1  

Line 4: Buy
Line 6: JOHN DOE A/C
Line 7: Trade Date: 1 Jul 2021
Line 11-12: Fields are space delineated except spaces in Security Description
Quantity: 25
Security Code: UMAX
Security Description: BETA S&P500 YIELDMAX
Price +1: 12.40
Consideration: $312.50
Currency: AUD

@flywire flywire changed the title Self Wealth PDF-Importer SelfWealth PDF-Importer Jul 14, 2021
@flywire
Copy link
Contributor Author

flywire commented Jul 17, 2021

@Nirus2000 is there a guide how to make these Importers?

@Nirus2000
Copy link
Member

Hallo @flywire
Wenn du die PDF-Importer meinst, gibt keinen direkte Anleitung für Importer.
Jedoch versuchen wir einen Standard zu implementieren.
Am besten schaust du dir hier die PDF-Importer an und hier die testcase zu den jeweiligen Importern.

Als Orientierung für einen einfachen Importer könntest du den Deutsche Bank anschauen mit den passenden TestCases.
Für komplexere Importer den FinTech mit diesen TestCases.
Die PDF-Importer und TestCases sollte, so den Standard entsprechen.

Grüße
Alex

@flywire
Copy link
Contributor Author

flywire commented Jul 17, 2021

This is a start.

Detail for later: GGT is tax, a refund for business but not personal investors.

package name.abuchen.portfolio.datatransfer.pdf;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Block;
import name.abuchen.portfolio.datatransfer.pdf.PDFParser.DocumentType;
import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Transaction;
import name.abuchen.portfolio.model.AccountTransaction;
import name.abuchen.portfolio.model.BuySellEntry;
import name.abuchen.portfolio.model.Client;
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.model.Transaction.Unit;
import name.abuchen.portfolio.money.Money;

@SuppressWarnings("nls")
public class SelfWealthPDFExtractor extends AbstractPDFExtractor
{
    public SelfWealthPDFExtractor(Client client)
    {
        super(client);

        addBankIdentifier("SelfWealth"); //$NON-NLS-1$
        addBankIdentifier("SelfWealth Limited ABN: 52 154 324 428 AFSL 421789 W: www.selfwealth.com.au E: support@selfwealth.com.au"); //$NON-NLS-1$

        addBuySellTransaction();
    }

    @Override
    public String getPDFAuthor()
    {
        return ""; //$NON-NLS-1$
    }

    @Override
    public String getLabel()
    {
        return "SelfWealth Limited ABN: 52 154 324 428 AFSL 421789 W: www.selfwealth.com.au E: support@selfwealth.com.au"; //$NON-NLS-1$
    }

    private void addBuySellTransaction()
    {
        DocumentType type = new DocumentType("(Buy|Sell) Confirmation");
        this.addDocumentTyp(type);

        Transaction<BuySellEntry> pdfTransaction = new Transaction<>();
        pdfTransaction.subject(() -> {
            BuySellEntry entry = new BuySellEntry();
            entry.setType(PortfolioTransaction.Type.BUY);
            return entry;
        });

        Block firstRelevantLine = new Block("^(Buy|Sell) Confirmation$");
        type.addBlock(firstRelevantLine);
        firstRelevantLine.set(pdfTransaction);

        pdfTransaction
                // Is type --> "Sell" change from BUY to SELL
                .section("type").optional()
                .match("^(?<type>Sell) Confirmation$")
                .assign((t, v) -> {
                    if (v.get("type").equals("Sell"))
                    {
                        t.setType(PortfolioTransaction.Type.SELL);
                    }
                })

                // JOHN DOE A/C Reference No: T20210701123456­-1
                .section("note")
                .match(" Reference No: (?<note>.*)$")
                .assign((t, v) -> {
                    t.setNote(asNote(v.get("note")));
                })


                // 1 LONG ROAD Trade Date: 1 Jul 2021
                .section("date")
                .match(" Settlement Date: (?<date>\\d+ \\D{3} \\d{4})$")
                .assign((t, v) -> {
                    if (v.get("time") != null)
                        t.setDate(asDate(v.get("date"), v.get("time")));
                    else
                        t.setDate(asDate(v.get("date")));
                })


                // 25 UMAX BETA S&P500 YIELDMAX 12.40 $312.50 AUD
                .section("shares", "symbol", "name", "quote", "amount", "currency")
                .match("^(?<shares>[.,\\d]+) (?<symbol>[\\D]+)") "name", "quote" \\$(?<amount>[.,\\d]+) (?<currency>[\\w]{3})$")
                .assign((t, v) -> {
                    t.setShares(asShares(v.get("shares")));
                    t.setSecurity(getOrCreateSecurity(v));
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                })


                // Brokerage* $9.50 AUD
                .section("brokerage_fee")
                .match("^Brokerage Fee//* //$(?<brokerage_fee>.*) [\\w]{3}$")
                // Adviser Fee* $0.00 AUD
                .section("adviser_fee")
                .match("^Adviser Fee//* //$(?<adviser_fee>.*)$")
                
                fees = brokerage_fee + adviser_fee
                .assign((t, v) -> {
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));


                // Net Value $322.00 AUD
                .section("amount")
                .match("^GST included in this invoice is //$(?<amount>.*) [\\w]{3}$")
                .assign((t, v) -> {
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));


                // GST included in this invoice is $0.86
                .section("gst")
                .match("^GST included in this invoice is //$(?<gst>.*)$")
                .assign((t, v) -> {
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));


                .wrap(BuySellEntryItem::new);

        addTaxesSectionsTransaction(pdfTransaction, type);
        addFeesSectionsTransaction(pdfTransaction, type);
    }

    private <T extends Transaction<?>> void addTaxesSectionsTransaction(T transaction, DocumentType type)
    {
        transaction
                // Kapitalertragsteuer (KESt)  - 9,88 USD - 8,71 EUR
                .section("tax", "currency").optional()
                .match("^Kapitalertragsteuer \\(KESt\\) ([\\s]+)?- [.,\\d]+ [\\w]{3} - (?<tax>[.,\\d]+) (?<currency>[\\w]{3})$")
                .assign((t, v) -> processTaxEntries(t, v, type))
    }

    private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction, DocumentType type)
    {
        transaction
                // Provision EUR 7,90
                // Provision EUR -7,90
                .section("currency", "fee").optional()
                .match("^Provision (?<currency>[\\w]{3}) ([-])?(?<fee>[.,\\d]+)$")
                .assign((t, v) -> processFeeEntries(t, v, type))
    }

    private void processTaxEntries(Object t, Map<String, String> v, DocumentType type)
    {
        if (t instanceof name.abuchen.portfolio.model.Transaction)
        {
            Money tax = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax")));
            PDFExtractorUtils.checkAndSetTax(tax, (name.abuchen.portfolio.model.Transaction) t, type);
        }
        else
        {
            Money tax = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("tax")));
            PDFExtractorUtils.checkAndSetTax(tax, ((name.abuchen.portfolio.model.BuySellEntry) t).getPortfolioTransaction(), type);
        }
    }

    private void processFeeEntries(Object t, Map<String, String> v, DocumentType type)
    {
        if (t instanceof name.abuchen.portfolio.model.Transaction)
        {
            Money fee = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee")));
            PDFExtractorUtils.checkAndSetFee(fee, 
                            (name.abuchen.portfolio.model.Transaction) t, type);
        }
        else
        {
            Money fee = Money.of(asCurrencyCode(v.get("currency")), asAmount(v.get("fee")));
            PDFExtractorUtils.checkAndSetFee(fee,
                            ((name.abuchen.portfolio.model.BuySellEntry) t).getPortfolioTransaction(), type);
        }
    }
}

@Nirus2000
Copy link
Member

Nirus2000 commented Jul 17, 2021

Hello @flywire
sorry that I have answered you in german.
For us it is better if you start a draft-request.
Then we look at it together and show you where there are still
changes should be made.

Is SelfWealth a bank or securities account?
delete the second identifier

addBankIdentifier("SelfWealth"); //$NON-NLS-1$
addBankIdentifier("SelfWealth Limited ABN: 52 154 324 428 AFSL 421789 W: www.selfwealth.com.au E: support@selfwealth.com.au"); //$NON-NLS-1$

Change getLabel() to this...

 public String getLabel()
 {
     return "SelfWealth"; //$NON-NLS-1$
 }

For the structur in addBuySellTransaction or other transactions....

  1. security with security currency
  2. date
  3. amount and currency
  4. sometimes forex calculation if security not in amount currency
  5. notes (...)

There is many other small thinks... so start in draft-request.

C ya
Alex

@Nirus2000
Copy link
Member

Hello and welcome,
Therefore, log in to the forum at https://forum.portfolio-performance.info/ and create a thread with the name "PDF-Importer SelfWealth"

For a PDF-Importer we need examples of buy, sell, dividend and so on.... post it there.
You can see how this works in the video tutorial.

Video tutorial:
Extract PDF documents for debugging

@flywire
Copy link
Contributor Author

flywire commented Jul 24, 2021

I don't think the words translate exactly so I'm misunderstanding something:

https://forum.portfolio-performance.info/t/buchungen-aus-pdf-dateien-importieren/38#tutorial-extrahieren

Neuen Github Issue Create with the text or post it here in the forum.

I assume translates to: create a new Github Issue or Post it in the forum.

#2329 (comment)

so start in draft-request

I assume means: new GitHub Pull Request

#2340 has draft code, test files and tests.


While no PDF-Importer users can enter manually or follow https://forum.portfolio-performance.info/t/import-csv-file/17123

buchen added a commit that referenced this issue Jul 25, 2021
Issue: #2340 #2329
Co-authored-by: flywire <flywire0@gmail.com>
Co-authored-by: Andreas Buchen <andreas.buchen@gmail.com>
@flywire flywire closed this as completed Jul 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants