Skip to content

Commit

Permalink
Merge pull request #153 from Team-40-IDATT2003/152-fix-portfolio
Browse files Browse the repository at this point in the history
152 fix portfolio
  • Loading branch information
etsorens authored May 26, 2026
2 parents 272b0a6 + 2f3b227 commit ded8d29
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 294 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -295,15 +295,19 @@ public Transaction sell(BigDecimal amount,
throw new IllegalArgumentException("Player does not own any shares of this stock!");
}

Share ownedPosition = matchingShares.getFirst();
BigDecimal totalOwned = ownedPosition.getQuantity();
BigDecimal totalOwned = matchingShares.stream()
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);

if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
}

Stock stock = ownedPosition.getStock();
Share shareToSell = new Share(stock, amount, stock.getSalesPrice());
Stock stock = matchingShares.getFirst().getStock();

BigDecimal oldestPurchasePrice = matchingShares.getFirst().getPurchasePrice();

Share shareToSell = new Share(stock, amount, oldestPurchasePrice);

Transaction sale = TransactionFactory.createTransaction(
TransactionType.SALE, shareToSell, getWeek()
Expand Down
107 changes: 52 additions & 55 deletions src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -17,9 +18,9 @@
public final class Portfolio {

/**
* Map used to handle internal shares.
* List of shares in portfolio.
* */
private final Map<String, Share> shares = new HashMap<>();
private final List<Share> shares = new ArrayList<>();

/**
* Creates an empty portfolio.
Expand All @@ -39,30 +40,13 @@ public void addShare(final Share share) throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
String symbol = share.getStock().getSymbol().toUpperCase();

if (shares.containsKey(symbol)) {
Share existingShare = shares.get(symbol);
BigDecimal totalQuantity =
existingShare.getQuantity().add(share.getQuantity());

shares.put(symbol,
new Share(
share.getStock(),
totalQuantity, existingShare.getPurchasePrice()
)
);
} else {
shares.put(symbol, share);
}
shares.add(share);
}

/**
* Removes a share from the portfolio.
*
* <p>Uses the quantity value to deduct share amount from the map.
* If quantity to remove is equal to amount held, removes share entirely.
* If not, splits the share.</p>
* <p>Removes based on FIFO (First In First Out)</p>
*
* @param share the share to remove
*
Expand All @@ -72,35 +56,42 @@ public void addShare(final Share share) throws IllegalArgumentException {
* @throws IllegalArgumentException if share is null.
*
*/
public boolean removeShare(final Share share)
throws IllegalArgumentException {
public boolean removeShare(final Share share) throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
String symbol = share.getStock().getSymbol().toUpperCase();
if (!shares.containsKey(symbol)) {
return false;
}

Share ownedShare = shares.get(symbol);
int comparison = ownedShare.getQuantity().compareTo(share.getQuantity());
String symbol = share.getStock().getSymbol();

if (comparison < 0) {
throw new IllegalArgumentException(
"Cannot remove more shares than are currently owned!");
} else if (comparison == 0) {

shares.remove(symbol);
} else {
BigDecimal remainingQuantity =
ownedShare.getQuantity().subtract(share.getQuantity());
shares.put(symbol,
new Share(
share.getStock(),
remainingQuantity,
ownedShare.getPurchasePrice()
)
);
List<Share> matchingShares = shares.stream()
.filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
.toList();

BigDecimal quantityToRemove = share.getQuantity();

BigDecimal totalOwned = matchingShares.stream()
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);

if (quantityToRemove.compareTo(totalOwned) > 0) {
throw new IllegalArgumentException("Cannot remove more shares than are currently owned!");
}

for (Share s : matchingShares) {
if (quantityToRemove.signum() <= 0) {
break;
}

int comparison = s.getQuantity().compareTo(quantityToRemove);
shares.remove(s);

if (comparison > 0) {
BigDecimal remainingQuantity = s.getQuantity().subtract(quantityToRemove);
shares.add(new Share(s.getStock(), remainingQuantity, s.getPurchasePrice()));
quantityToRemove = BigDecimal.ZERO;
} else {
quantityToRemove = quantityToRemove.subtract(s.getQuantity());
}
}
return true;
}
Expand All @@ -111,7 +102,7 @@ public boolean removeShare(final Share share)
* @return a list of shares
*/
public List<Share> getShares() {
return List.copyOf(shares.values());
return shares;
}

/**
Expand Down Expand Up @@ -141,8 +132,9 @@ public List<Share> getShares(final String symbol)
throw new IllegalArgumentException(
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
Share share = shares.get(symbol.toUpperCase());
return share != null ? List.of(share) : List.of();
return shares.stream()
.filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
.toList();
}

/**
Expand All @@ -160,10 +152,13 @@ public boolean contains(final Share share)
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
String symbol = share.getStock().getSymbol().toUpperCase();
Share owned = shares.get(symbol);
return owned != null
&& owned.getQuantity().compareTo(share.getQuantity()) >= 0;

BigDecimal totalOwned = shares.stream()
.filter(s -> s.getStock().getSymbol().equalsIgnoreCase(share.getStock().getSymbol()))
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);

return totalOwned.compareTo(share.getQuantity()) >= 0;
}

/**
Expand All @@ -174,7 +169,7 @@ public boolean contains(final Share share)
* */
public BigDecimal getNetWorth() {
BigDecimal netWorth = BigDecimal.ZERO;
for (Share s : shares.values()) {
for (Share s : shares) {
netWorth = netWorth.add(
s.getQuantity().multiply(s.getStock().getSalesPrice())
);
Expand All @@ -194,7 +189,9 @@ public BigDecimal getTotalShareQuantityBySymbol(final String symbol) {
if (symbol == null) {
return BigDecimal.ZERO;
}
Share share = shares.get(symbol.toUpperCase());
return share != null ? share.getQuantity() : BigDecimal.ZERO;
return shares.stream()
.filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewEnum;

import java.io.File;
import java.util.List;

import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
Expand All @@ -22,12 +20,10 @@
*
* <p>Extends {@link ViewController}</p>
*
* <p>
* Handles four user actions: navigating to the create-game screen,
* <p>Handles four user actions: navigating to the create-game screen,
* going back to the main menu, uploading a custom save file from
* disk, and opening a save when one of the displayed rows is
* clicked.
* </p>
* clicked.</p>
*/
public class PlayGameController extends ViewController<PlayGameView> {

Expand All @@ -38,19 +34,6 @@ public class PlayGameController extends ViewController<PlayGameView> {
* */
private final SaveGameService saveGameService;

/**
* Constructor with a default {@link SaveGameService} pointing at
* the bundled saves directory.
*
* @param view the {@link PlayGameView} this controller is
* attached to.
* @param eventManager the active {@link EventManager}.
*/
public PlayGameController(final PlayGameView view,
final EventManager eventManager) {
this(view, eventManager, new SaveGameService());
}

/**
* Constructor accepting an explicit {@link SaveGameService} for
* tests or custom save locations.
Expand Down Expand Up @@ -122,12 +105,10 @@ protected void initInteractions() {
* Re-reads every save from disk and returns the one whose name
* matches, or {@code null} if no matching save is found.
*
* <p>
* Used to defeat the staleness of the {@link SaveGame} instance
* <p>Used to defeat the staleness of the {@link SaveGame} instance
* bound to the clicked row in {@link PlayGameView}: between when
* {@link #refresh()} last ran and the click, the auto-save flow may
* have written newer content to disk.
* </p>
* have written newer content to disk.</p>
*
* @param name the save display name to look up.
* @return the freshly parsed {@link SaveGame}, or {@code null}.
Expand All @@ -149,10 +130,8 @@ private SaveGame findFreshSaveByName(final String name) {
* disk. Parses it using {@link SaveGameService#loadSaveFromFile} and
* appends the resulting save to the view's current list.
*
* <p>
* Shows an alert if the user picks a file that contains no valid
* save entry.
* </p>
* <p>Shows an alert if the user picks a file that contains no valid
* save entry.</p>
*/
private void handleUploadSave() {
FileChooser fileChooser = new FileChooser();
Expand All @@ -175,13 +154,27 @@ private void handleUploadSave() {
if (uploadedSave == null) {
showAlert(AlertType.WARNING,
"No saves found",
"Den valgte filen inneholdt ingen gyldig save.\n\n"
+ "Forventet JSON-format:\n"
"The chosen file includes an unsupported format!\n\n"
+ "Expected JSON format:\n"
+ " {\n"
+ " \"name\": \"MySave\",\n"
+ " \"balance\": 10000.00,\n"
+ " \"startingCapital\": 10000.00,\n"
+ " \"stockDataPath\": null\n"
+ " \"week\": 1\n"
+ " \"ownedShares\": [\n"
+ " { \"symbol\": \"AAPL\", \"quantity\": 5, \"purchasePrice\": 150.00 }\n"
+ " ]\n"
+ " \"transactions\": [\n"
+ " { \"type\": \"PURCHASE\", \"symbol\": \"AAPL\", \"quantity\": 5, \"price\": 150.00, \"week\": 1}\n"
+ " ]\n"
+ " \"stocks\": [\n"
+ " { \"symbol\": \"AAPL\", \"companyName\": \"Apple inc.\", \"salesPrice\": 155.25}\n"
+ " ]\n"
+ " \"netWorthHistory\": [\n"
+ " 1001.10,\n"
+ " 1025.24,\n"
+ " ]\n"
+ " }");
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public void handleContextUpdate(final Exchange criticalExchange, final Player ac
this.playerNetWorthHistory.addAll(this.player.getNetWorthHistory());
} else {
this.playerNetWorthHistory.add(this.player.getStartingMoney());
this.playerNetWorthHistory.add(this.player.getNetWorth());
}

getViewElement().setWeek(this.exchange.getWeek());
Expand Down
13 changes: 0 additions & 13 deletions src/main/resources/saves/Halleluja.json

This file was deleted.

Loading

0 comments on commit ded8d29

Please sign in to comment.