package org.hso.ecommerce.action.shop; import org.hso.ecommerce.action.booking.CreateBookingAction; import org.hso.ecommerce.entities.booking.*; import org.hso.ecommerce.entities.shop.Address; import org.hso.ecommerce.entities.shop.Article; import org.hso.ecommerce.entities.shop.CustomerOrder; import org.hso.ecommerce.entities.shop.CustomerOrderPosition; import org.hso.ecommerce.entities.user.User; import org.hso.ecommerce.entities.warehouse.WarehouseBooking; import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition; import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.List; public class CreateOrderAction { private User user; Address destination; private int expectedTotalGrossCent; private int totalNetCent; private int totalVatCent; private PaymentMethod method; private BookingAccountEntry latestUserBooking; private BookingAccountEntry latestVatBooking; private BookingAccountEntry latestMainBooking; private List orderItems = new ArrayList<>(); public CreateOrderAction( User user, int expectedTotalGrossCent, Address destination, PaymentMethod method, BookingAccountEntry latestUserBooking, BookingAccountEntry latestVatBooking, BookingAccountEntry latestMainBooking ) { this.user = user; this.expectedTotalGrossCent = expectedTotalGrossCent; this.destination = destination; this.method = method; this.latestUserBooking = latestUserBooking; assert latestUserBooking.userAccount.id == user.id; this.latestVatBooking = latestVatBooking; assert latestVatBooking.isVATAccount; this.latestMainBooking = latestMainBooking; assert latestMainBooking.isMainAccount; } public void addArticle(Article article, int quantity, List availableSlots) { for (WarehouseBookingPositionSlotEntry slot : availableSlots) { assert slot.article.id == article.id; } orderItems.add(new OrderItem(article, availableSlots, quantity)); totalNetCent += article.shopPricePerUnitNetCent * quantity; totalVatCent += article.getVat() * quantity; } public Result finish() throws ArticleNotInStockException { CustomerOrder order = createOrder(); CustomerPayment payment = createPayment(); List bookingList = new ArrayList<>(); Booking purchaseBooking = new CreateBookingAction( latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish(); Booking paymentBooking = new CreateBookingAction( null, purchaseBooking.source /* userAccount */, new BookingReason(payment), order.totalGrossCent).finish(); Booking vatBooking = new CreateBookingAction( purchaseBooking.destination /* mainAccount */, latestVatBooking, new BookingReason(order), order.totalVatCent).finish(); bookingList.add(purchaseBooking); bookingList.add(paymentBooking); bookingList.add(vatBooking); WarehouseBooking warehouseBooking = createWarehouseBooking(order); return new Result( order, warehouseBooking, bookingList ); } private WarehouseBooking createWarehouseBooking(CustomerOrder order) throws ArticleNotInStockException { WarehouseBooking booking = new WarehouseBooking(); booking.created = new Timestamp(new Date().getTime()); booking.reason = new BookingReason(order); for (OrderItem item : orderItems) { int needed = item.quantity; // Sort for most empty slot first; item.availableSlots.sort(Comparator.comparingInt(a -> a.newSumSlot)); for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) { if (slot.newSumSlot == 0) { continue; } int remove = Math.min(slot.newSumSlot, needed); needed -= remove; WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition(); bookingPosition.article = item.article; bookingPosition.amount = -remove; bookingPosition.slotEntry = slot.copyAddAmount(-remove, item.article); bookingPosition.booking = booking; booking.positions.add(bookingPosition); if (needed == 0) { break; } } if (needed > 0) { throw new ArticleNotInStockException(item.article); } } return booking; } private CustomerPayment createPayment() { CustomerPayment payment = new CustomerPayment(); payment.amountCent = totalNetCent + totalVatCent; payment.payment = method; return payment; } private CustomerOrder createOrder() { assert totalNetCent + totalVatCent == expectedTotalGrossCent; CustomerOrder customerOrder = new CustomerOrder(); customerOrder.customer = user; customerOrder.destination = destination; for (OrderItem item : orderItems) { CustomerOrderPosition position = new CustomerOrderPosition(); position.article = item.article; position.pricePerUnit = item.article.shopPricePerUnitNetCent; position.quantity = item.quantity; position.order = customerOrder; customerOrder.positions.add(position); } customerOrder.created = new Timestamp(new Date().getTime()); customerOrder.totalNetCent = totalNetCent; customerOrder.totalVatCent = totalVatCent; customerOrder.totalGrossCent = totalNetCent + totalVatCent; return customerOrder; } public static class Result { public final CustomerOrder customerOrder; public final WarehouseBooking warehouseBooking; public final List bookings; Result(CustomerOrder customerOrder, WarehouseBooking warehouseBooking, List bookings) { this.customerOrder = customerOrder; this.warehouseBooking = warehouseBooking; this.bookings = bookings; } } private static class OrderItem { List availableSlots; int quantity; Article article; public OrderItem(Article article, List availableSlots, int quantity) { this.article = article; this.availableSlots = availableSlots; this.quantity = quantity; } } public static class ArticleNotInStockException extends Exception { private Article article; public ArticleNotInStockException(Article article) { super("The quantity of article '" + article.title + "' is not in stock."); this.article = article; } public Article getArticle() { return article; } } }