Skip to content

Commit

Permalink
Merge pull request #46 from danieskj/transactions-view
Browse files Browse the repository at this point in the history
Transactions view
  • Loading branch information
pawelsa authored May 13, 2026
2 parents b6898ac + 2345a7e commit fa89a80
Show file tree
Hide file tree
Showing 22 changed files with 567 additions and 38 deletions.
4 changes: 4 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/model/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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));
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/edu/ntnu/idi/idatt/model/portfolio/Share.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

}
10 changes: 10 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/session/UserSession.java
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -21,13 +25,15 @@ public static UserSession getInstance() {

private Player player;
private Exchange exchange;
private final SimpleDoubleProperty moneyProperty = new SimpleDoubleProperty();

public Player getPlayer() {
return player;
}

public void setPlayer(Player player) {
this.player = player;
moneyProperty.set(player.getMoney().doubleValue());
}

public Exchange getExchange() {
Expand All @@ -42,6 +48,10 @@ public SessionBundle getSession() {
return new SessionBundle(player, exchange);
}

public SimpleDoubleProperty moneyProperty() {
return moneyProperty;
}

public class SessionBundle {

private Player player;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/storage/SessionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Transaction>, JsonDeserializer<Transaction> {

@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);
}

}

}
Original file line number Diff line number Diff line change
@@ -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<TransactionCalculator>, JsonDeserializer<TransactionCalculator> {

@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);
}

}

}
15 changes: 15 additions & 0 deletions src/main/java/edu/ntnu/idi/idatt/view/SceneFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Label> labels = List.of(totalValue, taxComissionValue, grossValue, amountOfShares, saleProfit);
labels.forEach(l -> CssUtils.apply(l, CssUtils.MED_TEXT_16));

UICompositor transactionComponent = new UICompositor.Builder()
.parent(new VBox())
.growWithAlignment(Pos.CENTER)
.addContent(title)
.addContent(name)
.filler()
.addContent(totalValue)
.addContent(taxComissionValue)
.addContent(grossValue)
.addContent(amountOfShares)
.addContent(saleProfit)
.build();

this.getChildren().add(transactionComponent.makeUI());

}

}
Loading

0 comments on commit fa89a80

Please sign in to comment.