From afc18a4a673607d2e6a7dffbe688b39b0fcc7529 Mon Sep 17 00:00:00 2001 From: FyloZ Date: Sat, 20 Mar 2021 00:12:30 -0400 Subject: [PATCH] =?UTF-8?q?Ajout=20d'un=20endpoint=20pour=20d=C3=A9duire?= =?UTF-8?q?=20les=20quantit=C3=A9s=20des=20produits=20d'un=20m=C3=A9lange?= =?UTF-8?q?=20selon=20un=20ratio=20donn=C3=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/WebSecurityConfig.kt | 4 +- .../trial/colorrecipesexplorer/model/Mix.kt | 24 +++++++++-- .../rest/InventoryController.kt | 35 ++++++++++++++++ .../rest/MaterialController.kt | 18 -------- .../service/InventoryService.kt | 28 ++++++++++++- .../service/InventoryServiceTest.kt | 42 +++++++++++++++++-- 6 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/InventoryController.kt diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/config/WebSecurityConfig.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/config/WebSecurityConfig.kt index 8ed109c..3d6cca9 100644 --- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/config/WebSecurityConfig.kt +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/config/WebSecurityConfig.kt @@ -310,12 +310,12 @@ private enum class ControllerAuthorizations( val permissions: Map ) { INVENTORY_ADD( - "/api/material/inventory/add", mapOf( + "/api/inventory/add/**", mapOf( HttpMethod.PUT to EmployeePermission.EDIT_MATERIAL ) ), INVENTORY_DEDUCT( - "/api/material/inventory/deduct", mapOf( + "/api/inventory/deduct/**", mapOf( HttpMethod.PUT to EmployeePermission.VIEW_MATERIAL ) ), diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt index a12201b..2ec0e41 100644 --- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/model/Mix.kt @@ -4,15 +4,18 @@ import com.fasterxml.jackson.annotation.JsonIgnore import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank import java.util.* import javax.persistence.* +import javax.validation.constraints.Min import javax.validation.constraints.NotBlank import javax.validation.constraints.NotNull -const val IDENTIFIER_MIX_TYPE_NAME = "mixType" - private const val MIX_ID_NULL_MESSAGE = "Un identifiant est requis" private const val MIX_NAME_NULL_MESSAGE = "Un nom est requis" private const val MIX_RECIPE_NULL_MESSAGE = "Un recette est requise" -private const val MIX_MATERIAL_TYPE_NULL_MESSAGE = "Un type de prodsuit est requis" +private const val MIX_MATERIAL_TYPE_NULL_MESSAGE = "Un type de produit est requis" + +private const val MIX_DEDUCT_MIX_ID_NULL_MESSAGE = "Un identifiant de mélange est requis" +private const val MIX_DEDUCT_RATIO_NULL_MESSAGE = "Un ratio est requis" +private const val MIX_DEDUCT_RATION_NEGATIVE_MESSAGE = "Le ratio doit être égal ou supérieur à 0" @Entity @Table(name = "mix") @@ -65,6 +68,15 @@ open class MixUpdateDto( override fun toEntity(): Mix = throw UnsupportedOperationException() } +data class MixDeductDto( + @field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE) + val id: Long, + + @field:NotNull(message = MIX_DEDUCT_RATIO_NULL_MESSAGE) + @field:Min(value = 0, message = MIX_DEDUCT_RATION_NEGATIVE_MESSAGE) + val ratio: Float +) + // ==== DSL ==== fun mix( id: Long? = null, @@ -90,3 +102,9 @@ fun mixUpdateDto( mixMaterials: Map? = mapOf(), op: MixUpdateDto.() -> Unit = {} ) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op) + +fun mixRatio( + id: Long = 0L, + ratio: Float = 1f, + op: MixDeductDto.() -> Unit = {} +) = MixDeductDto(id, ratio).apply(op) diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/InventoryController.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/InventoryController.kt new file mode 100644 index 0000000..19ebf31 --- /dev/null +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/InventoryController.kt @@ -0,0 +1,35 @@ +package dev.fyloz.trial.colorrecipesexplorer.rest + +import dev.fyloz.trial.colorrecipesexplorer.model.MaterialQuantityDto +import dev.fyloz.trial.colorrecipesexplorer.model.MixDeductDto +import dev.fyloz.trial.colorrecipesexplorer.service.InventoryService +import org.springframework.context.annotation.Profile +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PutMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +private const val INVENTORY_CONTROLLER_PATH = "api/inventory" + +@RestController +@RequestMapping(INVENTORY_CONTROLLER_PATH) +@Profile("rest") +class InventoryController( + private val inventoryService: InventoryService +) { + @PutMapping("add") + fun add(@RequestBody quantities: Collection): ResponseEntity> { + return ResponseEntity.ok(inventoryService.add(quantities)) + } + + @PutMapping("deduct") + fun deduct(@RequestBody quantities: Collection): ResponseEntity> { + return ResponseEntity.ok(inventoryService.deduct(quantities)) + } + + @PutMapping("deduct/mix") + fun deduct(@RequestBody mixRatio: MixDeductDto): ResponseEntity> { + return ResponseEntity.ok(inventoryService.deductMix(mixRatio)) + } +} diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/MaterialController.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/MaterialController.kt index 0f90c24..5bf686f 100644 --- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/MaterialController.kt +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/rest/MaterialController.kt @@ -13,7 +13,6 @@ import org.springframework.web.multipart.MultipartFile import javax.validation.Valid private const val MATERIAL_CONTROLLER_PATH = "api/material" -private const val INVENTORY_CONTROLLER_PATH = "api/material/inventory" @RestController @RequestMapping(MATERIAL_CONTROLLER_PATH) @@ -90,20 +89,3 @@ class MaterialController(materialService: MaterialService) : ResponseEntity.notFound().build() } -@RestController -@RequestMapping(INVENTORY_CONTROLLER_PATH) -@Profile("rest") -class InventoryController( - private val inventoryService: InventoryService -) { - @PutMapping("add") - fun add(@RequestBody quantities: Collection): ResponseEntity> { - return ResponseEntity.ok(inventoryService.add(quantities)) - } - - @PutMapping("deduct") - fun deduct(@RequestBody quantities: Collection): ResponseEntity> { - return ResponseEntity.ok(inventoryService.deduct(quantities)) - } -} - diff --git a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryService.kt b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryService.kt index 9984b2e..8cb63e9 100644 --- a/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryService.kt +++ b/src/main/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryService.kt @@ -3,6 +3,8 @@ package dev.fyloz.trial.colorrecipesexplorer.service import dev.fyloz.trial.colorrecipesexplorer.exception.LowQuantitiesException import dev.fyloz.trial.colorrecipesexplorer.exception.LowQuantityException import dev.fyloz.trial.colorrecipesexplorer.model.MaterialQuantityDto +import dev.fyloz.trial.colorrecipesexplorer.model.MixDeductDto +import dev.fyloz.trial.colorrecipesexplorer.model.MixMaterial import dev.fyloz.trial.colorrecipesexplorer.model.materialQuantityDto import dev.fyloz.trial.colorrecipesexplorer.service.utils.mapMayThrow import org.springframework.stereotype.Service @@ -15,6 +17,9 @@ interface InventoryService { /** Adds a given quantity to the given [Material]'s inventory quantity according to the given [materialQuantity] and returns the updated quantity. */ fun add(materialQuantity: MaterialQuantityDto): Float + /** Deducts the inventory quantity of each [Material]s in the mix according to the ratio defined in the given [mixRatio] and returns the updated quantities. */ + fun deductMix(mixRatio: MixDeductDto): Collection + /** Deducts the inventory quantity of each given [MaterialQuantityDto] and returns the updated quantities. */ fun deduct(materialQuantities: Collection): Collection @@ -24,7 +29,8 @@ interface InventoryService { @Service class InventoryServiceImpl( - private val materialService: MaterialService + private val materialService: MaterialService, + private val mixService: MixService ) : InventoryService { @Transactional override fun add(materialQuantities: Collection) = @@ -38,6 +44,26 @@ class InventoryServiceImpl( materialQuantity.quantity ) + @Transactional + override fun deductMix(mixRatio: MixDeductDto): Collection { + val mix = mixService.getById(mixRatio.id) + val firstMixMaterial = mix.mixMaterials.first() + val adjustedFirstMaterialQuantity = firstMixMaterial.quantity * mixRatio.ratio + + fun adjustQuantity(mixMaterial: MixMaterial): Float = + if (!mixMaterial.material.materialType!!.usePercentages) + mixMaterial.quantity * mixRatio.ratio + else + (mixMaterial.quantity * adjustedFirstMaterialQuantity) / 100f + + return deduct(mix.mixMaterials.map { + materialQuantityDto( + materialId = it.material.id!!, + quantity = adjustQuantity(it) + ) + }) + } + @Transactional override fun deduct(materialQuantities: Collection): Collection { val thrown = mutableListOf() diff --git a/src/test/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryServiceTest.kt b/src/test/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryServiceTest.kt index 8810d66..d6b2629 100644 --- a/src/test/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryServiceTest.kt +++ b/src/test/kotlin/dev/fyloz/trial/colorrecipesexplorer/service/InventoryServiceTest.kt @@ -3,9 +3,7 @@ package dev.fyloz.trial.colorrecipesexplorer.service import com.nhaarman.mockitokotlin2.* import dev.fyloz.trial.colorrecipesexplorer.exception.LowQuantitiesException import dev.fyloz.trial.colorrecipesexplorer.exception.LowQuantityException -import dev.fyloz.trial.colorrecipesexplorer.model.MaterialQuantityDto -import dev.fyloz.trial.colorrecipesexplorer.model.material -import dev.fyloz.trial.colorrecipesexplorer.model.materialQuantityDto +import dev.fyloz.trial.colorrecipesexplorer.model.* import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -14,7 +12,8 @@ import kotlin.test.assertTrue class InventoryServiceTest { private val materialService: MaterialService = mock() - private val service = spy(InventoryServiceImpl(materialService)) + private val mixService: MixService = mock() + private val service = spy(InventoryServiceImpl(materialService, mixService)) @AfterEach fun afterEach() { @@ -60,6 +59,41 @@ class InventoryServiceTest { } } + // deductMix() + + @Test + fun `deductMix() calls deduct() with a collection of MaterialQuantityDto with adjusted quantities`() { + val material = material(id = 0L, materialType = materialType(usePercentages = false)) + val materialPercents = material(id = 1L, materialType = materialType(usePercentages = true)) + val mixRatio = mixRatio(ratio = 1.5f) + val mix = mix( + id = mixRatio.id, + mixMaterials = mutableListOf( + mixMaterial(id = 0L, material = material, quantity = 1000f), + mixMaterial(id = 1L, material = materialPercents, quantity = 50f) + ) + ) + val expectedQuantities = mapOf( + 0L to 1500f, + 1L to 750f + ) + + whenever(mixService.getById(mix.id!!)).doReturn(mix) + doAnswer { + (it.arguments[0] as Collection).map { materialQuantity -> + materialQuantityDto(materialId = materialQuantity.material, quantity = 0f) + } + }.whenever(service).deduct(any>()) + + val found = service.deductMix(mixRatio) + + verify(service).deduct(argThat> { + this.all { it.quantity == expectedQuantities[it.material] } + }) + + assertEquals(expectedQuantities.size, found.size) + } + // deduct() @Test