Commit 65faecf1 authored by Javier Hernández's avatar Javier Hernández 🤓

Added 1st and 2nd Test cases

- Added PerfumeBuilder
- Deleted json objects for testing
- moved TaxesCalculator from services to util package
parent 229c70ec
...@@ -30,6 +30,7 @@ subprojects { ...@@ -30,6 +30,7 @@ subprojects {
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "io.github.microutils:kotlin-logging:$kt_logging_version" compile "io.github.microutils:kotlin-logging:$kt_logging_version"
implementation 'ch.qos.logback:logback-classic:1.2.3'
compile 'com.beust:klaxon:5.0.13' compile 'com.beust:klaxon:5.0.13'
......
package com.lm.sales.builders package com.lm.sales.builders
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes import com.lm.sales.model.Taxes
import com.lm.sales.model.stationery.Book import com.lm.sales.model.stationery.Book
...@@ -9,6 +10,7 @@ class BookBuilder { ...@@ -9,6 +10,7 @@ class BookBuilder {
private var author: String? = null private var author: String? = null
private var pages: Int = 0 private var pages: Int = 0
private var editorial: String? = null private var editorial: String? = null
private var presentation: Presentation? = null
private var isImported: Boolean = false private var isImported: Boolean = false
private var basePrice: Double = 0.0 private var basePrice: Double = 0.0
private lateinit var taxes: Taxes private lateinit var taxes: Taxes
...@@ -38,13 +40,18 @@ class BookBuilder { ...@@ -38,13 +40,18 @@ class BookBuilder {
return this return this
} }
fun withPresentation(presentation: Presentation): BookBuilder {
this.presentation = presentation
return this
}
fun withBasePrice(price: Double): BookBuilder { fun withBasePrice(price: Double): BookBuilder {
this.basePrice = price this.basePrice = price
return this return this
} }
fun build(): Book { fun build(): Book {
return Book(name, author, isImported, pages, editorial, basePrice) return Book(name, author, isImported, pages, editorial, basePrice, presentation)
} }
} }
\ No newline at end of file
package com.lm.sales.builders package com.lm.sales.builders
import com.lm.sales.model.Presentation
import com.lm.sales.model.music.CD import com.lm.sales.model.music.CD
class CDBuilder { class CDBuilder {
...@@ -8,6 +9,7 @@ class CDBuilder { ...@@ -8,6 +9,7 @@ class CDBuilder {
private var artist: String? = null private var artist: String? = null
private var tracksNumber: Int? = null private var tracksNumber: Int? = null
private var label: String? = null private var label: String? = null
private var presentation: Presentation? = null
private var isImported: Boolean = false private var isImported: Boolean = false
private var basePrice: Double = 0.0 private var basePrice: Double = 0.0
...@@ -31,6 +33,11 @@ class CDBuilder { ...@@ -31,6 +33,11 @@ class CDBuilder {
return this return this
} }
fun withPresentation(presentation: Presentation): CDBuilder {
this.presentation = presentation
return this
}
fun isImported(imported: Boolean): CDBuilder { fun isImported(imported: Boolean): CDBuilder {
this.isImported = imported this.isImported = imported
return this return this
...@@ -42,7 +49,7 @@ class CDBuilder { ...@@ -42,7 +49,7 @@ class CDBuilder {
} }
fun build(): CD { fun build(): CD {
return CD(discName, artist, tracksNumber, label, isImported, basePrice) return CD(discName, artist, tracksNumber, label, isImported, basePrice, presentation)
} }
} }
\ No newline at end of file
...@@ -2,13 +2,12 @@ package com.lm.sales.builders ...@@ -2,13 +2,12 @@ package com.lm.sales.builders
import com.lm.sales.model.Presentation import com.lm.sales.model.Presentation
import com.lm.sales.model.foods.Chocolates import com.lm.sales.model.foods.Chocolates
import com.lm.sales.model.music.CD
class ChocolatesBuilder { class ChocolatesBuilder {
private lateinit var name: String private lateinit var name: String
private lateinit var manufacturer: String private var manufacturer: String? = null
private lateinit var presentation: Presentation private var presentation: Presentation? = null
private var isImported: Boolean = false private var isImported: Boolean = false
private var basePrice: Double = 0.0 private var basePrice: Double = 0.0
......
package com.lm.sales.builders
import com.lm.sales.model.Presentation
import com.lm.sales.model.drugstore.Perfume
class PerfumeBuilder {
private lateinit var name: String
private var manufacturer: String? = null
private lateinit var presentation: Presentation
private var isImported: Boolean = false
private var basePrice: Double = 0.0
fun withName(name: String): PerfumeBuilder {
this.name = name
return this
}
fun withManufacturer(manufacturer: String): PerfumeBuilder {
this.manufacturer = manufacturer
return this
}
fun withPresentation(presentation: Presentation): PerfumeBuilder {
this.presentation = presentation
return this
}
fun isImported(imported: Boolean): PerfumeBuilder {
this.isImported = imported
return this
}
fun withBasePrice(price: Double): PerfumeBuilder {
this.basePrice = price
return this
}
fun build(): Perfume {
return Perfume(name, manufacturer, isImported, basePrice, presentation)
}
}
\ No newline at end of file
...@@ -7,9 +7,9 @@ class PillsBuilder { ...@@ -7,9 +7,9 @@ class PillsBuilder {
private lateinit var name: String private lateinit var name: String
private lateinit var pharma: String private lateinit var pharma: String
private lateinit var presentation: Presentation
private var isImported: Boolean = false private var isImported: Boolean = false
private var basePrice: Double = 0.0 private var basePrice: Double = 0.0
private lateinit var presentation: Presentation
fun withName(name: String): PillsBuilder { fun withName(name: String): PillsBuilder {
this.name = name this.name = name
...@@ -21,6 +21,11 @@ class PillsBuilder { ...@@ -21,6 +21,11 @@ class PillsBuilder {
return this return this
} }
fun withPresentation(presentation: Presentation): PillsBuilder {
this.presentation = presentation
return this
}
fun isImported(imported: Boolean): PillsBuilder { fun isImported(imported: Boolean): PillsBuilder {
this.isImported = imported this.isImported = imported
return this return this
...@@ -31,11 +36,6 @@ class PillsBuilder { ...@@ -31,11 +36,6 @@ class PillsBuilder {
return this return this
} }
fun withPresentation(presentation: Presentation): PillsBuilder {
this.presentation = presentation
return this
}
fun build(): Pills { fun build(): Pills {
return Pills(name, pharma, isImported, basePrice, presentation) return Pills(name, pharma, isImported, basePrice, presentation)
} }
......
package com.lm.sales.builders
import com.lm.sales.model.Product
import mu.KLogging
/**
* Builder to generate recipes, if we had different recipe types, we should use a factory
*
* @j.hernandez
*/
class RecipeBuilder {
companion object : KLogging()
private var products: MutableList<Product> = mutableListOf()
private var totalAmount: Double = 0.0
private var salesTaxes: Double = 0.0
private lateinit var recipeName: String
fun withProducts(productList: MutableList<Product>): RecipeBuilder{
this.products = productList
return this
}
fun withRecipeName(recipeName: String): RecipeBuilder{
this.recipeName = recipeName
return this
}
/**
* Prints the recipe or prints a log message if recipe doesn't have products
*/
fun printRecipe(){
if(this.products.isEmpty()) {
logger.info ("Can't print the recipe without products!!")
}else{
recipeName.let { logger.info { it } }
this.products.forEach{ salesTaxes = salesTaxes.plus(it.taxes!!.taxesAmount) }
this.products.forEach { totalAmount = totalAmount.plus(it.taxes!!.afterTaxesAmount) }
products.forEach{
// We only add presentation type if not null
var pres = ""
if(it.presentation != null)
pres = it.presentation!!.name.plus(" of")
// If produt is imported we write it in the recipe
var imported = ""
if(it.imported)
imported = "imported"
logger.info { "1 $imported $pres ${it.name}: ${it.taxes!!.afterTaxesAmount}" }
}
logger.info { "Sales Taxes: $salesTaxes" }
logger.info { "Total: $totalAmount" }
}
}
}
package com.lm.sales.factories
/**
* Factory to create the recipes
*
* We use a factory but in this case that we only have one type of [Recipe] we could use a builder
*
* @j.hernandezs
*/
class RecipeFactory {
}
\ No newline at end of file
...@@ -11,10 +11,11 @@ interface Product { ...@@ -11,10 +11,11 @@ interface Product {
val basePrice: Double val basePrice: Double
var taxes: Taxes? var taxes: Taxes?
val imported: Boolean val imported: Boolean
val presentation: Presentation?
} }
enum class Presentation { enum class Presentation {
box, bottle, packet, bar box, bottle, packet
} }
data class Taxes(var taxesAmount: Double = 0.0, data class Taxes(var taxesAmount: Double = 0.0,
......
...@@ -3,10 +3,8 @@ package com.lm.sales.model.cart ...@@ -3,10 +3,8 @@ package com.lm.sales.model.cart
import com.lm.sales.model.Product import com.lm.sales.model.Product
/** /**
* Data class to contain a list of bought products * Data class containing the list of bought products
* *
* @j.hernandez * @j.hernandez
*/ */
class Cart{ data class Cart(val productList: MutableList<Product> = mutableListOf())
var productList: MutableList<Product> = mutableListOf() \ No newline at end of file
}
\ No newline at end of file
package com.lm.sales.model.drugstore package com.lm.sales.model.drugstore
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product import com.lm.sales.model.Product
interface Drugstore: Product { interface Drugstore: Product {
val manufacturer: String val manufacturer: String?
val presentation: Presentation
// ... // ...
} }
...@@ -4,8 +4,8 @@ import com.lm.sales.model.Presentation ...@@ -4,8 +4,8 @@ import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes import com.lm.sales.model.Taxes
data class Perfume(override val name: String, data class Perfume(override val name: String,
override val manufacturer: String, override val manufacturer: String? = null,
override val imported: Boolean, override val imported: Boolean,
override val basePrice: Double, override val basePrice: Double,
override val presentation: Presentation, override val presentation: Presentation? = null,
override var taxes: Taxes? = null): Drugstore override var taxes: Taxes? = null): Drugstore
\ No newline at end of file
...@@ -8,8 +8,8 @@ import com.lm.sales.model.Taxes ...@@ -8,8 +8,8 @@ import com.lm.sales.model.Taxes
* *
*/ */
data class Chocolates (override val name: String, data class Chocolates (override val name: String,
override val manufacturer: String, override val manufacturer: String? = null,
override val presentation: Presentation, override val presentation: Presentation? = null,
override val imported: Boolean, override val imported: Boolean,
override val basePrice: Double, override val basePrice: Double,
override var taxes: Taxes? = null): Foods override var taxes: Taxes? = null): Foods
package com.lm.sales.model.foods package com.lm.sales.model.foods
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product import com.lm.sales.model.Product
interface Foods: Product { interface Foods: Product {
val manufacturer: String val manufacturer: String?
val presentation: Presentation?
// ... // ...
} }
\ No newline at end of file
package com.lm.sales.model.medicals package com.lm.sales.model.medicals
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product import com.lm.sales.model.Product
interface Medicals: Product { interface Medicals: Product {
val pharma: String val pharma: String
val presentation: Presentation
} }
\ No newline at end of file
...@@ -7,5 +7,5 @@ data class Pills(override val name: String, ...@@ -7,5 +7,5 @@ data class Pills(override val name: String,
override val pharma: String, override val pharma: String,
override val imported: Boolean, override val imported: Boolean,
override val basePrice: Double, override val basePrice: Double,
override val presentation: Presentation, override val presentation: Presentation?,
override var taxes: Taxes? = null): Medicals override var taxes: Taxes? = null): Medicals
\ No newline at end of file
package com.lm.sales.model.music package com.lm.sales.model.music
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes import com.lm.sales.model.Taxes
/** /**
...@@ -13,4 +14,5 @@ data class CD (override val name: String, ...@@ -13,4 +14,5 @@ data class CD (override val name: String,
override val recordLabel: String?, override val recordLabel: String?,
override val imported: Boolean, override val imported: Boolean,
override val basePrice: Double, override val basePrice: Double,
override val presentation: Presentation? = null,
override var taxes: Taxes? = null): Music override var taxes: Taxes? = null): Music
\ No newline at end of file
package com.lm.sales.model.stationery package com.lm.sales.model.stationery
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes import com.lm.sales.model.Taxes
/** /**
...@@ -13,4 +14,5 @@ data class Book (override val name: String, ...@@ -13,4 +14,5 @@ data class Book (override val name: String,
override val pages: Int?, override val pages: Int?,
override val editorial: String?, override val editorial: String?,
override val basePrice: Double, override val basePrice: Double,
override var taxes: Taxes? = null) : Stationery override val presentation: Presentation?,
\ No newline at end of file override var taxes: Taxes? = null): Stationery
\ No newline at end of file
...@@ -4,8 +4,6 @@ import com.lm.sales.model.Product ...@@ -4,8 +4,6 @@ import com.lm.sales.model.Product
interface Stationery: Product { interface Stationery: Product {
// parameter examples
//val type: <BookType> //Book, Notebook, Magazine, Diary... //val type: <BookType> //Book, Notebook, Magazine, Diary...
val editorial: String? val editorial: String?
val pages: Int? val pages: Int?
......
package com.lm.sales.services package com.lm.sales.util
import com.lm.sales.model.Product import com.lm.sales.model.Product
import com.lm.sales.model.Taxes import com.lm.sales.model.Taxes
import com.lm.sales.model.foods.Foods import com.lm.sales.model.foods.Foods
import com.lm.sales.model.medicals.Medicals import com.lm.sales.model.medicals.Medicals
import com.lm.sales.model.stationery.Stationery import com.lm.sales.model.stationery.Book
import java.math.BigDecimal
import java.math.RoundingMode
/** /**
* TaxesCalcultor service to calculate basic and importing taxes * TaxesCalcultor tool to calculate basic and importing taxes
* *
* @j.hernandez * @j.hernandez
*/ */
...@@ -28,18 +30,29 @@ class TaxesCalculator { ...@@ -28,18 +30,29 @@ class TaxesCalculator {
var taxes = 0.0 var taxes = 0.0
// Basic taxes: if product isn't a Stationery (Books), Foods or Medicals, it has a 10% more taxes // Basic taxes: if product isn't a book, foods or medicals, it has a 10% more taxes
if(product !is Stationery || product !is Foods || product !is Medicals) if(product !is Book && product !is Foods && product !is Medicals)
taxes = product.basePrice * 10 / 100 taxes = product.basePrice * 10 / 100
// If imported, we add a 5% more to taxes // If imported, we add a 5% more to taxes
if(product.imported) if(product.imported)
taxes.plus(product.basePrice * 5 /100) taxes = taxes.plus(product.basePrice * 5 /100)
// kotlin's round rounds automatically
return Taxes().apply { return Taxes().apply {
taxesAmount = taxes taxesAmount = round(taxes)
afterTaxesAmount = product.basePrice.plus(taxes) afterTaxesAmount = round(product.basePrice.plus(taxes))
} }
} }
/**
* Rounds to the closest 0.05 with a 2 decimal digits scale
*
* @param amount [Double]
*
*/
private fun round(amount: Double): Double{
return BigDecimal(amount).setScale(2, RoundingMode.HALF_EVEN).toDouble()
}
} }
} }
\ No newline at end of file
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import org.junit.Test
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JsonParserTest {
@Test
fun parseCD() {
val cd = "/music/CD.json".parse() as JsonObject
Assertions.assertEquals("music CD", cd.string("name"))
Assertions.assertEquals(false, cd.boolean("imported"))
Assertions.assertEquals(14.99, cd.double("price"))
}
private fun String.parse(): Any? {
val cls = Parser::class.java
return cls.getResourceAsStream(this)?.let { inputStream ->
return Parser.default().parse(inputStream)
}
}
}
\ No newline at end of file
...@@ -4,7 +4,7 @@ import com.lm.sales.builders.BookBuilder ...@@ -4,7 +4,7 @@ import com.lm.sales.builders.BookBuilder
import com.lm.sales.builders.CDBuilder import com.lm.sales.builders.CDBuilder
import com.lm.sales.builders.PillsBuilder import com.lm.sales.builders.PillsBuilder
import com.lm.sales.model.Presentation import com.lm.sales.model.Presentation
import com.lm.sales.services.TaxesCalculator import com.lm.sales.util.TaxesCalculator
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
......
package shopping package shopping
import com.lm.sales.builders.BookBuilder import com.lm.sales.builders.*
import com.lm.sales.builders.CDBuilder import com.lm.sales.model.Presentation
import com.lm.sales.model.cart.Cart import com.lm.sales.model.cart.Cart
import com.lm.sales.services.TaxesCalculator import com.lm.sales.util.TaxesCalculator
import mu.KLogging
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS) @TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ShoppingTests { class ShoppingTests {
companion object : KLogging()
@Test @Test
fun input1(){ fun input1(){
// Builds book from input 1 an calculates taxes
val book = BookBuilder().withName("book") val book = BookBuilder().withName("book")
.isImported(false) .isImported(false)
.withBasePrice(12.49) .withBasePrice(12.49)
...@@ -20,6 +24,7 @@ class ShoppingTests { ...@@ -20,6 +24,7 @@ class ShoppingTests {
taxes = TaxesCalculator.calculate(this) taxes = TaxesCalculator.calculate(this)
} }
// Builds CD from input 1 and calculates taxes
val cd = CDBuilder().withName("music CD") val cd = CDBuilder().withName("music CD")
.isImported(false) .isImported(false)
.withBasePrice(14.99) .withBasePrice(14.99)
...@@ -27,17 +32,52 @@ class ShoppingTests { ...@@ -27,17 +32,52 @@ class ShoppingTests {
taxes = TaxesCalculator.calculate(this) taxes = TaxesCalculator.calculate(this)
} }
// Builds chocolate bar from input1 and calculate taxes
val chocoBar = ChocolatesBuilder().withName("chocolate bar")
.withBasePrice(0.85)
.build().apply {
taxes = TaxesCalculator.calculate(this)
}
val cart = Cart().apply { val cart = Cart().apply {
productList.add(book) productList.add(book)
productList.add(cd) productList.add(cd)
productList.add(chocoBar)
} }
// Adds cart products to recipe and prints it
RecipeBuilder().withProducts(cart.productList).withRecipeName("Output 1:").printRecipe()
}
@Test
fun input2(){
// Builds chocolates box from input2 and calculate taxes
val chocoBox = ChocolatesBuilder().withName("chocolates")
.withPresentation(Presentation.box)
.isImported(true)
.withBasePrice(10.00)
.build().apply {
taxes = TaxesCalculator.calculate(this)
}
// Builds bottle of perfume from input2 and calculate taxes
val perfume = PerfumeBuilder().withName("perfume")
.withPresentation(Presentation.bottle)
.isImported(true)
.withBasePrice(47.50)
.build().apply {
taxes = TaxesCalculator.calculate(this)
} }
val cart = Cart().apply {
productList.add(chocoBox)
productList.add(perfume)
}
// Adds cart products to recipe and prints it
RecipeBuilder().withProducts(cart.productList).withRecipeName("Output 2:").printRecipe()
}
} }
\ No newline at end of file
{
"name": "headache pills",
"imported": true,
"price": 47.50,
"package": "bottle"
}
\ No newline at end of file
{
"name": "bottle of perfume",
"imported": false,
"price": 9.75,
"package": "packet"
}
\ No newline at end of file
{
"name": "bottle of perfume",
"imported": false,
"price": 9.75,
"package": "bottle"
}
\ No newline at end of file
{
"name": "chocolate bar",
"imported": false,
"price": 0.85,
"package": "box"
}
\ No newline at end of file
{
"name": "headache pills",
"imported": false,
"price": 9.75,
"package": "packet"
}
\ No newline at end of file
{
"name": "headache pills",
"imported": false,
"price": 9.75,
"package": "packet"
}
\ No newline at end of file
{
"name": "headache pills",
"imported": false,
"price": 9.75,
"package": "packet"
}
\ No newline at end of file
{
"name": "music CD",
"imported": false,
"price": 14.99
}
\ No newline at end of file
{
"name": "book",
"imported": false,
"price": 12.49
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment