<commit_before>package name.abuchen.portfolio.datatransfer.pdf;

import name.abuchen.portfolio.Messages;
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.money.CurrencyUnit;

@SuppressWarnings("nls")
public class JustTradePDFExtractor extends AbstractPDFExtractor
{
    /***
     * Information: 
     * Sutor always provides the amount in EUR, column "Betrag in EUR"
     */

    public JustTradePDFExtractor(Client client)
    {
        super(client);

        addBankIdentifier("justTRADE"); //$NON-NLS-1$
        addBankIdentifier("Sutor"); //$NON-NLS-1$
        addBankIdentifier("SUTOR BANK"); //$NON-NLS-1$

        addBuySellTransaction();
        addDividendeTransaction();
        addAccountStatementTransaction();
    }

    @Override
    public String getLabel()
    {
        return "Sutor Bank / justTRADE"; //$NON-NLS-1$
    }

    private void addBuySellTransaction()
    {
        DocumentType type = new DocumentType("((Transaktionsart:|Wertpapier Abrechnung) (Kauf|Verkauf)|F.lligkeit\\/Verfall)");
        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("^(F.lligkeit\\/Verfall|Wertpapier Abrechnung|WERTPAPIERABRECHNUNG).*$", "^Ausmachender Betrag.*$");
        type.addBlock(firstRelevantLine);
        firstRelevantLine.set(pdfTransaction);

        pdfTransaction
                // if type is "Verkauf" change from BUY to SELL
                .section("type").optional()
                .match("^(Transaktionsart: |Wertpapier Abrechnung )?(?<type>(Kauf|Verkauf|F.lligkeit\\/Verfall))$")
                .assign((t, v) -> {
                    if (v.get("type").equals("Verkauf") || v.get("type").equals("Fälligkeit/Verfall"))
                        t.setType(PortfolioTransaction.Type.SELL);
                })

                // Produktbezeichnung - Vanguard FTSE All-World U.ETF Re
                // Internationale Wertpapierkennnummer (ISIN): IE00BK5BQT80
                // Währung: EUR
                .section("name", "isin", "currency").optional()
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer \\(ISIN\\): (?<isin>[\\w]{12})$")
                .match("^W.hrung: (?<currency>[\\w]{3})$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));

                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Stück 53 ISHSII-DEV.MKTS PROP.YLD U.ETF IE00B1FZS350 (A0LEW8)
                // REGISTERED SHS USD (DIST) O.N.
                // Ausführungskurs 25,295 EUR
                .section("name", "isin", "wkn", "name1", "currency").optional()
                .match("^St.ck [\\.,\\d]+ (?<name>.*) (?<isin>[\\w]{12}) \\((?<wkn>.*)\\)$")
                .match("^(?<name1>.*)$")
                .match("^Ausf.hrungskurs [\\.,\\d]+ (?<currency>[\\w]{3})$")
                .assign((t, v) -> {
                    if (!v.get("name1").startsWith("Handels-/Ausf.hrungsplatz"))
                        v.put("name", v.get("name") + " " + v.get("name1"));

                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Produktbezeichnung - Discount Zertifikat auf Münchener Rückversicherungs AG:
                // Internationale Wertpapierkennnummer DE000VP63TQ3
                // Rückzahlungskurs EUR 230,000000
                .section("name", "isin", "currency").optional()
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer (?<isin>[\\w]{12})$")
                .match("^R.ckzahlungskurs (?<currency>[\\w]{3}) [\\.,\\d]+$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));
                    
                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Stück/Nominale: 29,00
                // Stück 53 ISHSII-DEV.MKTS PROP.YLD U.ETF IE00B1FZS350 (A0LEW8)
                // Anzahl/Nominale 9,00
                .section("shares")
                .match("^(St.ck|Anzahl)(\\/Nominale(:)?)? (?<shares>[\\.,\\d]+).*$")
                .assign((t, v) -> t.setShares(asShares(v.get("shares"))))

                // Orderausführung Datum/Zeit: 31 Jul 2020 21:00:15
                // Schlusstag/-Zeit 02.01.2020 10:49:34 Auftragserteilung/ -ort sonstige
                .section("time").optional()
                .match("^.* (?<time>[\\d]{2}:[\\d]{2}:[\\d]{2})( .*)?$")
                .assign((t, v) -> type.getCurrentContext().put("time", v.get("time")))

                .oneOf(
                                // Orderausführung Datum/Zeit: 31 Jul 2020 21:00:15
                                // Schlusstag/-Zeit 02.01.2020 10:49:34 Auftragserteilung/ -ort sonstige
                                section -> section
                                        .attributes("date")
                                        .match("^(Orderausf.hrung Datum\\/Zeit:|Schlusstag\\/\\-Zeit) (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{1,2} .* [\\d]{4})) .*$")
                                        .assign((t, v) -> {
                                            if (type.getCurrentContext().get("time") != null)
                                                t.setDate(asDate(v.get("date").replace("Mrz", "Mär"), type.getCurrentContext().get("time")));
                                            else
                                                t.setDate(asDate(v.get("date").replace("Mrz", "Mär")));
                                        })
                                ,
                                // Valutadatum 25. Juni 2021
                                section -> section
                                        .attributes("date")
                                        .match("^Valutadatum (?<date>[\\d]{1,2}\\. .* [\\d]{4})$")
                                        .assign((t, v) -> t.setDate(asDate(v.get("date").replace("Mrz", "Mär"))))
                        )

                .oneOf(
                                // Ausmachender Betrag: €2.083,94
                                section -> section
                                        .attributes("currency", "amount")
                                        .match("^Ausmachender Betrag: (?<currency>\\p{Sc})(?<amount>[\\.,\\d]+)$")
                                        .assign((t, v) -> {
                                            if (v.get("currency").equals("€"))
                                                v.put("currency", CurrencyUnit.EUR);
                                            else
                                                v.put("currency", CurrencyUnit.USD);
                                                
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                                ,
                                // Ausmachender Betrag 1.340,64- EUR
                                section -> section
                                        .attributes("amount", "currency")
                                        .match("^Ausmachender Betrag (?<amount>[\\.,\\d]+)(\\-)? (?<currency>[\\w]{3})$")
                                        .assign((t, v) -> {
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                                ,
                                // Ausmachender Betrag EUR 2.070,00
                                section -> section
                                        .attributes("amount", "currency")
                                        .match("^Ausmachender Betrag (?<currency>[\\w]{3}) (?<amount>[\\.,\\d]+)$")
                                        .assign((t, v) -> {
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                        )

                .wrap(BuySellEntryItem::new);

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

    private void addDividendeTransaction()
    {
        DocumentType type = new DocumentType("Dividende");
        this.addDocumentTyp(type);

        Block block = new Block("^(Dividende [^pro]).*$");
        type.addBlock(block);
        Transaction<AccountTransaction> pdfTransaction = new Transaction<AccountTransaction>()
            .subject(() -> {
                AccountTransaction entry = new AccountTransaction();
                entry.setType(AccountTransaction.Type.DIVIDENDS);
                return entry;
            });

        pdfTransaction
                // Produktbezeichnung - Kellogg Co.:
                // Internationale Wertpapierkennnummer US4878361082
                .section("name", "isin")
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer (?<isin>.*)$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));
                    
                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Anzahl/Nominale 30,00
                .section("shares")
                .match("^Anzahl\\/Nominale (?<shares>[\\.,\\d]+)$")
                .assign((t, v) -> t.setShares(asShares(v.get("shares"))))

                // Valutadatum 15. März 2021
                .section("date")
                .match("^Valutadatum (?<date>[\\d]{1,2}\\. .* [\\d]{4})$")
                .assign((t, v) -> t.setDateTime(asDate(v.get("date").replace("Mrz", "Mär"))))

                // Ausmachender Betrag EUR 12,15
                .section("currency", "amount")
                .match("^Ausmachender Betrag (?<currency>[\\w]{3}) (?<amount>[\\.,\\d]+)$")
                .assign((t, v) -> {
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));<commit_msg>Update JustTradePDFExtractor<commit_after>package name.abuchen.portfolio.datatransfer.pdf;

import name.abuchen.portfolio.Messages;
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.money.CurrencyUnit;

@SuppressWarnings("nls")
public class JustTradePDFExtractor extends AbstractPDFExtractor
{
    /***
     * Information: 
     * Sutor always provides the amount in EUR, column "Betrag in EUR"
     */

    public JustTradePDFExtractor(Client client)
    {
        super(client);

        addBankIdentifier("justTRADE"); //$NON-NLS-1$
        addBankIdentifier("Sutor"); //$NON-NLS-1$
        addBankIdentifier("SUTOR BANK"); //$NON-NLS-1$

        addBuySellTransaction();
        addDividendeTransaction();
        addAccountStatementTransaction();
    }

    @Override
    public String getLabel()
    {
        return "Sutor Bank / justTRADE"; //$NON-NLS-1$
    }

    private void addBuySellTransaction()
    {
        DocumentType type = new DocumentType("((Transaktionsart:|Wertpapier Abrechnung) (Kauf|Verkauf)|F.lligkeit\\/Verfall)");
        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("^(F.lligkeit\\/Verfall|Wertpapier Abrechnung|WERTPAPIERABRECHNUNG).*$", "^Ausmachender Betrag.*$");
        type.addBlock(firstRelevantLine);
        firstRelevantLine.set(pdfTransaction);

        pdfTransaction
                // if type is "Verkauf" change from BUY to SELL
                .section("type").optional()
                .match("^(Transaktionsart: |Wertpapier Abrechnung )?(?<type>(Kauf|Verkauf|F.lligkeit\\/Verfall))$")
                .assign((t, v) -> {
                    if (v.get("type").equals("Verkauf") || v.get("type").equals("Fälligkeit/Verfall"))
                        t.setType(PortfolioTransaction.Type.SELL);
                })

                // Produktbezeichnung - Vanguard FTSE All-World U.ETF Re
                // Internationale Wertpapierkennnummer (ISIN): IE00BK5BQT80
                // Währung: EUR
                .section("name", "isin", "currency").optional()
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer \\(ISIN\\): (?<isin>[\\w]{12})$")
                .match("^W.hrung: (?<currency>[\\w]{3})$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));

                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Stück 53 ISHSII-DEV.MKTS PROP.YLD U.ETF IE00B1FZS350 (A0LEW8)
                // REGISTERED SHS USD (DIST) O.N.
                // Ausführungskurs 25,295 EUR
                .section("name", "isin", "wkn", "name1", "currency").optional()
                .match("^St.ck [\\.,\\d]+ (?<name>.*) (?<isin>[\\w]{12}) \\((?<wkn>.*)\\)$")
                .match("^(?<name1>.*)$")
                .match("^Ausf.hrungskurs [\\.,\\d]+ (?<currency>[\\w]{3})$")
                .assign((t, v) -> {
                    if (!v.get("name1").startsWith("Handels-/Ausf.hrungsplatz"))
                        v.put("name", v.get("name") + " " + v.get("name1"));

                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Produktbezeichnung - Discount Zertifikat auf Münchener Rückversicherungs AG:
                // Internationale Wertpapierkennnummer DE000VP63TQ3
                // Rückzahlungskurs EUR 230,000000
                .section("name", "isin", "currency").optional()
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer (?<isin>[\\w]{12})$")
                .match("^R.ckzahlungskurs (?<currency>[\\w]{3}) [\\.,\\d]+$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));
                    
                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Stück/Nominale: 29,00
                // Stück 53 ISHSII-DEV.MKTS PROP.YLD U.ETF IE00B1FZS350 (A0LEW8)
                // Anzahl/Nominale 9,00
                .section("shares")
                .match("^(St.ck|Anzahl)(\\/Nominale(:)?)? (?<shares>[\\.,\\d]+).*$")
                .assign((t, v) -> t.setShares(asShares(v.get("shares"))))

                // Orderausführung Datum/Zeit: 31 Jul 2020 21:00:15
                // Schlusstag/-Zeit 02.01.2020 10:49:34 Auftragserteilung/ -ort sonstige
                .section("time").optional()
                .match("^.* (?<time>[\\d]{2}:[\\d]{2}:[\\d]{2})( .*)?$")
                .assign((t, v) -> type.getCurrentContext().put("time", v.get("time")))

                .oneOf(
                                // Orderausführung Datum/Zeit: 31 Jul 2020 21:00:15
                                // Schlusstag/-Zeit 02.01.2020 10:49:34 Auftragserteilung/ -ort sonstige
                                section -> section
                                        .attributes("date")
                                        .match("^(Orderausf.hrung Datum\\/Zeit:|Schlusstag\\/\\-Zeit) (?<date>([\\d]{2}\\.[\\d]{2}\\.[\\d]{4}|[\\d]{1,2} .* [\\d]{4})) .*$")
                                        .assign((t, v) -> {
                                            if (type.getCurrentContext().get("time") != null)
                                                t.setDate(asDate(v.get("date").replace("Mrz", "Mär"), type.getCurrentContext().get("time")));
                                            else
                                                t.setDate(asDate(v.get("date").replace("Mrz", "Mär")));
                                        })
                                ,
                                // Valutadatum 25. Juni 2021
                                section -> section
                                        .attributes("date")
                                        .match("^Valutadatum (?<date>[\\d]{1,2}\\. .* [\\d]{4})$")
                                        .assign((t, v) -> t.setDate(asDate(v.get("date").replace("Mrz", "Mär"))))
                        )

                .oneOf(
                                // Ausmachender Betrag: €2.083,94
                                section -> section
                                        .attributes("currency", "amount")
                                        .match("^Ausmachender Betrag: (?<currency>\\p{Sc})(?<amount>[\\.,\\d]+)$")
                                        .assign((t, v) -> {
                                            if (v.get("currency").equals("€"))
                                                v.put("currency", CurrencyUnit.EUR);
                                            else
                                                v.put("currency", CurrencyUnit.USD);
                                                
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                                ,
                                // Ausmachender Betrag 1.340,64- EUR
                                section -> section
                                        .attributes("amount", "currency")
                                        .match("^Ausmachender Betrag (?<amount>[\\.,\\d]+)(\\-)? (?<currency>[\\w]{3})$")
                                        .assign((t, v) -> {
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                                ,
                                // Ausmachender Betrag EUR 2.070,00
                                section -> section
                                        .attributes("amount", "currency")
                                        .match("^Ausmachender Betrag (?<currency>[\\w]{3}) (?<amount>[\\.,\\d]+)$")
                                        .assign((t, v) -> {
                                            t.setCurrencyCode(asCurrencyCode(v.get("currency")));
                                            t.setAmount(asAmount(v.get("amount")));
                                        })
                        )

                .wrap(BuySellEntryItem::new);

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

    private void addDividendeTransaction()
    {
        DocumentType type = new DocumentType("Dividende");
        this.addDocumentTyp(type);

        Block block = new Block("^(Dividende [^pro]).*$");
        type.addBlock(block);
        Transaction<AccountTransaction> pdfTransaction = new Transaction<AccountTransaction>().subject(() -> {
            AccountTransaction entry = new AccountTransaction();
            entry.setType(AccountTransaction.Type.DIVIDENDS);
            return entry;
        });

        pdfTransaction
                // Produktbezeichnung - Kellogg Co.:
                // Internationale Wertpapierkennnummer US4878361082
                .section("name", "isin")
                .match("^Produktbezeichnung \\- (?<name>.*)$")
                .match("^Internationale Wertpapierkennnummer (?<isin>.*)$")
                .assign((t, v) -> {
                    if (v.get("name").endsWith(":"))
                        v.put("name", v.get("name").substring(0, v.get("name").length()-1));
                    
                    t.setSecurity(getOrCreateSecurity(v));
                })

                // Anzahl/Nominale 30,00
                .section("shares")
                .match("^Anzahl\\/Nominale (?<shares>[\\.,\\d]+)$")
                .assign((t, v) -> t.setShares(asShares(v.get("shares"))))

                // Valutadatum 15. März 2021
                .section("date")
                .match("^Valutadatum (?<date>[\\d]{1,2}\\. .* [\\d]{4})$")
                .assign((t, v) -> t.setDateTime(asDate(v.get("date").replace("Mrz", "Mär"))))

                // Ausmachender Betrag EUR 12,15
                .section("currency", "amount")
                .match("^Ausmachender Betrag (?<currency>[\\w]{3}) (?<amount>[\\.,\\d]+)$")
                .assign((t, v) -> {
                    t.setAmount(asAmount(v.get("amount")));
                    t.setCurrencyCode(asCurrencyCode(v.get("currency")));
{'fraction_non_alphanumeric': 0.16072383669103227, 'fraction_numerical': 0.01720449277946158, 'mean_word_length': 1.3340268386559866, 'pattern_counts': {'":': 6, '<': 67, '<?xml version=': 0, '>': 111, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 110, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1469201', 'n_tokens_mistral': 6634, 'n_tokens_neox': 6160, 'n_words': 869}