Ajout d'un endpoint pour l'inventaire
This commit is contained in:
parent
376d244f29
commit
3033e104b2
|
@ -1,3 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.model.dto
|
||||
|
||||
data class InventoryDto(val mixId: Long, val materialIds: List<Long>, val quantities: List<Float>)
|
|
@ -2,6 +2,7 @@ package dev.fyloz.trial.colorrecipesexplorer.exception
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Material
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.MaterialQuantityDto
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpStatus
|
||||
|
@ -10,7 +11,6 @@ import org.springframework.validation.FieldError
|
|||
import org.springframework.web.bind.MethodArgumentNotValidException
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.ResponseStatus
|
||||
import org.springframework.web.context.request.WebRequest
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
|
||||
|
||||
|
@ -18,13 +18,13 @@ abstract class RestException(val exceptionMessage: String, val httpStatus: HttpS
|
|||
RuntimeException(exceptionMessage) {
|
||||
abstract fun buildBody(): RestExceptionBody
|
||||
|
||||
@Suppress("unused")
|
||||
open inner class RestExceptionBody(
|
||||
val status: Int = httpStatus.value(),
|
||||
@JsonProperty("message") val message: String = exceptionMessage
|
||||
)
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CONFLICT)
|
||||
class EntityAlreadyExistsException(val value: Any) :
|
||||
RestException("An entity with the given identifier already exists", HttpStatus.CONFLICT) {
|
||||
@Suppress("unused")
|
||||
|
@ -33,7 +33,6 @@ class EntityAlreadyExistsException(val value: Any) :
|
|||
}
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
class EntityNotFoundException(val value: Any) :
|
||||
RestException("An entity could not be found with the given identifier", HttpStatus.NOT_FOUND) {
|
||||
@Suppress("unused")
|
||||
|
@ -42,7 +41,6 @@ class EntityNotFoundException(val value: Any) :
|
|||
}
|
||||
}
|
||||
|
||||
@ResponseStatus(HttpStatus.CONFLICT)
|
||||
class CannotDeleteEntityException(val value: Long) :
|
||||
RestException(
|
||||
"The entity with the given identifier could not be deleted because it is required by other entities",
|
||||
|
@ -54,18 +52,37 @@ class CannotDeleteEntityException(val value: Long) :
|
|||
}
|
||||
}
|
||||
|
||||
@ResponseStatus
|
||||
class SimdutWriteException(val material: Material) :
|
||||
RestException(
|
||||
"Could not write the SIMDUT file to disk",
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
) {
|
||||
@Suppress("unused")
|
||||
override fun buildBody(): RestExceptionBody = RestExceptionBody()
|
||||
}
|
||||
|
||||
class LowQuantityException(val materialQuantity: MaterialQuantityDto) :
|
||||
RestException(
|
||||
"There is not enough of the given material in the inventory",
|
||||
HttpStatus.CONFLICT
|
||||
) {
|
||||
@Suppress("unused")
|
||||
override fun buildBody(): RestExceptionBody = object : RestExceptionBody() {
|
||||
val materialId = material.id
|
||||
val material = materialQuantity.material
|
||||
val quantity = materialQuantity.quantity
|
||||
}
|
||||
}
|
||||
|
||||
class LowQuantitiesException(val materialQuantities: Collection<MaterialQuantityDto>) :
|
||||
RestException(
|
||||
"There is not enough of one or more given materials in the inventory",
|
||||
HttpStatus.CONFLICT
|
||||
) {
|
||||
@Suppress
|
||||
override fun buildBody(): RestExceptionBody = object : RestExceptionBody() {
|
||||
val lowQuantities = materialQuantities
|
||||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
@Profile("rest")
|
||||
|
|
|
@ -64,11 +64,6 @@ data class Employee(
|
|||
) : Model {
|
||||
@JsonProperty("permissions")
|
||||
fun getFlattenedPermissions(): Iterable<EmployeePermission> = getPermissions()
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is Employee && id == other.id && firstName == other.firstName && lastName == other.lastName
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(id, firstName, lastName)
|
||||
}
|
||||
|
||||
/** DTO for creating employees. Allows a [password] a [groupId]. */
|
||||
|
@ -147,10 +142,7 @@ data class EmployeeGroup(
|
|||
val employees: MutableSet<Employee> = mutableSetOf()
|
||||
) : NamedModel {
|
||||
@JsonProperty("employeeCount")
|
||||
fun getEmployeeCount() = employees.size - 1 // Remove the default employee
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is EmployeeGroup && id == other.id && name == other.name
|
||||
override fun hashCode(): Int = Objects.hash(id, name)
|
||||
fun getEmployeeCount() = employees.size - 1 // -1 removes the default employee
|
||||
}
|
||||
|
||||
open class EmployeeGroupSaveDto(
|
||||
|
|
|
@ -18,10 +18,7 @@ data class Company(
|
|||
|
||||
@Column(unique = true)
|
||||
override val name: String
|
||||
) : NamedModel {
|
||||
override fun equals(other: Any?): Boolean = other is Company && name == other.name
|
||||
override fun hashCode(): Int = Objects.hash(name)
|
||||
}
|
||||
) : NamedModel
|
||||
|
||||
|
||||
open class CompanySaveDto(
|
||||
|
|
|
@ -2,9 +2,7 @@ package dev.fyloz.trial.colorrecipesexplorer.model
|
|||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrNotBlank
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.validation.NullOrSize
|
||||
import org.springframework.util.Assert
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.util.*
|
||||
import javax.persistence.*
|
||||
import javax.validation.constraints.Min
|
||||
import javax.validation.constraints.NotBlank
|
||||
|
@ -16,6 +14,12 @@ private const val MATERIAL_INVENTORY_QUANTITY_NULL_MESSAGE = "Une quantité est
|
|||
private const val MATERIAL_INVENTORY_QUANTITY_NEGATIVE_MESSAGE = "La quantité doit être supérieure ou égale à 0"
|
||||
private const val MATERIAL_TYPE_NULL_MESSAGE = "Un type de produit est requis"
|
||||
|
||||
private const val MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE = "Un produit est requis"
|
||||
private const val MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE = "Une quantité est requises"
|
||||
private const val MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE = "La quantité doit être supérieure ou égale à 0"
|
||||
|
||||
// === Entities ===
|
||||
|
||||
@Entity
|
||||
@Table(name = "material")
|
||||
data class Material(
|
||||
|
@ -35,20 +39,7 @@ data class Material(
|
|||
@ManyToOne
|
||||
@JoinColumn(name = "material_type_id")
|
||||
var materialType: MaterialType?
|
||||
) : NamedModel {
|
||||
constructor(name: String, inventoryQuantity: Float, isMixType: Boolean, materialType: MaterialType) : this(
|
||||
null,
|
||||
name,
|
||||
inventoryQuantity,
|
||||
isMixType,
|
||||
materialType
|
||||
)
|
||||
|
||||
constructor() : this(null, "", 0f, false, null)
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is Material && name == other.name
|
||||
override fun hashCode(): Int = Objects.hash(name)
|
||||
}
|
||||
) : NamedModel
|
||||
|
||||
open class MaterialSaveDto(
|
||||
@field:NotBlank(message = MATERIAL_NAME_NULL_MESSAGE)
|
||||
|
@ -85,27 +76,19 @@ open class MaterialUpdateDto(
|
|||
id, name ?: "", inventoryQuantity
|
||||
?: Float.MIN_VALUE, false, materialType
|
||||
)
|
||||
|
||||
fun toMaterial() = toEntity()
|
||||
}
|
||||
|
||||
data class InventoryMaterial(
|
||||
@NotNull val id: Long,
|
||||
@NotNull val name: String,
|
||||
@NotNull val inventoryQuantity: Float,
|
||||
@NotNull val materialTypeName: String,
|
||||
@NotNull val lowQuantity: Boolean = false
|
||||
data class MaterialQuantityDto(
|
||||
@field:NotNull(message = MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE)
|
||||
val material: Long,
|
||||
|
||||
@field:NotNull(message = MATERIAL_QUANTITY_QUANTITY_NULL_MESSAGE)
|
||||
@field:Min(value = 0, message = MATERIAL_QUANTITY_QUANTITY_NEGATIVE_MESSAGE)
|
||||
val quantity: Float
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
fun Material.toInventoryMaterial(minimumQuantity: Float): InventoryMaterial {
|
||||
Assert.notNull(id, "Cannot convert a material without id to an inventory material")
|
||||
Assert.notNull(name, "Cannot convert a material without name to an inventory material")
|
||||
Assert.notNull(materialType, "Cannot convert a material without a material type to an inventory material")
|
||||
return InventoryMaterial(id!!, name, inventoryQuantity, materialType!!.name, inventoryQuantity < minimumQuantity)
|
||||
}
|
||||
// === DSL ===
|
||||
|
||||
// ==== DSL ====
|
||||
fun material(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
|
@ -140,3 +123,9 @@ fun materialUpdateDto(
|
|||
simdutFile: MultipartFile? = null,
|
||||
op: MaterialUpdateDto.() -> Unit = {}
|
||||
) = MaterialUpdateDto(id, name, inventoryQuantity, materialType, simdutFile).apply(op)
|
||||
|
||||
fun materialQuantityDto(
|
||||
materialId: Long,
|
||||
quantity: Float,
|
||||
op: MaterialQuantityDto.() -> Unit = {}
|
||||
) = MaterialQuantityDto(materialId, quantity).apply(op)
|
||||
|
|
|
@ -36,10 +36,7 @@ data class MaterialType(
|
|||
@Column(name = "system_type")
|
||||
@ColumnDefault("false")
|
||||
val systemType: Boolean = false
|
||||
) : NamedModel {
|
||||
override fun equals(other: Any?): Boolean = other is MaterialType && name == other.name && prefix == other.prefix
|
||||
override fun hashCode(): Int = Objects.hash(name, prefix)
|
||||
}
|
||||
) : NamedModel
|
||||
|
||||
open class MaterialTypeSaveDto(
|
||||
@field:NotBlank(message = MATERIAL_TYPE_NAME_NULL_MESSAGE)
|
||||
|
|
|
@ -34,12 +34,7 @@ data class Mix(
|
|||
|
||||
@OneToMany(cascade = [CascadeType.ALL], mappedBy = "mix")
|
||||
var mixMaterials: MutableCollection<MixMaterial>,
|
||||
) : Model {
|
||||
constructor(recipe: Recipe, mixType: MixType) : this(null, null, recipe, mixType, mutableListOf())
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is Mix && recipe == other.recipe && mixType == other.mixType
|
||||
override fun hashCode(): Int = Objects.hash(recipe, mixType)
|
||||
}
|
||||
) : Model
|
||||
|
||||
open class MixSaveDto(
|
||||
@field:NotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
|
|
|
@ -21,14 +21,7 @@ data class MixMaterial(
|
|||
val material: Material,
|
||||
|
||||
var quantity: Float
|
||||
) : Model {
|
||||
constructor(mix: Mix, material: Material, quantity: Float) : this(null, mix, material, quantity)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is MixMaterial && other.mix == mix && other.material == material && other.quantity == quantity
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(mix, material, quantity)
|
||||
}
|
||||
) : Model
|
||||
|
||||
// ==== DSL ====
|
||||
fun mixMaterial(
|
||||
|
|
|
@ -18,17 +18,7 @@ data class MixType(
|
|||
@OneToOne(cascade = [CascadeType.ALL])
|
||||
@JoinColumn(name = "material_id")
|
||||
var material: Material
|
||||
) : NamedModel {
|
||||
constructor(name: String, material: Material) : this(null, name, material)
|
||||
constructor(name: String, materialType: MaterialType) : this(
|
||||
null,
|
||||
name,
|
||||
material(name = name, inventoryQuantity = Float.MIN_VALUE, isMixType = true, materialType = materialType)
|
||||
)
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is MixType && other.name == name && other.material == material
|
||||
override fun hashCode(): Int = Objects.hash(name, material)
|
||||
}
|
||||
) : NamedModel
|
||||
|
||||
// ==== DSL ====
|
||||
fun mixType(
|
||||
|
@ -42,7 +32,8 @@ fun mixType(
|
|||
name: String = "name",
|
||||
materialType: MaterialType = materialType(),
|
||||
op: MixType.() -> Unit = {}
|
||||
) = MixType(
|
||||
) = mixType(
|
||||
id = null,
|
||||
name,
|
||||
material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType)
|
||||
material = material(name = name, inventoryQuantity = 0f, isMixType = true, materialType = materialType)
|
||||
).apply(op)
|
||||
|
|
|
@ -57,61 +57,10 @@ data class Recipe(
|
|||
@OneToMany(cascade = [CascadeType.ALL], mappedBy = "recipe")
|
||||
var steps: List<RecipeStep>
|
||||
) : Model {
|
||||
constructor() : this(
|
||||
null,
|
||||
"name",
|
||||
"description",
|
||||
"#ffffff",
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
"remark",
|
||||
"note",
|
||||
company(),
|
||||
mutableListOf(),
|
||||
mutableListOf()
|
||||
)
|
||||
|
||||
constructor(
|
||||
id: Long,
|
||||
name: String,
|
||||
company: Company,
|
||||
description: String,
|
||||
color: String,
|
||||
gloss: Byte,
|
||||
sample: Int,
|
||||
approbationDate: LocalDate?,
|
||||
remark: String,
|
||||
note: String
|
||||
) : this(
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
color,
|
||||
gloss,
|
||||
sample,
|
||||
approbationDate,
|
||||
remark,
|
||||
note,
|
||||
company,
|
||||
mutableListOf(),
|
||||
mutableListOf()
|
||||
)
|
||||
|
||||
val mixesSortedById: Collection<Mix>
|
||||
@JsonIgnore
|
||||
get() = mixes.sortedBy { it.id }
|
||||
|
||||
/** The mix types contained in this recipe. */
|
||||
val mixTypes: Collection<MixType>
|
||||
@JsonIgnore
|
||||
get() = mixes.map { it.mixType }
|
||||
|
||||
/** Checks if the recipe contains the given [mixType]. */
|
||||
fun containsMixType(mixType: MixType) = mixTypes.contains(mixType)
|
||||
|
||||
override fun equals(other: Any?): Boolean = other is Recipe && other.name == name && other.company == company
|
||||
override fun hashCode(): Int = Objects.hash(name, company)
|
||||
}
|
||||
|
||||
data class RecipePublicDataDto(
|
||||
|
|
|
@ -17,14 +17,7 @@ data class RecipeStep(
|
|||
val recipe: Recipe?,
|
||||
|
||||
val message: String
|
||||
) : Model {
|
||||
constructor(recipe: Recipe?, message: String) : this(null, recipe, message)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is RecipeStep && other.recipe == recipe && other.message == message
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(recipe, message)
|
||||
}
|
||||
) : Model
|
||||
|
||||
|
||||
// ==== DSL ====
|
||||
|
|
|
@ -2,6 +2,7 @@ package dev.fyloz.trial.colorrecipesexplorer.repository
|
|||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Material
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.MaterialType
|
||||
import org.springframework.data.jpa.repository.Modifying
|
||||
import org.springframework.data.jpa.repository.Query
|
||||
import org.springframework.stereotype.Repository
|
||||
|
||||
|
@ -10,8 +11,10 @@ interface MaterialRepository : NamedJpaRepository<Material> {
|
|||
/** Checks if one or more materials have the given [materialType]. */
|
||||
fun existsByMaterialType(materialType: MaterialType): Boolean
|
||||
|
||||
/** Gets all the materials with the given [materialType]. */
|
||||
fun findAllByMaterialType(materialType: MaterialType): Collection<Material>
|
||||
/** Updates the [inventoryQuantity] of the [Material] with the given [id]. */
|
||||
@Modifying
|
||||
@Query("update Material m set m.inventoryQuantity = :inventoryQuantity where m.id = :id")
|
||||
fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.rest
|
||||
|
||||
//import dev.fyloz.trial.colorrecipesexplorer.model.InventoryMaterial
|
||||
//import dev.fyloz.trial.colorrecipesexplorer.service.InventoryService
|
||||
//import org.springframework.http.ResponseEntity
|
||||
//import org.springframework.web.bind.annotation.RestController
|
||||
//
|
||||
//@RestController
|
||||
//class InventoryController(val inventoryService: InventoryService) {
|
||||
// fun getAllMaterials(): ResponseEntity<Collection<InventoryMaterial>> = ResponseEntity.ok(inventoryService.getAllMaterials())
|
||||
//}
|
|
@ -1,6 +1,7 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.rest
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.InventoryService
|
||||
import dev.fyloz.trial.colorrecipesexplorer.service.MaterialService
|
||||
import org.springframework.context.annotation.Profile
|
||||
import org.springframework.http.HttpHeaders
|
||||
|
@ -12,6 +13,7 @@ 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)
|
||||
|
@ -83,3 +85,23 @@ class MaterialController(materialService: MaterialService) :
|
|||
override fun update(entity: MaterialUpdateDto): ResponseEntity<Void> =
|
||||
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<Void> {
|
||||
inventoryService.add(quantities)
|
||||
return ResponseEntity.ok().build()
|
||||
}
|
||||
|
||||
@PutMapping("deduct")
|
||||
fun deduct(@RequestBody quantities: Collection<MaterialQuantityDto>): ResponseEntity<Void> {
|
||||
inventoryService.deduct(quantities)
|
||||
return ResponseEntity.ok().build()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -309,7 +309,7 @@ class EmployeeGroupServiceImpl(
|
|||
override fun removeEmployeeFromGroup(group: EmployeeGroup, employee: Employee) {
|
||||
if (employee.group == null || employee.group != group) return
|
||||
|
||||
group.employees.remove(employee)
|
||||
group.employees.removeIf { it.id == employee.id }
|
||||
employee.group = null
|
||||
update(group)
|
||||
employeeService.update(employee)
|
||||
|
|
|
@ -1,6 +1,62 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.service
|
||||
|
||||
//interface InventoryService {
|
||||
// fun deduct(quantities: InventoryDeductDto)
|
||||
// fun deduct(material: Material, quantity: Float)
|
||||
//}
|
||||
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.service.utils.filterThrows
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
||||
interface InventoryService {
|
||||
/** Adds each given [MaterialQuantityDto] to the inventory. */
|
||||
fun add(materialQuantities: Collection<MaterialQuantityDto>)
|
||||
|
||||
/** Adds a given quantity to the given [Material]'s inventory quantity according to the given [materialQuantity]. */
|
||||
fun add(materialQuantity: MaterialQuantityDto)
|
||||
|
||||
/** Deducts the inventory quantity of each given [MaterialQuantityDto]. */
|
||||
fun deduct(materialQuantities: Collection<MaterialQuantityDto>)
|
||||
|
||||
/** Deducts the inventory quantity of a given [Material] by a given quantity according to the given [materialQuantity]. */
|
||||
fun deduct(materialQuantity: MaterialQuantityDto)
|
||||
}
|
||||
|
||||
@Service
|
||||
class InventoryServiceImpl(
|
||||
private val materialService: MaterialService
|
||||
) : InventoryService {
|
||||
@Transactional
|
||||
override fun add(materialQuantities: Collection<MaterialQuantityDto>) {
|
||||
materialQuantities.forEach(::add)
|
||||
}
|
||||
|
||||
override fun add(materialQuantity: MaterialQuantityDto) {
|
||||
materialService.updateQuantity(
|
||||
materialService.getById(materialQuantity.material),
|
||||
materialQuantity.quantity
|
||||
)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
override fun deduct(materialQuantities: Collection<MaterialQuantityDto>) {
|
||||
with(materialQuantities.filterThrows<MaterialQuantityDto, LowQuantityException> {
|
||||
deduct(it)
|
||||
}) {
|
||||
if (this.isNotEmpty()) {
|
||||
throw LowQuantitiesException(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deduct(materialQuantity: MaterialQuantityDto) {
|
||||
val material = materialService.getById(materialQuantity.material)
|
||||
if (material.inventoryQuantity >= materialQuantity.quantity) {
|
||||
materialService.updateQuantity(
|
||||
material,
|
||||
-materialQuantity.quantity
|
||||
)
|
||||
} else {
|
||||
throw LowQuantityException(materialQuantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ interface MaterialService :
|
|||
|
||||
/** Gets all materials available for updating the mix with the given [mixId], including normal materials and materials from [MixType]s included in the mix recipe, excluding the material of the [MixType] of the said mix. */
|
||||
fun getAllForMixUpdate(mixId: Long): Collection<Material>
|
||||
|
||||
/** Updates the quantity of the given [material] with the given [factor]. */
|
||||
fun updateQuantity(material: Material, factor: Float)
|
||||
}
|
||||
|
||||
@Service
|
||||
|
@ -70,6 +73,10 @@ class MaterialServiceImpl(
|
|||
}
|
||||
}
|
||||
|
||||
override fun updateQuantity(material: Material, factor: Float) = with(material) {
|
||||
repository.updateInventoryQuantityById(this.id!!, this.inventoryQuantity + factor)
|
||||
}
|
||||
|
||||
override fun getAllForMixCreation(recipeId: Long): Collection<Material> {
|
||||
val recipesMixTypes = recipeService.getById(recipeId).mixTypes
|
||||
return getAll()
|
||||
|
|
|
@ -36,7 +36,8 @@ class MixServiceImpl(
|
|||
val materialType = materialTypeService.getById(entity.materialTypeId)
|
||||
val mixType = mixTypeService.getOrCreateForNameAndMaterialType(entity.name, materialType)
|
||||
|
||||
var mix = save(mix(recipe = recipe, mixType = mixType))
|
||||
var mix = mix(recipe = recipe, mixType = mixType)
|
||||
mix = save(mix)
|
||||
val mixMaterials =
|
||||
if (entity.mixMaterials != null) mixMaterialService.createFromMap(mix, entity.mixMaterials) else listOf()
|
||||
mix = update(
|
||||
|
|
|
@ -81,12 +81,12 @@ class RecipeServiceImpl(
|
|||
|
||||
private fun updateSteps(recipe: Recipe, steps: List<RecipeStep>?): List<RecipeStep> =
|
||||
if (steps != null) {
|
||||
val toDelete = recipe.steps.filter { it !in steps }
|
||||
val toDelete = recipe.steps.filter { !steps.any { step -> step.message == it.message } }
|
||||
toDelete.forEach(stepService::delete)
|
||||
recipe.steps
|
||||
.filter { it !in toDelete } + steps
|
||||
.filter { !toDelete.any { step -> step.message == it.message } } + steps
|
||||
.map { recipeStep(recipe = recipe, message = it.message) }
|
||||
.filter { it !in recipe.steps }
|
||||
.filter { !recipe.steps.any { step -> step.message == it.message } }
|
||||
.toMutableList()
|
||||
} else {
|
||||
recipe.steps
|
||||
|
|
|
@ -2,6 +2,7 @@ package dev.fyloz.trial.colorrecipesexplorer.service
|
|||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Recipe
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.RecipeStep
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.recipeStep
|
||||
import dev.fyloz.trial.colorrecipesexplorer.repository.RecipeStepRepository
|
||||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
@ -19,7 +20,7 @@ class RecipeStepServiceImpl(recipeStepRepository: RecipeStepRepository) :
|
|||
AbstractModelService<RecipeStep, RecipeStepRepository>(recipeStepRepository),
|
||||
RecipeStepService {
|
||||
override fun createForRecipe(recipe: Recipe, message: String): RecipeStep =
|
||||
RecipeStep(recipe, message)
|
||||
recipeStep(recipe = recipe, message = message)
|
||||
|
||||
@Transactional
|
||||
override fun createAllForRecipe(recipe: Recipe, messages: Collection<String>): Collection<RecipeStep> =
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.service.utils
|
||||
|
||||
/** Returns a list containing only the elements which causes the given [consumer] to throw the given throwable [E]. */
|
||||
inline fun <T, reified E : Throwable> Iterable<T>.filterThrows(consumer: (T) -> Unit): List<T> = this.filter {
|
||||
try {
|
||||
consumer(it)
|
||||
false
|
||||
} catch (th: Throwable) {
|
||||
th is E
|
||||
}
|
||||
}
|
|
@ -3,3 +3,4 @@ spring.datasource.username=root
|
|||
spring.datasource.password=pass
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
|
|
|
@ -27,7 +27,6 @@ spring.jpa.show-sql=true
|
|||
spring.messages.fallback-to-system-locale=true
|
||||
spring.servlet.multipart.max-file-size=10MB
|
||||
spring.servlet.multipart.max-request-size=15MB
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.jpa.open-in-view=true
|
||||
server.http2.enabled=true
|
||||
server.error.whitelabel.enabled=false
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.EmployeeGroup
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.employee
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.employeeGroup
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
import kotlin.test.*
|
||||
|
||||
@Disabled
|
||||
@DataJpaTest
|
||||
class EmployeeRepositoryTest @Autowired constructor(private val employeeRepository: EmployeeRepository, val entityManager: TestEntityManager) {
|
||||
private var employeeGroup = employeeGroup()
|
||||
private val employee = employee(id = 0L, firstName = "fname", lastName = "lname")
|
||||
private val anotherEmployee = employee(id = 1L, firstName = "another fname", lastName = "another lname")
|
||||
private val groupEmployee = employee(id = 2L, firstName = "group fname", lastName = "group lname", group = employeeGroup)
|
||||
private val defaultGroupEmployee = employee(id = 3L, firstName = "default fname", lastName = "default lname", group = employeeGroup, isDefaultGroupUser = true)
|
||||
|
||||
@AfterEach
|
||||
fun afterEach() {
|
||||
if (employeeGroup.id != null) {
|
||||
entityManager.remove(employeeGroup)
|
||||
employeeGroup = employeeGroup(id = null)
|
||||
groupEmployee.group = employeeGroup
|
||||
defaultGroupEmployee.group = employeeGroup
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByFirstNameAndLastName() returns true when an employee with the given first name and last name exists`() {
|
||||
entityManager.persist(employee)
|
||||
|
||||
val exists = employeeRepository.existsByFirstNameAndLastName(employee.firstName, employee.lastName)
|
||||
|
||||
assertTrue(exists)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByFirstNameAndLastName() returns false when no employee with the given first name and last name exists`() {
|
||||
entityManager.persist(anotherEmployee)
|
||||
|
||||
val exists = employeeRepository.existsByFirstNameAndLastName(employee.firstName, employee.lastName)
|
||||
|
||||
assertFalse(exists)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByFirstNameAndLastName() returns the employee with the given first name and last name`() {
|
||||
val expected = entityManager.persist(employee)
|
||||
entityManager.persist(anotherEmployee) // Persist another employee to make sure the correct one is return by the tested method
|
||||
assertNotNull(expected)
|
||||
assertNotNull(expected.id)
|
||||
|
||||
val found = employeeRepository.findByFirstNameAndLastName(employee.firstName, employee.lastName)
|
||||
|
||||
assertEquals(found, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByFirstNameAndLastName() returns null when no employee with the given first name and last name exists`() {
|
||||
entityManager.persist(anotherEmployee)
|
||||
|
||||
val found = employeeRepository.findByFirstNameAndLastName(employee.firstName, employee.lastName)
|
||||
|
||||
assertNull(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAllByGroup() returns all employees in the given group`() {
|
||||
entityManager.persist(employeeGroup)
|
||||
entityManager.persist(employee)
|
||||
entityManager.persist(groupEmployee)
|
||||
entityManager.persist(defaultGroupEmployee)
|
||||
|
||||
val found = employeeRepository.findAllByGroup(employeeGroup)
|
||||
|
||||
assertTrue(found.contains(groupEmployee))
|
||||
assertTrue(found.contains(defaultGroupEmployee))
|
||||
assertFalse(found.contains(employee))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByIsDefaultGroupUserIsTrueAndGroupIs() returns the default employee of the given group`() {
|
||||
entityManager.persist(employeeGroup)
|
||||
entityManager.persist(employee)
|
||||
entityManager.persist(groupEmployee)
|
||||
entityManager.persist(defaultGroupEmployee)
|
||||
|
||||
val found = employeeRepository.findByIsDefaultGroupUserIsTrueAndGroupIs(employeeGroup)
|
||||
|
||||
assertEquals(found, defaultGroupEmployee)
|
||||
}
|
||||
}
|
||||
|
||||
@Disabled
|
||||
class EmployeeGroupRepositoryTest @Autowired constructor(employeeGroupRepository: EmployeeGroupRepository, entityManager: TestEntityManager) :
|
||||
AbstractNamedJpaRepositoryTest<EmployeeGroup, EmployeeGroupRepository>(employeeGroupRepository, entityManager) {
|
||||
override fun entity(name: String): EmployeeGroup = employeeGroup(name = name)
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Company
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.company
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
|
||||
@Disabled
|
||||
@DataJpaTest
|
||||
class CompanyRepositoryTest @Autowired constructor(companyRepository: CompanyRepository, entityManager: TestEntityManager) :
|
||||
AbstractNamedJpaRepositoryTest<Company, CompanyRepository>(companyRepository, entityManager) {
|
||||
override fun entity(name: String): Company = company(name = name)
|
||||
}
|
|
@ -1,62 +1,32 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.Material
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.material
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.materialType
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.springframework.test.context.junit4.SpringRunner
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@Disabled
|
||||
class MaterialRepositoryTest @Autowired constructor(materialRepository: MaterialRepository, entityManager: TestEntityManager) :
|
||||
AbstractNamedJpaRepositoryTest<Material, MaterialRepository>(materialRepository, entityManager) {
|
||||
override fun entity(name: String): Material = material(name = name, materialType = null)
|
||||
@RunWith(SpringRunner::class)
|
||||
@DataJpaTest(excludeAutoConfiguration = [LiquibaseAutoConfiguration::class])
|
||||
class MaterialRepositoryTest @Autowired constructor(
|
||||
private val materialRepository: MaterialRepository,
|
||||
private val entityManager: TestEntityManager
|
||||
) {
|
||||
// updateInventoryQuantityById()
|
||||
|
||||
@Test
|
||||
fun `existsByMaterialType() returns true when a material with the given material type exists`() {
|
||||
val materialType = materialType()
|
||||
val material = material(materialType = materialType)
|
||||
fun `updateInventoryQuantityById() updates the quantity of the material with the given identifier`() {
|
||||
var material = entityManager.persist(material(inventoryQuantity = 1000f, materialType = null))
|
||||
val updatedQuantity = 1235f
|
||||
|
||||
entityManager.persist(materialType)
|
||||
entityManager.persist(material)
|
||||
materialRepository.updateInventoryQuantityById(material.id!!, updatedQuantity)
|
||||
|
||||
val found = repository.existsByMaterialType(materialType)
|
||||
material = entityManager.refresh(material)
|
||||
|
||||
assertTrue(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByMaterialType() returns false when no material with the given material type exists`() {
|
||||
val materialType = materialType()
|
||||
|
||||
entityManager.persist(materialType)
|
||||
|
||||
val found = repository.existsByMaterialType(materialType)
|
||||
|
||||
assertFalse(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAllByMaterialType() returns all materials with the given material type`() {
|
||||
val materialType = materialType(name = "material type", prefix = "MAT")
|
||||
val anotherMaterialType = materialType(name = "another material type", prefix = "ANO")
|
||||
val material = material(name = "material", materialType = materialType)
|
||||
val anotherMaterial = material(name = "another material", materialType = materialType)
|
||||
val yetAnotherMaterial = material(name = "yet another material", materialType = anotherMaterialType)
|
||||
|
||||
entityManager.persist(materialType)
|
||||
entityManager.persist(anotherMaterialType)
|
||||
entityManager.persist(material)
|
||||
entityManager.persist(anotherMaterial)
|
||||
entityManager.persist(yetAnotherMaterial)
|
||||
|
||||
val found = repository.findAllByMaterialType(materialType)
|
||||
|
||||
assertTrue(found.contains(material))
|
||||
assertTrue(found.contains(anotherMaterial))
|
||||
assertFalse(found.contains(yetAnotherMaterial))
|
||||
assertEquals(updatedQuantity, material.inventoryQuantity)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.MaterialType
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.materialType
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.*
|
||||
|
||||
@Disabled
|
||||
class MaterialTypeRepositoryTest @Autowired constructor(materialTypeRepository: MaterialTypeRepository, entityManager: TestEntityManager) :
|
||||
AbstractNamedJpaRepositoryTest<MaterialType, MaterialTypeRepository>(materialTypeRepository, entityManager) {
|
||||
override fun entity(name: String): MaterialType = entity(name = name, prefix = "MAT")
|
||||
private fun entity(name: String = "materialType", prefix: String = "MAT"): MaterialType = materialType(name = name, prefix = prefix)
|
||||
|
||||
@Test
|
||||
fun `existsByPrefix() returns true when a material type with the given prefix exists`() {
|
||||
val materialType = entity()
|
||||
entityManager.persist(materialType)
|
||||
|
||||
val found = repository.existsByPrefix(materialType.prefix)
|
||||
|
||||
assertTrue(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByPrefix() returns false when no material type with the given prefix exists`() {
|
||||
val materialType = entity()
|
||||
val anotherMaterialType = entity(prefix = "ANO")
|
||||
entityManager.persist(anotherMaterialType)
|
||||
|
||||
val found = repository.existsByPrefix(materialType.prefix)
|
||||
|
||||
assertFalse(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAllBySystemTypeIs() returns all system types`() {
|
||||
val materialType = entity()
|
||||
val systemType = materialType(name = "system type", prefix = "SYT", systemType = true)
|
||||
val anotherSystemType = materialType(name = "another system type", prefix = "ASY", systemType = true)
|
||||
|
||||
entityManager.persist(materialType)
|
||||
entityManager.persist(systemType)
|
||||
entityManager.persist(anotherSystemType)
|
||||
|
||||
val found = repository.findAllBySystemTypeIs(true)
|
||||
|
||||
assertTrue(found.contains(systemType))
|
||||
assertTrue(found.contains(anotherSystemType))
|
||||
assertFalse(found.contains(materialType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findAllBySystemTypeIs() returns all material types which are not system types`() {
|
||||
val materialType = entity()
|
||||
val anotherMaterialType = entity(name = "another material type", prefix = "AMT")
|
||||
val systemType = materialType(name = "system type", prefix = "SYT", systemType = true)
|
||||
|
||||
entityManager.persist(materialType)
|
||||
entityManager.persist(anotherMaterialType)
|
||||
entityManager.persist(systemType)
|
||||
|
||||
val found = repository.findAllBySystemTypeIs(false)
|
||||
|
||||
assertTrue(found.contains(anotherMaterialType))
|
||||
assertTrue(found.contains(materialType))
|
||||
assertFalse(found.contains(systemType))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByPrefix() returns the material type with the given prefix`() {
|
||||
val materialType = entity()
|
||||
entityManager.persist(materialType)
|
||||
|
||||
val found = repository.findByPrefix(materialType.prefix)
|
||||
|
||||
assertNotNull(found)
|
||||
assertEquals(materialType, found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByPrefix() returns null when no material type with the given prefix exists`() {
|
||||
entityManager.persist(entity(prefix = "ANO"))
|
||||
|
||||
val found = repository.findByPrefix(entity().prefix)
|
||||
|
||||
assertNull(found)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
|
||||
@Disabled
|
||||
@DataJpaTest
|
||||
class MixMaterialRepositoryTest @Autowired constructor(
|
||||
private val mixMaterialRepository: MixMaterialRepository,
|
||||
val entityManager: TestEntityManager
|
||||
) {
|
||||
// TODO trouver pourquoi il y a toujours un PersistentObjectException
|
||||
|
||||
// private val material = material(id = 0L)
|
||||
// private val mixMaterial = mixMaterial(id = 0L, material = material)
|
||||
// private val anotherMixMaterial = mixMaterial(id = 1L, material = material(id = 1L))
|
||||
//
|
||||
// @Test
|
||||
// fun `existsByMaterial() returns true when a mix material with the given material exists`() {
|
||||
// entityManager.persist(mixMaterial)
|
||||
//
|
||||
// val exists = mixMaterialRepository.existsByMaterial(material)
|
||||
//
|
||||
// assertTrue(exists)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `existsByMaterial() returns false when no mix material with the given material exists`() {
|
||||
// entityManager.persist(anotherMixMaterial)
|
||||
//
|
||||
// val exists = mixMaterialRepository.existsByMaterial(material)
|
||||
//
|
||||
// assertFalse(exists)
|
||||
// }
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@Disabled
|
||||
@DataJpaTest
|
||||
class RecipeStepRepositoryTest @Autowired constructor(
|
||||
recipeStepRepository: RecipeStepRepository,
|
||||
entityManager: TestEntityManager
|
||||
) {
|
||||
// Nothing for now
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package dev.fyloz.trial.colorrecipesexplorer.repository
|
||||
|
||||
import dev.fyloz.trial.colorrecipesexplorer.model.NamedModel
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
|
||||
import kotlin.test.*
|
||||
|
||||
@DataJpaTest(excludeAutoConfiguration = [LiquibaseAutoConfiguration::class])
|
||||
abstract class AbstractNamedJpaRepositoryTest<E : NamedModel, R : NamedJpaRepository<E>>(
|
||||
protected val repository: R,
|
||||
protected val entityManager: TestEntityManager
|
||||
) {
|
||||
protected abstract fun entity(name: String = "entity"): E
|
||||
|
||||
@Test
|
||||
fun `existsByName() returns true when an entity with the given name exists`() {
|
||||
val entity = entity()
|
||||
entityManager.persist(entity)
|
||||
|
||||
val found = repository.existsByName(entity.name)
|
||||
|
||||
assertTrue(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `existsByName() returns false when no entity with the given name exist`() {
|
||||
val entity = entity()
|
||||
val anotherEntity = entity("another entity")
|
||||
entityManager.persist(anotherEntity)
|
||||
|
||||
val found = repository.existsByName(entity.name)
|
||||
|
||||
assertFalse(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByName() returns the entity with the given name`() {
|
||||
val entity = entity()
|
||||
entityManager.persist(entity)
|
||||
|
||||
val found = repository.findByName(entity.name)
|
||||
|
||||
assertNotNull(found)
|
||||
assertEquals(entity.name, found.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `findByName() returns null when no entity with the given name exists`() {
|
||||
val entity = entity()
|
||||
val anotherEntity = entity("another entity")
|
||||
entityManager.persist(anotherEntity)
|
||||
|
||||
val found = repository.findByName(entity.name)
|
||||
|
||||
assertNull(found)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteByName() removes the entity with the given name`() {
|
||||
val entity = entity()
|
||||
entityManager.persist(entity)
|
||||
|
||||
repository.deleteByName(entity.name)
|
||||
|
||||
assertFalse(repository.existsByName(entity.name))
|
||||
}
|
||||
}
|
|
@ -293,9 +293,6 @@ abstract class AbstractExternalModelServiceTest<E : Model, N : EntityDto<E>, U :
|
|||
reset(entitySaveDto, entityUpdateDto)
|
||||
super.afterEach()
|
||||
}
|
||||
|
||||
override fun `save(dto) calls and returns save() with the created entity`() =
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
abstract class AbstractExternalNamedModelServiceTest<E : NamedModel, N : EntityDto<E>, U : EntityDto<E>, S : ExternalNamedModelService<E, N, U, *>, R : NamedJpaRepository<E>> :
|
||||
|
@ -308,23 +305,21 @@ abstract class AbstractExternalNamedModelServiceTest<E : NamedModel, N : EntityD
|
|||
reset(entitySaveDto, entityUpdateDto)
|
||||
super.afterEach()
|
||||
}
|
||||
|
||||
override fun `save(dto) calls and returns save() with the created entity`() =
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
fun <E, N : EntityDto<E>> withBaseSaveDtoTest(
|
||||
fun <E : Model, N : EntityDto<E>> withBaseSaveDtoTest(
|
||||
entity: E,
|
||||
entitySaveDto: N,
|
||||
service: ExternalService<E, N, *, *>,
|
||||
saveMockMatcher: () -> E = { entity },
|
||||
op: () -> Unit = {}
|
||||
) {
|
||||
doReturn(entity).whenever(service).save(entity)
|
||||
doReturn(entity).whenever(service).save(saveMockMatcher())
|
||||
doReturn(entity).whenever(entitySaveDto).toEntity()
|
||||
|
||||
val found = service.save(entitySaveDto)
|
||||
|
||||
verify(service).save(entity)
|
||||
verify(service).save(saveMockMatcher())
|
||||
assertEquals(entity, found)
|
||||
|
||||
op()
|
||||
|
|
|
@ -154,15 +154,21 @@ class EmployeeServiceTest :
|
|||
assertEquals("${entity.firstName} ${entity.lastName}", exception.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service, {argThat {
|
||||
this.id == entity.id && this.firstName == entity.firstName && this.lastName == entity.lastName
|
||||
}})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save(dto) calls and returns save() with the created employee`() {
|
||||
whenever(entitySaveDto.toEntity()).doReturn(entitySaveDtoEmployee)
|
||||
doReturn(entitySaveDtoEmployee).whenever(service).save(entitySaveDtoEmployee)
|
||||
doReturn(entitySaveDtoEmployee).whenever(service).save(any<Employee>())
|
||||
|
||||
val found = service.save(entitySaveDto)
|
||||
|
||||
verify(service).save(entitySaveDtoEmployee)
|
||||
verify(service).save(argThat<Employee> { this.id == entity.id && this.firstName == entity.firstName && this.lastName == entity.lastName })
|
||||
assertEquals(entitySaveDtoEmployee, found)
|
||||
}
|
||||
|
||||
|
@ -306,7 +312,7 @@ class EmployeeGroupServiceTest :
|
|||
verify(service).update(group)
|
||||
verify(employeeService).update(employee)
|
||||
|
||||
assertTrue(group.employees.contains(employee))
|
||||
assertTrue(group.employees.any { it.id == employee.id })
|
||||
assertEquals(group, employee.group)
|
||||
}
|
||||
|
||||
|
@ -337,7 +343,7 @@ class EmployeeGroupServiceTest :
|
|||
verify(service).update(group)
|
||||
verify(employeeService, times(2)).update(employee)
|
||||
|
||||
assertTrue(group.employees.contains(employee))
|
||||
assertTrue(group.employees.any { it.id == employee.id })
|
||||
assertEquals(group, employee.group)
|
||||
}
|
||||
|
||||
|
@ -360,13 +366,13 @@ class EmployeeGroupServiceTest :
|
|||
val group = employeeGroup(employees = mutableSetOf(employee))
|
||||
employee.group = group
|
||||
|
||||
whenever(employeeService.update(employee)).doReturn(employee)
|
||||
whenever(employeeService.update(any<Employee>())).doReturn(employee)
|
||||
doReturn(group).whenever(service).update(group)
|
||||
|
||||
service.removeEmployeeFromGroup(group, employee)
|
||||
|
||||
verify(service).update(group)
|
||||
verify(employeeService).update(employee)
|
||||
verify(employeeService).update(argThat<Employee> { this.group == null })
|
||||
|
||||
assertFalse(group.employees.contains(employee))
|
||||
assertNull(employee.group)
|
||||
|
@ -396,6 +402,13 @@ class EmployeeGroupServiceTest :
|
|||
verify(employeeService, times(0)).update(employee)
|
||||
}
|
||||
|
||||
// save()
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
// update()
|
||||
|
||||
@Test
|
||||
|
|
|
@ -46,6 +46,13 @@ class CompanyServiceTest :
|
|||
assertFalse(found)
|
||||
}
|
||||
|
||||
// save()
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
// update()
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
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 org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class InventoryServiceTest {
|
||||
private val materialService: MaterialService = mock()
|
||||
private val service = spy(InventoryServiceImpl(materialService))
|
||||
|
||||
@AfterEach
|
||||
fun afterEach() {
|
||||
reset(materialService, service)
|
||||
}
|
||||
|
||||
// add()
|
||||
|
||||
@Test
|
||||
fun `add(materialQuantities) calls add() for each MaterialQuantityDto`() {
|
||||
val materialQuantities = listOf(
|
||||
materialQuantityDto(materialId = 1, quantity = 1234f),
|
||||
materialQuantityDto(materialId = 2, quantity = 2345f),
|
||||
materialQuantityDto(materialId = 3, quantity = 3456f),
|
||||
materialQuantityDto(materialId = 4, quantity = 4567f)
|
||||
)
|
||||
|
||||
service.add(materialQuantities)
|
||||
|
||||
materialQuantities.forEach {
|
||||
verify(service).add(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `add(materialQuantity) updates material's quantity`() {
|
||||
withGivenQuantities(0f, 1000f) {
|
||||
service.add(it)
|
||||
|
||||
verify(materialService).updateQuantity(
|
||||
argThat { this.id == it.material },
|
||||
eq(it.quantity)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// deduct()
|
||||
|
||||
@Test
|
||||
fun `deduct(materialQuantities) calls deduct() for each MaterialQuantityDto`() {
|
||||
val materialQuantities = listOf(
|
||||
materialQuantityDto(materialId = 1, quantity = 1234f),
|
||||
materialQuantityDto(materialId = 2, quantity = 2345f),
|
||||
materialQuantityDto(materialId = 3, quantity = 3456f),
|
||||
materialQuantityDto(materialId = 4, quantity = 4567f)
|
||||
)
|
||||
|
||||
service.deduct(materialQuantities)
|
||||
|
||||
materialQuantities.forEach {
|
||||
verify(service).deduct(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deduct(materialQuantities) throws LowQuantitiesException when there is not enough inventory of a given material`() {
|
||||
val materialQuantities = listOf(
|
||||
materialQuantityDto(materialId = 1, quantity = 1234f),
|
||||
materialQuantityDto(materialId = 2, quantity = 2345f),
|
||||
materialQuantityDto(materialId = 3, quantity = 3456f),
|
||||
materialQuantityDto(materialId = 4, quantity = 4567f)
|
||||
)
|
||||
val inventoryQuantity = 3000f
|
||||
val lowQuantities = materialQuantities.filter { it.quantity > inventoryQuantity }
|
||||
|
||||
materialQuantities.forEach {
|
||||
withGivenQuantities(inventoryQuantity, it)
|
||||
}
|
||||
|
||||
val exception = assertThrows<LowQuantitiesException> { service.deduct(materialQuantities) }
|
||||
assertTrue { exception.materialQuantities.containsAll(lowQuantities) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deduct(materialQuantity) updates material's quantity`() {
|
||||
withGivenQuantities(5000f, 1000f) {
|
||||
service.deduct(it)
|
||||
|
||||
verify(materialService).updateQuantity(
|
||||
argThat { this.id == it.material },
|
||||
eq(-it.quantity)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deduct(materialQuantity) throws LowQuantityException when there is not enough inventory of the given material`() {
|
||||
withGivenQuantities(0f, 1000f) {
|
||||
val exception = assertThrows<LowQuantityException> { service.deduct(it) }
|
||||
assertEquals(it, exception.materialQuantity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun withGivenQuantities(
|
||||
inventory: Float,
|
||||
deductedQuantity: Float,
|
||||
materialId: Long = 0L,
|
||||
test: (MaterialQuantityDto) -> Unit = {}
|
||||
) {
|
||||
val materialQuantity = materialQuantityDto(materialId = materialId, quantity = deductedQuantity)
|
||||
|
||||
withGivenQuantities(inventory, materialQuantity, test)
|
||||
}
|
||||
|
||||
private fun withGivenQuantities(
|
||||
inventory: Float,
|
||||
materialQuantity: MaterialQuantityDto,
|
||||
test: (MaterialQuantityDto) -> Unit = {}
|
||||
) {
|
||||
val material = material(id = materialQuantity.material, inventoryQuantity = inventory)
|
||||
|
||||
whenever(materialService.getById(material.id!!)).doReturn(material)
|
||||
|
||||
test(materialQuantity)
|
||||
}
|
||||
}
|
|
@ -103,6 +103,11 @@ class MaterialServiceTest :
|
|||
assertEquals(entity.name, exception.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `save(dto) calls simdutService_write() with the saved entity`() {
|
||||
val mockMultipartFile = spy(MockMultipartFile("simdut", byteArrayOf()))
|
||||
|
@ -131,6 +136,19 @@ class MaterialServiceTest :
|
|||
assertEquals(material.name, exception.value)
|
||||
}
|
||||
|
||||
// updateQuantity()
|
||||
|
||||
@Test
|
||||
fun `updateQuantity() updates the quantity of the the given material in the repository`() {
|
||||
val material = material(id = 0L, inventoryQuantity = 4321f)
|
||||
val quantity = 1234f
|
||||
val totalQuantity = material.inventoryQuantity + quantity
|
||||
|
||||
service.updateQuantity(material, quantity)
|
||||
|
||||
verify(repository).updateInventoryQuantityById(material.id!!, totalQuantity)
|
||||
}
|
||||
|
||||
// getAllForMixCreation()
|
||||
|
||||
@Test
|
||||
|
@ -190,6 +208,9 @@ class MaterialServiceTest :
|
|||
verify(simdutService).update(eq(mockSimdutFile), any())
|
||||
}
|
||||
|
||||
// updateQuantity()
|
||||
|
||||
|
||||
// delete()
|
||||
|
||||
override fun `deleteById() deletes the entity with the given id in the repository`() {
|
||||
|
|
|
@ -96,6 +96,13 @@ class MaterialTypeServiceTest :
|
|||
assertTrue(found.contains(anotherEntity))
|
||||
}
|
||||
|
||||
// save()
|
||||
|
||||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
}
|
||||
|
||||
// saveMaterialType()
|
||||
|
||||
@Test
|
||||
|
|
|
@ -50,28 +50,30 @@ class MixServiceTest : AbstractExternalModelServiceTest<Mix, MixSaveDto, MixUpda
|
|||
val mix = mix(recipe = recipe, mixType = mixType)
|
||||
val mixWithId = mix(id = 0L, recipe = recipe, mixType = mixType)
|
||||
val mixMaterials = listOf(mixMaterial(mix = mixWithId, material = material(id = 1L), quantity = 1000f))
|
||||
val mixWithMaterials = mix.apply { this.mixMaterials.addAll(mixMaterials) }
|
||||
|
||||
whenever(recipeService.getById(recipe.id!!)).doReturn(recipe)
|
||||
whenever(materialTypeService.getById(materialType.id!!)).doReturn(materialType)
|
||||
whenever(mixMaterialService.createFromMap(mixWithId, entitySaveDto.mixMaterials!!)).doReturn(mixMaterials)
|
||||
whenever(mixTypeService.getOrCreateForNameAndMaterialType(mixType.name, mixType.material.materialType!!)).doReturn(
|
||||
mixType
|
||||
)
|
||||
whenever(
|
||||
mixTypeService.getOrCreateForNameAndMaterialType(
|
||||
mixType.name,
|
||||
mixType.material.materialType!!
|
||||
)
|
||||
).doReturn(mixType)
|
||||
doReturn(true).whenever(service).existsById(mixWithId.id!!)
|
||||
doReturn(mixWithId).whenever(service).save(mix)
|
||||
doReturn(mixWithMaterials).whenever(service).update(mixWithMaterials)
|
||||
doReturn(mixWithId).whenever(service).save(any<Mix>())
|
||||
doReturn(mixWithId).whenever(service).update(any<Mix>())
|
||||
|
||||
val found = service.save(entitySaveDto)
|
||||
|
||||
verify(service).save(mix)
|
||||
verify(service).update(mixWithMaterials)
|
||||
verify(recipeService).addMix(recipe, mix)
|
||||
verify(service).save(argThat<Mix> { this.recipe == mix.recipe })
|
||||
verify(service).update(argThat<Mix> { this.id == mixWithId.id && this.recipe == mixWithId.recipe && this.mixMaterials == mixMaterials })
|
||||
verify(recipeService).addMix(recipe, mixWithId)
|
||||
|
||||
// Verify if this method is called instead of the MixType's constructor, which does not check if the name is already taken by a material.
|
||||
verify(mixTypeService).getOrCreateForNameAndMaterialType(mixType.name, mixType.material.materialType!!)
|
||||
|
||||
assertEquals(mixWithMaterials, found)
|
||||
assertEquals(mixWithId, found)
|
||||
}
|
||||
|
||||
// update()
|
||||
|
|
|
@ -73,7 +73,7 @@ class RecipeServiceTest :
|
|||
@Test
|
||||
override fun `save(dto) calls and returns save() with the created entity`() {
|
||||
whenever(companyService.getById(company.id!!)).doReturn(company)
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service)
|
||||
withBaseSaveDtoTest(entity, entitySaveDto, service, { argThat { this.id == null && this.color == color } })
|
||||
}
|
||||
|
||||
// update()
|
||||
|
@ -99,12 +99,16 @@ class RecipeServiceTest :
|
|||
val recipe = recipe(id = 0L, steps = originalSteps)
|
||||
val dto = spy(recipeUpdateDto(id = recipe.id!!, steps = updatedSteps))
|
||||
|
||||
withBaseUpdateDtoTest(recipe, dto, service, { any() }) {
|
||||
verify(stepService).delete(argThat { this in deletedSteps })
|
||||
doAnswer { it.arguments[0] }.whenever(service).update(any<Recipe>())
|
||||
doReturn(recipe).whenever(dto).toEntity()
|
||||
doReturn(recipe).whenever(service).getById(recipe.id!!)
|
||||
doReturn(true).whenever(service).existsById(recipe.id!!)
|
||||
|
||||
assertTrue {
|
||||
this.steps.containsAll(updatedSteps)
|
||||
}
|
||||
val found = service.update(dto)
|
||||
|
||||
verify(stepService).delete(argThat { this in deletedSteps })
|
||||
updatedSteps.forEach {
|
||||
assertTrue { found.steps.any { step -> step.message == it.message } }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue