Skip to content

Commit

Permalink
Improve Stake PDF-Importer
Browse files Browse the repository at this point in the history
Update source
Format source to standard eclipse format
Rename importer
  • Loading branch information
Nirus2000 authored and buchen committed Mar 21, 2024
1 parent f8ac033 commit 9d26514
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package name.abuchen.portfolio.datatransfer.pdf.stake;
package name.abuchen.portfolio.datatransfer.pdf.stakeshopptyltd;

import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasAmount;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.hasCurrencyCode;
Expand All @@ -16,6 +16,9 @@
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.purchase;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.sale;
import static name.abuchen.portfolio.datatransfer.ExtractorMatchers.security;
import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countAccountTransactions;
import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countBuySell;
import static name.abuchen.portfolio.datatransfer.ExtractorTestUtilities.countSecurities;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
Expand All @@ -29,23 +32,26 @@
import name.abuchen.portfolio.datatransfer.Extractor.Item;
import name.abuchen.portfolio.datatransfer.actions.AssertImportActions;
import name.abuchen.portfolio.datatransfer.pdf.PDFInputFile;
import name.abuchen.portfolio.datatransfer.pdf.StakePDFExtractor;
import name.abuchen.portfolio.datatransfer.pdf.StakeshopPtyLtdPDFExtractor;
import name.abuchen.portfolio.model.Client;
import name.abuchen.portfolio.model.Security;

@SuppressWarnings("nls")
public class StakePDFExtractorTest
public class StakeshopPtyLtdPDFExtractorTest
{
@Test
public void testSecurityBuy01()
{
StakePDFExtractor extractor = new StakePDFExtractor(new Client());
StakeshopPtyLtdPDFExtractor extractor = new StakeshopPtyLtdPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Buy01.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, "AUD");

Expand All @@ -56,7 +62,8 @@ public void testSecurityBuy01()

assertThat(results, hasItem(purchase( //
hasDate("2022-05-27"), hasShares(512), //
hasSource("Buy01.txt"), hasNote("0000001"), //
hasSource("Buy01.txt"), //
hasNote("0000001"), //
hasAmount("AUD", 10458.04), hasGrossValue("AUD", 10455.04), //
hasTaxes("AUD", 0.00), hasFees("AUD", 3.00))));
}
Expand All @@ -70,7 +77,7 @@ public void testSecurityBuy01_matchExistingSecurityByTickerWithoutExchange()
flt.setTickerSymbol("FLT.AX");
client.addSecurity(flt);

StakePDFExtractor extractor = new StakePDFExtractor(client);
StakeshopPtyLtdPDFExtractor extractor = new StakeshopPtyLtdPDFExtractor(client);

List<Exception> errors = new ArrayList<>();

Expand All @@ -82,21 +89,25 @@ public void testSecurityBuy01_matchExistingSecurityByTickerWithoutExchange()

assertThat(results, hasItem(purchase( //
hasDate("2022-05-27"), hasShares(512), //
hasSource("Buy01.txt"), hasNote("0000001"), //
hasSource("Buy01.txt"), //
hasNote("0000001"), //
hasAmount("AUD", 10458.04), hasGrossValue("AUD", 10455.04), //
hasTaxes("AUD", 0.00), hasFees("AUD", 3.00))));
}

@Test
public void testSecurityBuy02()
{
StakePDFExtractor extractor = new StakePDFExtractor(new Client());
StakeshopPtyLtdPDFExtractor extractor = new StakeshopPtyLtdPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Buy02.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, "AUD");

Expand All @@ -107,21 +118,25 @@ public void testSecurityBuy02()

assertThat(results, hasItem(purchase( //
hasDate("2023-04-05"), hasShares(1000), //
hasSource("Buy02.txt"), hasNote("0000002"), //
hasSource("Buy02.txt"), //
hasNote("0000002"), //
hasAmount("AUD", 503.00), hasGrossValue("AUD", 500), //
hasTaxes("AUD", 0.00), hasFees("AUD", 3.00))));
}

