Skip to content
Draft
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
4 changes: 2 additions & 2 deletions java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>22</source>
<target>22</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
Expand Down
82 changes: 82 additions & 0 deletions java/src/main/java/dojo/supermarket/HtmlReceiptFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package dojo.supermarket;

import dojo.supermarket.model.*;

import java.util.Locale;

public class HtmlReceiptFormatter implements ReceiptFormatter {

@Override
public String formatReceipt(Receipt receipt, int columns) {
StringBuilder result = new StringBuilder();

result.append("<html>\n<body>\n");
result.append("<h1>Receipt</h1>\n");
result.append("<table>\n");

for (ReceiptItem item : receipt.getItems()) {
String receiptItem = presentReceiptItem(item);
result.append(receiptItem);
}

for (Discount discount : receipt.getDiscounts()) {
String discountPresentation = presentDiscount(discount);
result.append(discountPresentation);
}

result.append(presentTotal(receipt));
result.append("</table>\n</body>\n</html>");
return result.toString();
}

private String presentReceiptItem(ReceiptItem item) {
StringBuilder result = new StringBuilder();
result.append(" <tr>\n");
result.append(" <td>").append(escapeHtml(item.getProduct().getName())).append("</td>\n");
result.append(" <td>").append(presentPrice(item.getTotalPrice())).append("</td>\n");
result.append(" </tr>\n");

if (item.getQuantity() != 1) {
result.append(" <tr>\n");
result.append(" <td colspan=\"2\">").append(presentPrice(item.getPrice()))
.append(" * ").append(presentQuantity(item)).append("</td>\n");
result.append(" </tr>\n");
}
return result.toString();
}

private String presentDiscount(Discount discount) {
String name = discount.getDescription() + "(" + discount.getProduct().getName() + ")";
String value = presentPrice(discount.getDiscountAmount());

return " <tr class=\"discount\">\n" +
" <td>" + escapeHtml(name) + "</td>\n" +
" <td>" + value + "</td>\n" +
" </tr>\n";
}

private String presentTotal(Receipt receipt) {
return " <tr class=\"total\">\n" +
" <td><strong>Total:</strong></td>\n" +
" <td><strong>" + presentPrice(receipt.getTotalPrice()) + "</strong></td>\n" +
" </tr>\n";
}

private static String presentPrice(double price) {
return String.format(Locale.UK, "%.2f", price);
}

private static String presentQuantity(ReceiptItem item) {
return ProductUnit.EACH.equals(item.getProduct().getUnit())
? String.format("%d", (int)item.getQuantity())
: String.format(Locale.UK, "%.3f", item.getQuantity());
}

private static String escapeHtml(String text) {
return text.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#39;");
}
}
7 changes: 7 additions & 0 deletions java/src/main/java/dojo/supermarket/ReceiptFormatter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dojo.supermarket;

import dojo.supermarket.model.Receipt;

public interface ReceiptFormatter {
String formatReceipt(Receipt receipt, int columns);
}
30 changes: 30 additions & 0 deletions java/src/main/java/dojo/supermarket/ReceiptPrinter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dojo.supermarket;

import dojo.supermarket.model.*;

