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 {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
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'
......
package com.lm.sales.builders
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes
import com.lm.sales.model.stationery.Book
......@@ -9,6 +10,7 @@ class BookBuilder {
private var author: String? = null
private var pages: Int = 0
private var editorial: String? = null
private var presentation: Presentation? = null
private var isImported: Boolean = false
private var basePrice: Double = 0.0
private lateinit var taxes: Taxes
......@@ -38,13 +40,18 @@ class BookBuilder {
return this
}
fun withPresentation(presentation: Presentation): BookBuilder {
this.presentation = presentation
return this
}
fun withBasePrice(price: Double): BookBuilder {
this.basePrice = price
return this
}
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
import com.lm.sales.model.Presentation
import com.lm.sales.model.music.CD
class CDBuilder {
......@@ -8,6 +9,7 @@ class CDBuilder {
private var artist: String? = null
private var tracksNumber: Int? = null
private var label: String? = null
private var presentation: Presentation? = null
private var isImported: Boolean = false
private var basePrice: Double = 0.0
......@@ -31,6 +33,11 @@ class CDBuilder {
return this
}
fun withPresentation(presentation: Presentation): CDBuilder {
this.presentation = presentation
return this
}
fun isImported(imported: Boolean): CDBuilder {
this.isImported = imported
return this
......@@ -42,7 +49,7 @@ class CDBuilder {
}
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
import com.lm.sales.model.Presentation
import com.lm.sales.model.foods.Chocolates
import com.lm.sales.model.music.CD
class ChocolatesBuilder {
private lateinit var name: String
private lateinit var manufacturer: String
private lateinit var presentation: Presentation
private var manufacturer: String? = null
private var presentation: Presentation? = null
private var isImported: Boolean = false
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 {
private lateinit var name: String
private lateinit var pharma: String
private lateinit var presentation: Presentation
private var isImported: Boolean = false
private var basePrice: Double = 0.0
private lateinit var presentation: Presentation
fun withName(name: String): PillsBuilder {
this.name = name
......@@ -21,6 +21,11 @@ class PillsBuilder {
return this
}
fun withPresentation(presentation: Presentation): PillsBuilder {
this.presentation = presentation
return this
}
fun isImported(imported: Boolean): PillsBuilder {
this.isImported = imported
return this
......@@ -31,11 +36,6 @@ class PillsBuilder {
return this
}
fun withPresentation(presentation: Presentation): PillsBuilder {
this.presentation = presentation
return this
}
fun build(): Pills {
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 {
val basePrice: Double
var taxes: Taxes?
val imported: Boolean
val presentation: Presentation?
}
enum class Presentation {
box, bottle, packet, bar
box, bottle, packet
}
data class Taxes(var taxesAmount: Double = 0.0,
......
......@@ -3,10 +3,8 @@ package com.lm.sales.model.cart
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
*/
class Cart{
var productList: MutableList<Product> = mutableListOf()
}
\ No newline at end of file
data class Cart(val productList: MutableList<Product> = mutableListOf())
\ No newline at end of file
package com.lm.sales.model.drugstore
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product
interface Drugstore: Product {
val manufacturer: String
val presentation: Presentation
val manufacturer: String?
// ...
}
......@@ -4,8 +4,8 @@ import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes
data class Perfume(override val name: String,
override val manufacturer: String,
override val manufacturer: String? = null,
override val imported: Boolean,
override val basePrice: Double,
override val presentation: Presentation,
override val presentation: Presentation? = null,
override var taxes: Taxes? = null): Drugstore
\ No newline at end of file
......@@ -8,8 +8,8 @@ import com.lm.sales.model.Taxes
*
*/
data class Chocolates (override val name: String,
override val manufacturer: String,
override val presentation: Presentation,
override val manufacturer: String? = null,
override val presentation: Presentation? = null,
override val imported: Boolean,
override val basePrice: Double,
override var taxes: Taxes? = null): Foods
package com.lm.sales.model.foods
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product
interface Foods: Product {
val manufacturer: String
val presentation: Presentation?
val manufacturer: String?
// ...
}
\ No newline at end of file
package com.lm.sales.model.medicals
import com.lm.sales.model.Presentation
import com.lm.sales.model.Product
interface Medicals: Product {
val pharma: String
val presentation: Presentation
}
\ No newline at end of file
......@@ -7,5 +7,5 @@ data class Pills(override val name: String,
override val pharma: String,
override val imported: Boolean,
override val basePrice: Double,
override val presentation: Presentation,
override val presentation: Presentation?,
override var taxes: Taxes? = null): Medicals
\ No newline at end of file
package com.lm.sales.model.music
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes
/**
......@@ -13,4 +14,5 @@ data class CD (override val name: String,
override val recordLabel: String?,
override val imported: Boolean,
override val basePrice: Double,
override val presentation: Presentation? = null,
override var taxes: Taxes? = null): Music
\ No newline at end of file
package com.lm.sales.model.stationery
import com.lm.sales.model.Presentation
import com.lm.sales.model.Taxes
/**
......@@ -13,4 +14,5 @@ data class Book (override val name: String,
override val pages: Int?,
override val editorial: String?,
override val basePrice: Double,
override var taxes: Taxes? = null) : Stationery
\ No newline at end of file
override val presentation: Presentation?,
override var taxes: Taxes? = null): Stationery
\ No newline at end of file
......@@ -4,8 +4,6 @@ import com.lm.sales.model.Product
interface Stationery: Product {
// parameter examples
//val type: <BookType> //Book, Notebook, Magazine, Diary...
val editorial: String?
val pages: Int?
......
package com.lm.sales.services
package com.lm.sales.util
import com.lm.sales.model.Product
import com.lm.sales.model.Taxes
import com.lm.sales.model.foods.Foods
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
*/
......@@ -28,18 +30,29 @@ class TaxesCalculator {
var taxes = 0.0
// Basic taxes: if product isn't a Stationery (Books), Foods or Medicals, it has a 10% more taxes
if(product !is Stationery || product !is Foods || product !is Medicals)
// Basic taxes: if product isn't a book, foods or medicals, it has a 10% more taxes
if(product !is Book && product !is Foods && product !is Medicals)
taxes = product.basePrice * 10 / 100
// If imported, we add a 5% more to taxes
if(product.imported)
taxes.plus(product.basePrice * 5 /100)
taxes = taxes.plus(product.basePrice * 5 /100)
// kotlin's round rounds automatically
return Taxes().apply {
taxesAmount = taxes
afterTaxesAmount = product.basePrice.plus(taxes)
taxesAmount = round(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
import com.lm.sales.builders.CDBuilder
import com.lm.sales.builders.PillsBuilder
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.jupiter.api.Assertions
import org.junit.jupiter.api.TestInstance
......
package shopping
import com.lm.sales.builders.BookBuilder
import com.lm.sales.builders.CDBuilder
import com.lm.sales.builders.*
import com.lm.sales.model.Presentation
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.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ShoppingTests {
companion object : KLogging()
@Test
fun input1(){
// Builds book from input 1 an calculates taxes
val book = BookBuilder().withName("book")
.isImported(false)
.withBasePrice(12.49)
......@@ -20,6 +24,7 @@ class ShoppingTests {
taxes = TaxesCalculator.calculate(this)
}
// Builds CD from input 1 and calculates taxes
val cd = CDBuilder().withName("music CD")
.isImported(false)
.withBasePrice(14.99)
......@@ -27,17 +32,52 @@ class ShoppingTests {
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 {
productList.add(book)
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