@Test
public void testSecuritySell01()
{
StakePDFExtractor extractor = new StakePDFExtractor(new Client());
StakeshopPtyLtdPDFExtractor extractor = new StakeshopPtyLtdPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Sell01.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, "AUD");

Expand All @@ -132,21 +147,25 @@ public void testSecuritySell01()

assertThat(results, hasItem(sale( //
hasDate("2022-09-30"), hasShares(512), //
hasSource("Sell01.txt"), hasNote("0000003"), //
hasSource("Sell01.txt"), //
hasNote("0000003"), //
hasAmount("AUD", 7267.40), hasGrossValue("AUD", 7270.40), //
hasTaxes("AUD", 0.00), hasFees("AUD", 3.00))));
}

@Test
public void testSecuritySell02()
{
StakePDFExtractor extractor = new StakePDFExtractor(new Client());
StakeshopPtyLtdPDFExtractor extractor = new StakeshopPtyLtdPDFExtractor(new Client());

List<Exception> errors = new ArrayList<>();

List<Item> results = extractor.extract(PDFInputFile.loadTestCase(getClass(), "Sell02.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, "AUD");

Expand All @@ -157,7 +176,8 @@ public void testSecuritySell02()

assertThat(results, hasItem(sale( //
hasDate("2023-03-13"), hasShares(105), //
hasSource("Sell02.txt"), hasNote("0000004"), //
hasSource("Sell02.txt"), //
hasNote("0000004"), //
hasAmount("AUD", 2278.65), hasGrossValue("AUD", 2281.65), //
hasTaxes("AUD", 0.00), hasFees("AUD", 3.00))));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public PDFImportAssistant(Client client, List<File> files)
extractors.add(new SelfWealthPDFExtractor(client));
extractors.add(new SimpelPDFExtractor(client));
extractors.add(new SolarisbankAGPDFExtractor(client));
extractors.add(new StakePDFExtractor(client));
extractors.add(new StakeshopPtyLtdPDFExtractor(client));
extractors.add(new SuresseDirektBankPDFExtractor(client));
extractors.add(new SwissquotePDFExtractor(client));
extractors.add(new TargobankPDFExtractor(client));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import static name.abuchen.portfolio.util.TextUtil.trim;

import java.math.BigDecimal;

import name.abuchen.portfolio.datatransfer.ExtractorUtils;
import name.abuchen.portfolio.datatransfer.pdf.PDFParser.Block;
import name.abuchen.portfolio.datatransfer.pdf.PDFParser.DocumentType;
Expand All @@ -13,84 +11,101 @@
import name.abuchen.portfolio.model.PortfolioTransaction;
import name.abuchen.portfolio.money.Values;

/**
* @formatter:off
* @implNote Stakeshop Pty Ltd, trading as Stake, is a Australian dollar-based financial services company.
* The currency is AUD --> A$.
*
* @implSpec The PDF does not include the name of the security.
* If it is created, identify it at least by the ticker.
* @formatter:on
*/

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

addBankIdentifier("Stake");
addBankIdentifier("Stakeshop Pty Ltd");

addBuySellTransaction();
}

@Override
public String getLabel()
{
return "Stake";
return "Stakeshop Pty Ltd (Stake)";
}

private void addBuySellTransaction()
{
DocumentType type = new DocumentType("(BUY|SELL) CONFIRMATION");
this.addDocumentTyp(type);

Transaction<BuySellEntry> pdfTransaction = new Transaction<>();

Block firstRelevantLine = new Block("^(BUY|SELL) CONFIRMATION$");
type.addBlock(firstRelevantLine);
firstRelevantLine.set(pdfTransaction);

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

firstRelevantLine.set(pdfTransaction);
.subject(() -> {
BuySellEntry portfolioTransaction = new BuySellEntry();
portfolioTransaction.setType(PortfolioTransaction.Type.BUY);
return portfolioTransaction;
})

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

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

// @formatter:off
// EFFECTIVE PRICE $20.43 TICKER FLT.ASX
.section("tickerSymbol", "currency")
.match("^EFFECTIVE PRICE \\p{Sc}[\\.,\\d]+ TICKER (?<tickerSymbol>[\\w]{3,4})\\..*$")
// @formatter:on
.section("tickerSymbol", "currency") //
.match("^EFFECTIVE PRICE \\p{Sc}[\\.,\\d]+ TICKER (?<tickerSymbol>[\\w]{3,4})\\..*$") //
.match("^VALUE (?<currency>[\\w]{1}\\p{Sc}).*") //
.assign((t, v) -> {
// the PDF does not include the name of the
// security; if it is created, identify it at least
// by the ticker
v.put("name", v.get("tickerSymbol"));
t.setSecurity(getOrCreateSecurity(v));
})

// @formatter:off
// QUANTITY 512
// @formatter:on
.section("shares") //
.match("^QUANTITY (?<shares>[\\.,\\d]+)$") //
.assign((t, v) -> t.setShares(asShares(v.get("shares"))))

// @formatter:off
// VALUE A$10,455.04 EXECUTION DATE 27-05-2022
// @formatter:on
.section("date") //
.match("^VALUE A\\p{Sc}[\\.,\\d]+ EXECUTION DATE (?<date>[\\d]+-.*-[\\d]{4})$")
.match("^VALUE A\\p{Sc}[\\.,\\d]+ EXECUTION DATE (?<date>[\\d]{1,2}\\-[\\d]{1,2}\\-[\\d]{4})$") //
.assign((t, v) -> t.setDate(asDate(v.get("date"))))

// @formatter:off
// AMOUNT DUE & PAYABLE A$10,458.04 Funds have already
// been deducted from your buying power. No action
.section("currency", "amount")
.match("^AMOUNT DUE & PAYABLE (?<currency>[\\w]{1}\\p{Sc})(?<amount>[\\.,\\d]+) .*")
// @formatter:on
.section("currency", "amount") //
.match("^AMOUNT DUE & PAYABLE (?<currency>A\\p{Sc})(?<amount>[\\.,\\d]+).*") //
.assign((t, v) -> {
t.setCurrencyCode(asCurrencyCode(v.get("currency")));
t.setAmount(asAmount(v.get("amount")));
})

// @formatter:off
// CONFIRMATION NUMBER 0000001 PID 3556
// @formatter:on
.section("note").optional()//
.match("^CONFIRMATION NUMBER (?<note>.*) PID 3556$")
.match("^CONFIRMATION NUMBER (?<note>.*) PID [\\d]+$")
.assign((t, v) -> t.setNote(trim(v.get("note"))))

.wrap(BuySellEntryItem::new);
Expand All @@ -100,10 +115,13 @@ private void addBuySellTransaction()

private <T extends Transaction<?>> void addFeesSectionsTransaction(T transaction, DocumentType type)
{
transaction
transaction //

// @formatter:off
// BROKERAGE & GST A$3.00 SIDE BUY
// @formatter:on
.section("fee", "currency").optional() //
.match("^BROKERAGE & GST (?<currency>[\\w]{1}\\p{Sc})(?<fee>[\\.,\\d]+) SIDE (BUY|SELL)$")
.match("^BROKERAGE & GST (?<currency>A\\p{Sc})(?<fee>[\\.,\\d]+) SIDE (BUY|SELL)$")
.assign((t, v) -> processFeeEntries(t, v, type));
}

Expand All @@ -118,10 +136,4 @@ protected long asShares(String value)
{
return ExtractorUtils.convertToNumberLong(value, Values.Share, "en", "AU");
}

@Override
protected BigDecimal asExchangeRate(String value)
{
return ExtractorUtils.convertToNumberBigDecimal(value, Values.Share, "en", "AU");
}
}

0 comments on commit 9d26514

Please sign in to comment.