Tout fonctionne!
This commit is contained in:
parent
64829f74cb
commit
ef27e57f47
|
@ -1,6 +1,8 @@
|
|||
package dev.fyloz.colorrecipesexplorer.config
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.MaterialTypeProperties
|
||||
import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES
|
||||
import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
||||
import org.springframework.context.ApplicationListener
|
||||
|
@ -10,10 +12,13 @@ import org.springframework.core.annotation.Order
|
|||
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
class InitialDataLoader(
|
||||
private val materialTypeService: MaterialTypeService,
|
||||
private val materialTypeProperties: MaterialTypeProperties
|
||||
class ApplicationReadyListener(
|
||||
private val materialTypeService: MaterialTypeService,
|
||||
private val materialTypeProperties: MaterialTypeProperties,
|
||||
private val creProperties: CreProperties
|
||||
) : ApplicationListener<ApplicationReadyEvent> {
|
||||
override fun onApplicationEvent(event: ApplicationReadyEvent) =
|
||||
materialTypeService.saveSystemTypes(materialTypeProperties.systemTypes)
|
||||
override fun onApplicationEvent(event: ApplicationReadyEvent) {
|
||||
materialTypeService.saveSystemTypes(materialTypeProperties.systemTypes)
|
||||
CRE_PROPERTIES = creProperties
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
package dev.fyloz.colorrecipesexplorer.model
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank
|
||||
|
@ -25,139 +23,143 @@ private const val EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE = "Le mot de passe doit co
|
|||
@Entity
|
||||
@Table(name = "employee")
|
||||
data class Employee(
|
||||
@Id
|
||||
override val id: Long,
|
||||
@Id
|
||||
override val id: Long,
|
||||
|
||||
@Column(name = "first_name")
|
||||
val firstName: String = "",
|
||||
@Column(name = "first_name")
|
||||
val firstName: String = "",
|
||||
|
||||
@Column(name = "last_name")
|
||||
val lastName: String = "",
|
||||
@Column(name = "last_name")
|
||||
val lastName: String = "",
|
||||
|
||||
@JsonIgnore
|
||||
val password: String = "",
|
||||
val password: String = "",
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "default_group_user")
|
||||
val isDefaultGroupUser: Boolean = false,
|
||||
@Column(name = "default_group_user")
|
||||
val isDefaultGroupUser: Boolean = false,
|
||||
|
||||
@JsonIgnore
|
||||
@Column(name = "system_user")
|
||||
val isSystemUser: Boolean = false,
|
||||
@Column(name = "system_user")
|
||||
val isSystemUser: Boolean = false,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "group_id")
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var group: EmployeeGroup? = null,
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "group_id")
|
||||
@Fetch(FetchMode.SELECT)
|
||||
var group: EmployeeGroup? = null,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")])
|
||||
@Column(name = "permission")
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@get:JsonProperty("explicitPermissions")
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@CollectionTable(name = "employee_permission", joinColumns = [JoinColumn(name = "employee_id")])
|
||||
@Column(name = "permission")
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
|
||||
@Column(name = "last_login_time")
|
||||
var lastLoginTime: LocalDateTime? = null
|
||||
@Column(name = "last_login_time")
|
||||
var lastLoginTime: LocalDateTime? = null
|
||||
) : Model {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
get() = permissions
|
||||
.flatMap { it.flat() }
|
||||
.filter { !it.deprecated }
|
||||
.toMutableSet()
|
||||
.apply {
|
||||
if (group != null) this.addAll(group!!.flatPermissions)
|
||||
}
|
||||
.flatMap { it.flat() }
|
||||
.filter { !it.deprecated }
|
||||
.toMutableSet()
|
||||
.apply {
|
||||
if (group != null) this.addAll(group!!.flatPermissions)
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val authorities: Set<GrantedAuthority>
|
||||
get() = flatPermissions.map { it.toAuthority() }.toMutableSet()
|
||||
}
|
||||
|
||||
/** DTO for creating employees. Allows a [password] a [groupId]. */
|
||||
open class EmployeeSaveDto(
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String,
|
||||
@field:NotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String,
|
||||
@field:NotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String,
|
||||
|
||||
@field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE)
|
||||
@field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE)
|
||||
val password: String,
|
||||
@field:NotBlank(message = EMPLOYEE_PASSWORD_EMPTY_MESSAGE)
|
||||
@field:Size(min = 8, message = EMPLOYEE_PASSWORD_TOO_SHORT_MESSAGE)
|
||||
val password: String,
|
||||
|
||||
val groupId: Long?,
|
||||
val groupId: Long?,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf()
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf()
|
||||
) : EntityDto<Employee>
|
||||
|
||||
open class EmployeeUpdateDto(
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = EMPLOYEE_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String?,
|
||||
@field:NullOrNotBlank(message = EMPLOYEE_FIRST_NAME_EMPTY_MESSAGE)
|
||||
val firstName: String?,
|
||||
|
||||
@field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String?,
|
||||
@field:NullOrNotBlank(message = EMPLOYEE_LAST_NAME_EMPTY_MESSAGE)
|
||||
val lastName: String?,
|
||||
|
||||
val groupId: Long?,
|
||||
val groupId: Long?,
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: Set<EmployeePermission>?
|
||||
@Enumerated(EnumType.STRING)
|
||||
val permissions: Set<EmployeePermission>?
|
||||
) : EntityDto<Employee>
|
||||
|
||||
data class EmployeeOutputDto(
|
||||
override val id: Long,
|
||||
val firstName: String,
|
||||
val lastName: String,
|
||||
val group: EmployeeGroup?,
|
||||
val permissions: Set<EmployeePermission>,
|
||||
val explicitPermissions: Set<EmployeePermission>,
|
||||
val lastLoginTime: LocalDateTime?
|
||||
) : Model
|
||||
|
||||
data class EmployeeLoginRequest(val id: Long, val password: String)
|
||||
|
||||
// ==== DSL ====
|
||||
fun employee(
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
isDefaultGroupUser: Boolean = false,
|
||||
isSystemUser: Boolean = false,
|
||||
group: EmployeeGroup? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
lastLoginTime: LocalDateTime? = null,
|
||||
op: Employee.() -> Unit = {}
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
isDefaultGroupUser: Boolean = false,
|
||||
isSystemUser: Boolean = false,
|
||||
group: EmployeeGroup? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
lastLoginTime: LocalDateTime? = null,
|
||||
op: Employee.() -> Unit = {}
|
||||
) = Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
password,
|
||||
isDefaultGroupUser,
|
||||
isSystemUser,
|
||||
group,
|
||||
permissions,
|
||||
lastLoginTime
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
password,
|
||||
isDefaultGroupUser,
|
||||
isSystemUser,
|
||||
group,
|
||||
permissions,
|
||||
lastLoginTime
|
||||
).apply(op)
|
||||
|
||||
fun employeeSaveDto(
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeSaveDto.() -> Unit = {}
|
||||
passwordEncoder: PasswordEncoder = BCryptPasswordEncoder(),
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
password: String = passwordEncoder.encode("password"),
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeSaveDto.() -> Unit = {}
|
||||
) = EmployeeSaveDto(id, firstName, lastName, password, groupId, permissions).apply(op)
|
||||
|
||||
fun employeeUpdateDto(
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeUpdateDto.() -> Unit = {}
|
||||
id: Long = 0L,
|
||||
firstName: String = "firstName",
|
||||
lastName: String = "lastName",
|
||||
groupId: Long? = null,
|
||||
permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
op: EmployeeUpdateDto.() -> Unit = {}
|
||||
) = EmployeeUpdateDto(id, firstName, lastName, groupId, permissions).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
|
@ -166,26 +168,26 @@ private const val EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE = "Employee already ex
|
|||
private const val EMPLOYEE_EXCEPTION_ERROR_CODE = "employee"
|
||||
|
||||
fun employeeIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"An employee with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
NotFoundException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"An employee with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
|
||||
fun employeeIdAlreadyExistsException(id: Long) =
|
||||
AlreadyExistsException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"An employee with the id $id already exists",
|
||||
id
|
||||
)
|
||||
AlreadyExistsException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"An employee with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
||||
fun employeeFullNameAlreadyExistsException(firstName: String, lastName: String) =
|
||||
AlreadyExistsException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"An employee with the name '$firstName $lastName' already exists",
|
||||
"$firstName $lastName",
|
||||
"fullName"
|
||||
)
|
||||
AlreadyExistsException(
|
||||
EMPLOYEE_EXCEPTION_ERROR_CODE,
|
||||
EMPLOYEE_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"An employee with the name '$firstName $lastName' already exists",
|
||||
"$firstName $lastName",
|
||||
"fullName"
|
||||
)
|
||||
|
|
|
@ -31,10 +31,8 @@ data class EmployeeGroup(
|
|||
@CollectionTable(name = "group_permission", joinColumns = [JoinColumn(name = "group_id")])
|
||||
@Column(name = "permission")
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@get:JsonProperty("explicitPermissions")
|
||||
val permissions: MutableSet<EmployeePermission> = mutableSetOf(),
|
||||
) : NamedModel {
|
||||
@get:JsonProperty("permissions")
|
||||
val flatPermissions: Set<EmployeePermission>
|
||||
get() = this.permissions
|
||||
.flatMap { it.flat() }
|
||||
|
@ -69,6 +67,13 @@ open class EmployeeGroupUpdateDto(
|
|||
EmployeeGroup(id, name, permissions)
|
||||
}
|
||||
|
||||
data class EmployeeGroupOutputDto(
|
||||
override val id: Long,
|
||||
val name: String,
|
||||
val permissions: Set<EmployeePermission>,
|
||||
val explicitPermissions: Set<EmployeePermission>
|
||||
): Model
|
||||
|
||||
fun employeeGroup(
|
||||
id: Long? = null,
|
||||
name: String = "name",
|
||||
|
|
|
@ -6,8 +6,11 @@ import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
|||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize
|
||||
import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES
|
||||
import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.net.URI
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.persistence.*
|
||||
import javax.validation.constraints.Min
|
||||
import javax.validation.constraints.NotBlank
|
||||
|
@ -49,6 +52,8 @@ data class Material(
|
|||
@JsonIgnore
|
||||
@Transient
|
||||
get() = "$SIMDUT_FILES_PATH/$name.pdf"
|
||||
|
||||
|
||||
}
|
||||
|
||||
open class MaterialSaveDto(
|
||||
|
@ -81,13 +86,13 @@ open class MaterialUpdateDto(
|
|||
) : EntityDto<Material>
|
||||
|
||||
data class MaterialOutputDto(
|
||||
val id: Long,
|
||||
override val id: Long,
|
||||
val name: String,
|
||||
val inventoryQuantity: Float,
|
||||
val isMixType: Boolean,
|
||||
val materialType: MaterialType,
|
||||
val simdutUrl: String?
|
||||
)
|
||||
) : Model
|
||||
|
||||
data class MaterialQuantityDto(
|
||||
@field:NotNull(message = MATERIAL_QUANTITY_MATERIAL_NULL_MESSAGE)
|
||||
|
@ -135,19 +140,6 @@ fun materialUpdateDto(
|
|||
op: MaterialUpdateDto.() -> Unit = {}
|
||||
) = MaterialUpdateDto(id, name, inventoryQuantity, materialTypeId, simdutFile).apply(op)
|
||||
|
||||
fun materialOutputDto(
|
||||
material: Material,
|
||||
simdutUrl: String?,
|
||||
op: MaterialOutputDto.() -> Unit = {}
|
||||
) = MaterialOutputDto(
|
||||
id = material.id!!,
|
||||
name = material.name,
|
||||
inventoryQuantity = material.inventoryQuantity,
|
||||
isMixType = material.isMixType,
|
||||
materialType = material.materialType!!,
|
||||
simdutUrl = simdutUrl
|
||||
).apply(op)
|
||||
|
||||
fun materialQuantityDto(
|
||||
materialId: Long,
|
||||
quantity: Float,
|
||||
|
@ -155,7 +147,8 @@ fun materialQuantityDto(
|
|||
) = MaterialQuantityDto(materialId, quantity).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
private const val MATERIAL_NOT_FOUND_EXCEPTION_TITLE = "Material not found"
|
||||
private const
|
||||
val MATERIAL_NOT_FOUND_EXCEPTION_TITLE = "Material not found"
|
||||
private const val MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE = "Material already exists"
|
||||
private const val MATERIAL_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete material"
|
||||
private const val MATERIAL_EXCEPTION_ERROR_CODE = "material"
|
||||
|
|
|
@ -22,107 +22,116 @@ private const val MIX_DEDUCT_RATION_NEGATIVE_MESSAGE = "Le ratio doit être éga
|
|||
@Entity
|
||||
@Table(name = "mix")
|
||||
data class Mix(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
|
||||
var location: String?,
|
||||
var location: String?,
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "recipe_id")
|
||||
val recipe: Recipe,
|
||||
@JsonIgnore
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "recipe_id")
|
||||
val recipe: Recipe,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "mix_type_id")
|
||||
var mixType: MixType,
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "mix_type_id")
|
||||
var mixType: MixType,
|
||||
|
||||
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(name = "mix_id")
|
||||
var mixMaterials: MutableSet<MixMaterial>,
|
||||
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
||||
@JoinColumn(name = "mix_id")
|
||||
var mixMaterials: MutableSet<MixMaterial>,
|
||||
) : Model
|
||||
|
||||
open class MixSaveDto(
|
||||
@field:NotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
val name: String,
|
||||
@field:NotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
val name: String,
|
||||
|
||||
@field:NotNull(message = MIX_RECIPE_NULL_MESSAGE)
|
||||
val recipeId: Long,
|
||||
@field:NotNull(message = MIX_RECIPE_NULL_MESSAGE)
|
||||
val recipeId: Long,
|
||||
|
||||
@field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE)
|
||||
val materialTypeId: Long,
|
||||
@field:NotNull(message = MIX_MATERIAL_TYPE_NULL_MESSAGE)
|
||||
val materialTypeId: Long,
|
||||
|
||||
val mixMaterials: Set<MixMaterialDto>?
|
||||
val mixMaterials: Set<MixMaterialDto>?
|
||||
) : EntityDto<Mix> {
|
||||
override fun toEntity(): Mix = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
open class MixUpdateDto(
|
||||
@field:NotNull(message = MIX_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
@field:NotNull(message = MIX_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
|
||||
@field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
val name: String?,
|
||||
@field:NullOrNotBlank(message = MIX_NAME_NULL_MESSAGE)
|
||||
val name: String?,
|
||||
|
||||
val materialTypeId: Long?,
|
||||
val materialTypeId: Long?,
|
||||
|
||||
var mixMaterials: Set<MixMaterialDto>?
|
||||
var mixMaterials: Set<MixMaterialDto>?
|
||||
) : EntityDto<Mix> {
|
||||
override fun toEntity(): Mix = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
data class MixDeductDto(
|
||||
@field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE)
|
||||
val id: Long,
|
||||
data class MixOutputDto(
|
||||
val id: Long,
|
||||
val location: String?,
|
||||
val mixType: MixType,
|
||||
val mixMaterials: Set<MixMaterialOutputDto>
|
||||
)
|
||||
|
||||
@field:NotNull(message = MIX_DEDUCT_RATIO_NULL_MESSAGE)
|
||||
@field:Min(value = 0, message = MIX_DEDUCT_RATION_NEGATIVE_MESSAGE)
|
||||
val ratio: Float
|
||||
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
|
||||
)
|
||||
|
||||
data class MixLocationDto(
|
||||
@field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE)
|
||||
val mixId: Long,
|
||||
@field:NotNull(message = MIX_DEDUCT_MIX_ID_NULL_MESSAGE)
|
||||
val mixId: Long,
|
||||
|
||||
val location: String?
|
||||
val location: String?
|
||||
)
|
||||
|
||||
//fun Mix.toOutput() =
|
||||
|
||||
// ==== DSL ====
|
||||
fun mix(
|
||||
id: Long? = null,
|
||||
location: String? = "location",
|
||||
recipe: Recipe = recipe(),
|
||||
mixType: MixType = mixType(),
|
||||
mixMaterials: MutableSet<MixMaterial> = mutableSetOf(),
|
||||
op: Mix.() -> Unit = {}
|
||||
id: Long? = null,
|
||||
location: String? = "location",
|
||||
recipe: Recipe = recipe(),
|
||||
mixType: MixType = mixType(),
|
||||
mixMaterials: MutableSet<MixMaterial> = mutableSetOf(),
|
||||
op: Mix.() -> Unit = {}
|
||||
) = Mix(id, location, recipe, mixType, mixMaterials).apply(op)
|
||||
|
||||
fun mixSaveDto(
|
||||
name: String = "name",
|
||||
recipeId: Long = 0L,
|
||||
materialTypeId: Long = 0L,
|
||||
mixMaterials: Set<MixMaterialDto>? = setOf(),
|
||||
op: MixSaveDto.() -> Unit = {}
|
||||
name: String = "name",
|
||||
recipeId: Long = 0L,
|
||||
materialTypeId: Long = 0L,
|
||||
mixMaterials: Set<MixMaterialDto>? = setOf(),
|
||||
op: MixSaveDto.() -> Unit = {}
|
||||
) = MixSaveDto(name, recipeId, materialTypeId, mixMaterials).apply(op)
|
||||
|
||||
fun mixUpdateDto(
|
||||
id: Long = 0L,
|
||||
name: String? = "name",
|
||||
materialTypeId: Long? = 0L,
|
||||
mixMaterials: Set<MixMaterialDto>? = setOf(),
|
||||
op: MixUpdateDto.() -> Unit = {}
|
||||
id: Long = 0L,
|
||||
name: String? = "name",
|
||||
materialTypeId: Long? = 0L,
|
||||
mixMaterials: Set<MixMaterialDto>? = setOf(),
|
||||
op: MixUpdateDto.() -> Unit = {}
|
||||
) = MixUpdateDto(id, name, materialTypeId, mixMaterials).apply(op)
|
||||
|
||||
fun mixRatio(
|
||||
id: Long = 0L,
|
||||
ratio: Float = 1f,
|
||||
op: MixDeductDto.() -> Unit = {}
|
||||
id: Long = 0L,
|
||||
ratio: Float = 1f,
|
||||
op: MixDeductDto.() -> Unit = {}
|
||||
) = MixDeductDto(id, ratio).apply(op)
|
||||
|
||||
fun mixLocationDto(
|
||||
mixId: Long = 0L,
|
||||
location: String? = "location",
|
||||
op: MixLocationDto.() -> Unit = {}
|
||||
mixId: Long = 0L,
|
||||
location: String? = "location",
|
||||
op: MixLocationDto.() -> Unit = {}
|
||||
) = MixLocationDto(mixId, location).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
|
@ -132,24 +141,24 @@ private const val MIX_CANNOT_DELETE_EXCEPTION_TITLE = "Cannot delete mix"
|
|||
private const val MIX_EXCEPTION_ERROR_CODE = "mix"
|
||||
|
||||
fun mixIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
NotFoundException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
|
||||
fun mixIdAlreadyExistsException(id: Long) =
|
||||
AlreadyExistsException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix with the id $id already exists",
|
||||
id
|
||||
)
|
||||
AlreadyExistsException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
||||
fun cannotDeleteMixException(mix: Mix) =
|
||||
CannotDeleteException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_CANNOT_DELETE_EXCEPTION_TITLE,
|
||||
"Cannot delete the mix ${mix.mixType.name} because one or more mixes depends on it"
|
||||
)
|
||||
CannotDeleteException(
|
||||
MIX_EXCEPTION_ERROR_CODE,
|
||||
MIX_CANNOT_DELETE_EXCEPTION_TITLE,
|
||||
"Cannot delete the mix ${mix.mixType.name} because one or more mixes depends on it"
|
||||
)
|
||||
|
|
|
@ -13,44 +13,51 @@ private const val MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE = "La quantité ne
|
|||
@Entity
|
||||
@Table(name = "mix_material")
|
||||
data class MixMaterial(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
override val id: Long?,
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "material_id")
|
||||
val material: Material,
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "material_id")
|
||||
val material: Material,
|
||||
|
||||
var quantity: Float,
|
||||
var quantity: Float,
|
||||
|
||||
var position: Int
|
||||
var position: Int
|
||||
) : Model
|
||||
|
||||
data class MixMaterialOutputDto(
|
||||
val id: Long,
|
||||
val material: MaterialOutputDto,
|
||||
val quantity: Float,
|
||||
val position: Int
|
||||
)
|
||||
|
||||
data class MixMaterialDto(
|
||||
@field:NotNull(message = MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE)
|
||||
val materialId: Long,
|
||||
@field:NotNull(message = MIX_MATERIAL_DTO_MATERIAL_ID_NULL_MESSAGE)
|
||||
val materialId: Long,
|
||||
|
||||
@field:NotNull(message = MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE)
|
||||
@field:Min(value = 0, message = MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE)
|
||||
val quantity: Float,
|
||||
@field:NotNull(message = MIX_MATERIAL_DTO_QUANTITY_NULL_MESSAGE)
|
||||
@field:Min(value = 0, message = MIX_MATERIAL_DTO_QUANTITY_NEGATIVE_MESSAGE)
|
||||
val quantity: Float,
|
||||
|
||||
val position: Int
|
||||
val position: Int
|
||||
)
|
||||
|
||||
// ==== DSL ====
|
||||
fun mixMaterial(
|
||||
id: Long? = null,
|
||||
material: Material = material(),
|
||||
quantity: Float = 0f,
|
||||
position: Int = 0,
|
||||
op: MixMaterial.() -> Unit = {}
|
||||
id: Long? = null,
|
||||
material: Material = material(),
|
||||
quantity: Float = 0f,
|
||||
position: Int = 0,
|
||||
op: MixMaterial.() -> Unit = {}
|
||||
) = MixMaterial(id, material, quantity, position).apply(op)
|
||||
|
||||
fun mixMaterialDto(
|
||||
materialId: Long = 0L,
|
||||
quantity: Float = 0f,
|
||||
position: Int = 0,
|
||||
op: MixMaterialDto.() -> Unit = {}
|
||||
materialId: Long = 0L,
|
||||
quantity: Float = 0f,
|
||||
position: Int = 0,
|
||||
op: MixMaterialDto.() -> Unit = {}
|
||||
) = MixMaterialDto(materialId, quantity, position).apply(op)
|
||||
|
||||
// ==== Exceptions ====
|
||||
|
@ -59,17 +66,17 @@ private const val MIX_MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE = "Mix material al
|
|||
private const val MIX_MATERIAL_EXCEPTION_ERROR_CODE = "mixmaterial"
|
||||
|
||||
fun mixMaterialIdNotFoundException(id: Long) =
|
||||
NotFoundException(
|
||||
MIX_MATERIAL_EXCEPTION_ERROR_CODE,
|
||||
MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix material with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
NotFoundException(
|
||||
MIX_MATERIAL_EXCEPTION_ERROR_CODE,
|
||||
MIX_MATERIAL_NOT_FOUND_EXCEPTION_TITLE,
|
||||
"A mix material with the id $id could not be found",
|
||||
id
|
||||
)
|
||||
|
||||
fun mixMaterialIdAlreadyExistsException(id: Long) =
|
||||
AlreadyExistsException(
|
||||
MIX_MATERIAL_EXCEPTION_ERROR_CODE,
|
||||
MIX_MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix material with the id $id already exists",
|
||||
id
|
||||
)
|
||||
AlreadyExistsException(
|
||||
MIX_MATERIAL_EXCEPTION_ERROR_CODE,
|
||||
MIX_MATERIAL_ALREADY_EXISTS_EXCEPTION_TITLE,
|
||||
"A mix material with the id $id already exists",
|
||||
id
|
||||
)
|
||||
|
|
|
@ -3,10 +3,12 @@ package dev.fyloz.colorrecipesexplorer.model
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.NullOrNotBlank
|
||||
import dev.fyloz.colorrecipesexplorer.model.validation.NullOrSize
|
||||
import org.springframework.http.HttpStatus
|
||||
import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES
|
||||
import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.time.LocalDate
|
||||
import javax.persistence.*
|
||||
import javax.validation.constraints.*
|
||||
|
@ -76,6 +78,14 @@ data class Recipe(
|
|||
|
||||
fun groupInformationForGroup(groupId: Long) =
|
||||
groupsInformation.firstOrNull { it.group.id == groupId }
|
||||
|
||||
fun imageUrl(name: String) =
|
||||
"${CRE_PROPERTIES.deploymentUrl}$FILE_CONTROLLER_PATH?path=${
|
||||
URLEncoder.encode(
|
||||
"${this.imagesDirectoryPath}/$name",
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
}"
|
||||
}
|
||||
|
||||
open class RecipeSaveDto(
|
||||
|
@ -143,7 +153,7 @@ open class RecipeUpdateDto(
|
|||
) : EntityDto<Recipe>
|
||||
|
||||
data class RecipeOutputDto(
|
||||
val id: Long,
|
||||
override val id: Long,
|
||||
val name: String,
|
||||
val description: String,
|
||||
val color: String,
|
||||
|
@ -152,10 +162,10 @@ data class RecipeOutputDto(
|
|||
val approbationDate: LocalDate?,
|
||||
val remark: String?,
|
||||
val company: Company,
|
||||
val mixes: Set<Mix>,
|
||||
val mixes: Set<MixOutputDto>,
|
||||
val groupsInformation: Set<RecipeGroupInformation>,
|
||||
val imagesUrls: Set<String>
|
||||
)
|
||||
var imagesUrls: Set<String>
|
||||
) : Model
|
||||
|
||||
@Entity
|
||||
@Table(name = "recipe_group_information")
|
||||
|
@ -252,25 +262,6 @@ fun recipeUpdateDto(
|
|||
op: RecipeUpdateDto.() -> Unit = {}
|
||||
) = RecipeUpdateDto(id, name, description, color, gloss, sample, approbationDate, remark, steps).apply(op)
|
||||
|
||||
fun recipeOutputDto(
|
||||
recipe: Recipe,
|
||||
imagesUrls: Set<String>,
|
||||
op: RecipeOutputDto.() -> Unit = {}
|
||||
) = RecipeOutputDto(
|
||||
recipe.id!!,
|
||||
recipe.name,
|
||||
recipe.description,
|
||||
recipe.color,
|
||||
recipe.gloss,
|
||||
recipe.sample,
|
||||
recipe.approbationDate,
|
||||
recipe.remark,
|
||||
recipe.company,
|
||||
recipe.mixes.toSet(),
|
||||
recipe.groupsInformation,
|
||||
imagesUrls
|
||||
).apply(op)
|
||||
|
||||
fun recipeGroupInformation(
|
||||
id: Long? = null,
|
||||
group: EmployeeGroup = employeeGroup(),
|
||||
|
|
|
@ -4,7 +4,7 @@ import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeEditUsers
|
|||
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeRemoveUsers
|
||||
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewUsers
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.service.EmployeeGroupServiceImpl
|
||||
import dev.fyloz.colorrecipesexplorer.service.EmployeeGroupService
|
||||
import dev.fyloz.colorrecipesexplorer.service.EmployeeService
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
|
@ -23,52 +23,56 @@ class EmployeeController(private val employeeService: EmployeeService) {
|
|||
@GetMapping
|
||||
@PreAuthorizeViewUsers
|
||||
fun getAll() =
|
||||
ok(employeeService.getAll())
|
||||
ok(employeeService.getAllForOutput())
|
||||
|
||||
@GetMapping("{id}")
|
||||
@PreAuthorizeViewUsers
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(employeeService.getById(id))
|
||||
ok(employeeService.getByIdForOutput(id))
|
||||
|
||||
@GetMapping("current")
|
||||
fun getCurrent(loggedInEmployee: Principal?) =
|
||||
if (loggedInEmployee != null)
|
||||
ok(
|
||||
employeeService.getById(
|
||||
loggedInEmployee.name.toLong(),
|
||||
ignoreDefaultGroupUsers = false,
|
||||
ignoreSystemUsers = false
|
||||
if (loggedInEmployee != null)
|
||||
ok(
|
||||
with(employeeService) {
|
||||
getById(
|
||||
loggedInEmployee.name.toLong(),
|
||||
ignoreDefaultGroupUsers = false,
|
||||
ignoreSystemUsers = false
|
||||
).toOutput()
|
||||
}
|
||||
)
|
||||
)
|
||||
else
|
||||
forbidden()
|
||||
else
|
||||
forbidden()
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorizeEditUsers
|
||||
fun save(@Valid @RequestBody employee: EmployeeSaveDto) =
|
||||
created<Employee>(EMPLOYEE_CONTROLLER_PATH) {
|
||||
employeeService.save(employee)
|
||||
}
|
||||
created<EmployeeOutputDto>(EMPLOYEE_CONTROLLER_PATH) {
|
||||
with(employeeService) {
|
||||
save(employee).toOutput()
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@PreAuthorizeEditUsers
|
||||
fun update(@Valid @RequestBody employee: EmployeeUpdateDto) =
|
||||
noContent {
|
||||
employeeService.update(employee)
|
||||
}
|
||||
noContent {
|
||||
employeeService.update(employee)
|
||||
}
|
||||
|
||||
@PutMapping("{id}/password", consumes = [MediaType.TEXT_PLAIN_VALUE])
|
||||
@PreAuthorizeEditUsers
|
||||
fun updatePassword(@PathVariable id: Long, @RequestBody password: String) =
|
||||
noContent {
|
||||
employeeService.updatePassword(id, password)
|
||||
}
|
||||
noContent {
|
||||
employeeService.updatePassword(id, password)
|
||||
}
|
||||
|
||||
@PutMapping("{employeeId}/permissions/{permission}")
|
||||
@PreAuthorizeEditUsers
|
||||
fun addPermission(
|
||||
@PathVariable employeeId: Long,
|
||||
@PathVariable permission: EmployeePermission
|
||||
@PathVariable employeeId: Long,
|
||||
@PathVariable permission: EmployeePermission
|
||||
) = noContent {
|
||||
employeeService.addPermission(employeeId, permission)
|
||||
}
|
||||
|
@ -76,8 +80,8 @@ class EmployeeController(private val employeeService: EmployeeService) {
|
|||
@DeleteMapping("{employeeId}/permissions/{permission}")
|
||||
@PreAuthorizeEditUsers
|
||||
fun removePermission(
|
||||
@PathVariable employeeId: Long,
|
||||
@PathVariable permission: EmployeePermission
|
||||
@PathVariable employeeId: Long,
|
||||
@PathVariable permission: EmployeePermission
|
||||
) = noContent {
|
||||
employeeService.removePermission(employeeId, permission)
|
||||
}
|
||||
|
@ -85,59 +89,69 @@ class EmployeeController(private val employeeService: EmployeeService) {
|
|||
@DeleteMapping("{id}")
|
||||
@PreAuthorizeRemoveUsers
|
||||
fun deleteById(@PathVariable id: Long) =
|
||||
employeeService.deleteById(id)
|
||||
employeeService.deleteById(id)
|
||||
}
|
||||
|
||||
@RestController
|
||||
@RequestMapping(EMPLOYEE_GROUP_CONTROLLER_PATH)
|
||||
class GroupsController(private val groupService: EmployeeGroupServiceImpl) {
|
||||
class GroupsController(
|
||||
private val groupService: EmployeeGroupService,
|
||||
private val employeeService: EmployeeService
|
||||
) {
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyAuthority('VIEW_RECIPES', 'VIEW_USERS')")
|
||||
fun getAll() =
|
||||
ok(groupService.getAll())
|
||||
ok(groupService.getAllForOutput())
|
||||
|
||||
@GetMapping("{id}")
|
||||
@PreAuthorizeViewUsers
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(groupService.getById(id))
|
||||
ok(groupService.getByIdForOutput(id))
|
||||
|
||||
@GetMapping("{id}/employees")
|
||||
@PreAuthorizeViewUsers
|
||||
fun getEmployeesForGroup(@PathVariable id: Long) =
|
||||
ok(groupService.getEmployeesForGroup(id))
|
||||
ok(with(employeeService) {
|
||||
groupService.getEmployeesForGroup(id)
|
||||
.map { it.toOutput() }
|
||||
})
|
||||
|
||||
@PostMapping("default/{groupId}")
|
||||
@PreAuthorizeViewUsers
|
||||
fun setDefaultGroup(@PathVariable groupId: Long, response: HttpServletResponse) =
|
||||
noContent {
|
||||
groupService.setResponseDefaultGroup(groupId, response)
|
||||
}
|
||||
noContent {
|
||||
groupService.setResponseDefaultGroup(groupId, response)
|
||||
}
|
||||
|
||||
@GetMapping("default")
|
||||
@PreAuthorizeViewUsers
|
||||
fun getRequestDefaultGroup(request: HttpServletRequest) =
|
||||
ok(groupService.getRequestDefaultGroup(request))
|
||||
ok(with(groupService) {
|
||||
getRequestDefaultGroup(request).toOutput()
|
||||
})
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorizeEditUsers
|
||||
fun save(@Valid @RequestBody group: EmployeeGroupSaveDto) =
|
||||
created<EmployeeGroup>(EMPLOYEE_GROUP_CONTROLLER_PATH) {
|
||||
groupService.save(group)
|
||||
}
|
||||
created<EmployeeGroupOutputDto>(EMPLOYEE_GROUP_CONTROLLER_PATH) {
|
||||
with(groupService) {
|
||||
save(group).toOutput()
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@PreAuthorizeEditUsers
|
||||
fun update(@Valid @RequestBody group: EmployeeGroupUpdateDto) =
|
||||
noContent {
|
||||
groupService.update(group)
|
||||
}
|
||||
noContent {
|
||||
groupService.update(group)
|
||||
}
|
||||
|
||||
@DeleteMapping("{id}")
|
||||
@PreAuthorizeRemoveUsers
|
||||
fun deleteById(@PathVariable id: Long) =
|
||||
noContent {
|
||||
groupService.deleteById(id)
|
||||
}
|
||||
noContent {
|
||||
groupService.deleteById(id)
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
|
@ -145,7 +159,7 @@ class GroupsController(private val groupService: EmployeeGroupServiceImpl) {
|
|||
class LogoutController(private val employeeService: EmployeeService) {
|
||||
@GetMapping("logout")
|
||||
fun logout(request: HttpServletRequest) =
|
||||
ok<Void> {
|
||||
employeeService.logout(request)
|
||||
}
|
||||
ok<Void> {
|
||||
employeeService.logout(request)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ private const val COMPANY_CONTROLLER_PATH = "api/company"
|
|||
class CompanyController(private val companyService: CompanyService) {
|
||||
@GetMapping
|
||||
fun getAll() =
|
||||
ok(companyService.getAll())
|
||||
ok(companyService.getAllForOutput())
|
||||
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(companyService.getById(id))
|
||||
ok(companyService.getByIdForOutput(id))
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('EDIT_COMPANIES')")
|
||||
|
|
|
@ -15,21 +15,20 @@ private const val INVENTORY_CONTROLLER_PATH = "api/inventory"
|
|||
@RestController
|
||||
@RequestMapping(INVENTORY_CONTROLLER_PATH)
|
||||
class InventoryController(
|
||||
private val inventoryService: InventoryService
|
||||
private val inventoryService: InventoryService
|
||||
) {
|
||||
@PutMapping("add")
|
||||
@PreAuthorize("hasAuthority('ADD_TO_INVENTORY')")
|
||||
fun add(@RequestBody quantities: Collection<MaterialQuantityDto>): ResponseEntity<Collection<MaterialQuantityDto>> {
|
||||
return ResponseEntity.ok(inventoryService.add(quantities))
|
||||
}
|
||||
fun add(@RequestBody quantities: Collection<MaterialQuantityDto>) =
|
||||
ok(inventoryService.add(quantities))
|
||||
|
||||
@PutMapping("deduct")
|
||||
@PreAuthorize("hasAuthority('DEDUCT_FROM_INVENTORY')")
|
||||
fun deduct(@RequestBody quantities: Collection<MaterialQuantityDto>) =
|
||||
ok(inventoryService.deduct(quantities))
|
||||
ok(inventoryService.deduct(quantities))
|
||||
|
||||
@PutMapping("deduct/mix")
|
||||
@PreAuthorize("hasAuthority('DEDUCT_FROM_INVENTORY')")
|
||||
fun deduct(@RequestBody mixRatio: MixDeductDto) =
|
||||
ok(inventoryService.deductMix(mixRatio))
|
||||
ok(inventoryService.deductMix(mixRatio))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package dev.fyloz.colorrecipesexplorer.rest
|
|||
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewCatalog
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH
|
||||
import dev.fyloz.colorrecipesexplorer.service.MaterialService
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
|
@ -11,8 +10,6 @@ import org.springframework.security.access.prepost.PreAuthorize
|
|||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.web.multipart.MultipartFile
|
||||
import java.net.URI
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.validation.Valid
|
||||
|
||||
private const val MATERIAL_CONTROLLER_PATH = "api/material"
|
||||
|
@ -21,12 +18,11 @@ private const val MATERIAL_CONTROLLER_PATH = "api/material"
|
|||
@RequestMapping(MATERIAL_CONTROLLER_PATH)
|
||||
@PreAuthorizeViewCatalog
|
||||
class MaterialController(
|
||||
private val materialService: MaterialService,
|
||||
private val creProperties: CreProperties
|
||||
private val materialService: MaterialService
|
||||
) {
|
||||
@GetMapping
|
||||
fun getAll() =
|
||||
ok(materialService.getAll())
|
||||
ok(materialService.getAllForOutput())
|
||||
|
||||
@GetMapping("notmixtype")
|
||||
fun getAllNotMixType() =
|
||||
|
@ -34,20 +30,22 @@ class MaterialController(
|
|||
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(materialService.getById(id))
|
||||
ok(materialService.getByIdForOutput(id))
|
||||
|
||||
@PostMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIALS')")
|
||||
fun save(@Valid material: MaterialSaveDto, simdutFile: MultipartFile?) =
|
||||
created {
|
||||
materialService.save(
|
||||
materialSaveDto(
|
||||
name = material.name,
|
||||
inventoryQuantity = material.inventoryQuantity,
|
||||
materialTypeId = material.materialTypeId,
|
||||
simdutFile = simdutFile
|
||||
)
|
||||
)
|
||||
created<MaterialOutputDto>(MATERIAL_CONTROLLER_PATH) {
|
||||
with(materialService) {
|
||||
save(
|
||||
materialSaveDto(
|
||||
name = material.name,
|
||||
inventoryQuantity = material.inventoryQuantity,
|
||||
materialTypeId = material.materialTypeId,
|
||||
simdutFile = simdutFile
|
||||
)
|
||||
).toOutput()
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping(consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
|
@ -79,30 +77,5 @@ class MaterialController(
|
|||
@GetMapping("mix/update/{mixId}")
|
||||
fun getAllForMixUpdate(@PathVariable mixId: Long) =
|
||||
ok(materialService.getAllForMixUpdate(mixId))
|
||||
|
||||
private fun ok(material: Material) =
|
||||
ok(material.toOutput())
|
||||
|
||||
private fun ok(materials: Collection<Material>) =
|
||||
ok(materials.map { it.toOutput() })
|
||||
|
||||
private fun created(producer: () -> Material): ResponseEntity<MaterialOutputDto> = with(producer().toOutput()) {
|
||||
ResponseEntity
|
||||
.created(URI.create("$MATERIAL_CONTROLLER_PATH/${this.id}"))
|
||||
.body(this)
|
||||
}
|
||||
|
||||
private fun Material.toOutput() = materialOutputDto(
|
||||
this,
|
||||
if (materialService.hasSimdut(this)) this.simdutUrl() else null
|
||||
)
|
||||
|
||||
private fun Material.simdutUrl() =
|
||||
"${creProperties.deploymentUrl}$FILE_CONTROLLER_PATH?path=${
|
||||
URLEncoder.encode(
|
||||
this.simdutFilePath,
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
}"
|
||||
}
|
||||
|
||||
|
|
|
@ -17,11 +17,11 @@ private const val MATERIAL_TYPE_CONTROLLER_PATH = "api/materialtype"
|
|||
class MaterialTypeController(private val materialTypeService: MaterialTypeService) {
|
||||
@GetMapping
|
||||
fun getAll() =
|
||||
ok(materialTypeService.getAll())
|
||||
ok(materialTypeService.getAllForOutput())
|
||||
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(materialTypeService.getById(id))
|
||||
ok(materialTypeService.getByIdForOutput(id))
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('EDIT_MATERIAL_TYPES')")
|
||||
|
|
|
@ -27,22 +27,23 @@ private const val MIX_CONTROLLER_PATH = "api/recipe/mix"
|
|||
@PreAuthorizeViewRecipes
|
||||
class RecipeController(
|
||||
private val recipeService: RecipeService,
|
||||
private val recipeImageService: RecipeImageService,
|
||||
private val creProperties: CreProperties
|
||||
private val recipeImageService: RecipeImageService
|
||||
) {
|
||||
@GetMapping
|
||||
fun getAll() =
|
||||
ok(recipeService.getAll())
|
||||
ok(recipeService.getAllForOutput())
|
||||
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(recipeService.getById(id))
|
||||
ok(recipeService.getByIdForOutput(id))
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorizeEditRecipes
|
||||
fun save(@Valid @RequestBody recipe: RecipeSaveDto) =
|
||||
created<Recipe>(RECIPE_CONTROLLER_PATH) {
|
||||
recipeService.save(recipe)
|
||||
created<RecipeOutputDto>(RECIPE_CONTROLLER_PATH) {
|
||||
with(recipeService) {
|
||||
save(recipe).toOutput()
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
|
@ -66,7 +67,7 @@ class RecipeController(
|
|||
recipeService.deleteById(id)
|
||||
}
|
||||
|
||||
@PutMapping("{recipeId}/image", consumes = [MediaType.APPLICATION_OCTET_STREAM_VALUE])
|
||||
@PutMapping("{recipeId}/image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||
@PreAuthorizeEditRecipes
|
||||
fun downloadImage(@PathVariable recipeId: Long, image: MultipartFile): ResponseEntity<RecipeOutputDto> {
|
||||
recipeImageService.download(image, recipeService.getById(recipeId))
|
||||
|
@ -79,28 +80,6 @@ class RecipeController(
|
|||
noContent {
|
||||
recipeImageService.delete(recipeService.getById(recipeId), name)
|
||||
}
|
||||
|
||||
private fun ok(recipe: Recipe) =
|
||||
ok(recipe.toOutput())
|
||||
|
||||
private fun ok(recipes: Collection<Recipe>) =
|
||||
ok(recipes.map { it.toOutput() })
|
||||
|
||||
private fun Recipe.toOutput() =
|
||||
recipeOutputDto(
|
||||
this,
|
||||
recipeImageService.getAllImages(this)
|
||||
.map { this.imageUrl(it) }
|
||||
.toSet()
|
||||
)
|
||||
|
||||
private fun Recipe.imageUrl(name: String) =
|
||||
"${creProperties.deploymentUrl}$FILE_CONTROLLER_PATH?path=${
|
||||
URLEncoder.encode(
|
||||
"${this.imagesDirectoryPath}/$name",
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
}"
|
||||
}
|
||||
|
||||
@RestController
|
||||
|
@ -109,7 +88,7 @@ class RecipeController(
|
|||
class MixController(private val mixService: MixService) {
|
||||
@GetMapping("{id}")
|
||||
fun getById(@PathVariable id: Long) =
|
||||
ok(mixService.getById(id))
|
||||
ok(mixService.getByIdForOutput(id))
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorizeEditRecipes
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package dev.fyloz.colorrecipesexplorer.rest
|
||||
|
||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||
import dev.fyloz.colorrecipesexplorer.model.Model
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.HttpStatus
|
||||
|
@ -7,6 +8,8 @@ import org.springframework.http.MediaType
|
|||
import org.springframework.http.ResponseEntity
|
||||
import java.net.URI
|
||||
|
||||
lateinit var CRE_PROPERTIES: CreProperties
|
||||
|
||||
/** Creates a HTTP OK [ResponseEntity] from the given [body]. */
|
||||
fun <T> ok(body: T): ResponseEntity<T> =
|
||||
ResponseEntity.ok(body)
|
||||
|
|
|
@ -20,7 +20,8 @@ import javax.servlet.http.HttpServletRequest
|
|||
import javax.servlet.http.HttpServletResponse
|
||||
import javax.transaction.Transactional
|
||||
|
||||
interface EmployeeService : ExternalModelService<Employee, EmployeeSaveDto, EmployeeUpdateDto, EmployeeRepository> {
|
||||
interface EmployeeService :
|
||||
ExternalModelService<Employee, EmployeeSaveDto, EmployeeUpdateDto, EmployeeOutputDto, EmployeeRepository> {
|
||||
/** Check if an [Employee] with the given [firstName] and [lastName] exists. */
|
||||
fun existsByFirstNameAndLastName(firstName: String, lastName: String): Boolean
|
||||
|
||||
|
@ -56,7 +57,7 @@ interface EmployeeService : ExternalModelService<Employee, EmployeeSaveDto, Empl
|
|||
}
|
||||
|
||||
interface EmployeeGroupService :
|
||||
ExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupRepository> {
|
||||
ExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupOutputDto, EmployeeGroupRepository> {
|
||||
/** Gets all the employees of the group with the given [id]. */
|
||||
fun getEmployeesForGroup(id: Long): Collection<Employee>
|
||||
|
||||
|
@ -74,50 +75,62 @@ interface EmployeeUserDetailsService : UserDetailsService {
|
|||
|
||||
@Service
|
||||
class EmployeeServiceImpl(
|
||||
employeeRepository: EmployeeRepository,
|
||||
@Lazy val groupService: EmployeeGroupService,
|
||||
@Lazy val passwordEncoder: PasswordEncoder,
|
||||
) : AbstractExternalModelService<Employee, EmployeeSaveDto, EmployeeUpdateDto, EmployeeRepository>(employeeRepository),
|
||||
EmployeeService {
|
||||
employeeRepository: EmployeeRepository,
|
||||
@Lazy val groupService: EmployeeGroupService,
|
||||
@Lazy val passwordEncoder: PasswordEncoder,
|
||||
) : AbstractExternalModelService<Employee, EmployeeSaveDto, EmployeeUpdateDto, EmployeeOutputDto, EmployeeRepository>(
|
||||
employeeRepository
|
||||
),
|
||||
EmployeeService {
|
||||
override fun idNotFoundException(id: Long) = employeeIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = employeeIdAlreadyExistsException(id)
|
||||
|
||||
override fun Employee.toOutput() = EmployeeOutputDto(
|
||||
this.id,
|
||||
this.firstName,
|
||||
this.lastName,
|
||||
this.group,
|
||||
this.flatPermissions,
|
||||
this.permissions,
|
||||
this.lastLoginTime
|
||||
)
|
||||
|
||||
override fun existsByFirstNameAndLastName(firstName: String, lastName: String): Boolean =
|
||||
repository.existsByFirstNameAndLastName(firstName, lastName)
|
||||
repository.existsByFirstNameAndLastName(firstName, lastName)
|
||||
|
||||
override fun getAll(): Collection<Employee> =
|
||||
super.getAll().filter { !it.isSystemUser && !it.isDefaultGroupUser }
|
||||
super.getAll().filter { !it.isSystemUser && !it.isDefaultGroupUser }
|
||||
|
||||
override fun getById(id: Long): Employee =
|
||||
getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
|
||||
getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
|
||||
|
||||
override fun getById(id: Long, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee =
|
||||
super.getById(id).apply {
|
||||
if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser)
|
||||
throw idNotFoundException(id)
|
||||
}
|
||||
super.getById(id).apply {
|
||||
if (ignoreSystemUsers && isSystemUser || ignoreDefaultGroupUsers && isDefaultGroupUser)
|
||||
throw idNotFoundException(id)
|
||||
}
|
||||
|
||||
override fun getByGroup(group: EmployeeGroup): Collection<Employee> =
|
||||
repository.findAllByGroup(group).filter {
|
||||
!it.isSystemUser && !it.isDefaultGroupUser
|
||||
}
|
||||
repository.findAllByGroup(group).filter {
|
||||
!it.isSystemUser && !it.isDefaultGroupUser
|
||||
}
|
||||
|
||||
override fun getDefaultGroupEmployee(group: EmployeeGroup): Employee =
|
||||
repository.findByIsDefaultGroupUserIsTrueAndGroupIs(group)
|
||||
repository.findByIsDefaultGroupUserIsTrueAndGroupIs(group)
|
||||
|
||||
override fun save(entity: EmployeeSaveDto): Employee =
|
||||
save(with(entity) {
|
||||
Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
passwordEncoder.encode(password),
|
||||
isDefaultGroupUser = false,
|
||||
isSystemUser = false,
|
||||
group = if (groupId != null) groupService.getById(groupId) else null,
|
||||
permissions = permissions
|
||||
)
|
||||
})
|
||||
save(with(entity) {
|
||||
Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
passwordEncoder.encode(password),
|
||||
isDefaultGroupUser = false,
|
||||
isSystemUser = false,
|
||||
group = if (groupId != null) groupService.getById(groupId) else null,
|
||||
permissions = permissions
|
||||
)
|
||||
})
|
||||
|
||||
override fun save(entity: Employee): Employee {
|
||||
if (existsById(entity.id))
|
||||
|
@ -129,14 +142,14 @@ class EmployeeServiceImpl(
|
|||
|
||||
override fun saveDefaultGroupEmployee(group: EmployeeGroup) {
|
||||
save(
|
||||
employee(
|
||||
id = 1000000L + group.id!!,
|
||||
firstName = group.name,
|
||||
lastName = "EmployeeModel",
|
||||
password = passwordEncoder.encode(group.name),
|
||||
group = group,
|
||||
isDefaultGroupUser = true
|
||||
)
|
||||
employee(
|
||||
id = 1000000L + group.id!!,
|
||||
firstName = group.name,
|
||||
lastName = "EmployeeModel",
|
||||
password = passwordEncoder.encode(group.name),
|
||||
group = group,
|
||||
isDefaultGroupUser = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -144,9 +157,9 @@ class EmployeeServiceImpl(
|
|||
val employee = getById(employeeId, ignoreDefaultGroupUsers = true, ignoreSystemUsers = false)
|
||||
employee.lastLoginTime = time
|
||||
return update(
|
||||
employee,
|
||||
ignoreDefaultGroupUsers = true,
|
||||
ignoreSystemUsers = false
|
||||
employee,
|
||||
ignoreDefaultGroupUsers = true,
|
||||
ignoreSystemUsers = false
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -154,21 +167,21 @@ class EmployeeServiceImpl(
|
|||
val persistedEmployee by lazy { getById(entity.id) }
|
||||
return update(with(entity) {
|
||||
Employee(
|
||||
id = id,
|
||||
firstName = firstName or persistedEmployee.firstName,
|
||||
lastName = lastName or persistedEmployee.lastName,
|
||||
password = persistedEmployee.password,
|
||||
isDefaultGroupUser = false,
|
||||
isSystemUser = false,
|
||||
group = if (entity.groupId != null) groupService.getById(entity.groupId) else persistedEmployee.group,
|
||||
permissions = permissions?.toMutableSet() ?: persistedEmployee.permissions,
|
||||
lastLoginTime = persistedEmployee.lastLoginTime
|
||||
id = id,
|
||||
firstName = firstName or persistedEmployee.firstName,
|
||||
lastName = lastName or persistedEmployee.lastName,
|
||||
password = persistedEmployee.password,
|
||||
isDefaultGroupUser = false,
|
||||
isSystemUser = false,
|
||||
group = if (entity.groupId != null) groupService.getById(entity.groupId) else persistedEmployee.group,
|
||||
permissions = permissions?.toMutableSet() ?: persistedEmployee.permissions,
|
||||
lastLoginTime = persistedEmployee.lastLoginTime
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun update(entity: Employee): Employee =
|
||||
update(entity, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
|
||||
update(entity, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
|
||||
|
||||
override fun update(entity: Employee, ignoreDefaultGroupUsers: Boolean, ignoreSystemUsers: Boolean): Employee {
|
||||
with(repository.findByFirstNameAndLastName(entity.firstName, entity.lastName)) {
|
||||
|
@ -183,24 +196,24 @@ class EmployeeServiceImpl(
|
|||
val persistedEmployee = getById(id, ignoreDefaultGroupUsers = true, ignoreSystemUsers = true)
|
||||
return super<AbstractExternalModelService>.update(with(persistedEmployee) {
|
||||
Employee(
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
passwordEncoder.encode(password),
|
||||
isDefaultGroupUser,
|
||||
isSystemUser,
|
||||
group,
|
||||
permissions,
|
||||
lastLoginTime
|
||||
id,
|
||||
firstName,
|
||||
lastName,
|
||||
passwordEncoder.encode(password),
|
||||
isDefaultGroupUser,
|
||||
isSystemUser,
|
||||
group,
|
||||
permissions,
|
||||
lastLoginTime
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun addPermission(employeeId: Long, permission: EmployeePermission): Employee =
|
||||
super<AbstractExternalModelService>.update(getById(employeeId).apply { permissions += permission })
|
||||
super<AbstractExternalModelService>.update(getById(employeeId).apply { permissions += permission })
|
||||
|
||||
override fun removePermission(employeeId: Long, permission: EmployeePermission): Employee =
|
||||
super<AbstractExternalModelService>.update(getById(employeeId).apply { permissions -= permission })
|
||||
super<AbstractExternalModelService>.update(getById(employeeId).apply { permissions -= permission })
|
||||
|
||||
override fun logout(request: HttpServletRequest) {
|
||||
val authorizationCookie = WebUtils.getCookie(request, "Authorization")
|
||||
|
@ -217,20 +230,27 @@ const val defaultGroupCookieMaxAge = 10 * 365 * 24 * 60 * 60 // 10 ans
|
|||
|
||||
@Service
|
||||
class EmployeeGroupServiceImpl(
|
||||
private val employeeService: EmployeeService,
|
||||
employeeGroupRepository: EmployeeGroupRepository
|
||||
) : AbstractExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupRepository>(
|
||||
employeeGroupRepository
|
||||
private val employeeService: EmployeeService,
|
||||
employeeGroupRepository: EmployeeGroupRepository
|
||||
) : AbstractExternalNamedModelService<EmployeeGroup, EmployeeGroupSaveDto, EmployeeGroupUpdateDto, EmployeeGroupOutputDto, EmployeeGroupRepository>(
|
||||
employeeGroupRepository
|
||||
),
|
||||
EmployeeGroupService {
|
||||
EmployeeGroupService {
|
||||
override fun idNotFoundException(id: Long) = employeeGroupIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = employeeGroupIdAlreadyExistsException(id)
|
||||
override fun nameNotFoundException(name: String) = employeeGroupNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = employeeGroupNameAlreadyExistsException(name)
|
||||
|
||||
override fun EmployeeGroup.toOutput() = EmployeeGroupOutputDto(
|
||||
this.id!!,
|
||||
this.name,
|
||||
this.permissions,
|
||||
this.flatPermissions
|
||||
)
|
||||
|
||||
override fun existsByName(name: String): Boolean = repository.existsByName(name)
|
||||
override fun getEmployeesForGroup(id: Long): Collection<Employee> =
|
||||
employeeService.getByGroup(getById(id))
|
||||
employeeService.getByGroup(getById(id))
|
||||
|
||||
@Transactional
|
||||
override fun save(entity: EmployeeGroup): EmployeeGroup {
|
||||
|
@ -243,9 +263,9 @@ class EmployeeGroupServiceImpl(
|
|||
val persistedGroup by lazy { getById(entity.id) }
|
||||
return update(with(entity) {
|
||||
EmployeeGroup(
|
||||
entity.id,
|
||||
if (name.isNotBlank()) entity.name else persistedGroup.name,
|
||||
if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions
|
||||
entity.id,
|
||||
if (name.isNotBlank()) entity.name else persistedGroup.name,
|
||||
if (permissions.isNotEmpty()) entity.permissions else persistedGroup.permissions
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -258,11 +278,11 @@ class EmployeeGroupServiceImpl(
|
|||
|
||||
override fun getRequestDefaultGroup(request: HttpServletRequest): EmployeeGroup {
|
||||
val defaultGroupCookie = WebUtils.getCookie(request, defaultGroupCookieName)
|
||||
?: throw NoDefaultGroupException()
|
||||
?: throw NoDefaultGroupException()
|
||||
val defaultGroupUser = employeeService.getById(
|
||||
defaultGroupCookie.value.toLong(),
|
||||
ignoreDefaultGroupUsers = false,
|
||||
ignoreSystemUsers = true
|
||||
defaultGroupCookie.value.toLong(),
|
||||
ignoreDefaultGroupUsers = false,
|
||||
ignoreSystemUsers = true
|
||||
)
|
||||
return defaultGroupUser.group!!
|
||||
}
|
||||
|
@ -271,17 +291,17 @@ class EmployeeGroupServiceImpl(
|
|||
val group = getById(groupId)
|
||||
val defaultGroupUser = employeeService.getDefaultGroupEmployee(group)
|
||||
response.addHeader(
|
||||
"Set-Cookie",
|
||||
"$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=${defaultGroupCookieMaxAge}; Path=/api; HttpOnly; Secure; SameSite=strict"
|
||||
"Set-Cookie",
|
||||
"$defaultGroupCookieName=${defaultGroupUser.id}; Max-Age=${defaultGroupCookieMaxAge}; Path=/api; HttpOnly; Secure; SameSite=strict"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Service
|
||||
class EmployeeUserDetailsServiceImpl(
|
||||
private val employeeService: EmployeeService
|
||||
private val employeeService: EmployeeService
|
||||
) :
|
||||
EmployeeUserDetailsService {
|
||||
EmployeeUserDetailsService {
|
||||
override fun loadUserByUsername(username: String): UserDetails {
|
||||
try {
|
||||
return loadUserByEmployeeId(username.toLong(), true)
|
||||
|
@ -294,9 +314,9 @@ class EmployeeUserDetailsServiceImpl(
|
|||
|
||||
override fun loadUserByEmployeeId(employeeId: Long, ignoreDefaultGroupUsers: Boolean): UserDetails {
|
||||
val employee = employeeService.getById(
|
||||
employeeId,
|
||||
ignoreDefaultGroupUsers = ignoreDefaultGroupUsers,
|
||||
ignoreSystemUsers = false
|
||||
employeeId,
|
||||
ignoreDefaultGroupUsers = ignoreDefaultGroupUsers,
|
||||
ignoreSystemUsers = false
|
||||
)
|
||||
return User(employee.id.toString(), employee.password, employee.authorities)
|
||||
}
|
||||
|
|
|
@ -5,23 +5,28 @@ import dev.fyloz.colorrecipesexplorer.repository.CompanyRepository
|
|||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
interface CompanyService : ExternalNamedModelService<Company, CompanySaveDto, CompanyUpdateDto, CompanyRepository> {
|
||||
interface CompanyService :
|
||||
ExternalNamedModelService<Company, CompanySaveDto, CompanyUpdateDto, Company, CompanyRepository> {
|
||||
/** Checks if the given [company] is used by one or more recipes. */
|
||||
fun isLinkedToRecipes(company: Company): Boolean
|
||||
}
|
||||
|
||||
@Service
|
||||
class CompanyServiceImpl(
|
||||
companyRepository: CompanyRepository,
|
||||
@Lazy val recipeService: RecipeService
|
||||
companyRepository: CompanyRepository,
|
||||
@Lazy val recipeService: RecipeService
|
||||
) :
|
||||
AbstractExternalNamedModelService<Company, CompanySaveDto, CompanyUpdateDto, CompanyRepository>(companyRepository),
|
||||
CompanyService {
|
||||
AbstractExternalNamedModelService<Company, CompanySaveDto, CompanyUpdateDto, Company, CompanyRepository>(
|
||||
companyRepository
|
||||
),
|
||||
CompanyService {
|
||||
override fun idNotFoundException(id: Long) = companyIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = companyIdAlreadyExistsException(id)
|
||||
override fun nameNotFoundException(name: String) = companyNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = companyNameAlreadyExistsException(name)
|
||||
|
||||
override fun Company.toOutput() = this
|
||||
|
||||
override fun isLinkedToRecipes(company: Company): Boolean = recipeService.existsByCompany(company)
|
||||
|
||||
override fun update(entity: CompanyUpdateDto): Company {
|
||||
|
@ -30,8 +35,8 @@ class CompanyServiceImpl(
|
|||
|
||||
return update(with(entity) {
|
||||
company(
|
||||
id = id,
|
||||
name = if (name != null && name.isNotBlank()) name else persistedCompany.name
|
||||
id = id,
|
||||
name = if (name != null && name.isNotBlank()) name else persistedCompany.name
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,13 +2,17 @@ package dev.fyloz.colorrecipesexplorer.service
|
|||
|
||||
import dev.fyloz.colorrecipesexplorer.model.*
|
||||
import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository
|
||||
import dev.fyloz.colorrecipesexplorer.rest.CRE_PROPERTIES
|
||||
import dev.fyloz.colorrecipesexplorer.rest.files.FILE_CONTROLLER_PATH
|
||||
import dev.fyloz.colorrecipesexplorer.service.files.FileService
|
||||
import io.jsonwebtoken.lang.Assert
|
||||
import org.springframework.context.annotation.Lazy
|
||||
import org.springframework.stereotype.Service
|
||||
import java.net.URLEncoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
interface MaterialService :
|
||||
ExternalNamedModelService<Material, MaterialSaveDto, MaterialUpdateDto, MaterialRepository> {
|
||||
ExternalNamedModelService<Material, MaterialSaveDto, MaterialUpdateDto, MaterialOutputDto, MaterialRepository> {
|
||||
/** Checks if a material with the given [materialType] exists. */
|
||||
fun existsByMaterialType(materialType: MaterialType): Boolean
|
||||
|
||||
|
@ -16,13 +20,13 @@ interface MaterialService :
|
|||
fun hasSimdut(material: Material): Boolean
|
||||
|
||||
/** Gets all materials that are not a mix type. */
|
||||
fun getAllNotMixType(): Collection<Material>
|
||||
fun getAllNotMixType(): Collection<MaterialOutputDto>
|
||||
|
||||
/** Gets all materials available for the creation of a mix for the recipe with the given [recipeId], including normal materials and materials from [MixType]s included in the said recipe. */
|
||||
fun getAllForMixCreation(recipeId: Long): Collection<Material>
|
||||
fun getAllForMixCreation(recipeId: Long): Collection<MaterialOutputDto>
|
||||
|
||||
/** 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>
|
||||
fun getAllForMixUpdate(mixId: Long): Collection<MaterialOutputDto>
|
||||
|
||||
/** Updates the quantity of the given [material] with the given [factor] and returns the updated quantity. */
|
||||
fun updateQuantity(material: Material, factor: Float): Float
|
||||
|
@ -36,7 +40,7 @@ class MaterialServiceImpl(
|
|||
@Lazy val materialTypeService: MaterialTypeService,
|
||||
val fileService: FileService
|
||||
) :
|
||||
AbstractExternalNamedModelService<Material, MaterialSaveDto, MaterialUpdateDto, MaterialRepository>(
|
||||
AbstractExternalNamedModelService<Material, MaterialSaveDto, MaterialUpdateDto, MaterialOutputDto, MaterialRepository>(
|
||||
materialRepository
|
||||
),
|
||||
MaterialService {
|
||||
|
@ -45,11 +49,28 @@ class MaterialServiceImpl(
|
|||
override fun nameNotFoundException(name: String) = materialNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = materialNameAlreadyExistsException(name)
|
||||
|
||||
override fun Material.toOutput(): MaterialOutputDto =
|
||||
MaterialOutputDto(
|
||||
id = this.id!!,
|
||||
name = this.name,
|
||||
inventoryQuantity = this.inventoryQuantity,
|
||||
isMixType = this.isMixType,
|
||||
materialType = this.materialType!!,
|
||||
simdutUrl = if (fileService.exists(this.simdutFilePath))
|
||||
"${CRE_PROPERTIES.deploymentUrl}$FILE_CONTROLLER_PATH?path=${
|
||||
URLEncoder.encode(
|
||||
this.simdutFilePath,
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
}"
|
||||
else null
|
||||
)
|
||||
|
||||
override fun existsByMaterialType(materialType: MaterialType): Boolean =
|
||||
repository.existsByMaterialType(materialType)
|
||||
|
||||
override fun hasSimdut(material: Material): Boolean = fileService.exists(material.simdutFilePath)
|
||||
override fun getAllNotMixType(): Collection<Material> = getAll().filter { !it.isMixType }
|
||||
override fun getAllNotMixType(): Collection<MaterialOutputDto> = getAllForOutput().filter { !it.isMixType }
|
||||
|
||||
override fun save(entity: MaterialSaveDto): Material =
|
||||
save(with(entity) {
|
||||
|
@ -95,16 +116,16 @@ class MaterialServiceImpl(
|
|||
updatedQuantity
|
||||
}
|
||||
|
||||
override fun getAllForMixCreation(recipeId: Long): Collection<Material> {
|
||||
override fun getAllForMixCreation(recipeId: Long): Collection<MaterialOutputDto> {
|
||||
val recipesMixTypes = recipeService.getById(recipeId).mixTypes
|
||||
return getAll()
|
||||
return getAllForOutput()
|
||||
.filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } }
|
||||
}
|
||||
|
||||
override fun getAllForMixUpdate(mixId: Long): Collection<Material> {
|
||||
override fun getAllForMixUpdate(mixId: Long): Collection<MaterialOutputDto> {
|
||||
val mix = mixService.getById(mixId)
|
||||
val recipesMixTypes = mix.recipe.mixTypes
|
||||
return getAll()
|
||||
return getAllForOutput()
|
||||
.filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } }
|
||||
.filter { it.id != mix.mixType.material.id }
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import dev.fyloz.colorrecipesexplorer.repository.MaterialTypeRepository
|
|||
import org.springframework.stereotype.Service
|
||||
|
||||
interface MaterialTypeService :
|
||||
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeRepository> {
|
||||
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> {
|
||||
/** Checks if a material type with the given [prefix] exists. */
|
||||
fun existsByPrefix(prefix: String): Boolean
|
||||
|
||||
|
@ -27,7 +27,7 @@ interface MaterialTypeService :
|
|||
|
||||
@Service
|
||||
class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val materialService: MaterialService) :
|
||||
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialTypeRepository>(
|
||||
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>(
|
||||
repository
|
||||
), MaterialTypeService {
|
||||
override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id)
|
||||
|
@ -35,6 +35,8 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
|
|||
override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name)
|
||||
override fun nameAlreadyExistsException(name: String) = materialTypeNameAlreadyExistsException(name)
|
||||
|
||||
override fun MaterialType.toOutput() = this
|
||||
|
||||
override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix)
|
||||
override fun isUsedByMaterial(materialType: MaterialType): Boolean =
|
||||
materialService.existsByMaterialType(materialType)
|
||||
|
|
|
@ -28,31 +28,40 @@ interface MixMaterialService : ModelService<MixMaterial, MixMaterialRepository>
|
|||
* If any of those criteria are not met, an [InvalidGroupStepsPositionsException] will be thrown.
|
||||
*/
|
||||
fun validateMixMaterials(mixMaterials: Set<MixMaterial>)
|
||||
|
||||
fun MixMaterial.toOutput(): MixMaterialOutputDto
|
||||
}
|
||||
|
||||
@Service
|
||||
class MixMaterialServiceImpl(
|
||||
mixMaterialRepository: MixMaterialRepository,
|
||||
@Lazy val materialService: MaterialService
|
||||
mixMaterialRepository: MixMaterialRepository,
|
||||
@Lazy val materialService: MaterialService
|
||||
) : AbstractModelService<MixMaterial, MixMaterialRepository>(mixMaterialRepository), MixMaterialService {
|
||||
override fun idNotFoundException(id: Long) = mixMaterialIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = mixMaterialIdAlreadyExistsException(id)
|
||||
|
||||
override fun MixMaterial.toOutput() = MixMaterialOutputDto(
|
||||
this.id!!,
|
||||
with(materialService) { this@toOutput.material.toOutput() },
|
||||
this.quantity,
|
||||
this.position
|
||||
)
|
||||
|
||||
override fun existsByMaterial(material: Material): Boolean = repository.existsByMaterial(material)
|
||||
override fun create(mixMaterials: Set<MixMaterialDto>): Set<MixMaterial> =
|
||||
mixMaterials.map(::create).toSet()
|
||||
mixMaterials.map(::create).toSet()
|
||||
|
||||
override fun create(mixMaterial: MixMaterialDto): MixMaterial =
|
||||
mixMaterial(
|
||||
material = materialService.getById(mixMaterial.materialId),
|
||||
quantity = mixMaterial.quantity,
|
||||
position = mixMaterial.position
|
||||
)
|
||||
mixMaterial(
|
||||
material = materialService.getById(mixMaterial.materialId),
|
||||
quantity = mixMaterial.quantity,
|
||||
position = mixMaterial.position
|
||||
)
|
||||
|
||||
override fun updateQuantity(mixMaterial: MixMaterial, quantity: Float) =
|
||||
update(mixMaterial.apply {
|
||||
this.quantity = quantity
|
||||
})
|
||||
update(mixMaterial.apply {
|
||||
this.quantity = quantity
|
||||
})
|
||||
|
||||
override fun validateMixMaterials(mixMaterials: Set<MixMaterial>) {
|
||||
if (mixMaterials.isEmpty()) return
|
||||
|
@ -63,17 +72,17 @@ class MixMaterialServiceImpl(
|
|||
|
||||
// Check if the first mix material position is 1
|
||||
fun isFirstMixMaterialPositionInvalid() =
|
||||
sortedMixMaterials[0].position != 1
|
||||
sortedMixMaterials[0].position != 1
|
||||
|
||||
// Check if the first mix material is expressed in percents
|
||||
fun isFirstMixMaterialPercentages() =
|
||||
sortedMixMaterials[0].material.materialType!!.usePercentages
|
||||
sortedMixMaterials[0].material.materialType!!.usePercentages
|
||||
|
||||
// Check if any positions is duplicated
|
||||
fun getDuplicatedPositionsErrors() =
|
||||
sortedMixMaterials
|
||||
.findDuplicated { it.position }
|
||||
.map { duplicatedMixMaterialsPositions(it) }
|
||||
sortedMixMaterials
|
||||
.findDuplicated { it.position }
|
||||
.map { duplicatedMixMaterialsPositions(it) }
|
||||
|
||||
// Find all errors and throw if there is any
|
||||
if (isFirstMixMaterialPositionInvalid()) errors += invalidFirstMixMaterialPosition(sortedMixMaterials[0])
|
||||
|
@ -90,32 +99,32 @@ class MixMaterialServiceImpl(
|
|||
}
|
||||
|
||||
class InvalidMixMaterialsPositionsError(
|
||||
val type: String,
|
||||
val details: String
|
||||
val type: String,
|
||||
val details: String
|
||||
)
|
||||
|
||||
class InvalidMixMaterialsPositionsException(
|
||||
val errors: Set<InvalidMixMaterialsPositionsError>
|
||||
val errors: Set<InvalidMixMaterialsPositionsError>
|
||||
) : RestException(
|
||||
"invalid-mixmaterial-position",
|
||||
"Invalid mix materials positions",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"The position of mix materials are invalid",
|
||||
mapOf(
|
||||
"invalidMixMaterials" to errors
|
||||
)
|
||||
"invalid-mixmaterial-position",
|
||||
"Invalid mix materials positions",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"The position of mix materials are invalid",
|
||||
mapOf(
|
||||
"invalidMixMaterials" to errors
|
||||
)
|
||||
)
|
||||
|
||||
class InvalidFirstMixMaterial(
|
||||
val mixMaterial: MixMaterial
|
||||
val mixMaterial: MixMaterial
|
||||
) : RestException(
|
||||
"invalid-mixmaterial-first",
|
||||
"Invalid first mix material",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"The first mix material is invalid because its material must not be expressed in percents",
|
||||
mapOf(
|
||||
"mixMaterial" to mixMaterial
|
||||
)
|
||||
"invalid-mixmaterial-first",
|
||||
"Invalid first mix material",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
"The first mix material is invalid because its material must not be expressed in percents",
|
||||
mapOf(
|
||||
"mixMaterial" to mixMaterial
|
||||
)
|
||||
)
|
||||
|
||||
const val INVALID_FIRST_MIX_MATERIAL_POSITION_ERROR_CODE = "first"
|
||||
|
@ -123,20 +132,20 @@ const val DUPLICATED_MIX_MATERIALS_POSITIONS_ERROR_CODE = "duplicated"
|
|||
const val GAP_BETWEEN_MIX_MATERIALS_POSITIONS_ERROR_CODE = "gap"
|
||||
|
||||
private fun invalidFirstMixMaterialPosition(mixMaterial: MixMaterial) =
|
||||
InvalidMixMaterialsPositionsError(
|
||||
INVALID_FIRST_MIX_MATERIAL_POSITION_ERROR_CODE,
|
||||
"The position ${mixMaterial.position} is under the minimum of 1"
|
||||
)
|
||||
InvalidMixMaterialsPositionsError(
|
||||
INVALID_FIRST_MIX_MATERIAL_POSITION_ERROR_CODE,
|
||||
"The position ${mixMaterial.position} is under the minimum of 1"
|
||||
)
|
||||
|
||||
private fun duplicatedMixMaterialsPositions(position: Int) =
|
||||
InvalidMixMaterialsPositionsError(
|
||||
DUPLICATED_MIX_MATERIALS_POSITIONS_ERROR_CODE,
|
||||
"The position $position is duplicated"
|
||||
)
|
||||
InvalidMixMaterialsPositionsError(
|
||||
DUPLICATED_MIX_MATERIALS_POSITIONS_ERROR_CODE,
|
||||
"The position $position is duplicated"
|
||||
)
|
||||
|
||||
private fun gapBetweenStepsPositions() =
|
||||
InvalidMixMaterialsPositionsError(
|
||||
GAP_BETWEEN_MIX_MATERIALS_POSITIONS_ERROR_CODE,
|
||||
"There is a gap between mix materials positions"
|
||||
)
|
||||
InvalidMixMaterialsPositionsError(
|
||||
GAP_BETWEEN_MIX_MATERIALS_POSITIONS_ERROR_CODE,
|
||||
"There is a gap between mix materials positions"
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.springframework.context.annotation.Lazy
|
|||
import org.springframework.stereotype.Service
|
||||
import javax.transaction.Transactional
|
||||
|
||||
interface MixService : ExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixRepository> {
|
||||
interface MixService : ExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixOutputDto, MixRepository> {
|
||||
/** Gets all mixes with the given [mixType]. */
|
||||
fun getAllByMixType(mixType: MixType): Collection<Mix>
|
||||
|
||||
|
@ -23,19 +23,30 @@ interface MixService : ExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixRe
|
|||
|
||||
@Service
|
||||
class MixServiceImpl(
|
||||
mixRepository: MixRepository,
|
||||
@Lazy val recipeService: RecipeService,
|
||||
@Lazy val materialTypeService: MaterialTypeService,
|
||||
val mixMaterialService: MixMaterialService,
|
||||
val mixTypeService: MixTypeService
|
||||
) : AbstractModelService<Mix, MixRepository>(mixRepository),
|
||||
MixService {
|
||||
mixRepository: MixRepository,
|
||||
@Lazy val recipeService: RecipeService,
|
||||
@Lazy val materialTypeService: MaterialTypeService,
|
||||
val mixMaterialService: MixMaterialService,
|
||||
val mixTypeService: MixTypeService
|
||||
) : AbstractExternalModelService<Mix, MixSaveDto, MixUpdateDto, MixOutputDto, MixRepository>(mixRepository),
|
||||
MixService {
|
||||
override fun idNotFoundException(id: Long) = mixIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = mixIdAlreadyExistsException(id)
|
||||
|
||||
override fun getAllByMixType(mixType: MixType): Collection<Mix> = repository.findAllByMixType(mixType)
|
||||
override fun mixTypeIsShared(mixType: MixType): Boolean = getAllByMixType(mixType).count() > 1
|
||||
|
||||
override fun Mix.toOutput() = MixOutputDto(
|
||||
this.id!!,
|
||||
this.location,
|
||||
this.mixType,
|
||||
this.mixMaterials.map {
|
||||
with(mixMaterialService) {
|
||||
return@with it.toOutput()
|
||||
}
|
||||
}.toSet()
|
||||
)
|
||||
|
||||
@Transactional
|
||||
override fun save(entity: MixSaveDto): Mix {
|
||||
val recipe = recipeService.getById(entity.recipeId)
|
||||
|
|
|
@ -11,7 +11,8 @@ import org.springframework.web.multipart.MultipartFile
|
|||
import java.io.File
|
||||
import javax.transaction.Transactional
|
||||
|
||||
interface RecipeService : ExternalModelService<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeRepository> {
|
||||
interface RecipeService :
|
||||
ExternalModelService<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeOutputDto, RecipeRepository> {
|
||||
/** Checks if one or more recipes have the given [company]. */
|
||||
fun existsByCompany(company: Company): Boolean
|
||||
|
||||
|
@ -34,13 +35,37 @@ class RecipeServiceImpl(
|
|||
val companyService: CompanyService,
|
||||
val mixService: MixService,
|
||||
val recipeStepService: RecipeStepService,
|
||||
@Lazy val groupService: EmployeeGroupService
|
||||
@Lazy val groupService: EmployeeGroupService,
|
||||
val recipeImageService: RecipeImageService
|
||||
) :
|
||||
AbstractExternalModelService<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeRepository>(recipeRepository),
|
||||
AbstractExternalModelService<Recipe, RecipeSaveDto, RecipeUpdateDto, RecipeOutputDto, RecipeRepository>(
|
||||
recipeRepository
|
||||
),
|
||||
RecipeService {
|
||||
override fun idNotFoundException(id: Long) = recipeIdNotFoundException(id)
|
||||
override fun idAlreadyExistsException(id: Long) = recipeIdAlreadyExistsException(id)
|
||||
|
||||
override fun Recipe.toOutput() = RecipeOutputDto(
|
||||
this.id!!,
|
||||
this.name,
|
||||
this.description,
|
||||
this.color,
|
||||
this.gloss,
|
||||
this.sample,
|
||||
this.approbationDate,
|
||||
this.remark,
|
||||
this.company,
|
||||
this.mixes.map {
|
||||
with(mixService) {
|
||||
it.toOutput()
|
||||
}
|
||||
}.toSet(),
|
||||
this.groupsInformation,
|
||||
recipeImageService.getAllImages(this)
|
||||
.map { this.imageUrl(it) }
|
||||
.toSet()
|
||||
)
|
||||
|
||||
override fun existsByCompany(company: Company): Boolean = repository.existsByCompany(company)
|
||||
override fun getAllByCompany(company: Company): Collection<Recipe> = repository.findAllByCompany(company)
|
||||
|
||||
|
@ -157,7 +182,6 @@ const val RECIPE_IMAGE_EXTENSION = ".jpg"
|
|||
|
||||
@Service
|
||||
class RecipeImageServiceImpl(
|
||||
val recipeService: RecipeService,
|
||||
val fileService: FileService
|
||||
) : RecipeImageService {
|
||||
override fun getAllImages(recipe: Recipe): Set<String> {
|
||||
|
|
|
@ -62,7 +62,7 @@ abstract class AbstractService<E, R : JpaRepository<E, *>>(override val reposito
|
|||
}
|
||||
|
||||
abstract class AbstractModelService<E : Model, R : JpaRepository<E, Long>>(repository: R) :
|
||||
AbstractService<E, R>(repository), ModelService<E, R> {
|
||||
AbstractService<E, R>(repository), ModelService<E, R> {
|
||||
protected abstract fun idNotFoundException(id: Long): NotFoundException
|
||||
protected abstract fun idAlreadyExistsException(id: Long): AlreadyExistsException
|
||||
|
||||
|
@ -83,7 +83,7 @@ abstract class AbstractModelService<E : Model, R : JpaRepository<E, Long>>(repos
|
|||
}
|
||||
|
||||
override fun deleteById(id: Long) =
|
||||
delete(getById(id)) // Use delete(entity) to prevent code duplication and to ease testing
|
||||
delete(getById(id)) // Use delete(entity) to prevent code duplication and to ease testing
|
||||
|
||||
protected fun assertId(id: Long?) {
|
||||
Assert.notNull(id, "${javaClass.simpleName}.update() was called with a null identifier")
|
||||
|
@ -91,7 +91,7 @@ abstract class AbstractModelService<E : Model, R : JpaRepository<E, Long>>(repos
|
|||
}
|
||||
|
||||
abstract class AbstractNamedModelService<E : NamedModel, R : NamedJpaRepository<E>>(repository: R) :
|
||||
AbstractModelService<E, R>(repository), NamedModelService<E, R> {
|
||||
AbstractModelService<E, R>(repository), NamedModelService<E, R> {
|
||||
protected abstract fun nameNotFoundException(name: String): NotFoundException
|
||||
protected abstract fun nameAlreadyExistsException(name: String): AlreadyExistsException
|
||||
|
||||
|
@ -126,33 +126,57 @@ abstract class AbstractNamedModelService<E : NamedModel, R : NamedJpaRepository<
|
|||
* @param S The entity save DTO type
|
||||
* @param U The entity update DTO type
|
||||
*/
|
||||
interface ExternalService<E, S : EntityDto<E>, U : EntityDto<E>, R : JpaRepository<E, *>> : Service<E, R> {
|
||||
interface ExternalService<E, S : EntityDto<E>, U : EntityDto<E>, O, R : JpaRepository<E, *>> : Service<E, R> {
|
||||
/** Gets all entities mapped to their output model. */
|
||||
fun getAllForOutput(): Collection<O>
|
||||
|
||||
/** Saves a given [entity]. */
|
||||
fun save(entity: S): E = save(entity.toEntity())
|
||||
|
||||
/** Updates a given [entity]. */
|
||||
fun update(entity: U): E = update(entity.toEntity())
|
||||
fun update(entity: U): E
|
||||
|
||||
/** Convert the given entity to its output model. */
|
||||
fun E.toOutput(): O
|
||||
}
|
||||
|
||||
/** An [ExternalService] for entities implementing the [Model] interface. */
|
||||
interface ExternalModelService<E : Model, S : EntityDto<E>, U : EntityDto<E>, R : JpaRepository<E, *>> :
|
||||
ModelService<E, R>, ExternalService<E, S, U, R>
|
||||
interface ExternalModelService<E : Model, S : EntityDto<E>, U : EntityDto<E>, O, R : JpaRepository<E, *>> :
|
||||
ModelService<E, R>, ExternalService<E, S, U, O, R> {
|
||||
/** Gets the entity with the given [id] mapped to its output model. */
|
||||
fun getByIdForOutput(id: Long): O
|
||||
}
|
||||
|
||||
/** An [ExternalService] for entities implementing the [NamedModel] interface. */
|
||||
interface ExternalNamedModelService<E : NamedModel, S : EntityDto<E>, U : EntityDto<E>, R : JpaRepository<E, *>> :
|
||||
NamedModelService<E, R>, ExternalModelService<E, S, U, R>
|
||||
interface ExternalNamedModelService<E : NamedModel, S : EntityDto<E>, U : EntityDto<E>, O, R : JpaRepository<E, *>> :
|
||||
NamedModelService<E, R>, ExternalModelService<E, S, U, O, R>
|
||||
|
||||
/** An [AbstractService] with the functionalities of a [ExternalService]. */
|
||||
@Suppress("unused")
|
||||
abstract class AbstractExternalService<E, S : EntityDto<E>, U : EntityDto<E>, R : JpaRepository<E, *>>(repository: R) :
|
||||
AbstractService<E, R>(repository), ExternalService<E, S, U, R>
|
||||
abstract class AbstractExternalService<E, S : EntityDto<E>, U : EntityDto<E>, O, R : JpaRepository<E, *>>(repository: R) :
|
||||
AbstractService<E, R>(repository), ExternalService<E, S, U, O, R> {
|
||||
override fun getAllForOutput() =
|
||||
getAll().map { it.toOutput() }
|
||||
}
|
||||
|
||||
/** An [AbstractModelService] with the functionalities of a [ExternalService]. */
|
||||
abstract class AbstractExternalModelService<E : Model, S : EntityDto<E>, U : EntityDto<E>, R : JpaRepository<E, Long>>(
|
||||
repository: R
|
||||
) : AbstractModelService<E, R>(repository), ExternalModelService<E, S, U, R>
|
||||
abstract class AbstractExternalModelService<E : Model, S : EntityDto<E>, U : EntityDto<E>, O, R : JpaRepository<E, Long>>(
|
||||
repository: R
|
||||
) : AbstractModelService<E, R>(repository), ExternalModelService<E, S, U, O, R> {
|
||||
override fun getAllForOutput() =
|
||||
getAll().map { it.toOutput() }
|
||||
|
||||
override fun getByIdForOutput(id: Long) =
|
||||
getById(id).toOutput()
|
||||
}
|
||||
|
||||
/** An [AbstractNamedModelService] with the functionalities of a [ExternalService]. */
|
||||
abstract class AbstractExternalNamedModelService<E : NamedModel, S : EntityDto<E>, U : EntityDto<E>, R : NamedJpaRepository<E>>(
|
||||
repository: R
|
||||
) : AbstractNamedModelService<E, R>(repository), ExternalNamedModelService<E, S, U, R>
|
||||
abstract class AbstractExternalNamedModelService<E : NamedModel, S : EntityDto<E>, U : EntityDto<E>, O, R : NamedJpaRepository<E>>(
|
||||
repository: R
|
||||
) : AbstractNamedModelService<E, R>(repository), ExternalNamedModelService<E, S, U, O, R> {
|
||||
override fun getAllForOutput() =
|
||||
getAll().map { it.toOutput() }
|
||||
|
||||
override fun getByIdForOutput(id: Long) =
|
||||
getById(id).toOutput()
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ abstract class AbstractServiceTest<E, S : Service<E, *>, R : JpaRepository<E, *>
|
|||
|
||||
protected val entityList: List<E>
|
||||
get() = listOf(
|
||||
entity,
|
||||
anotherEntity
|
||||
entity,
|
||||
anotherEntity
|
||||
)
|
||||
|
||||
@AfterEach
|
||||
|
@ -91,7 +91,7 @@ abstract class AbstractServiceTest<E, S : Service<E, *>, R : JpaRepository<E, *>
|
|||
}
|
||||
|
||||
abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : JpaRepository<E, Long>> :
|
||||
AbstractServiceTest1<E, S, R>() {
|
||||
AbstractServiceTest1<E, S, R>() {
|
||||
|
||||
// existsById()
|
||||
|
||||
|
@ -129,7 +129,7 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : J
|
|||
whenever(repository.findById(entity.id!!)).doReturn(Optional.empty())
|
||||
|
||||
assertThrows<NotFoundException> { service.getById(entity.id!!) }
|
||||
.assertErrorCode()
|
||||
.assertErrorCode()
|
||||
}
|
||||
|
||||
// save()
|
||||
|
@ -139,7 +139,7 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : J
|
|||
doReturn(true).whenever(service).existsById(entity.id!!)
|
||||
|
||||
assertThrows<AlreadyExistsException> { service.save(entity) }
|
||||
.assertErrorCode()
|
||||
.assertErrorCode()
|
||||
}
|
||||
|
||||
// update()
|
||||
|
@ -161,7 +161,7 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : J
|
|||
doReturn(false).whenever(service).existsById(entity.id!!)
|
||||
|
||||
assertThrows<NotFoundException> { service.update(entity) }
|
||||
.assertErrorCode()
|
||||
.assertErrorCode()
|
||||
}
|
||||
|
||||
// deleteById()
|
||||
|
@ -177,7 +177,7 @@ abstract class AbstractModelServiceTest<E : Model, S : ModelService<E, *>, R : J
|
|||
}
|
||||
|
||||
abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelService<E, *>, R : NamedJpaRepository<E>> :
|
||||
AbstractModelServiceTest<E, S, R>() {
|
||||
AbstractModelServiceTest<E, S, R>() {
|
||||
protected abstract val entityWithEntityName: E
|
||||
|
||||
// existsByName()
|
||||
|
@ -216,7 +216,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
whenever(repository.findByName(entity.name)).doReturn(null)
|
||||
|
||||
assertThrows<NotFoundException> { service.getByName(entity.name) }
|
||||
.assertErrorCode("name")
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
// save()
|
||||
|
@ -226,7 +226,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
doReturn(true).whenever(service).existsByName(entity.name)
|
||||
|
||||
assertThrows<AlreadyExistsException> { service.save(entity) }
|
||||
.assertErrorCode("name")
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
// update()
|
||||
|
@ -258,7 +258,7 @@ abstract class AbstractNamedModelServiceTest<E : NamedModel, S : NamedModelServi
|
|||
doReturn(entity).whenever(service).getById(entity.id!!)
|
||||
|
||||
assertThrows<AlreadyExistsException> { service.update(entity) }
|
||||
.assertErrorCode("name")
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,8 +269,8 @@ interface ExternalModelServiceTest {
|
|||
|
||||
// ==== IMPLEMENTATIONS FOR EXTERNAL SERVICES ====
|
||||
// Lots of code duplication but I don't have a better solution for now
|
||||
abstract class AbstractExternalModelServiceTest<E : Model, N : EntityDto<E>, U : EntityDto<E>, S : ExternalModelService<E, N, U, *>, R : JpaRepository<E, Long>> :
|
||||
AbstractModelServiceTest<E, S, R>(), ExternalModelServiceTest {
|
||||
abstract class AbstractExternalModelServiceTest<E : Model, N : EntityDto<E>, U : EntityDto<E>, S : ExternalModelService<E, N, U, *, *>, R : JpaRepository<E, Long>> :
|
||||
AbstractModelServiceTest<E, S, R>(), ExternalModelServiceTest {
|
||||
protected abstract val entitySaveDto: N
|
||||
protected abstract val entityUpdateDto: U
|
||||
|
||||
|
@ -281,8 +281,8 @@ abstract class AbstractExternalModelServiceTest<E : Model, N : EntityDto<E>, U :
|
|||
}
|
||||
}
|
||||
|
||||
abstract class AbstractExternalNamedModelServiceTest<E : NamedModel, N : EntityDto<E>, U : EntityDto<E>, S : ExternalNamedModelService<E, N, U, *>, R : NamedJpaRepository<E>> :
|
||||
AbstractNamedModelServiceTest<E, S, R>(), ExternalModelServiceTest {
|
||||
abstract class AbstractExternalNamedModelServiceTest<E : NamedModel, N : EntityDto<E>, U : EntityDto<E>, S : ExternalNamedModelService<E, N, U, *, *>, R : NamedJpaRepository<E>> :
|
||||
AbstractNamedModelServiceTest<E, S, R>(), ExternalModelServiceTest {
|
||||
protected abstract val entitySaveDto: N
|
||||
protected abstract val entityUpdateDto: U
|
||||
|
||||
|
@ -294,10 +294,10 @@ abstract class AbstractExternalNamedModelServiceTest<E : NamedModel, N : EntityD
|
|||
}
|
||||
|
||||
fun NotFoundException.assertErrorCode(identifierName: String = "id") =
|
||||
this.assertErrorCode("notfound", identifierName)
|
||||
this.assertErrorCode("notfound", identifierName)
|
||||
|
||||
fun AlreadyExistsException.assertErrorCode(identifierName: String = "id") =
|
||||
this.assertErrorCode("exists", identifierName)
|
||||
this.assertErrorCode("exists", identifierName)
|
||||
|
||||
fun RestException.assertErrorCode(type: String, identifierName: String) {
|
||||
assertTrue {
|
||||
|
@ -311,11 +311,11 @@ fun RestException.assertErrorCode(errorCode: String) {
|
|||
}
|
||||
|
||||
fun <E : Model, N : EntityDto<E>> withBaseSaveDtoTest(
|
||||
entity: E,
|
||||
entitySaveDto: N,
|
||||
service: ExternalService<E, N, *, *>,
|
||||
saveMockMatcher: () -> E = { entity },
|
||||
op: () -> Unit = {}
|
||||
entity: E,
|
||||
entitySaveDto: N,
|
||||
service: ExternalService<E, N, *, *, *>,
|
||||
saveMockMatcher: () -> E = { entity },
|
||||
op: () -> Unit = {}
|
||||
) {
|
||||
doReturn(entity).whenever(service).save(saveMockMatcher())
|
||||
doReturn(entity).whenever(entitySaveDto).toEntity()
|
||||
|
@ -329,11 +329,11 @@ fun <E : Model, N : EntityDto<E>> withBaseSaveDtoTest(
|
|||
}
|
||||
|
||||
fun <E : Model, U : EntityDto<E>> withBaseUpdateDtoTest(
|
||||
entity: E,
|
||||
entityUpdateDto: U,
|
||||
service: ExternalModelService<E, *, U, *>,
|
||||
updateMockMatcher: () -> E,
|
||||
op: E.() -> Unit = {}
|
||||
entity: E,
|
||||
entityUpdateDto: U,
|
||||
service: ExternalModelService<E, *, U, *, *>,
|
||||
updateMockMatcher: () -> E,
|
||||
op: E.() -> Unit = {}
|
||||
) {
|
||||
doAnswer { it.arguments[0] }.whenever(service).update(updateMockMatcher())
|
||||
doReturn(entity).whenever(entityUpdateDto).toEntity()
|
||||
|
|
|
@ -14,16 +14,17 @@ import kotlin.test.assertFalse
|
|||
import kotlin.test.assertTrue
|
||||
|
||||
class MaterialServiceTest :
|
||||
AbstractExternalNamedModelServiceTest<Material, MaterialSaveDto, MaterialUpdateDto, MaterialService, MaterialRepository>() {
|
||||
AbstractExternalNamedModelServiceTest<Material, MaterialSaveDto, MaterialUpdateDto, MaterialService, MaterialRepository>() {
|
||||
override val repository: MaterialRepository = mock()
|
||||
private val recipeService: RecipeService = mock()
|
||||
private val mixService: MixService = mock()
|
||||
private val materialTypeService: MaterialTypeService = mock()
|
||||
private val fileService: FileService = mock()
|
||||
override val service: MaterialService =
|
||||
spy(MaterialServiceImpl(repository, recipeService, mixService, materialTypeService, fileService))
|
||||
spy(MaterialServiceImpl(repository, recipeService, mixService, materialTypeService, fileService))
|
||||
|
||||
override val entity: Material = material(id = 0L, name = "material")
|
||||
private val entityOutput = materialOutputDto(entity)
|
||||
override val anotherEntity: Material = material(id = 1L, name = "another material")
|
||||
override val entityWithEntityName: Material = material(id = 2L, name = "material")
|
||||
override val entitySaveDto: MaterialSaveDto = spy(materialSaveDto())
|
||||
|
@ -83,15 +84,16 @@ class MaterialServiceTest :
|
|||
|
||||
@Test
|
||||
fun `getAllNotMixType() returns a list containing every material that are not a mix type`() {
|
||||
val mixTypeMaterial = material(name = "mix type material", isMixType = true)
|
||||
val mixTypeMaterial = material(id = 1L, name = "mix type material", isMixType = true)
|
||||
val mixTypeMaterialOutput = materialOutputDto(mixTypeMaterial)
|
||||
val materialList = listOf(entity, mixTypeMaterial)
|
||||
|
||||
doReturn(materialList).whenever(service).getAll()
|
||||
|
||||
val found = service.getAllNotMixType()
|
||||
|
||||
assertTrue(found.contains(entity))
|
||||
assertFalse(found.contains(mixTypeMaterial))
|
||||
assertTrue(found.contains(entityOutput))
|
||||
assertFalse(found.contains(mixTypeMaterialOutput))
|
||||
}
|
||||
|
||||
// save()
|
||||
|
@ -101,7 +103,7 @@ class MaterialServiceTest :
|
|||
doReturn(true).whenever(service).existsByName(entity.name)
|
||||
|
||||
assertThrows<AlreadyExistsException> { service.save(entity) }
|
||||
.assertErrorCode("name")
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -133,7 +135,7 @@ class MaterialServiceTest :
|
|||
doReturn(entity).whenever(service).getById(material.id!!)
|
||||
|
||||
assertThrows<AlreadyExistsException> { service.update(material) }
|
||||
.assertErrorCode("name")
|
||||
.assertErrorCode("name")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -173,16 +175,16 @@ class MaterialServiceTest :
|
|||
val anotherMixTypeMaterial = material(id = 2L, isMixType = true)
|
||||
val materials = listOf(normalMaterial, mixTypeMaterial, anotherMixTypeMaterial)
|
||||
val recipe =
|
||||
recipe(id = 0L, mixes = mutableListOf(mix(mixType = mixType(id = 0L, material = mixTypeMaterial))))
|
||||
recipe(id = 0L, mixes = mutableListOf(mix(mixType = mixType(id = 0L, material = mixTypeMaterial))))
|
||||
|
||||
whenever(recipeService.getById(recipe.id!!)).doReturn(recipe)
|
||||
doReturn(materials).whenever(service).getAll()
|
||||
|
||||
val found = service.getAllForMixCreation(recipe.id!!)
|
||||
|
||||
assertTrue(normalMaterial in found)
|
||||
assertTrue(mixTypeMaterial in found)
|
||||
assertFalse(anotherMixTypeMaterial in found)
|
||||
assertTrue(materialOutputDto(normalMaterial) in found)
|
||||
assertTrue(materialOutputDto(mixTypeMaterial) in found)
|
||||
assertFalse(materialOutputDto(anotherMixTypeMaterial) in found)
|
||||
}
|
||||
|
||||
// getAllForMixUpdate()
|
||||
|
@ -202,9 +204,9 @@ class MaterialServiceTest :
|
|||
|
||||
val found = service.getAllForMixUpdate(mix.id!!)
|
||||
|
||||
assertTrue(normalMaterial in found)
|
||||
assertTrue(mixTypeMaterial in found)
|
||||
assertFalse(anotherMixTypeMaterial in found)
|
||||
assertTrue(materialOutputDto(normalMaterial) in found)
|
||||
assertTrue(materialOutputDto(mixTypeMaterial) in found)
|
||||
assertFalse(materialOutputDto(anotherMixTypeMaterial) in found)
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,4 +235,13 @@ class MaterialServiceTest :
|
|||
|
||||
test()
|
||||
}
|
||||
|
||||
private fun materialOutputDto(material: Material) = MaterialOutputDto(
|
||||
id = material.id!!,
|
||||
name = material.name,
|
||||
inventoryQuantity = material.inventoryQuantity,
|
||||
isMixType = material.isMixType,
|
||||
materialType = material.materialType!!,
|
||||
simdutUrl = null
|
||||
)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class RecipeServiceTest :
|
|||
private val groupService: EmployeeGroupService = mock()
|
||||
private val recipeStepService: RecipeStepService = mock()
|
||||
override val service: RecipeService =
|
||||
spy(RecipeServiceImpl(repository, companyService, mixService, recipeStepService, groupService))
|
||||
spy(RecipeServiceImpl(repository, companyService, mixService, recipeStepService, groupService, mock()))
|
||||
|
||||
private val company: Company = company(id = 0L)
|
||||
override val entity: Recipe = recipe(id = 0L, name = "recipe", company = company)
|
||||
|
@ -168,7 +168,7 @@ private class RecipeImageServiceTestContext {
|
|||
every { write(any(), any(), any()) } just Runs
|
||||
every { delete(any()) } just Runs
|
||||
}
|
||||
val recipeImageService = spyk(RecipeImageServiceImpl(mockk(), fileService))
|
||||
val recipeImageService = spyk(RecipeImageServiceImpl(fileService))
|
||||
val recipe = spyk(recipe())
|
||||
val recipeImagesIds = setOf(1L, 10L, 21L)
|
||||
val recipeImagesNames = recipeImagesIds.map { it.imageName }.toSet()
|
||||
|
|
Loading…
Reference in New Issue