Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into 129-final-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
tommyah committed May 26, 2026
2 parents dd7cf02 + bd5e087 commit 3d8e11c
Show file tree
Hide file tree
Showing 4 changed files with 413 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import edu.ntnu.idi.idatt2003.g40.mappe.engine.Exchange;
import edu.ntnu.idi.idatt2003.g40.mappe.model.*;
import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventSubscriber;
Expand Down Expand Up @@ -77,6 +79,106 @@ public DashBoardController(final DashBoardView viewElement,
private void handleStockSelection(final Stock stock, final float amountOwned) {
getViewElement().setCurrentStock(stock, amountOwned);
getViewElement().updateGraph(selectedTimeRange);
updatePreviews();
}

/**
* Recalculates both the cost preview (for buying) and the
* sale preview (for selling) based on the currently selected
* stock's price and the value of the share quantity input
* field, and pushes the results to the view.
*
* <p>Uses {@link PurchaseCalculator} for the cost preview so it
* reflects the same rates that are applied when an actual
* purchase is committed (0.5% commission, no tax).</p>
*
* <p>Uses {@link SaleCalculator} for the sale preview, built on
* a {@link Share} that reuses the player's existing position
* (purchase price and capped quantity), so the calculated tax
* and net amount reflect what the player would actually receive
* if they sold right now. If the player owns no shares of the
* currently selected stock, the sale preview shows zero.</p>
* */
private void updatePreviews() {
Stock current = getViewElement().getCurrentStock();
if (current == null) {
getViewElement().updateCostPreview(0f, 0f);
getViewElement().updateSalePreview(0f, 0f);
return;
}
String text = getViewElement().getQuantityInputField().getText();
if (!Validator.NOT_EMPTY.isValid(text)) {
getViewElement().updateCostPreview(0f, 0f);
getViewElement().updateSalePreview(0f, 0f);
return;
}
try {
BigDecimal quantity = new BigDecimal(text);
if (quantity.compareTo(BigDecimal.ZERO) <= 0) {
getViewElement().updateCostPreview(0f, 0f);
getViewElement().updateSalePreview(0f, 0f);
return;
}
updateCostPreview(current, quantity);
updateSalePreview(current, quantity);
} catch (IllegalArgumentException e) {
getViewElement().updateCostPreview(0f, 0f);
getViewElement().updateSalePreview(0f, 0f);
}
}

/**
* Computes and pushes the cost preview (total cost +
* commission) for buying the given quantity of the given
* stock at the current price.
*
* @param stock the stock to compute the cost for.
* @param quantity the amount of shares to buy.
* */
private void updateCostPreview(final Stock stock,
final BigDecimal quantity) {
Share previewShare = new Share(stock, quantity, stock.getSalesPrice());
PurchaseCalculator calc = new PurchaseCalculator(previewShare);
getViewElement().updateCostPreview(
calc.calculateTotal().floatValue(),
calc.calculateCommission().floatValue()
);
}

/**
* Computes and pushes the sale preview (net amount received
* + tax) for selling the given quantity of the given stock
* at the current price.
*
* <p>Caps the requested quantity at the amount the player
* actually owns (mirroring the behavior in
* {@link Exchange#sell}). If the player owns nothing of
* the stock, the preview is set to zero.</p>
*
* @param stock the stock to compute the sale for.
* @param quantity the amount of shares the player wants to sell.
* */
private void updateSalePreview(final Stock stock,
final BigDecimal quantity) {
List<Share> owned = player.getPortfolio().getShares(stock.getSymbol());
if (owned.isEmpty()) {
getViewElement().updateSalePreview(0f, 0f);
return;
}
Share ownedPosition = owned.getFirst();
BigDecimal sellQuantity = quantity.min(ownedPosition.getQuantity());
if (sellQuantity.compareTo(BigDecimal.ZERO) <= 0) {
getViewElement().updateSalePreview(0f, 0f);
return;
}
Share previewShare = new Share(
stock, sellQuantity, ownedPosition.getPurchasePrice()
);
SaleCalculator calc = new SaleCalculator(previewShare);
getViewElement().updateSalePreview(
calc.calculateTotal().floatValue(),
calc.calculateTax().floatValue()
);
}

/**
Expand Down Expand Up @@ -136,6 +238,7 @@ protected void initInteractions() {
populateStockList("");
getViewElement().setCurrentStock(stockList.getFirst(), 0);
getViewElement().updateGraph(selectedTimeRange);
updatePreviews();
getViewElement().setOnAction(DashBoardActions.BUY_SHARES, () -> {
if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText())
&& Float.parseFloat(getViewElement().getQuantityInputField().getText()) > 0) {
Expand All @@ -147,6 +250,7 @@ protected void initInteractions() {
);
if (purchase.isCommited()) {
getViewElement().addOwnedShares(purchase.getShare().getQuantity().floatValue());
updatePreviews();
}
}
});
Expand All @@ -161,6 +265,7 @@ protected void initInteractions() {

if(sale.isCommited()) {
getViewElement().addOwnedShares(-sale.getShare().getQuantity().floatValue());
updatePreviews();
}
}
});
Expand Down Expand Up @@ -194,6 +299,7 @@ protected void initInteractions() {
exchange.weekProperty().addListener((observable,o,n) -> {
getViewElement().updateGraph(selectedTimeRange);
populateStockList(selectedFilter);
updatePreviews();
});

getViewElement().getQuantityInputField().setTextFormatter(new TextFormatter<>(change -> {
Expand All @@ -203,6 +309,10 @@ protected void initInteractions() {
return null;
}));

getViewElement().getQuantityInputField().textProperty().addListener((observable, o, n) -> {
updatePreviews();
});

getViewElement().getQuantityInputField().focusedProperty().addListener((observable, wasFocused, isNowFocused) -> {
if (!isNowFocused && getViewElement().getQuantityInputField().getText().trim().isEmpty()) {
getViewElement().getQuantityInputField().setText("1.0");
Expand Down Expand Up @@ -241,6 +351,7 @@ public void handleStockPoolUpdate(final List<Stock> updatedStocks) {
.getTotalShareQuantityBySymbol(firstStock.getSymbol());

handleStockSelection(firstStock, ownedAmount.floatValue());
updatePreviews();
}

/**
Expand Down Expand Up @@ -268,5 +379,6 @@ public <T> void handleEvent(final EventData<T> data) {
);
}

updatePreviews();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,32 @@ public final class DashBoardView extends ViewElement<HBox, DashBoardActions> {
* */
private Label highPriceLabel;

/**
* The label showing the estimated cost of buying/selling
* the chosen quantity of shares at the current price.
* */
private Label costPreviewLabel;

/**
* The label showing the estimated commission and tax
* for the chosen quantity of shares at the current price.
* */
private Label feesPreviewLabel;

/**
* The label showing the estimated total amount received
* when selling the chosen quantity of shares at the
* current price (gross minus commission and tax).
* */
private Label saleTotalPreviewLabel;

/**
* The label showing the estimated tax that will be paid
* when selling the chosen quantity of shares at the
* current price.
* */
private Label saleTaxPreviewLabel;

/**
* Constructor.
* */
Expand Down Expand Up @@ -300,6 +326,11 @@ protected void initLayout() {
buySharesBtn = new Button("buy");
sellSharesBtn = new Button("sell");

costPreviewLabel = new Label("Cost: 0 NOK");
feesPreviewLabel = new Label("Commission: 0 NOK Tax: 0 NOK");
saleTotalPreviewLabel = new Label("Sale total: 0 NOK");
saleTaxPreviewLabel = new Label("Tax: 0 NOK");

grid = new GridPane();
grid.setHgap(0);
grid.setVgap(0);
Expand All @@ -325,8 +356,16 @@ protected void initLayout() {
HBox.setHgrow(buySharesBtn, Priority.ALWAYS);
HBox.setHgrow(sellSharesBtn, Priority.ALWAYS);

grid.add(qtySection, 0, 0, 6,1);
grid.add(tradeBtns, 0, 1, 6, 1);
grid.add(costPreviewLabel, 0, 0, 6, 1);
grid.add(feesPreviewLabel, 0, 1, 6, 1);
grid.add(qtySection, 0, 2, 6, 1);
grid.add(saleTotalPreviewLabel, 0, 3, 6, 1);
grid.add(saleTaxPreviewLabel, 0, 4, 6, 1);
grid.add(tradeBtns, 0, 5, 6, 1);
GridPane.setHalignment(costPreviewLabel, HPos.CENTER);
GridPane.setHalignment(feesPreviewLabel, HPos.CENTER);
GridPane.setHalignment(saleTotalPreviewLabel, HPos.CENTER);
GridPane.setHalignment(saleTaxPreviewLabel, HPos.CENTER);

HBox.setHgrow(stockIdentity, Priority.ALWAYS);
HBox.setHgrow(priceStats, Priority.ALWAYS);
Expand Down Expand Up @@ -382,6 +421,10 @@ protected void initStyling() {
rightQty.getStyleClass().add("dashboard-rightQty");
qtySection.getStyleClass().add("dashboard-qtySection");
tradeBtns.getStyleClass().add("dashboard-tradeBtns");
costPreviewLabel.getStyleClass().add("dashboard-cost-preview");
feesPreviewLabel.getStyleClass().add("dashboard-fees-preview");
saleTotalPreviewLabel.getStyleClass().add("dashboard-sale-preview");
saleTaxPreviewLabel.getStyleClass().add("dashboard-tax-preview");
timeRangeSelector.getStyleClass().add("dashboard-combo-box");
}

Expand Down Expand Up @@ -516,6 +559,45 @@ private void setHighPriceLabel(final float value) {
highPriceLabel.setText("High: " + Math.round(value * 100f) / 100f + " NOK");
}

/**
* Updates the cost preview labels, showing how much it will
* cost to buy the chosen amount of shares at the current
* stock price (gross + commission), as well as the
* commission part of the cost on its own.
*
* @param cost the estimated total cost in NOK
* (gross + commission).
* @param commission the estimated commission in NOK.
* */
public void updateCostPreview(final float cost,
final float commission) {
costPreviewLabel.setText("Cost: " + Math.round(cost * 100f) / 100f + " NOK");
feesPreviewLabel.setText(
"Commission: " + Math.round(commission * 100f) / 100f + " NOK"
);
}

/**
* Updates the sale preview labels, showing how much the
* player would receive on their account if they sold the
* chosen amount of currently owned shares right now
* (gross minus commission and tax), as well as the tax
* part of that calculation on its own.
*
* @param saleNet the estimated net amount received from
* the sale in NOK (gross - commission - tax).
* @param tax the estimated tax in NOK.
* */
public void updateSalePreview(final float saleNet,
final float tax) {
saleTotalPreviewLabel.setText(
"Sale total: " + Math.round(saleNet * 100f) / 100f + " NOK"
);
saleTaxPreviewLabel.setText(
"Tax: " + Math.round(tax * 100f) / 100f + " NOK"
);
}

/**
* Getter method for the search text field in the sidebar.
*
Expand Down
Loading

0 comments on commit 3d8e11c

Please sign in to comment.