diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml
new file mode 100644
index 000000000..b477d81d3
--- /dev/null
+++ b/.github/workflows/github-actions.yml
@@ -0,0 +1,18 @@
+name: CI for Java Invoice
+on: [push]
+jobs:
+ test:
+ name: Unit tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK
+ uses: actions/setup-java@v2
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+ - name: Test
+ run: mvn test
+ - name: Checkstyle
+ run: mvn checkstyle:check
+
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 000000000..fbcd05132
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2d40c3e8a..03c23ac83 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,27 @@
17
+
+
+ org.apache.maven.plugins
+
+ maven-checkstyle-plugin
+
+ 3.6.0
+
+
+
+ checkstyle.xml
+
+ true
+
+ true
+
+
+
+
+
+
diff --git a/src/main/java/pl/edu/agh/mwo/invoice/Invoice.java b/src/main/java/pl/edu/agh/mwo/invoice/Invoice.java
index 56fe02359..87ab080f0 100644
--- a/src/main/java/pl/edu/agh/mwo/invoice/Invoice.java
+++ b/src/main/java/pl/edu/agh/mwo/invoice/Invoice.java
@@ -1,30 +1,87 @@
package pl.edu.agh.mwo.invoice;
import java.math.BigDecimal;
-import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import pl.edu.agh.mwo.invoice.product.Product;
public class Invoice {
- private Collection products;
+
+ private static int nextInvoiceNumber = 1;
+ private final int invoiceNumber;
+
+ private Map products = new HashMap();
+
+
+ public Invoice() {
+ this.invoiceNumber = nextInvoiceNumber;
+ nextInvoiceNumber++;
+ }
+
+ public int getInvoiceNumber() {
+ return invoiceNumber;
+ }
public void addProduct(Product product) {
- // TODO: implement
+ addProduct(product, 1);
}
public void addProduct(Product product, Integer quantity) {
- // TODO: implement
+ if (product == null || quantity <= 0) {
+ throw new IllegalArgumentException();
+
+ }
+ if (products.containsKey(product)) {
+ products.put(product, products.get(product) + quantity);
+ } else {
+ products.put(product, quantity);
+ }
}
- public BigDecimal getSubtotal() {
- return null;
+ public BigDecimal getNetTotal() {
+ BigDecimal totalNet = BigDecimal.ZERO;
+ for (Product product : products.keySet()) {
+ BigDecimal quantity = new BigDecimal(products.get(product));
+ totalNet = totalNet.add(product.getPrice().multiply(quantity));
+ }
+ return totalNet;
}
- public BigDecimal getTax() {
- return null;
+ public BigDecimal getTaxTotal() {
+ return getGrossTotal().subtract(getNetTotal());
}
- public BigDecimal getTotal() {
- return null;
+ public BigDecimal getGrossTotal() {
+ BigDecimal totalGross = BigDecimal.ZERO;
+ for (Product product : products.keySet()) {
+ BigDecimal quantity = new BigDecimal(products.get(product));
+ totalGross = totalGross.add(product.getPriceWithTax().multiply(quantity));
+ }
+ return totalGross;
}
-}
+
+
+ public String printInvoice() {
+ if (products.isEmpty()) {
+ throw new IllegalStateException();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("Faktura nr ").append(invoiceNumber).append("\n");
+
+ for (Product product : products.keySet()) {
+ Integer quantity = products.get(product);
+ builder.append(product.getName())
+ .append("\t")
+ .append(quantity)
+ .append("\t")
+ .append(product.getPriceWithTax())
+ .append("\n");
+ }
+
+ builder.append("Liczba pozycji: ").append(products.size());
+ return builder.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/pl/edu/agh/mwo/invoice/product/Product.java b/src/main/java/pl/edu/agh/mwo/invoice/product/Product.java
index 318de9ac9..5951f7d3f 100644
--- a/src/main/java/pl/edu/agh/mwo/invoice/product/Product.java
+++ b/src/main/java/pl/edu/agh/mwo/invoice/product/Product.java
@@ -10,24 +10,49 @@ public abstract class Product {
private final BigDecimal taxPercent;
protected Product(String name, BigDecimal price, BigDecimal tax) {
+ if (name == null
+ || name.equals("")
+ || price == null
+ || tax == null
+ || tax.compareTo(new BigDecimal(0)) < 0
+ || price.compareTo(new BigDecimal(0)) < 0) {
+ throw new IllegalArgumentException("Invalid product data");
+ }
this.name = name;
this.price = price;
this.taxPercent = tax;
}
public String getName() {
- return null;
+ return name;
}
public BigDecimal getPrice() {
- return null;
+ return price;
}
public BigDecimal getTaxPercent() {
- return null;
+ return taxPercent;
}
public BigDecimal getPriceWithTax() {
- return null;
+ return price.multiply(taxPercent).add(price);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ Product other = (Product) obj;
+ return name.equals(other.name) && price.compareTo(other.price) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(name, price);
}
}
diff --git a/src/test/java/pl/edu/agh/mwo/invoice/InvoiceTest.java b/src/test/java/pl/edu/agh/mwo/invoice/InvoiceTest.java
index 7f4b6f795..7c350935c 100644
--- a/src/test/java/pl/edu/agh/mwo/invoice/InvoiceTest.java
+++ b/src/test/java/pl/edu/agh/mwo/invoice/InvoiceTest.java
@@ -23,17 +23,17 @@ public void createEmptyInvoiceForTheTest() {
@Test
public void testEmptyInvoiceHasEmptySubtotal() {
- Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getNetTotal()));
}
@Test
public void testEmptyInvoiceHasEmptyTaxAmount() {
- Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getTax()));
+ Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getTaxTotal()));
}
@Test
public void testEmptyInvoiceHasEmptyTotal() {
- Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getTotal()));
+ Assert.assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(invoice.getGrossTotal()));
}
@Test
@@ -42,21 +42,21 @@ public void testInvoiceSubtotalWithTwoDifferentProducts() {
Product apples = new TaxFreeProduct("Owoce", new BigDecimal("10"));
invoice.addProduct(onions);
invoice.addProduct(apples);
- Assert.assertThat(new BigDecimal("20"), Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(new BigDecimal("20"), Matchers.comparesEqualTo(invoice.getNetTotal()));
}
@Test
public void testInvoiceSubtotalWithManySameProducts() {
Product onions = new TaxFreeProduct("Warzywa", BigDecimal.valueOf(10));
invoice.addProduct(onions, 100);
- Assert.assertThat(new BigDecimal("1000"), Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(new BigDecimal("1000"), Matchers.comparesEqualTo(invoice.getNetTotal()));
}
@Test
public void testInvoiceHasTheSameSubtotalAndTotalIfTaxIsZero() {
Product taxFreeProduct = new TaxFreeProduct("Warzywa", new BigDecimal("199.99"));
invoice.addProduct(taxFreeProduct);
- Assert.assertThat(invoice.getTotal(), Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(invoice.getNetTotal(), Matchers.comparesEqualTo(invoice.getGrossTotal()));
}
@Test
@@ -64,7 +64,7 @@ public void testInvoiceHasProperSubtotalForManyProducts() {
invoice.addProduct(new TaxFreeProduct("Owoce", new BigDecimal("200")));
invoice.addProduct(new DairyProduct("Maslanka", new BigDecimal("100")));
invoice.addProduct(new OtherProduct("Wino", new BigDecimal("10")));
- Assert.assertThat(new BigDecimal("310"), Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(new BigDecimal("310"), Matchers.comparesEqualTo(invoice.getNetTotal()));
}
@Test
@@ -75,7 +75,7 @@ public void testInvoiceHasProperTaxValueForManyProduct() {
invoice.addProduct(new DairyProduct("Kefir", new BigDecimal("100")));
// tax: 2.30
invoice.addProduct(new OtherProduct("Piwko", new BigDecimal("10")));
- Assert.assertThat(new BigDecimal("10.30"), Matchers.comparesEqualTo(invoice.getTax()));
+ Assert.assertThat(new BigDecimal("10.30"), Matchers.comparesEqualTo(invoice.getTaxTotal()));
}
@Test
@@ -86,7 +86,7 @@ public void testInvoiceHasProperTotalValueForManyProduct() {
invoice.addProduct(new DairyProduct("Maslo", new BigDecimal("100")));
// price with tax: 12.30
invoice.addProduct(new OtherProduct("Chipsy", new BigDecimal("10")));
- Assert.assertThat(new BigDecimal("320.30"), Matchers.comparesEqualTo(invoice.getTotal()));
+ Assert.assertThat(new BigDecimal("320.30"), Matchers.comparesEqualTo(invoice.getGrossTotal()));
}
@Test
@@ -97,7 +97,7 @@ public void testInvoiceHasPropoerSubtotalWithQuantityMoreThanOne() {
invoice.addProduct(new DairyProduct("Kozi Serek", new BigDecimal("10")), 3);
// 1000x pinezka - price: 10
invoice.addProduct(new OtherProduct("Pinezka", new BigDecimal("0.01")), 1000);
- Assert.assertThat(new BigDecimal("50"), Matchers.comparesEqualTo(invoice.getSubtotal()));
+ Assert.assertThat(new BigDecimal("50"), Matchers.comparesEqualTo(invoice.getNetTotal()));
}
@Test
@@ -108,7 +108,7 @@ public void testInvoiceHasPropoerTotalWithQuantityMoreThanOne() {
invoice.addProduct(new DairyProduct("Chedar", new BigDecimal("10")), 3);
// 1000x pinezka - price with tax: 12.30
invoice.addProduct(new OtherProduct("Pinezka", new BigDecimal("0.01")), 1000);
- Assert.assertThat(new BigDecimal("54.70"), Matchers.comparesEqualTo(invoice.getTotal()));
+ Assert.assertThat(new BigDecimal("54.70"), Matchers.comparesEqualTo(invoice.getGrossTotal()));
}
@Test(expected = IllegalArgumentException.class)
@@ -125,4 +125,90 @@ public void testInvoiceWithNegativeQuantity() {
public void testAddingNullProduct() {
invoice.addProduct(null);
}
+
+ @Test
+ public void testInvoiceHasNumberGreaterThan0 () {
+ Invoice invoice = new Invoice();
+ int number = invoice.getInvoiceNumber();
+ Assert.assertThat(number, Matchers.greaterThan(0));
+ }
+
+ @Test
+ public void testTwoInvoicesHaveDifferentNumber() {
+ int number1 = new Invoice().getInvoiceNumber();
+ int number2 = new Invoice().getInvoiceNumber();
+ Assert.assertNotEquals(number1, number2);
+ }
+
+ @Test
+ public void testInvoiceNumberDoesNotChange() {
+ Invoice invoice = new Invoice();
+ int firstCheck = invoice.getInvoiceNumber();
+ int secondCheck = invoice.getInvoiceNumber();
+ Assert.assertEquals(firstCheck, secondCheck);
+
+ }
+ @Test
+ public void testPrintInvoiceContainsInvoiceNumber() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")));
+ String result = invoice.printInvoice();
+ Assert.assertTrue(result.contains("Faktura nr " + invoice.getInvoiceNumber()));
+ }
+
+
+ @Test
+ public void testPrintInvoiceContainsProductNames() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 2);
+ invoice.addProduct(new DairyProduct("Mleko", new BigDecimal("4.00")), 1);
+ String result = invoice.printInvoice();
+ Assert.assertTrue(result.contains("Chleb"));
+ Assert.assertTrue(result.contains("Mleko"));
+ }
+
+
+ @Test
+ public void testPrintInvoiceEndsWithPositionCount() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("A", new BigDecimal("1.00")));
+ invoice.addProduct(new OtherProduct("B", new BigDecimal("2.00")));
+ invoice.addProduct(new DairyProduct("C", new BigDecimal("3.00")));
+ String result = invoice.printInvoice();
+ Assert.assertTrue(result.contains("Liczba pozycji: 3"));
+
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testPrintInvoiceForEmptyInvoiceThrowsException() {
+ Invoice invoice = new Invoice();
+ invoice.printInvoice();
+ }
+
+ @Test
+ public void testInvoiceMergesDuplicateProductsByName() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 2);
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 3);
+ Assert.assertEquals(new BigDecimal("18.45"),
+ invoice.getGrossTotal().setScale(2, java.math.RoundingMode.HALF_UP));
+ }
+
+ @Test
+ public void testInvoiceWithDuplicateProductHasOnlyOnePosition() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 2);
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 3);
+ String result = invoice.printInvoice();
+ Assert.assertTrue(result.contains("Liczba pozycji: 1"));
+ }
+
+ @Test
+ public void testInvoiceTreatsDifferentPricesAsSeparateProducts() {
+ Invoice invoice = new Invoice();
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("3.00")), 2);
+ invoice.addProduct(new OtherProduct("Chleb", new BigDecimal("2.50")), 3);
+ String result = invoice.printInvoice();
+ Assert.assertTrue(result.contains("Liczba pozycji: 2"));
+ }
}