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 f75b184..5648efb 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 @@ -2,6 +2,7 @@ import edu.ntnu.idi.idatt.model.portfolio.Portfolio; import edu.ntnu.idi.idatt.model.transaction.TransactionArchive; +import edu.ntnu.idi.idatt.session.UserSession; import java.math.BigDecimal; @@ -49,10 +50,13 @@ 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. } public void withdrawMoney(BigDecimal amount) { this.money = this.money.subtract(amount); + UserSession.getInstance().moneyProperty().set(this.money.doubleValue()); // Hook } /** diff --git a/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java b/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java index 070653d..5bfba56 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Portfolio.java @@ -1,6 +1,7 @@ package edu.ntnu.idi.idatt.model.portfolio; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; @@ -72,12 +73,15 @@ public BigDecimal getProfitFromStock(String symbol) { .reduce(BigDecimal.ZERO, BigDecimal::add); } - public double getChangeFromStock(String symbol) { + public BigDecimal getChangeFromStock(String symbol) { BigDecimal profitTotal = getProfitFromStock(symbol); BigDecimal costTotal = getShares(symbol).stream().map(s -> s.getPurchasePrice()) .reduce(BigDecimal.ZERO, BigDecimal::add); - return (profitTotal.subtract(costTotal).doubleValue()) / 100; + if (costTotal.compareTo(BigDecimal.ZERO) <= 0) + return BigDecimal.ZERO; + + return profitTotal.divide(costTotal, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); } /** diff --git a/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java b/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java index eeaa73f..a00a65f 100644 --- a/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java +++ b/src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java @@ -53,7 +53,8 @@ public BigDecimal getPurchasePrice() { // TODO: JAVADOCS, JUNIT public BigDecimal getProfit() { - return new SaleCalculator(this).calculateGross().subtract(purchasePrice); + BigDecimal totalCost = purchasePrice.multiply(quantity); + return new SaleCalculator(this).calculateGross().subtract(totalCost); } } diff --git a/src/main/java/edu/ntnu/idi/idatt/service/transaction/SaleCalculator.java b/src/main/java/edu/ntnu/idi/idatt/service/transaction/SaleCalculator.java index 8bbe827..102f1a5 100644 --- a/src/main/java/edu/ntnu/idi/idatt/service/transaction/SaleCalculator.java +++ b/src/main/java/edu/ntnu/idi/idatt/service/transaction/SaleCalculator.java @@ -76,4 +76,9 @@ public BigDecimal calculateTotal() { return calculateGross().subtract(calculateCommision()).subtract(calculateTax()); } + // TODO: Javadocs, junit + public BigDecimal calculateProfit() { + return calculateTotal().divide(purchasePrice.multiply(quantity)); + } + } 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 841a22a..b9dbfba 100644 --- a/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java +++ b/src/main/java/edu/ntnu/idi/idatt/session/UserSession.java @@ -1,7 +1,11 @@ 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; public class UserSession { @@ -21,6 +25,7 @@ public static UserSession getInstance() { private Player player; private Exchange exchange; + private final SimpleDoubleProperty moneyProperty = new SimpleDoubleProperty(); public Player getPlayer() { return player; @@ -28,6 +33,7 @@ public Player getPlayer() { public void setPlayer(Player player) { this.player = player; + moneyProperty.set(player.getMoney().doubleValue()); } public Exchange getExchange() { @@ -42,6 +48,10 @@ public SessionBundle getSession() { return new SessionBundle(player, exchange); } + public SimpleDoubleProperty moneyProperty() { + return moneyProperty; + } + public class SessionBundle { private Player player; diff --git a/src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java b/src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java index d2f1e55..8b40489 100644 --- a/src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java +++ b/src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java @@ -18,8 +18,12 @@ import edu.ntnu.idi.idatt.model.Exchange; import edu.ntnu.idi.idatt.model.player.Player; +import edu.ntnu.idi.idatt.model.transaction.Transaction; +import edu.ntnu.idi.idatt.service.transaction.TransactionCalculator; import edu.ntnu.idi.idatt.session.UserSession; import edu.ntnu.idi.idatt.session.UserSession.SessionBundle; +import edu.ntnu.idi.idatt.storage.util.TransactionAdapter; +import edu.ntnu.idi.idatt.storage.util.TransactionCalculatorAdapter; /** * Class for managing user sessions. @@ -36,6 +40,8 @@ public class SessionManager { private static Gson gson = new GsonBuilder() .setPrettyPrinting() + .registerTypeAdapter(Transaction.class, new TransactionAdapter()) + .registerTypeAdapter(TransactionCalculator.class, new TransactionCalculatorAdapter()) .create(); // Static initiator to ensure persistent storage file. diff --git a/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionAdapter.java b/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionAdapter.java new file mode 100644 index 0000000..acc53be --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionAdapter.java @@ -0,0 +1,56 @@ +package edu.ntnu.idi.idatt.storage.util; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import edu.ntnu.idi.idatt.model.transaction.Purchase; +import edu.ntnu.idi.idatt.model.transaction.Sale; +import edu.ntnu.idi.idatt.model.transaction.Transaction; + +public class TransactionAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(Transaction t, Type typeOfT, JsonSerializationContext context) { + + JsonObject obj = context.serialize(t).getAsJsonObject(); + + if (t instanceof Purchase) { + obj.addProperty("type", "purchase"); + } else if (t instanceof Sale) { + obj.addProperty("type", "sale"); + } + + return obj; + } + + @Override + public Transaction deserialize(JsonElement json, + Type typeOfT, + JsonDeserializationContext context) + throws JsonParseException { + + JsonObject obj = json.getAsJsonObject(); + + String type = obj.get("type").getAsString(); + + switch (type) { + case "purchase": + return context.deserialize(obj, Purchase.class); + + case "sale": + return context.deserialize(obj, Sale.class); + + default: + throw new UnsupportedOperationException("Unknown type " + type); + } + + } + +} diff --git a/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionCalculatorAdapter.java b/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionCalculatorAdapter.java new file mode 100644 index 0000000..89f3204 --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/storage/util/TransactionCalculatorAdapter.java @@ -0,0 +1,57 @@ +package edu.ntnu.idi.idatt.storage.util; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import edu.ntnu.idi.idatt.service.transaction.PurchaseCalculator; +import edu.ntnu.idi.idatt.service.transaction.SaleCalculator; +import edu.ntnu.idi.idatt.service.transaction.TransactionCalculator; + +public class TransactionCalculatorAdapter + implements JsonSerializer, JsonDeserializer { + + @Override + public JsonElement serialize(TransactionCalculator calc, Type typeOfT, JsonSerializationContext context) { + + JsonObject obj = context.serialize(calc).getAsJsonObject(); + + if (calc instanceof PurchaseCalculator) { + obj.addProperty("type", "purchase"); + } else if (calc instanceof SaleCalculator) { + obj.addProperty("type", "sale"); + } + + return obj; + } + + @Override + public TransactionCalculator deserialize(JsonElement json, + Type typeOfT, + JsonDeserializationContext context) + throws JsonParseException { + + JsonObject obj = json.getAsJsonObject(); + + String type = obj.get("type").getAsString(); + + switch (type) { + case "purchase": + return context.deserialize(obj, PurchaseCalculator.class); + + case "sale": + return context.deserialize(obj, SaleCalculator.class); + + default: + throw new UnsupportedOperationException("Unknown type " + type); + } + + } + +} 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 6f290f8..4c8f571 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java @@ -10,6 +10,9 @@ import edu.ntnu.idi.idatt.view.primary.stock.StockController; import edu.ntnu.idi.idatt.view.primary.stock.StockModel; import edu.ntnu.idi.idatt.view.primary.stock.StockView; +import edu.ntnu.idi.idatt.view.primary.transactions.TransactionController; +import edu.ntnu.idi.idatt.view.primary.transactions.TransactionModel; +import edu.ntnu.idi.idatt.view.primary.transactions.TransactionView; import javafx.scene.Parent; public class SceneFactory { @@ -52,4 +55,16 @@ public static Parent createStockView(Stock stock) { return view.getInstance(); } + public static Parent createTransactionView() { + + TransactionModel model = new TransactionModel(); + TransactionView view = new TransactionView(); + TransactionController controller = new TransactionController(model); + + view.setModel(model); + view.setController(controller); + + return view.getInstance(); + } + } diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java b/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java index 5d9fd48..8c1a119 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/AbstractViewUI.java @@ -59,7 +59,9 @@ public void createUIComponents() { navigation = new VBox(); navigation.setMaxHeight(Double.MAX_VALUE); navigation.getStyleClass().add("dark"); - navigation.setMaxWidth(200); + navigation.setPrefWidth(150); // ScrollPane's affect this massively + navigation.setMaxWidth(150); + navigation.setMinWidth(150); header = new HBox(); header.getStyleClass().add("light"); diff --git a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TextValueComponent.java b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TextValueComponent.java index dbc4c19..21b8617 100644 --- a/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TextValueComponent.java +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TextValueComponent.java @@ -18,7 +18,8 @@ public TextValueComponent(String prefix) { CssUtils.apply(valueLabel, CssUtils.MED_TEXT_16); color.addListener((obs, oldVal, newVal) -> { - CssUtils.apply(valueLabel, newVal); + CssUtils.set(valueLabel, newVal); + CssUtils.apply(valueLabel, CssUtils.MED_TEXT_16); }); this.getChildren().addAll(prefixLabel, valueLabel); 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 new file mode 100644 index 0000000..850af4a --- /dev/null +++ b/src/main/java/edu/ntnu/idi/idatt/view/components/elements/TransactionComponent.java @@ -0,0 +1,94 @@ +package edu.ntnu.idi.idatt.view.components.elements; + +import java.util.List; + +import edu.ntnu.idi.idatt.model.market.Stock; +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.view.components.ui.UICompositor; +import edu.ntnu.idi.idatt.view.util.CssUtils; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.layout.VBox; + +public class TransactionComponent extends VBox { + + public TransactionComponent(Transaction transaction) { + + Stock stock = transaction.getShare().getStock(); + + Label title; + Label name = new Label(stock.getCompany() + " (" + stock.getSymbol() + ")"); + Label totalValue; + Label taxComissionValue; + Label grossValue; + Label amountOfShares; + Label saleProfit; + + 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(); + + } 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"); + + CssUtils.apply(saleProfit, CssUtils.generateValueColors(calculator.calculateProfit().doubleValue())); + } + + else { + System.out.println("Failed to initialize transactionComponent!"); + return; + } + + this.setPrefSize(400, Double.MAX_VALUE); + this.setMinWidth(400); + this.setPadding(new Insets(40)); + this.setAlignment(Pos.TOP_CENTER); + + // TODO: CHANGE AND IN STOCKComponent + this.setStyle("-fx-background-color: #404950;"); + + CssUtils.apply(title, CssUtils.BIG_TEXT_32); + CssUtils.apply(name, CssUtils.BIG_TEXT_32); + List