public class ReceiptPrinter {

private final int columns;
private final ReceiptFormatter formatter;

public ReceiptPrinter() {
this(40, new TextReceiptFormatter());
}

public ReceiptPrinter(int columns) {
this(columns, new TextReceiptFormatter());
}

public ReceiptPrinter(ReceiptFormatter formatter) {
this(40, formatter);
}

public ReceiptPrinter(int columns, ReceiptFormatter formatter) {
this.columns = columns;
this.formatter = formatter;
}

public String printReceipt(Receipt receipt) {
return formatter.formatReceipt(receipt, columns);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,56 @@

import java.util.Locale;

public class ReceiptPrinter {
public class TextReceiptFormatter implements ReceiptFormatter {

private final int columns;

public ReceiptPrinter() {
this(40);
}

public ReceiptPrinter(int columns) {
this.columns = columns;
}

public String printReceipt(Receipt receipt) {
@Override
public String formatReceipt(Receipt receipt, int columns) {
StringBuilder result = new StringBuilder();

for (ReceiptItem item : receipt.getItems()) {
String receiptItem = presentReceiptItem(item);
String receiptItem = presentReceiptItem(item, columns);
result.append(receiptItem);
}

for (Discount discount : receipt.getDiscounts()) {
String discountPresentation = presentDiscount(discount);
String discountPresentation = presentDiscount(discount, columns);
result.append(discountPresentation);
}

result.append("\n");
result.append(presentTotal(receipt));
result.append(presentTotal(receipt, columns));
return result.toString();
}

private String presentReceiptItem(ReceiptItem item) {
private String presentReceiptItem(ReceiptItem item, int columns) {
String totalPricePresentation = presentPrice(item.getTotalPrice());
String name = item.getProduct().getName();

String line = formatLineWithWhitespace(name, totalPricePresentation);
String line = formatLineWithWhitespace(name, totalPricePresentation, columns);

if (item.getQuantity() != 1) {
line += " " + presentPrice(item.getPrice()) + " * " + presentQuantity(item) + "\n";
}
return line;
}

private String presentDiscount(Discount discount) {
private String presentDiscount(Discount discount, int columns) {
String name = discount.getDescription() + "(" + discount.getProduct().getName() + ")";
String value = presentPrice(discount.getDiscountAmount());

return formatLineWithWhitespace(name, value);
return formatLineWithWhitespace(name, value, columns);
}

private String presentTotal(Receipt receipt) {
private String presentTotal(Receipt receipt, int columns) {
String name = "Total: ";
String value = presentPrice(receipt.getTotalPrice());
return formatLineWithWhitespace(name, value);
return formatLineWithWhitespace(name, value, columns);
}

private String formatLineWithWhitespace(String name, String value) {
private String formatLineWithWhitespace(String name, String value, int columns) {
StringBuilder line = new StringBuilder();
line.append(name);
int whitespaceSize = this.columns - name.length() - value.length();
int whitespaceSize = columns - name.length() - value.length();
for (int i = 0; i < whitespaceSize; i++) {
line.append(" ");
}
Expand All @@ -78,4 +71,4 @@ private static String presentQuantity(ReceiptItem item) {
? String.format("%d", (int)item.getQuantity())
: String.format(Locale.UK, "%.3f", item.getQuantity());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dojo.supermarket.model;

public interface DiscountStrategy {
Discount calculateDiscount(Product product, double quantity, double unitPrice, double argument);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dojo.supermarket.model;

public class DiscountStrategyFactory {
public static DiscountStrategy createStrategy(SpecialOfferType offerType) {
switch (offerType) {
case THREE_FOR_TWO:
return new ThreeForTwoDiscountStrategy();
case TEN_PERCENT_DISCOUNT:
return new TenPercentDiscountStrategy();
case TWO_FOR_AMOUNT:
return new TwoForAmountDiscountStrategy();
case FIVE_FOR_AMOUNT:
return new FiveForAmountDiscountStrategy();
default:
throw new IllegalArgumentException("Unknown offer type: " + offerType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dojo.supermarket.model;

public class FiveForAmountDiscountStrategy implements DiscountStrategy {
@Override
public Discount calculateDiscount(Product product, double quantity, double unitPrice, double argument) {
int quantityAsInt = (int) quantity;
if (quantityAsInt >= 5) {
int x = 5;
int numberOfXs = quantityAsInt / x;
double discountTotal = unitPrice * quantity - (argument * numberOfXs + quantityAsInt % 5 * unitPrice);
return new Discount(product, x + " for " + argument, -discountTotal);
}
return null;
}
}
36 changes: 6 additions & 30 deletions java/src/main/java/dojo/supermarket/model/ShoppingCart.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,37 +38,13 @@ void handleOffers(Receipt receipt, Map<Product, Offer> offers, SupermarketCatalo
if (offers.containsKey(p)) {
Offer offer = offers.get(p);
double unitPrice = catalog.getUnitPrice(p);
int quantityAsInt = (int) quantity;
Discount discount = null;
int x = 1;
if (offer.offerType == SpecialOfferType.THREE_FOR_TWO) {
x = 3;

} else if (offer.offerType == SpecialOfferType.TWO_FOR_AMOUNT) {
x = 2;
if (quantityAsInt >= 2) {
double total = offer.argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice;
double discountN = unitPrice * quantity - total;
discount = new Discount(p, "2 for " + offer.argument, -discountN);
}

} if (offer.offerType == SpecialOfferType.FIVE_FOR_AMOUNT) {
x = 5;
}
int numberOfXs = quantityAsInt / x;
if (offer.offerType == SpecialOfferType.THREE_FOR_TWO && quantityAsInt > 2) {
double discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice);
discount = new Discount(p, "3 for 2", -discountAmount);
}
if (offer.offerType == SpecialOfferType.TEN_PERCENT_DISCOUNT) {
discount = new Discount(p, offer.argument + "% off", -quantity * unitPrice * offer.argument / 100.0);
}
if (offer.offerType == SpecialOfferType.FIVE_FOR_AMOUNT && quantityAsInt >= 5) {
double discountTotal = unitPrice * quantity - (offer.argument * numberOfXs + quantityAsInt % 5 * unitPrice);
discount = new Discount(p, x + " for " + offer.argument, -discountTotal);
}
if (discount != null)

DiscountStrategy strategy = DiscountStrategyFactory.createStrategy(offer.offerType);
Discount discount = strategy.calculateDiscount(p, quantity, unitPrice, offer.argument);

if (discount != null) {
receipt.addDiscount(discount);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dojo.supermarket.model;

public class TenPercentDiscountStrategy implements DiscountStrategy {
@Override
public Discount calculateDiscount(Product product, double quantity, double unitPrice, double argument) {
return new Discount(product, argument + "% off", -quantity * unitPrice * argument / 100.0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dojo.supermarket.model;

public class ThreeForTwoDiscountStrategy implements DiscountStrategy {
@Override
public Discount calculateDiscount(Product product, double quantity, double unitPrice, double argument) {
int quantityAsInt = (int) quantity;
if (quantityAsInt > 2) {
int x = 3;
int numberOfXs = quantityAsInt / x;
double discountAmount = quantity * unitPrice - ((numberOfXs * 2 * unitPrice) + quantityAsInt % 3 * unitPrice);
return new Discount(product, "3 for 2", -discountAmount);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dojo.supermarket.model;

public class TwoForAmountDiscountStrategy implements DiscountStrategy {
@Override
public Discount calculateDiscount(Product product, double quantity, double unitPrice, double argument) {
int quantityAsInt = (int) quantity;
if (quantityAsInt >= 2) {
int x = 2;
double total = argument * (quantityAsInt / x) + quantityAsInt % 2 * unitPrice;
double discountN = unitPrice * quantity - total;
return new Discount(product, "2 for " + argument, -discountN);
}
return null;
}
}
Loading