Skip to content

her #148

Merged
merged 1 commit into from
May 26, 2026
Merged

her #148

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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