Permalink
Browse files

Fixed the bug with the selling part that did not function correct wit…

…h reservations of items
  • Loading branch information...
1 parent 3a3e575 commit 1a352313ef4ead02c3f5727f461820be9fcbd683 @jettro jettro committed Jan 27, 2012
Showing with 222 additions and 33 deletions.
  1. +4 −0 app/src/main/java/org/axonframework/samples/trader/app/command/trading/Portfolio.java
  2. +1 −1 app/src/main/java/org/axonframework/samples/trader/app/command/trading/SellTradeManagerSaga.java
  3. +2 −1 app/src/main/java/org/axonframework/samples/trader/app/command/trading/Transaction.java
  4. +10 −0 app/src/main/java/org/axonframework/samples/trader/app/query/portfolio/ItemEntry.java
  5. +13 −0 app/src/main/java/org/axonframework/samples/trader/app/query/portfolio/PortfolioEntry.java
  6. +1 −0 ...rc/main/java/org/axonframework/samples/trader/app/query/portfolio/PortfolioItemEventListener.java
  7. +1 −1 ...rc/main/java/org/axonframework/samples/trader/app/query/transaction/TransactionEventListener.java
  8. +1 −1 app/src/main/java/org/axonframework/samples/trader/app/query/transaction/TransactionState.java
  9. +3 −1 ...g/axonframework/samples/trader/app/query/transaction/repositories/TransactionQueryRepository.java
  10. +17 −0 app/src/test/java/org/axonframework/samples/trader/app/command/trading/BuyTradeManagerSagaTest.java
  11. +14 −24 app/src/test/java/org/axonframework/samples/trader/app/query/portfolio/PortfolioEntryMatcher.java
  12. +51 −0 app/src/test/java/org/axonframework/samples/trader/app/query/portfolio/PortfolioEntryTest.java
  13. +8 −1 ...est/java/org/axonframework/samples/trader/app/query/portfolio/PortfolioItemEventListenerTest.java
  14. +19 −0 web-ui/src/main/java/org/axonframework/samples/trader/webui/dashboard/DashboardController.java
  15. +1 −0 web-ui/src/main/resources/META-INF/spring/security-context.xml
  16. +2 −1 web-ui/src/main/webapp/WEB-INF/decorators/master.jsp
  17. +74 −2 web-ui/src/main/webapp/WEB-INF/jsp/dashboard/index.jsp
  18. BIN web-ui/src/main/webapp/favicon.ico
