Merge branch 'inventory' into 'master'
Inventory See merge request color-recipes-explorer/backend!18
This commit is contained in:
commit
a500a8e46e
|
@ -310,12 +310,12 @@ private enum class ControllerAuthorizations(
|
|||
val permissions: Map<HttpMethod, EmployeePermission>
|
||||
) {
|
||||
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
|
||||
)
|
||||
),
|
||||
|
|
|
@ -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<Long, Float>? = 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)
|
||||
|
|
|
@ -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<MaterialQuantityDto>): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.add(quantities))
|
||||
}
|
||||
|
||||
@PutMapping("deduct")
|
||||
fun deduct(@RequestBody quantities: Collection<MaterialQuantityDto>): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.deduct(quantities))
|
||||
}
|
||||
|
||||
@PutMapping("deduct/mix")
|
||||
fun deduct(@RequestBody mixRatio: MixDeductDto): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.deductMix(mixRatio))
|
||||
}
|
||||
}
|
|
@ -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<MaterialQuantityDto>): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.add(quantities))
|
||||
}
|
||||
|
||||
@PutMapping("deduct")
|
||||
fun deduct(@RequestBody quantities: Collection<MaterialQuantityDto>): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.deduct(quantities))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<MaterialQuantityDto>
|
||||
|
||||
/** Deducts the inventory quantity of each given [MaterialQuantityDto] and returns the updated quantities. */
|
||||
fun deduct(materialQuantities: Collection<MaterialQuantityDto>): Collection<MaterialQuantityDto>
|
||||
|
||||
|
@ -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<MaterialQuantityDto>) =
|
||||
|
@ -38,6 +44,26 @@ class InventoryServiceImpl(
|
|||
materialQuantity.quantity
|
||||
)
|
||||
|
||||
@Transactional
|
||||
override fun deductMix(mixRatio: MixDeductDto): Collection<MaterialQuantityDto> {
|
||||
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<MaterialQuantityDto>): Collection<MaterialQuantityDto> {
|
||||
val thrown = mutableListOf<MaterialQuantityDto>()
|
||||
|
|
|
@ -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<MaterialQuantityDto>).map { materialQuantity ->
|
||||
materialQuantityDto(materialId = materialQuantity.material, quantity = 0f)
|
||||
}
|
||||
}.whenever(service).deduct(any<Collection<MaterialQuantityDto>>())
|
||||
|
||||
val found = service.deductMix(mixRatio)
|
||||
|
||||
verify(service).deduct(argThat<Collection<MaterialQuantityDto>> {
|
||||
this.all { it.quantity == expectedQuantities[it.material] }
|
||||
})
|
||||
|
||||
assertEquals(expectedQuantities.size, found.size)
|
||||
}
|
||||
|
||||
// deduct()
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue