diff --git a/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java b/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java index d009ed1..3b96c81 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/Exchange.java @@ -1,6 +1,8 @@ package edu.ntnu.idi.idatt.model; import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; import java.util.*; import edu.ntnu.idi.idatt.model.market.Stock; @@ -9,6 +11,7 @@ import edu.ntnu.idi.idatt.model.transaction.Purchase; import edu.ntnu.idi.idatt.model.transaction.Sale; import edu.ntnu.idi.idatt.model.transaction.Transaction; +import edu.ntnu.idi.idatt.session.UserSession; /** * Exchange class @@ -194,6 +197,14 @@ public Transaction sell(Share share, Player player) { * @see Stock */ public void advance() { + this.week += 1; + Random random = new Random(); + stockMap.values() + .forEach(s -> s.addNewSalesPrice(s.getSalesPrice() + .multiply(BigDecimal.valueOf(random.nextDouble(0.8, 1.4)).setScale(2, RoundingMode.HALF_UP)))); + UserSession.getInstance().netWorthProperty().set( + UserSession.getInstance().getPlayer().getNetWorth().doubleValue()); // TODO: Remove hook from here, just for + // testing } } diff --git a/src/main/java/edu/ntnu/idi/idatt/model/player/Player.java b/src/main/java/edu/ntnu/idi/idatt/model/player/Player.java index 5648efb..7af1065 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/player/Player.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/player/Player.java @@ -51,12 +51,18 @@ public TransactionArchive getTransactionArchive() { public void addMoney(BigDecimal amount) { this.money = this.money.add(amount); UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Workaround instead of PropertyChange due - // to JSON saving. + // to JSON saving + UserSession.getInstance().netWorthProperty().set(this.getNetWorth().doubleValue()); // Portfolio doesnt change + // without monetary change + // (either buy or sale) so thats + // the decision to hook net + // worth here and under. } public void withdrawMoney(BigDecimal amount) { this.money = this.money.subtract(amount); - UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Hook + UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Hooks + UserSession.getInstance().netWorthProperty().set(this.getNetWorth().doubleValue()); } /** diff --git a/src/main/java/edu/ntnu/idi/idatt/model/transaction/TransactionArchive.java b/src/main/java/edu/ntnu/idi/idatt/model/transaction/TransactionArchive.java index 2dd77d4..6112c89 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/transaction/TransactionArchive.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/transaction/TransactionArchive.java @@ -44,6 +44,11 @@ public List getTransactions(int week) { return transactions.stream().filter(transaction -> transaction.getWeek() == week).toList(); } + // TODO: java, junit + public List getTransactions() { + return transactions; + } + /** * Getter for purchases done * @@ -56,6 +61,13 @@ public List getPurchases(int week) { .toList(); } + // TODO: java, junit + public List getPurchases() { + return getTransactions().stream().filter(t -> t instanceof Purchase) + .map(t -> (Purchase) t) + .toList(); + } + /** * Getter for sales done * @@ -68,6 +80,13 @@ public List getSales(int week) { .toList(); } + // TODO: java, junit + public List getSales() { + return getTransactions().stream().filter(t -> t instanceof Sale) + .map(t -> (Sale) t) + .toList(); + } + /** * Part 2 * diff --git a/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java b/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java index b9dbfba..3dcf5ab 100644 --- a/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java +++ b/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java @@ -1,8 +1,5 @@ package edu.ntnu.idi.idatt.session; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - import edu.ntnu.idi.idatt.model.Exchange; import edu.ntnu.idi.idatt.model.player.Player; import javafx.beans.property.SimpleDoubleProperty; @@ -25,7 +22,6 @@ public static UserSession getInstance() { private Player player; private Exchange exchange; - private final SimpleDoubleProperty moneyProperty = new SimpleDoubleProperty(); public Player getPlayer() { return player; @@ -33,7 +29,7 @@ public Player getPlayer() { public void setPlayer(Player player) { this.player = player; - moneyProperty.set(player.getMoney().doubleValue()); + moneyProperty.set(player.getMoney().doubleValue()); // Startup hook } public Exchange getExchange() { @@ -44,14 +40,21 @@ public void setExchange(Exchange exchange) { this.exchange = exchange; } - public SessionBundle getSession() { - return new SessionBundle(player, exchange); - } + private final SimpleDoubleProperty moneyProperty = new SimpleDoubleProperty(); + private final SimpleDoubleProperty netWorthProperty = new SimpleDoubleProperty(); public SimpleDoubleProperty moneyProperty() { return moneyProperty; } + public SimpleDoubleProperty netWorthProperty() { + return netWorthProperty; + } + + public SessionBundle getSession() { + return new SessionBundle(player, exchange); + } + public class SessionBundle { private Player player; diff --git a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java index 4c8f571..8ffb7d8 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java @@ -1,6 +1,10 @@ package edu.ntnu.idi.idatt.view; +import java.util.ArrayDeque; +import java.util.Deque; + import edu.ntnu.idi.idatt.model.market.Stock; +import edu.ntnu.idi.idatt.session.UserSession; import edu.ntnu.idi.idatt.view.entry.StartController; import edu.ntnu.idi.idatt.view.entry.StartModel; import edu.ntnu.idi.idatt.view.entry.StartView; @@ -17,6 +21,39 @@ public class SceneFactory { + @FunctionalInterface + public interface MVCInitializer { + Parent execute(); + } + + private static Deque navigation = new ArrayDeque<>(); + private static boolean navigatingBack = false; + + public static void goBack() { + if (navigation.size() > 1) { + navigation.pop(); + navigatingBack = true; + SceneManager.switchTo(navigation.peek().execute()); + navigatingBack = false; + } + } + + public static void reloadCurrent() { + navigatingBack = true; + SceneManager.switchTo(navigation.peek().execute()); + navigatingBack = false; + } + + private static void mark(MVCInitializer initializer) { + if (!navigatingBack) { + navigation.push(initializer); + } + } + + public static boolean isFinal() { + return navigation.size() == 1; + } + public static Parent createStartView() { StartModel model = new StartModel(); @@ -32,6 +69,8 @@ public static Parent createStartView() { public static Parent createExchangeView() { + mark(() -> createExchangeView()); + ExchangeModel model = new ExchangeModel(); ExchangeView view = new ExchangeView(); ExchangeController controller = new ExchangeController(model); @@ -43,7 +82,10 @@ public static Parent createExchangeView() { } - public static Parent createStockView(Stock stock) { + public static Parent createStockView(String symbol) { + + mark(() -> createStockView(symbol)); + Stock stock = UserSession.getInstance().getExchange().getStock(symbol); StockModel model = new StockModel(); StockView view = new StockView(); @@ -57,6 +99,8 @@ public static Parent createStockView(Stock stock) { public static Parent createTransactionView() { + mark(() -> createTransactionView()); + TransactionModel model = new TransactionModel(); TransactionView view = new TransactionView(); TransactionController controller = new TransactionController(model); diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/StockComponent.java b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/StockComponent.java index b87d333..2edb0c8 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/StockComponent.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/StockComponent.java @@ -1,6 +1,5 @@ package edu.ntnu.idi.idatt.view.components.elements; -import java.math.BigDecimal; import java.util.List; import java.util.function.Consumer; @@ -25,13 +24,13 @@ public StockComponent(Stock stock) { this.setStyle("-fx-background-color: #404950;"); - Label title = new Label(this.getTitle()); + Label title = new Label(String.format("%s (%s)", stock.getCompany(), stock.getSymbol())); Label latestText = new Label("Latest"); - Label latestValue = new Label(stock.getSalesPrice().toString() + "USD"); + Label latestValue = new Label(String.format("%.2f USD", stock.getSalesPrice())); Label changeText = new Label("Change"); - Label changeValue = new Label(stock.getLatestPriceChange().toString() + "USD"); + Label changeValue = new Label(String.format("%.2f USD", stock.getLatestPriceChange())); Label changeValuePercent = new Label( String.valueOf(stock.getLatestPriceChangePercent()) + "%"); @@ -63,16 +62,8 @@ public StockComponent(Stock stock) { this.getChildren().addAll(stockComponent.makeUI()); } - public String getTitle() { - return stock.getCompany() + " (" + stock.getSymbol() + ")"; - } - - public String getSymbol() { - return stock.getSymbol(); - } - public void onStockClick(Consumer handler) { - this.setOnMouseClicked((e) -> handler.accept(this.getSymbol())); + this.setOnMouseClicked((e) -> handler.accept(stock.getSymbol())); } } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TransactionComponent.java b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TransactionComponent.java index 850af4a..1864b15 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TransactionComponent.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TransactionComponent.java @@ -6,8 +6,8 @@ import edu.ntnu.idi.idatt.model.transaction.Purchase; import edu.ntnu.idi.idatt.model.transaction.Sale; import edu.ntnu.idi.idatt.model.transaction.Transaction; -import edu.ntnu.idi.idatt.service.transaction.PurchaseCalculator; import edu.ntnu.idi.idatt.service.transaction.SaleCalculator; +import edu.ntnu.idi.idatt.service.transaction.TransactionCalculator; import edu.ntnu.idi.idatt.view.components.ui.UICompositor; import edu.ntnu.idi.idatt.view.util.CssUtils; import javafx.geometry.Insets; @@ -29,31 +29,27 @@ public TransactionComponent(Transaction transaction) { Label amountOfShares; Label saleProfit; + TransactionCalculator calculator = transaction.getCalculator(); + totalValue = new Label(String.format("Total: %.2f USD", calculator.calculateTotal())); + taxComissionValue = new Label(String.format( + "Tax & Comission: %.2f USD", calculator.calculateTax().add(calculator.calculateCommision()))); + grossValue = new Label(String.format("Gross: %.2f USD", calculator.calculateGross())); + amountOfShares = new Label( + String.format("Shares: %.2f [%s]", transaction.getShare().getQuantity(), stock.getSymbol())); + if (transaction instanceof Purchase) { title = new Label("PURCHASE"); - CssUtils.apply(title, CssUtils.GREEN); - - PurchaseCalculator calculator = (PurchaseCalculator) transaction.getCalculator(); - totalValue = new Label("Total: " + calculator.calculateTotal() + " USD"); - taxComissionValue = new Label( - "Tax & Comission: " + calculator.calculateTax().add(calculator.calculateCommision()) + " USD"); - grossValue = new Label("Gross: " + calculator.calculateGross() + " USD"); - amountOfShares = new Label("Shares: " + transaction.getShare().getQuantity() + " [" + stock.getSymbol() + "]"); saleProfit = new Label(); + CssUtils.apply(title, CssUtils.GREEN); } else if (transaction instanceof Sale) { title = new Label("SALE"); CssUtils.apply(title, CssUtils.RED); - SaleCalculator calculator = (SaleCalculator) transaction.getCalculator(); - totalValue = new Label("Total: " + calculator.calculateTotal() + " USD"); - taxComissionValue = new Label( - "Tax & Comission: " + calculator.calculateTax().add(calculator.calculateCommision()) + " USD"); - grossValue = new Label("Gross: " + calculator.calculateGross() + " USD"); - amountOfShares = new Label("Shares: " + transaction.getShare().getQuantity() + " [" + stock.getSymbol() + "]"); - saleProfit = new Label("Profit: " + calculator.calculateProfit() + " USD"); + SaleCalculator sCalc = (SaleCalculator) transaction.getCalculator(); + saleProfit = new Label(String.format("Profit: %.2f USD", sCalc.calculateProfit())); - CssUtils.apply(saleProfit, CssUtils.generateValueColors(calculator.calculateProfit().doubleValue())); + CssUtils.apply(saleProfit, CssUtils.generateValueColors(sCalc.calculateProfit().doubleValue())); } else { diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java index 00ec8b9..6b1b3c7 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/ui/UIFactory.java @@ -5,23 +5,49 @@ import java.util.function.Consumer; import edu.ntnu.idi.idatt.session.UserSession; +import edu.ntnu.idi.idatt.view.SceneFactory; import edu.ntnu.idi.idatt.view.components.elements.IconComponent; import edu.ntnu.idi.idatt.view.components.elements.SearchBarComponent; import edu.ntnu.idi.idatt.view.components.primitives.ActionEventHandler; import edu.ntnu.idi.idatt.view.util.CssUtils; import edu.ntnu.idi.idatt.view.util.ResourceUtils; -import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Parent; import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.MenuButton; +import javafx.scene.control.MenuItem; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; public class UIFactory { public static Parent createHeader(String placeholder, Consumer onSearchQuery) { + return createDefaultHeader(placeholder, onSearchQuery); + } + + public static Parent createHeader(String placeholder, Consumer onSearchQuery, List sortItems, + ActionEventHandler... sortHandlers) { + + if (sortItems.size() != sortHandlers.length) { + System.out.println("Failed to build header!"); + return null; + } + + MenuButton menu = new MenuButton("Sort by.."); + HBox.setMargin(menu, new Insets(20)); + + sortItems.forEach(item -> { + MenuItem m = new MenuItem(item); + m.setOnAction((e) -> sortHandlers[sortItems.indexOf(item)].handle()); + menu.getItems().add(m); + }); + + return createDefaultHeader(placeholder, onSearchQuery, menu); + } + + private static Parent createDefaultHeader(String placeholder, Consumer onSearchQuery, Parent... addons) { SearchBarComponent bar = new SearchBarComponent(placeholder); HBox.setMargin(bar, new Insets(20)); @@ -29,6 +55,7 @@ public static Parent createHeader(String placeholder, Consumer onSearchQ UICompositor header = new UICompositor.Builder() .parent(new HBox()) .growWithAlignment(Pos.CENTER) + .addAllContent(addons) .filler() .addContent(bar) .filler() @@ -53,6 +80,7 @@ private static Parent createDefaultNavigation(Label titleLabel, List but ActionEventHandler... handlers) { if (buttonLables.size() != handlers.length) { System.out.println("Failed to build navigation!"); + return null; } CssUtils.apply(titleLabel, CssUtils.BIG_TEXT_32); @@ -68,10 +96,19 @@ private static Parent createDefaultNavigation(Label titleLabel, List but UICompositor.Builder navigationBuilder = new UICompositor.Builder() .parent(new VBox()) + .growWithAlignment(Pos.CENTER) .addContent(titleLabel); buttons.forEach(b -> navigationBuilder.addContent(b)); + navigationBuilder.filler(); + + if (!SceneFactory.isFinal()) { + Button previous = new Button("Previous"); + previous.setOnAction((e) -> SceneFactory.goBack()); + navigationBuilder.addContent(previous); + } + UICompositor navigation = navigationBuilder.build(); return navigation.makeUI(); @@ -103,6 +140,16 @@ public static Parent createMenu(String title, List buttonLables, ActionE buttons.forEach(b -> menuBuilder.addContent(b)); + menuBuilder.filler(); + + Button progress = new Button("Advance to next week"); + progress.setOnAction((e) -> { + UserSession.getInstance().getExchange().advance(); + SceneFactory.reloadCurrent(); + }); + + menuBuilder.addContent(progress); + UICompositor menu = menuBuilder.build(); return menu.makeUI(); @@ -112,10 +159,11 @@ public static Parent createMenu(String title, List buttonLables, ActionE public static Parent createToolbar(ActionEventHandler menuHandle, ActionEventHandler quitHandle) { Label balance = new Label(); balance.textProperty().bind( - Bindings.concat("Balance: ", UserSession.getInstance().moneyProperty(), "USD")); + UserSession.getInstance().moneyProperty().asString("Balance: %.2f $")); Label playerStatus = new Label("Status:" + UserSession.getInstance().getPlayer().getStatus()); - Label playerNetWorth = new Label( - "Net Worth: " + UserSession.getInstance().getPlayer().getNetWorth().toString() + " USD"); + Label playerNetWorth = new Label(); + playerNetWorth.textProperty().bind( + UserSession.getInstance().netWorthProperty().asString("Net Worth: %.2f $")); CssUtils.apply(balance, CssUtils.MED_TEXT_16); CssUtils.apply(playerStatus, CssUtils.MED_TEXT_16); diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java deleted file mode 100644 index dc34b99..0000000 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/MainView.java +++ /dev/null @@ -1,49 +0,0 @@ -package edu.ntnu.idi.idatt.view.primary; - -import java.util.List; - -import edu.ntnu.idi.idatt.view.SceneFactory; -import edu.ntnu.idi.idatt.view.SceneManager; -import edu.ntnu.idi.idatt.view.components.AbstractViewUI; -import edu.ntnu.idi.idatt.view.components.ui.UIFactory; -import javafx.scene.Parent; -import javafx.scene.layout.StackPane; - -public class MainView extends AbstractViewUI { - - @Override - public Parent createContent() { - StackPane root = new StackPane(); - root.getStyleClass().add("primary"); - return root; - } - - @Override - public Parent createNavigation() { - return UIFactory.createNavigation("Title", - List.of(" • Newspaper"), - () -> System.out.println("Newspaper clicked")); - } - - @Override - public Parent createHeader() { - return UIFactory.createHeader("Search for stocks", - query -> System.out.println(query)); - } - - @Override - public Parent createToolbar() { - return UIFactory.createToolbar(() -> this.toggleMenu(), - () -> SceneManager.switchTo(SceneFactory.createStartView())); - } - - @Override - public Parent createMenu() { - return UIFactory.createMenu("Account", - List.of(" • Portfolio", " • Transactions"), - () -> System.out.println("Portfolio clicked!"), - () -> System.out.println("Transaction clicked!")); - - } - -} diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeController.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeController.java index eed868d..5725e60 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeController.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeController.java @@ -1,6 +1,7 @@ package edu.ntnu.idi.idatt.view.primary.exchange; import java.util.ArrayList; +import java.util.List; import edu.ntnu.idi.idatt.model.market.Stock; import edu.ntnu.idi.idatt.session.UserSession; @@ -11,42 +12,74 @@ public class ExchangeController extends AbstractController { + public enum SortAction { + NONE, + GAINERS, + LOSERS + } + private UserSession session = UserSession.getInstance(); - private final ArrayList loadedStockComponents = new ArrayList<>(); + private ArrayList stocksSorted = new ArrayList<>(); public ExchangeController(ExchangeModel model) { super(model); - for (Stock stock : session.getExchange().getStocks()) { + List initialStocksLoad = session.getExchange().getStocks(); + stocksSorted.addAll(initialStocksLoad); + setStocksModel(initialStocksLoad); + } + + public void setStocksModel(List stockList) { + model.getStockList().clear(); + for (Stock stock : stockList) { StockComponent stockComponent = new StockComponent(stock); stockComponent.onStockClick((symbol) -> redirectView(symbol)); - loadedStockComponents.add(stockComponent); + model.getStockList().add(stockComponent); } - // Initial seeding of view's root. - initStockList(); - } - - public void initStockList() { - this.model.getStockList().setAll(loadedStockComponents); } public void handleSearchQuery(String query) { if (query == null || query.isBlank()) { - initStockList(); + setStocksModel(stocksSorted); // Get back to "no search" return; } - // Clear view's root - this.model.getStockList().clear(); - for (StockComponent stockComponent : loadedStockComponents) { // TODO: Exchange already contains this method - if (stockComponent.getTitle().contains(query)) { - this.model.getStockList().add(stockComponent); + List stocksFound = stocksSorted.stream() + .filter(stock -> { + String name = stock.getCompany() + " (" + stock.getSymbol() + ")"; + return name.contains(query); + }).toList(); + + setStocksModel(stocksFound); + + } + + public void sortStocksBy(SortAction action) { + stocksSorted.clear(); + + switch (action) { + + case NONE: { + stocksSorted.addAll(session.getExchange().getStocks()); + break; + } + + case GAINERS: { + stocksSorted.addAll(session.getExchange().getGainers(Integer.MAX_VALUE)); // TODO: fix? + break; } + + case LOSERS: { + stocksSorted.addAll(session.getExchange().getLosers(Integer.MAX_VALUE)); + } + } + setStocksModel(stocksSorted); + } public void redirectView(String symbol) { - SceneManager.switchTo(SceneFactory.createStockView(session.getExchange().getStock(symbol))); + SceneManager.switchTo(SceneFactory.createStockView(symbol)); } } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeView.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeView.java index 86df130..367f39c 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeView.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/exchange/ExchangeView.java @@ -8,6 +8,7 @@ import edu.ntnu.idi.idatt.view.SceneManager; import edu.ntnu.idi.idatt.view.components.AbstractViewUI; import edu.ntnu.idi.idatt.view.components.ui.UIFactory; +import edu.ntnu.idi.idatt.view.primary.exchange.ExchangeController.SortAction; import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -19,6 +20,7 @@ public class ExchangeView extends AbstractViewUI { Consumer searchQueryHandler; + Consumer sortHandle; private VBox root; @Override @@ -50,7 +52,14 @@ public Parent createNavigation() { @Override public Parent createHeader() { return UIFactory.createHeader("Search for stocks", - query -> searchQueryHandler.accept(query)); + query -> searchQueryHandler.accept(query), + List.of( + "None", + "Gainers (week)", + "Losers (week)"), + () -> sortHandle.accept(SortAction.NONE), + () -> sortHandle.accept(SortAction.GAINERS), + () -> sortHandle.accept(SortAction.LOSERS)); } @Override @@ -74,6 +83,7 @@ public void setModel(ExchangeModel model) { public void setController(ExchangeController controller) { searchQueryHandler = (query) -> controller.handleSearchQuery(query); + sortHandle = (sortAction) -> controller.sortStocksBy(sortAction); } } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/stock/StockController.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/stock/StockController.java index b5d673a..078074a 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/stock/StockController.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/stock/StockController.java @@ -71,6 +71,14 @@ public void renderGraph() { model.getGraphNodes().setAll(lineChart); } + private void resetDisplayBuffers() { + model.getBuyPrice().set("0 $"); + model.getBuyCost().set("0 $"); + model.getTotalPrice().set("0 $"); + model.getResultMessage().set(""); + model.getResultMessageColorProperty().set(CssUtils.RED); + } + private void initHooks() { resetDisplayBuffers(); model.getBuyInputField().addListener((obervable, oldVal, newVal) -> displayBuyInfo(newVal)); @@ -100,14 +108,6 @@ public void buyButtonClicked() { } - private void resetDisplayBuffers() { - model.getBuyPrice().set("0 USD"); - model.getBuyCost().set("0 USD"); - model.getTotalPrice().set("0 USD"); - model.getResultMessage().set(""); - model.getResultMessageColorProperty().set(CssUtils.RED); - } - private void displayBuyInfo(String amountString) { resetDisplayBuffers(); // Reset buffers share = null; @@ -134,36 +134,47 @@ private void displayBuyInfo(String amountString) { session.getExchange().getStock(this.getSymbol()).getSalesPrice()); purchaseCalculator = new PurchaseCalculator(share); - model.getBuyPrice().set(formatter.format(purchaseCalculator.calculateGross()) + " USD"); - model.getBuyCost() - .set(formatter.format(purchaseCalculator.calculateCommision().add(purchaseCalculator.calculateTax())) + " USD"); - model.getTotalPrice().set(formatter.format(purchaseCalculator.calculateTotal()) + " USD"); + String gross = String.format("%s $", formatter.format(purchaseCalculator.calculateGross())); + String cost = String.format("%s $", + formatter.format(purchaseCalculator.calculateCommision().add(purchaseCalculator.calculateTax()))); + String total = String.format("%s $", formatter.format(purchaseCalculator.calculateTotal())); + + model.getBuyPrice().set(gross); + model.getBuyCost().set(cost); + model.getTotalPrice().set(total); } public void setCurrentPrice() { - model.getStockPrice().set(this.stock.getSalesPrice() + " USD"); + model.getStockPrice().set(String.format("%.2f $", this.stock.getSalesPrice())); } public void setLatestChange() { - double change = this.stock.getLatestPriceChange().doubleValue(); - double changePercent = this.stock.getLatestPriceChangePercent().doubleValue(); + BigDecimal change = this.stock.getLatestPriceChange(); + BigDecimal changePercent = this.stock.getLatestPriceChangePercent(); + + String format = String.format("%.2f $ ( %.2f %%)", change, changePercent); - model.getLatestChange().set(change + " USD " - + "(" + changePercent + "%)"); - model.getLatestChangeColorProperty().set(CssUtils.generateValueColors(change)); + model.getLatestChange().set(format); + model.getLatestChangeColorProperty().set(CssUtils.generateValueColors(change.doubleValue())); } public void setAllTimeScore() { - model.getAllTimeScore().set(this.stock.getHighestPrice() + "/" - + this.stock.getLowestPrice() + " USD"); + BigDecimal highest = this.stock.getHighestPrice(); + BigDecimal lowest = this.stock.getLowestPrice(); + + String format = String.format("%.2f / %.2f", highest, lowest); + + model.getAllTimeScore().set(format); } public void setOwnedAmount() { double ownedAmount = session.getPlayer().getPortfolio().getOwnedAmount( this.stock.getSymbol()).doubleValue(); + String symbol = this.stock.getSymbol(); - model.getOwnedAmount().set( - ownedAmount + " [" + this.stock.getSymbol() + "]"); + String format = String.format("%.2f [%s]", ownedAmount, symbol); + + model.getOwnedAmount().set(format); } public void setTotalProfits() { @@ -172,7 +183,9 @@ public void setTotalProfits() { double profitPercent = session.getPlayer().getPortfolio().getChangeFromStock( this.stock.getSymbol()).doubleValue(); - model.getTotalProfits().set(profit + " USD (" + profitPercent + "%)"); + String format = String.format("%.2f $ (%.2f %%)", profit, profitPercent); + + model.getTotalProfits().set(format); model.getTotalProfitsColorProperty().set(CssUtils.generateValueColors(profit)); } } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionController.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionController.java index d402ccc..ed7c640 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionController.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionController.java @@ -1,24 +1,127 @@ package edu.ntnu.idi.idatt.view.primary.transactions; import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import edu.ntnu.idi.idatt.model.transaction.Purchase; +import edu.ntnu.idi.idatt.model.transaction.Sale; import edu.ntnu.idi.idatt.model.transaction.Transaction; +import edu.ntnu.idi.idatt.service.transaction.SaleCalculator; import edu.ntnu.idi.idatt.session.UserSession; import edu.ntnu.idi.idatt.view.components.AbstractController; import edu.ntnu.idi.idatt.view.components.elements.TransactionComponent; public class TransactionController extends AbstractController { + public enum SortAction { + NONE, + NEWEST_DESCENDING, + OLDEST_ASCENDING, + PURCHASE, + SALE, + PROFIT, + LOSS + } + private UserSession session = UserSession.getInstance(); - private final ArrayList loadedTransactionComponents = new ArrayList<>(); + private ArrayList transactionsSorted = new ArrayList<>(); public TransactionController(TransactionModel model) { super(model); - for (Transaction transaction : session.getPlayer().getTransactionArchive().getTransactions(1)) { - TransactionComponent transactionComponent = new TransactionComponent(transaction); - loadedTransactionComponents.add(transactionComponent); + List initialTransactionLoad = session.getPlayer().getTransactionArchive().getTransactions(); + transactionsSorted.addAll(initialTransactionLoad); + setTransactionModel(initialTransactionLoad); + } + + public void setTransactionModel(List list) { + model.getTransactionList().clear(); + for (Transaction transaction : list) { + model.getTransactionList().add(new TransactionComponent(transaction)); + } + } + + public void handleSearchQuery(String query) { + if (query == null || query.isBlank()) { + setTransactionModel(transactionsSorted); + return; } - model.transactionList.addAll(loadedTransactionComponents); + + List transactionsFound = transactionsSorted.stream() + .filter(transaction -> { + String name = transaction.getShare().getStock().getCompany() + " (" + + transaction.getShare().getStock().getSymbol() + + ")"; + return name.contains(query); + }).toList(); + + setTransactionModel(transactionsFound); + } + + public void sortTransactionsBy(SortAction action) { + int currentWeek = session.getExchange().getWeek(); + transactionsSorted.clear(); // Clear buffers + + switch (action) { + + case NONE: { + transactionsSorted.addAll(session.getPlayer().getTransactionArchive().getTransactions()); + break; + } + + case NEWEST_DESCENDING: { + for (int i = 1; i <= currentWeek; i++) { // Exchanges start at week 1. + List getCurrentTransactions = session.getPlayer().getTransactionArchive().getTransactions(i); + transactionsSorted.addAll(getCurrentTransactions); + } + break; + } + + case OLDEST_ASCENDING: { + for (int i = currentWeek; i >= 1; i--) { + List getCurrentTransactions = session.getPlayer().getTransactionArchive().getTransactions(i); + transactionsSorted.addAll(getCurrentTransactions); + } + break; + } + + case PURCHASE: { + for (int i = currentWeek; i >= 1; i--) { + List getCurrentTransactions = session.getPlayer().getTransactionArchive().getPurchases(i); + transactionsSorted.addAll(getCurrentTransactions); + } + break; + } + + case SALE: { + for (int i = currentWeek; i >= 1; i--) { + List getCurrentTransactions = session.getPlayer().getTransactionArchive().getSales(i); + transactionsSorted.addAll(getCurrentTransactions); + } + break; + } + + case PROFIT: { + List getCurrentTransactions = new ArrayList<>(session.getPlayer().getTransactionArchive().getSales()); + getCurrentTransactions + .sort(Comparator.comparing((Sale sale) -> ((SaleCalculator) sale.getCalculator()).calculateProfit()) + .reversed()); + transactionsSorted.addAll(getCurrentTransactions); + break; + } + + case LOSS: { + List getCurrentTransactions = new ArrayList<>(session.getPlayer().getTransactionArchive().getSales()); + getCurrentTransactions + .sort(Comparator.comparing(sale -> ((SaleCalculator) sale.getCalculator()).calculateProfit())); + transactionsSorted.addAll(getCurrentTransactions); + break; + } + + } + + setTransactionModel(transactionsSorted); + } } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionModel.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionModel.java index 30d51ee..6622bf5 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionModel.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionModel.java @@ -7,7 +7,7 @@ public class TransactionModel implements Model { - ObservableList transactionList = FXCollections.observableArrayList(); + private final ObservableList transactionList = FXCollections.observableArrayList(); public ObservableList getTransactionList() { return transactionList; diff --git a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionView.java b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionView.java index 6ee7cac..33a5c10 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionView.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/primary/transactions/TransactionView.java @@ -1,11 +1,13 @@ package edu.ntnu.idi.idatt.view.primary.transactions; import java.util.List; +import java.util.function.Consumer; import edu.ntnu.idi.idatt.view.SceneFactory; import edu.ntnu.idi.idatt.view.SceneManager; import edu.ntnu.idi.idatt.view.components.AbstractViewUI; import edu.ntnu.idi.idatt.view.components.ui.UIFactory; +import edu.ntnu.idi.idatt.view.primary.transactions.TransactionController.SortAction; import javafx.beans.binding.Bindings; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -17,6 +19,8 @@ public class TransactionView extends AbstractViewUI { HBox root; + Consumer searchQueryHandler; + Consumer sortHandle; @Override public Parent createContent() { @@ -44,7 +48,22 @@ public Parent createNavigation() { @Override public Parent createHeader() { return UIFactory.createHeader("Search for transactions...", - query -> System.out.println(query)); + (query) -> searchQueryHandler.accept(query), + List.of( + "None", + "Week (newest - oldest)", + "Week (oldest - newest)", + "Purchases only (newest)", + "Sales only (newest)", + "Highest profit", + "Highest loss"), + () -> sortHandle.accept(SortAction.NONE), + () -> sortHandle.accept(SortAction.NEWEST_DESCENDING), + () -> sortHandle.accept(SortAction.OLDEST_ASCENDING), + () -> sortHandle.accept(SortAction.PURCHASE), + () -> sortHandle.accept(SortAction.SALE), + () -> sortHandle.accept(SortAction.PROFIT), + () -> sortHandle.accept(SortAction.LOSS)); } @Override @@ -58,7 +77,8 @@ public Parent createMenu() { return UIFactory.createMenu("Account", List.of(" • Portfolio", " • Transactions"), () -> System.out.println("Portfolio clicked!"), - () -> System.out.println("Transaction clicked!")); + () -> { + }); } @@ -67,7 +87,8 @@ public void setModel(TransactionModel model) { } public void setController(TransactionController controller) { - + searchQueryHandler = (query) -> controller.handleSearchQuery(query); + sortHandle = (sortAction) -> controller.sortTransactionsBy(sortAction); } }