@@ -135,6 +135,10 @@ public void onItemsReserved(ItemsReservedEvent event) {
public void onReservationConfirmed(ItemReservationConfirmedForPortfolioEvent event) {
long reserved = obtainCurrentReservedItems(event.getOrderBookIdentifier());
reservedItems.put(event.getOrderBookIdentifier(), reserved - event.getAmountOfConfirmedItems());
+
+ long available = obtainCurrentAvailableItems(event.getOrderBookIdentifier());
+ availableItems.put(event.getOrderBookIdentifier(), available - event.getAmountOfConfirmedItems());
+
}
@EventHandler
@@ -68,7 +68,7 @@ public void handle(NotEnoughItemsAvailableToReserveInPortfolio event) {
@SagaEventHandler(associationProperty = "transactionIdentifier")
public void handle(SellTransactionConfirmedEvent event) {
- logger.debug("Sell Transaction {} is approved to make the buy order", event.getTransactionIdentifier());
+ logger.debug("Sell Transaction {} is approved to make the sell order", event.getTransactionIdentifier());
CreateSellOrderCommand command = new CreateSellOrderCommand(getPortfolioIdentifier(), getOrderbookIdentifier(), getTransactionIdentifier(), getTotalItems(), getPricePerItem());
getCommandBus().dispatch(command);
@@ -117,7 +117,8 @@ public void onTransactionConfirmed(BuyTransactionConfirmedEvent event) {
@EventHandler
public void onTransactionConfirmed(SellTransactionConfirmedEvent event) {
- // do nothing for now
+ logger.debug("Sell transaction is confirmed, but we do not have to do anything. (Id of transaction is {}",
+ getIdentifier().asString());
}
@EventHandler
@@ -55,4 +55,14 @@ public String getIdentifier() {
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
+
+ @Override
+ public String toString() {
+ return "ItemEntry{" +
+ "amount=" + amount +
+ ", identifier='" + identifier + '\'' +
+ ", companyIdentifier='" + companyIdentifier + '\'' +
+ ", companyName='" + companyName + '\'' +
+ '}';
+ }
}
@@ -141,4 +141,17 @@ private void handleRemoveItem(Map<String, ItemEntry> items, String itemIdentifie
}
}
}
+
+ @Override
+ public String toString() {
+ return "PortfolioEntry{" +
+ "amountOfMoney=" + amountOfMoney +
+ ", identifier='" + identifier + '\'' +
+ ", userIdentifier='" + userIdentifier + '\'' +
+ ", userName='" + userName + '\'' +
+ ", reservedAmountOfMoney=" + reservedAmountOfMoney +
+ ", itemsInPossession=" + itemsInPossession +
+ ", itemsReserved=" + itemsReserved +
+ '}';
+ }
}
@@ -65,6 +65,7 @@ public void handleEvent(ItemReservationConfirmedForPortfolioEvent event) {
logger.debug("Handle ItemReservationConfirmedForPortfolioEvent for orderbook with identifier {}", event.getOrderBookIdentifier());
PortfolioEntry portfolioEntry = portfolioRepository.findOne(event.getPortfolioIdentifier().asString());
portfolioEntry.removeReservedItem(event.getOrderBookIdentifier().asString(), event.getAmountOfConfirmedItems());
+ portfolioEntry.removeItemsInPossession(event.getOrderBookIdentifier().asString(), event.getAmountOfConfirmedItems());
portfolioRepository.save(portfolioEntry);
}
@@ -88,7 +88,7 @@ public void handleEvent(SellTransactionPartiallyExecutedEvent event) {
private void partiallyExecuteTransaction(AbstractTransactionPartiallyExecutedEvent event) {
TransactionEntry transactionEntry = transactionQueryRepository.findOne(event.getTransactionIdentifier().asString());
- transactionEntry.setState(EXECUTED);
+ transactionEntry.setState(PARTIALLYEXECUTED);
transactionEntry.setAmountOfExecutedItems((int) event.getTotalOfExecutedItems());
transactionEntry.setPricePerItem(event.getItemPrice());
transactionQueryRepository.save(transactionEntry);
@@ -19,5 +19,5 @@
* @author Jettro Coenradie
*/
public enum TransactionState {
- STARTED, CONFIRMED, CANCELLED, EXECUTED
+ STARTED, CONFIRMED, CANCELLED, EXECUTED, PARTIALLYEXECUTED
}
@@ -18,9 +18,11 @@
import org.axonframework.samples.trader.app.query.transaction.TransactionEntry;
import org.springframework.data.repository.PagingAndSortingRepository;
+import java.util.List;
+
/**
* @author Jettro Coenradie
*/
public interface TransactionQueryRepository extends PagingAndSortingRepository<TransactionEntry, String> {
- TransactionEntry findByPortfolioIdentifier(String portfolioIdentifier);
+ List<TransactionEntry> findByPortfolioIdentifier(String portfolioIdentifier);
}
@@ -134,4 +134,21 @@ public void testHandle_BuyTransactionPartiallyExecuted() {
new AddItemsToPortfolioCommandMatcher(portfolioIdentifier, orderbookIdentifier, 50)));
}
+ @Test
+ public void testHandle_MultipleBuyTransactionPartiallyExecuted() {
+ AggregateIdentifier sellOrderIdentifier = new UUIDAggregateIdentifier();
+ AggregateIdentifier sellTransactionIdentifier = new UUIDAggregateIdentifier();
+
+ fixture.givenAggregate(transactionIdentifier).published(new BuyTransactionStartedEvent(orderbookIdentifier, portfolioIdentifier, TOTAL_ITEMS, PRICE_PER_ITEM))
+ .andThenAggregate(portfolioIdentifier).published(new MoneyReservedFromPortfolioEvent(transactionIdentifier, TOTAL_ITEMS * PRICE_PER_ITEM))
+ .andThenAggregate(transactionIdentifier).published(new BuyTransactionConfirmedEvent())
+ .andThenAggregate(orderbookIdentifier).published(new TradeExecutedEvent(50, 99, orderbookIdentifier, sellOrderIdentifier, transactionIdentifier, sellTransactionIdentifier))
+ .whenAggregate(transactionIdentifier).publishes(new BuyTransactionPartiallyExecutedEvent(50, 50, 99))
+ .expectActiveSagas(1)
+ .expectDispatchedCommandsMatching(
+ exactSequenceOf(
+ new ConfirmMoneyReservationFromPortfolionCommandMatcher(portfolioIdentifier, 50 * 99),
+ new AddItemsToPortfolioCommandMatcher(portfolioIdentifier, orderbookIdentifier, 50)));
+ }
+
}
@@ -22,7 +22,6 @@
* @author Jettro Coenradie
*/
public class PortfolioEntryMatcher extends ArgumentMatcher<PortfolioEntry> {
- private String problem;
private int itemsInPossession;
private String itemIdentifier;
private int amountOfItemInPossession;
@@ -40,37 +39,28 @@ public PortfolioEntryMatcher(String itemIdentifier, int itemsInPossession, int a
@Override
public boolean matches(Object argument) {
if (!(argument instanceof PortfolioEntry)) {
- problem = String.format("Wrong argument type, required %s but received %s", PortfolioEntry.class.getName(), argument.getClass().getName());
return false;
}
PortfolioEntry portfolioEntry = (PortfolioEntry) argument;
- if (portfolioEntry.getItemsInPossession().size() != itemsInPossession) {
- problem = String.format("Amount of item entries in possession should be %d but was %d", itemsInPossession, portfolioEntry.getItemsInPossession().size());
- return false;
- }
- long foundAmountOfItemsInPossession = portfolioEntry.findItemInPossession(itemIdentifier).getAmount();
- if (foundAmountOfItemsInPossession != amountOfItemInPossession) {
- problem = String.format("The amount of the item in possession should be %d but was %d", amountOfItemInPossession, foundAmountOfItemsInPossession);
- return false;
- }
- if (portfolioEntry.getItemsReserved().size() != itemsInReservation) {
- problem = String.format("The amount of reserved item entries should be %d but was %d", itemsInReservation, portfolioEntry.getItemsReserved().size());
- return false;
- }
- if (itemsInReservation != 0) {
- long foundAmountOfItemsInReservation = portfolioEntry.findReservedItemByIdentifier(itemIdentifier).getAmount();
- if (foundAmountOfItemsInReservation != amountOfItemInReservation) {
- problem = String.format("The amount of the reserved items should be %d but was %d", amountOfItemInReservation, foundAmountOfItemsInReservation);
- return false;
- }
- }
+ return portfolioEntry.getItemsInPossession().size() == itemsInPossession
+ && amountOfItemInPossession == portfolioEntry.findItemInPossession(itemIdentifier).getAmount()
+ && portfolioEntry.getItemsReserved().size() == itemsInReservation
+ && !(itemsInReservation != 0 && (amountOfItemInReservation != portfolioEntry.findReservedItemByIdentifier(itemIdentifier).getAmount()));
- return true;
}
@Override
public void describeTo(Description description) {
- description.appendText(problem);
+ description.appendText("PortfolioEntry with itemsInPossession [")
+ .appendValue(itemsInPossession)
+ .appendText("] and amountOfItemsInPossession [")
+ .appendValue(amountOfItemInPossession)
+ .appendText("] and amountOfItemsInReservation [")
+ .appendValue(amountOfItemInReservation)
+ .appendText("] and itemsInReservation [")
+ .appendValue(itemsInReservation)
+ .appendText("]");
+
}
}
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012. Gridshore
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.axonframework.samples.trader.app.query.portfolio;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Jettro Coenradie
+ */
+public class PortfolioEntryTest {
+ @Test
+ public void testRemovingItems() {
+ PortfolioEntry portfolio = new PortfolioEntry();
+ ItemEntry item1InPossession = new ItemEntry();
+ item1InPossession.setIdentifier("item1");
+ item1InPossession.setAmount(100);
+ item1InPossession.setCompanyIdentifier("company1");
+ item1InPossession.setCompanyName("Company One");
+ portfolio.addItemInPossession(item1InPossession);
+
+ ItemEntry item1InReservation = new ItemEntry();
+ item1InReservation.setIdentifier("item1");
+ item1InReservation.setAmount(33);
+ item1InReservation.setCompanyIdentifier("company1");
+ item1InReservation.setCompanyName("Company One");
+ portfolio.addReservedItem(item1InReservation);
+
+ portfolio.removeReservedItem("item1", 11);
+ assertEquals(22, portfolio.findReservedItemByIdentifier("item1").getAmount());
+
+ portfolio.removeItemsInPossession("item1", 11);
+ assertEquals(89, portfolio.findItemInPossession("item1").getAmount());
+ }
+
+
+}
@@ -32,6 +32,9 @@
import static org.mockito.Mockito.*;
/**
+ * We setup this test with a default portfolio and a default orderBook. The portfolio contains the default amount of
+ * items in Reservation. This means that all available items are reserved.
+ *
* @author Jettro Coenradie
*/
public class PortfolioItemEventListenerTest {
@@ -92,6 +95,10 @@ public void testHandleEventCancelItemReservation() throws Exception {
0)));
}
+ /**
+ * We are going to confirm 50 of the items in the reservation. Therefore we expect the reservation to become 50
+ * less than the default amount of items.
+ */
@Test
public void testHandleEventConfirmItemReservation() {
ItemReservationConfirmedForPortfolioEvent event = new ItemReservationConfirmedForPortfolioEvent(itemIdentifier, transactionIdentifier, 50);
@@ -102,7 +109,7 @@ public void testHandleEventConfirmItemReservation() {
verify(portfolioQueryRepository).save(argThat(new PortfolioEntryMatcher(
itemIdentifier.asString(),
1,
- DEFAULT_AMOUNT_ITEMS,
+ DEFAULT_AMOUNT_ITEMS - 50,
1,
DEFAULT_AMOUNT_ITEMS - 50)));
}
@@ -17,6 +17,8 @@
import org.axonframework.samples.trader.app.query.portfolio.PortfolioEntry;
import org.axonframework.samples.trader.app.query.portfolio.repositories.PortfolioQueryRepository;
+import org.axonframework.samples.trader.app.query.transaction.TransactionEntry;
+import org.axonframework.samples.trader.app.query.transaction.repositories.TransactionQueryRepository;
import org.axonframework.samples.trader.webui.util.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,6 +28,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
+import java.util.List;
+
/**
* @author Jettro Coenradie
*/
@@ -34,13 +38,22 @@
public class DashboardController {
private final static Logger logger = LoggerFactory.getLogger(DashboardController.class);
private PortfolioQueryRepository portfolioRepository;
+ private TransactionQueryRepository transactionRepository;
@RequestMapping(method = RequestMethod.GET)
public String show(Model model) {
String identifier = SecurityUtil.obtainLoggedinUserIdentifier();
logger.debug("Requested to obtain the portfolio for the user: {}", identifier);
+
PortfolioEntry portfolio = portfolioRepository.findByUserIdentifier(identifier);
+ if (portfolio == null) {
+ throw new RuntimeException("You most certainly changed the id of the current logged in user " +
+ "and the user did not logout.");
+ }
model.addAttribute("portfolio", portfolio);
+
+ List<TransactionEntry> transactions = transactionRepository.findByPortfolioIdentifier(portfolio.getIdentifier());
+ model.addAttribute("transactions", transactions);
return "dashboard/index";
}
@@ -49,4 +62,10 @@ public String show(Model model) {
public void setPortfolioRepository(PortfolioQueryRepository portfolioRepository) {
this.portfolioRepository = portfolioRepository;
}
+
+ @SuppressWarnings("SpringJavaAutowiringInspection")
+ @Autowired
+ public void setTransactionRepository(TransactionQueryRepository transactionRepository) {
+ this.transactionRepository = transactionRepository;
+ }
}
@@ -26,6 +26,7 @@
<context:component-scan base-package="org.axonframework.samples.trader.webui.security"/>
<http pattern="/js/**" security="none"/>
+ <http pattern="/favicon.ico" security="none"/>
<http pattern="/style/**" security="none"/>
<http pattern="/login.jsp*" security="none"/>
@@ -40,7 +40,8 @@
<div class="container">
<a class="brand" href="${ctx}/">Axon Trader</a>
<ul class="nav">
- <li class="active"><a href="${ctx}/">Home</a></li>
+ <li><a href="${ctx}/">Home</a></li>
+ <li><a href="${ctx}/dashboard">Dashboard</a></li>
<li><a href="${ctx}/company">Companies</a></li>
<li><a href="${ctx}/data/collections">Data</a></li>
<li><a href="${ctx}/admin/portfolio">Portfolio</a></li>
Oops, something went wrong.

0 comments on commit 1a35231

Please sign in to comment.