#25 Separate mix materials and mix types
This commit is contained in:
parent
eb4bb6b52a
commit
129fc4dcb9
|
@ -2,17 +2,17 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
group = "dev.fyloz.colorrecipesexplorer"
|
group = "dev.fyloz.colorrecipesexplorer"
|
||||||
|
|
||||||
val kotlinVersion = "1.6.0"
|
val kotlinVersion = "1.6.20"
|
||||||
val springBootVersion = "2.6.1"
|
val springBootVersion = "2.6.1"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Outer scope variables can't be accessed in the plugins section, so we have to redefine them here
|
// Outer scope variables can't be accessed in the plugins section, so we have to redefine them here
|
||||||
val kotlinVersion = "1.6.0"
|
val kotlinVersion = "1.6.20"
|
||||||
val springBootVersion = "2.6.1"
|
val springBootVersion = "2.6.1"
|
||||||
|
|
||||||
id("java")
|
id("java")
|
||||||
id("org.jetbrains.kotlin.jvm") version kotlinVersion
|
id("org.jetbrains.kotlin.jvm") version kotlinVersion
|
||||||
id("org.jetbrains.dokka") version "1.4.32"
|
id("org.jetbrains.dokka") version "1.6.10"
|
||||||
id("org.springframework.boot") version springBootVersion
|
id("org.springframework.boot") version springBootVersion
|
||||||
id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
|
id("org.jetbrains.kotlin.plugin.spring") version kotlinVersion
|
||||||
id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion
|
id("org.jetbrains.kotlin.plugin.jpa") version kotlinVersion
|
||||||
|
@ -30,7 +30,7 @@ dependencies {
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom:${kotlinVersion}"))
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom:${kotlinVersion}"))
|
||||||
|
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
||||||
implementation("dev.fyloz.colorrecipesexplorer:database-manager:5.2.1")
|
implementation("dev.fyloz.colorrecipesexplorer:database-manager:6.2")
|
||||||
implementation("dev.fyloz:memorycache:1.0")
|
implementation("dev.fyloz:memorycache:1.0")
|
||||||
implementation("io.github.microutils:kotlin-logging-jvm:2.1.21")
|
implementation("io.github.microutils:kotlin-logging-jvm:2.1.21")
|
||||||
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
|
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.xlsx;
|
package dev.fyloz.colorrecipesexplorer.xlsx;
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixDto;
|
import dev.fyloz.colorrecipesexplorer.dtos.MixDto;
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialDto;
|
import dev.fyloz.colorrecipesexplorer.dtos.MixQuantityOutputDto;
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.RecipeDto;
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeDto;
|
||||||
import dev.fyloz.colorrecipesexplorer.xlsx.component.Document;
|
import dev.fyloz.colorrecipesexplorer.xlsx.component.Document;
|
||||||
import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet;
|
import dev.fyloz.colorrecipesexplorer.xlsx.component.Sheet;
|
||||||
|
@ -64,15 +64,15 @@ public class XlsxExporter {
|
||||||
sheet.registerCell(new SectionTitleCell("Recette"));
|
sheet.registerCell(new SectionTitleCell("Recette"));
|
||||||
|
|
||||||
for (MixDto mix : recipeMixes) {
|
for (MixDto mix : recipeMixes) {
|
||||||
Table mixTable = new Table(4, mix.getMixMaterials().size() + 1, mix.getMixType().getName());
|
Table mixTable = new Table(4, mix.getMixQuantities().getAll().size() + 1, mix.getMixType().getName());
|
||||||
mixTable.setColumnName(0, "Quantité");
|
mixTable.setColumnName(0, "Quantité");
|
||||||
mixTable.setColumnName(2, "Unités");
|
mixTable.setColumnName(2, "Unités");
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
for (MixMaterialDto mixMaterial : mix.getMixMaterials()) {
|
for (MixQuantityOutputDto mixQuantity : mix.getMixQuantitiesOutput()) {
|
||||||
mixTable.setRowName(row, mixMaterial.getMaterial().getName());
|
mixTable.setRowName(row, mixQuantity.getMaterial().getName());
|
||||||
mixTable.setContent(new Position(1, row + 1), mixMaterial.getQuantity());
|
mixTable.setContent(new Position(1, row + 1), mixQuantity.getQuantity());
|
||||||
mixTable.setContent(new Position(3, row + 1), mixMaterial.getMaterial().getMaterialType().getUsePercentages() ? "%" : "mL");
|
mixTable.setContent(new Position(3, row + 1), mixQuantity.getMaterial().getMaterialType().getUsePercentages() ? "%" : "mL");
|
||||||
|
|
||||||
row++;
|
row++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@ package dev.fyloz.colorrecipesexplorer
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
object ControllerPaths {
|
object ControllerPaths {
|
||||||
|
const val COMPANY = "/api/company"
|
||||||
const val FILE = "/api/file"
|
const val FILE = "/api/file"
|
||||||
const val MATERIAL = "/api/material"
|
const val MATERIAL = "/api/material"
|
||||||
const val MATERIAL_TYPE = "/api/materialtype"
|
const val MATERIAL_TYPE = "/api/materialtype"
|
||||||
const val MIX = "/api/recipe/mix"
|
const val MIX = "/api/recipe/mix"
|
||||||
const val RECIPE = "/api/recipe"
|
const val RECIPE = "/api/recipe"
|
||||||
|
const val TOUCH_UP_KIT = "/api/touchupkit"
|
||||||
}
|
}
|
||||||
|
|
||||||
object FilePaths {
|
object FilePaths {
|
||||||
|
@ -14,9 +16,22 @@ object Constants {
|
||||||
private const val IMAGES = "images"
|
private const val IMAGES = "images"
|
||||||
|
|
||||||
const val SIMDUT = "$PDF/simdut"
|
const val SIMDUT = "$PDF/simdut"
|
||||||
|
const val TOUCH_UP_KITS = "$PDF/touchupkits"
|
||||||
const val RECIPE_IMAGES = "$IMAGES/recipes"
|
const val RECIPE_IMAGES = "$IMAGES/recipes"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ModelNames {
|
||||||
|
const val COMPANY = "Company"
|
||||||
|
const val MATERIAL = "Material"
|
||||||
|
const val MATERIAL_TYPE = "MaterialType"
|
||||||
|
const val MIX = "Mix"
|
||||||
|
const val MIX_MATERIAL = "MixMaterial"
|
||||||
|
const val MIX_TYPE = "MixType"
|
||||||
|
const val RECIPE = "Recipe"
|
||||||
|
const val RECIPE_STEP = "RecipeStep"
|
||||||
|
const val TOUCH_UP_KIT = "TouchUpKit"
|
||||||
|
}
|
||||||
|
|
||||||
object ValidationMessages {
|
object ValidationMessages {
|
||||||
const val SIZE_GREATER_OR_EQUALS_ZERO = "Must be greater or equals to 0"
|
const val SIZE_GREATER_OR_EQUALS_ZERO = "Must be greater or equals to 0"
|
||||||
const val SIZE_GREATER_OR_EQUALS_ONE = "Must be greater or equals to 1"
|
const val SIZE_GREATER_OR_EQUALS_ONE = "Must be greater or equals to 1"
|
||||||
|
|
|
@ -15,7 +15,7 @@ import org.springframework.core.env.ConfigurableEnvironment
|
||||||
import javax.sql.DataSource
|
import javax.sql.DataSource
|
||||||
import org.springframework.context.annotation.Configuration as SpringConfiguration
|
import org.springframework.context.annotation.Configuration as SpringConfiguration
|
||||||
|
|
||||||
const val SUPPORTED_DATABASE_VERSION = 5
|
const val SUPPORTED_DATABASE_VERSION = 6
|
||||||
const val ENV_VAR_ENABLE_DATABASE_UPDATE_NAME = "CRE_ENABLE_DB_UPDATE"
|
const val ENV_VAR_ENABLE_DATABASE_UPDATE_NAME = "CRE_ENABLE_DB_UPDATE"
|
||||||
val DATABASE_NAME_REGEX = Regex("(\\w+)$")
|
val DATABASE_NAME_REGEX = Regex("(\\w+)$")
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.config.initializers
|
package dev.fyloz.colorrecipesexplorer.config.initializers
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixDto
|
import dev.fyloz.colorrecipesexplorer.dtos.*
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.MixLogic
|
import dev.fyloz.colorrecipesexplorer.logic.MixLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.merge
|
import dev.fyloz.colorrecipesexplorer.utils.merge
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
|
@ -25,18 +24,19 @@ class MixInitializer(
|
||||||
logger.debug("Validating mix materials positions...")
|
logger.debug("Validating mix materials positions...")
|
||||||
|
|
||||||
mixLogic.getAll()
|
mixLogic.getAll()
|
||||||
.filter { mix -> mix.mixMaterials.any { it.position == 0 } }
|
.filter { it.mixQuantities.all.any { mq -> mq.position == 0 } }
|
||||||
.forEach(this::fixMixPositions)
|
.forEach(this::fixMixPositions)
|
||||||
|
|
||||||
logger.debug("Mix materials positions are valid!")
|
logger.debug("Mix materials positions are valid!")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fixMixPositions(mix: MixDto) {
|
private fun fixMixPositions(mix: MixDto) {
|
||||||
val maxPosition = mix.mixMaterials.maxOf { it.position }
|
val mixQuantities = mix.mixQuantitiesOutput
|
||||||
|
val maxPosition = mixQuantities.maxOf { it.position }
|
||||||
|
|
||||||
logger.warn("Mix ${mix.id} (mix name: ${mix.mixType.name}, recipe id: ${mix.recipeId}) has invalid positions:")
|
logger.warn("Mix ${mix.id} (mix name: ${mix.mixType.name}, recipe id: ${mix.recipeId}) has invalid positions:")
|
||||||
|
|
||||||
val invalidMixMaterials: Collection<MixMaterialDto> = with(mix.mixMaterials.filter { it.position == 0 }) {
|
val invalidMixQuantities: Collection<MixQuantityOutputDto> = with(mixQuantities.filter { it.position == 0 }) {
|
||||||
if (maxPosition == 0 && this.size > 1) {
|
if (maxPosition == 0 && this.size > 1) {
|
||||||
orderMixMaterials(this)
|
orderMixMaterials(this)
|
||||||
} else {
|
} else {
|
||||||
|
@ -44,28 +44,37 @@ class MixInitializer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fixedMixMaterials = increaseMixMaterialsPosition(invalidMixMaterials, maxPosition + 1)
|
val fixedMixQuantities = increaseMixMaterialsPosition(invalidMixQuantities, maxPosition + 1)
|
||||||
val updatedMixMaterials = mix.mixMaterials.merge(fixedMixMaterials)
|
val updatedMixQuantities =
|
||||||
|
mixQuantities.map { MixQuantitySaveDto(it.id, it.material.id, it.quantity, it.position, it.isMixType) }
|
||||||
|
.merge(fixedMixQuantities)
|
||||||
|
|
||||||
with(mix.copy(mixMaterials = updatedMixMaterials)) {
|
val updatedMix = MixSaveDto(mix.id, mix.mixType.name, mix.recipeId, mix.mixType.materialType.id, updatedMixQuantities)
|
||||||
mixLogic.update(this)
|
mixLogic.update(updatedMix)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun increaseMixMaterialsPosition(mixMaterials: Iterable<MixMaterialDto>, firstPosition: Int) =
|
private fun increaseMixMaterialsPosition(mixQuantities: Iterable<MixQuantityOutputDto>, firstPosition: Int) =
|
||||||
mixMaterials
|
mixQuantities
|
||||||
.mapIndexed { index, mixMaterial -> mixMaterial.copy(position = firstPosition + index) }
|
.mapIndexed { index, mixQuantity ->
|
||||||
|
MixQuantitySaveDto(
|
||||||
|
mixQuantity.id,
|
||||||
|
mixQuantity.material.id,
|
||||||
|
mixQuantity.quantity,
|
||||||
|
firstPosition + index,
|
||||||
|
mixQuantity.isMixType
|
||||||
|
)
|
||||||
|
}
|
||||||
.onEach {
|
.onEach {
|
||||||
logger.info("\tPosition of material ${it.material.id} (${it.material.name}) has been set to ${it.position}")
|
logger.info("\tPosition of material ${it.id} (mixType: ${it.isMixType}) has been set to ${it.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun orderMixMaterials(mixMaterials: Collection<MixMaterialDto>) =
|
private fun orderMixMaterials(mixQuantities: Collection<MixQuantityOutputDto>) =
|
||||||
LinkedList(mixMaterials).apply {
|
LinkedList(mixQuantities).apply {
|
||||||
while (this.peek().material.materialType.usePercentages) {
|
while (this.peek().material.materialType.usePercentages) {
|
||||||
// The first mix material can't use percents, so move it to the end of the queue
|
// The first mix material can't use percents, so move it to the end of the queue
|
||||||
val pop = this.pop()
|
val pop = this.pop()
|
||||||
this.add(pop)
|
this.add(pop)
|
||||||
logger.debug("\tMaterial ${pop.material.id} (${pop.material.name}) uses percents, moving to the end of the queue")
|
logger.debug("\tMaterial ${pop.id} (mixType: ${pop.isMixType}) uses percents, moving to the end of the queue")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ data class MaterialDto(
|
||||||
|
|
||||||
val materialType: MaterialTypeDto,
|
val materialType: MaterialTypeDto,
|
||||||
|
|
||||||
val simdutUrl: String? = null
|
val hasSimdut: Boolean = false
|
||||||
) : EntityDto
|
) : EntityDto
|
||||||
|
|
||||||
data class MaterialSaveDto(
|
data class MaterialSaveDto(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.dtos
|
package dev.fyloz.colorrecipesexplorer.dtos
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import dev.fyloz.colorrecipesexplorer.Constants
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import javax.validation.constraints.Min
|
import javax.validation.constraints.Min
|
||||||
import javax.validation.constraints.NotBlank
|
import javax.validation.constraints.NotBlank
|
||||||
|
@ -15,8 +16,39 @@ data class MixDto(
|
||||||
|
|
||||||
val mixType: MixTypeDto,
|
val mixType: MixTypeDto,
|
||||||
|
|
||||||
val mixMaterials: List<MixMaterialDto>
|
@JsonIgnore
|
||||||
) : EntityDto
|
val mixQuantities: MixQuantitiesDto,
|
||||||
|
) : EntityDto {
|
||||||
|
@Suppress("unused")
|
||||||
|
@get:JsonProperty("mixQuantities")
|
||||||
|
val mixQuantitiesOutput by lazy {
|
||||||
|
mixQuantities.materials.map {
|
||||||
|
MixQuantityOutputDto(it.id, it.material, it.quantity, it.position, false)
|
||||||
|
} + mixQuantities.mixTypes.map {
|
||||||
|
MixQuantityOutputDto(it.id, it.mixType.asMaterial(), it.quantity, it.position, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MixQuantitiesDto(
|
||||||
|
val materials: List<MixMaterialDto> = listOf(),
|
||||||
|
|
||||||
|
val mixTypes: List<MixMixTypeDto> = listOf()
|
||||||
|
) {
|
||||||
|
val all get() = materials + mixTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MixQuantityOutputDto(
|
||||||
|
val id: Long,
|
||||||
|
|
||||||
|
val material: MaterialDto,
|
||||||
|
|
||||||
|
val quantity: Float,
|
||||||
|
|
||||||
|
val position: Int,
|
||||||
|
|
||||||
|
val isMixType: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
data class MixSaveDto(
|
data class MixSaveDto(
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
|
@ -28,7 +60,7 @@ data class MixSaveDto(
|
||||||
|
|
||||||
val materialTypeId: Long,
|
val materialTypeId: Long,
|
||||||
|
|
||||||
val mixMaterials: List<MixMaterialSaveDto>
|
val mixQuantities: List<MixQuantitySaveDto>
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MixDeductDto(
|
data class MixDeductDto(
|
||||||
|
|
|
@ -3,17 +3,47 @@ package dev.fyloz.colorrecipesexplorer.dtos
|
||||||
import dev.fyloz.colorrecipesexplorer.Constants
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import javax.validation.constraints.Min
|
import javax.validation.constraints.Min
|
||||||
|
|
||||||
|
sealed interface MixQuantityDto : EntityDto {
|
||||||
|
val quantity: Float
|
||||||
|
val position: Int
|
||||||
|
|
||||||
|
val materialType: MaterialTypeDto
|
||||||
|
val name: String
|
||||||
|
}
|
||||||
|
|
||||||
data class MixMaterialDto(
|
data class MixMaterialDto(
|
||||||
override val id: Long = 0L,
|
override val id: Long = 0L,
|
||||||
|
|
||||||
val material: MaterialDto,
|
val material: MaterialDto,
|
||||||
|
|
||||||
val quantity: Float,
|
override val quantity: Float,
|
||||||
|
|
||||||
val position: Int
|
override val position: Int
|
||||||
) : EntityDto
|
) : MixQuantityDto {
|
||||||
|
override val materialType: MaterialTypeDto
|
||||||
|
get() = material.materialType
|
||||||
|
|
||||||
data class MixMaterialSaveDto(
|
override val name: String
|
||||||
|
get() = material.name
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MixMixTypeDto(
|
||||||
|
override val id: Long,
|
||||||
|
|
||||||
|
val mixType: MixTypeDto,
|
||||||
|
|
||||||
|
override val quantity: Float,
|
||||||
|
|
||||||
|
override val position: Int
|
||||||
|
) : MixQuantityDto {
|
||||||
|
override val materialType: MaterialTypeDto
|
||||||
|
get() = mixType.materialType
|
||||||
|
|
||||||
|
override val name: String
|
||||||
|
get() = mixType.name
|
||||||
|
}
|
||||||
|
|
||||||
|
data class MixQuantitySaveDto(
|
||||||
override val id: Long = 0L,
|
override val id: Long = 0L,
|
||||||
|
|
||||||
val materialId: Long,
|
val materialId: Long,
|
||||||
|
@ -21,5 +51,7 @@ data class MixMaterialSaveDto(
|
||||||
@field:Min(0, message = Constants.ValidationMessages.SIZE_GREATER_OR_EQUALS_ZERO)
|
@field:Min(0, message = Constants.ValidationMessages.SIZE_GREATER_OR_EQUALS_ZERO)
|
||||||
val quantity: Float,
|
val quantity: Float,
|
||||||
|
|
||||||
val position: Int
|
val position: Int,
|
||||||
|
|
||||||
|
val isMixType: Boolean
|
||||||
) : EntityDto
|
) : EntityDto
|
|
@ -5,5 +5,10 @@ data class MixTypeDto(
|
||||||
|
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
||||||
val material: MaterialDto
|
val materialType: MaterialTypeDto,
|
||||||
) : EntityDto
|
|
||||||
|
val material: MaterialDto? = null
|
||||||
|
) : EntityDto {
|
||||||
|
fun asMaterial() =
|
||||||
|
MaterialDto(id, name, 0f, true, materialType)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.dtos
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
|
import java.time.LocalDate
|
||||||
|
import javax.validation.constraints.Min
|
||||||
|
import javax.validation.constraints.NotBlank
|
||||||
|
import javax.validation.constraints.NotEmpty
|
||||||
|
|
||||||
|
data class TouchUpKitDto(
|
||||||
|
override val id: Long = 0L,
|
||||||
|
|
||||||
|
@field:NotBlank
|
||||||
|
val project: String,
|
||||||
|
|
||||||
|
@field:NotBlank
|
||||||
|
val buggy: String,
|
||||||
|
|
||||||
|
@field:NotBlank
|
||||||
|
val company: String,
|
||||||
|
|
||||||
|
@field:Min(1, message = Constants.ValidationMessages.SIZE_GREATER_OR_EQUALS_ONE)
|
||||||
|
val quantity: Int,
|
||||||
|
|
||||||
|
val shippingDate: LocalDate,
|
||||||
|
|
||||||
|
val completionDate: LocalDate?,
|
||||||
|
|
||||||
|
val completed: Boolean = false,
|
||||||
|
|
||||||
|
val expired: Boolean = false,
|
||||||
|
|
||||||
|
@field:NotEmpty
|
||||||
|
val finish: List<String>,
|
||||||
|
|
||||||
|
@field:NotEmpty
|
||||||
|
val material: List<String>,
|
||||||
|
|
||||||
|
@field:NotEmpty
|
||||||
|
val content: List<TouchUpKitProductDto>
|
||||||
|
) : EntityDto
|
||||||
|
|
||||||
|
data class TouchUpKitProductDto(
|
||||||
|
override val id: Long = 0L,
|
||||||
|
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
val description: String?,
|
||||||
|
|
||||||
|
val quantity: Float,
|
||||||
|
|
||||||
|
val ready: Boolean
|
||||||
|
) : EntityDto
|
|
@ -1,15 +1,15 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.CompanyDto
|
import dev.fyloz.colorrecipesexplorer.dtos.CompanyDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Company
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.CompanyService
|
import dev.fyloz.colorrecipesexplorer.service.CompanyService
|
||||||
|
|
||||||
interface CompanyLogic : Logic<CompanyDto, CompanyService>
|
interface CompanyLogic : Logic<CompanyDto, CompanyService>
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
class DefaultCompanyLogic(service: CompanyService) :
|
class DefaultCompanyLogic(service: CompanyService) :
|
||||||
BaseLogic<CompanyDto, CompanyService>(service, Company::class.simpleName!!), CompanyLogic {
|
BaseLogic<CompanyDto, CompanyService>(service, Constants.ModelNames.COMPANY), CompanyLogic {
|
||||||
override fun save(dto: CompanyDto): CompanyDto {
|
override fun save(dto: CompanyDto): CompanyDto {
|
||||||
throwIfNameAlreadyExists(dto.name)
|
throwIfNameAlreadyExists(dto.name)
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,10 @@ class DefaultInventoryLogic(
|
||||||
@Transactional
|
@Transactional
|
||||||
override fun deductMix(mixRatio: MixDeductDto): Collection<MaterialQuantityDto> {
|
override fun deductMix(mixRatio: MixDeductDto): Collection<MaterialQuantityDto> {
|
||||||
val mix = mixLogic.getById(mixRatio.id)
|
val mix = mixLogic.getById(mixRatio.id)
|
||||||
|
val mixMaterials = mix.mixQuantities.materials
|
||||||
|
|
||||||
return deduct(getMaterialsWithAdjustedQuantities(mix.mixMaterials, mixRatio))
|
if (mixMaterials.isEmpty()) return listOf()
|
||||||
|
return deduct(getMaterialsWithAdjustedQuantities(mixMaterials, mixRatio))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialSaveDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MaterialSaveDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Material
|
import dev.fyloz.colorrecipesexplorer.model.Material
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MaterialService
|
import dev.fyloz.colorrecipesexplorer.service.MaterialService
|
||||||
|
@ -11,21 +13,18 @@ interface MaterialLogic : Logic<MaterialDto, MaterialService> {
|
||||||
/** Checks if a material with the given [name] exists. */
|
/** Checks if a material with the given [name] exists. */
|
||||||
fun existsByName(name: String): Boolean
|
fun existsByName(name: String): Boolean
|
||||||
|
|
||||||
/** Gets all materials that are not a mix type. */
|
/**
|
||||||
fun getAllNotMixType(): Collection<MaterialDto>
|
* Returns every material available in the context of the recipe with the given [recipeId].
|
||||||
|
* The materials included contains every non mix type material, and the materials generated for the recipe mix types.
|
||||||
|
*/
|
||||||
|
fun getAllForRecipe(recipeId: Long): Collection<MaterialDto>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all materials available for the creation of a mix for the recipe with the given [recipeId],
|
* Returns every material available in the context of the mix with the given [mixId].
|
||||||
* including normal materials and materials from mix types included in the said recipe.
|
* The materials included contains every non mix type material, and the materials generated for
|
||||||
|
* the mix's recipe mix types, excluding the mix's mix type.
|
||||||
*/
|
*/
|
||||||
fun getAllForMixCreation(recipeId: Long): Collection<MaterialDto>
|
fun getAllForMix(mixId: Long): Collection<MaterialDto>
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all materials available for updating the mix with the given [mixId],
|
|
||||||
* including normal materials and materials from mix types included in the mix recipe
|
|
||||||
* and excluding the material of the mix type of the said mix.
|
|
||||||
*/
|
|
||||||
fun getAllForMixUpdate(mixId: Long): Collection<MaterialDto>
|
|
||||||
|
|
||||||
/** Saves the given [dto]. */
|
/** Saves the given [dto]. */
|
||||||
fun save(dto: MaterialSaveDto): MaterialDto
|
fun save(dto: MaterialSaveDto): MaterialDto
|
||||||
|
@ -44,24 +43,26 @@ class DefaultMaterialLogic(
|
||||||
val mixLogic: MixLogic,
|
val mixLogic: MixLogic,
|
||||||
val materialTypeLogic: MaterialTypeLogic,
|
val materialTypeLogic: MaterialTypeLogic,
|
||||||
val fileLogic: WriteableFileLogic
|
val fileLogic: WriteableFileLogic
|
||||||
) : BaseLogic<MaterialDto, MaterialService>(service, Material::class.simpleName!!), MaterialLogic {
|
) : BaseLogic<MaterialDto, MaterialService>(service, Constants.ModelNames.MATERIAL), MaterialLogic {
|
||||||
override fun existsByName(name: String) = service.existsByName(name, null)
|
override fun existsByName(name: String) = service.existsByName(name, null)
|
||||||
override fun getAllNotMixType() = service.getAllNotMixType()
|
|
||||||
|
|
||||||
override fun getAllForMixCreation(recipeId: Long): Collection<MaterialDto> {
|
override fun getAllForRecipe(recipeId: Long): Collection<MaterialDto> {
|
||||||
val recipesMixTypes = recipeLogic.getById(recipeId).mixTypes
|
val recipe = recipeLogic.getById(recipeId)
|
||||||
|
|
||||||
return getAll().filter { !it.isMixType || recipesMixTypes.any { mixType -> mixType.material.id == it.id } }
|
return getAllWithMixTypesMaterials(recipe.mixTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllForMixUpdate(mixId: Long): Collection<MaterialDto> {
|
override fun getAllForMix(mixId: Long): Collection<MaterialDto> {
|
||||||
val mix = mixLogic.getById(mixId)
|
val mix = mixLogic.getById(mixId)
|
||||||
val recipe = recipeLogic.getById(mix.recipeId)
|
val recipe = recipeLogic.getById(mix.recipeId)
|
||||||
|
|
||||||
return getAll().filter { !it.isMixType || recipe.mixTypes.any { mixType -> mixType.material.id == it.id } }
|
val availableMixTypes = recipe.mixTypes.filter { it != mix.mixType }
|
||||||
.filter { it.id != mix.mixType.material.id }
|
return getAllWithMixTypesMaterials(availableMixTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getAllWithMixTypesMaterials(mixTypes: Collection<MixTypeDto>) =
|
||||||
|
getAll() + mixTypes.map { it.asMaterial() }
|
||||||
|
|
||||||
override fun save(dto: MaterialSaveDto) = save(saveDtoToDto(dto, false)).also { saveSimdutFile(dto, false) }
|
override fun save(dto: MaterialSaveDto) = save(saveDtoToDto(dto, false)).also { saveSimdutFile(dto, false) }
|
||||||
override fun save(dto: MaterialDto): MaterialDto {
|
override fun save(dto: MaterialDto): MaterialDto {
|
||||||
throwIfNameAlreadyExists(dto.name)
|
throwIfNameAlreadyExists(dto.name)
|
||||||
|
@ -101,7 +102,7 @@ class DefaultMaterialLogic(
|
||||||
val isMixType = !updating || getById(saveDto.id).isMixType
|
val isMixType = !updating || getById(saveDto.id).isMixType
|
||||||
val materialType = materialTypeLogic.getById(saveDto.materialTypeId)
|
val materialType = materialTypeLogic.getById(saveDto.materialTypeId)
|
||||||
|
|
||||||
return MaterialDto(saveDto.id, saveDto.name, saveDto.inventoryQuantity, isMixType, materialType, null)
|
return MaterialDto(saveDto.id, saveDto.name, saveDto.inventoryQuantity, isMixType, materialType)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveSimdutFile(dto: MaterialSaveDto, updating: Boolean) {
|
private fun saveSimdutFile(dto: MaterialSaveDto, updating: Boolean) {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
import dev.fyloz.colorrecipesexplorer.exception.CannotUpdateException
|
||||||
import dev.fyloz.colorrecipesexplorer.model.MaterialType
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService
|
import dev.fyloz.colorrecipesexplorer.service.MaterialTypeService
|
||||||
|
|
||||||
interface MaterialTypeLogic : Logic<MaterialTypeDto, MaterialTypeService> {
|
interface MaterialTypeLogic : Logic<MaterialTypeDto, MaterialTypeService> {
|
||||||
|
@ -19,7 +19,7 @@ interface MaterialTypeLogic : Logic<MaterialTypeDto, MaterialTypeService> {
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
class DefaultMaterialTypeLogic(service: MaterialTypeService) :
|
class DefaultMaterialTypeLogic(service: MaterialTypeService) :
|
||||||
BaseLogic<MaterialTypeDto, MaterialTypeService>(service, MaterialType::class.simpleName!!), MaterialTypeLogic {
|
BaseLogic<MaterialTypeDto, MaterialTypeService>(service, Constants.ModelNames.MATERIAL_TYPE), MaterialTypeLogic {
|
||||||
override fun getAll(systemType: Boolean) = service.getAll(systemType)
|
override fun getAll(systemType: Boolean) = service.getAll(systemType)
|
||||||
override fun getByName(name: String) = service.getByName(name)
|
override fun getByName(name: String) = service.getByName(name)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixLocationDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixLocationDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixSaveDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixSaveDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Mix
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MixService
|
import dev.fyloz.colorrecipesexplorer.service.MixService
|
||||||
import org.springframework.context.annotation.Lazy
|
import org.springframework.context.annotation.Lazy
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
@ -26,8 +26,8 @@ class DefaultMixLogic(
|
||||||
@Lazy private val recipeLogic: RecipeLogic,
|
@Lazy private val recipeLogic: RecipeLogic,
|
||||||
@Lazy private val materialTypeLogic: MaterialTypeLogic,
|
@Lazy private val materialTypeLogic: MaterialTypeLogic,
|
||||||
private val mixTypeLogic: MixTypeLogic,
|
private val mixTypeLogic: MixTypeLogic,
|
||||||
private val mixMaterialLogic: MixMaterialLogic
|
private val mixQuantityLogic: MixQuantityLogic
|
||||||
) : BaseLogic<MixDto, MixService>(service, Mix::class.simpleName!!), MixLogic {
|
) : BaseLogic<MixDto, MixService>(service, Constants.ModelNames.MIX), MixLogic {
|
||||||
@Transactional
|
@Transactional
|
||||||
override fun save(dto: MixSaveDto): MixDto {
|
override fun save(dto: MixSaveDto): MixDto {
|
||||||
val recipe = recipeLogic.getById(dto.recipeId)
|
val recipe = recipeLogic.getById(dto.recipeId)
|
||||||
|
@ -36,7 +36,7 @@ class DefaultMixLogic(
|
||||||
val mix = MixDto(
|
val mix = MixDto(
|
||||||
recipeId = recipe.id,
|
recipeId = recipe.id,
|
||||||
mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(dto.name, materialType),
|
mixType = mixTypeLogic.getOrCreateForNameAndMaterialType(dto.name, materialType),
|
||||||
mixMaterials = mixMaterialLogic.validateAndSaveAll(dto.mixMaterials)
|
mixQuantities = mixQuantityLogic.validateAndPrepareForMix(dto.mixQuantities)
|
||||||
)
|
)
|
||||||
|
|
||||||
return save(mix)
|
return save(mix)
|
||||||
|
@ -47,12 +47,19 @@ class DefaultMixLogic(
|
||||||
val materialType = materialTypeLogic.getById(dto.materialTypeId)
|
val materialType = materialTypeLogic.getById(dto.materialTypeId)
|
||||||
val mix = getById(dto.id)
|
val mix = getById(dto.id)
|
||||||
|
|
||||||
|
// Update the mix type if it has been changed
|
||||||
|
val mixType = if (mix.mixType.name != dto.name || mix.mixType.materialType.id != dto.materialTypeId) {
|
||||||
|
mixTypeLogic.updateOrCreateForNameAndMaterialType(mix.mixType, dto.name, materialType)
|
||||||
|
} else {
|
||||||
|
mix.mixType
|
||||||
|
}
|
||||||
|
|
||||||
return update(
|
return update(
|
||||||
MixDto(
|
MixDto(
|
||||||
id = dto.id,
|
id = dto.id,
|
||||||
recipeId = dto.recipeId,
|
recipeId = mix.recipeId,
|
||||||
mixType = mixTypeLogic.updateOrCreateForNameAndMaterialType(mix.mixType, dto.name, materialType),
|
mixType = mixType,
|
||||||
mixMaterials = mixMaterialLogic.validateAndSaveAll(dto.mixMaterials)
|
mixQuantities = mixQuantityLogic.validateAndPrepareForMix(dto.mixQuantities)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialDto
|
import dev.fyloz.colorrecipesexplorer.dtos.*
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialSaveDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||||
import dev.fyloz.colorrecipesexplorer.model.MixMaterial
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MixMaterialService
|
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
||||||
import org.springframework.context.annotation.Lazy
|
import org.springframework.context.annotation.Lazy
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
|
|
||||||
interface MixMaterialLogic : Logic<MixMaterialDto, MixMaterialService> {
|
interface MixQuantityLogic {
|
||||||
/**
|
/**
|
||||||
* Validates if the given [mixMaterials]. To be valid, the position of each mix material must be greater or equals to 1 and unique in the set.
|
* Validates if the given [mixMaterials]. To be valid, the position of each mix material must be greater or equals to 1 and unique in the set.
|
||||||
* There must also be no gap between the positions. Also, the quantity of the first mix material in the set must not be expressed in percentages.
|
* There must also be no gap between the positions. Also, the quantity of the first mix material in the set must not be expressed in percentages.
|
||||||
* If any of those criteria are not met, an [InvalidGroupStepsPositionsException] will be thrown.
|
* If any of those criteria are not met, an [InvalidGroupStepsPositionsException] will be thrown.
|
||||||
*/
|
*/
|
||||||
fun validateMixMaterials(mixMaterials: Set<MixMaterialDto>)
|
fun validateMixQuantities(mixMaterials: List<MixQuantityDto>)
|
||||||
|
|
||||||
/** Validates the given mix materials [dtos] and save them. */
|
/** Validates the given mix quantities [dtos] and put them in [MixQuantitiesDto] to be consumed by a mix. */
|
||||||
fun validateAndSaveAll(dtos: List<MixMaterialSaveDto>): List<MixMaterialDto>
|
fun validateAndPrepareForMix(dtos: List<MixQuantitySaveDto>): MixQuantitiesDto
|
||||||
}
|
}
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
class DefaultMixMaterialLogic(service: MixMaterialService, @Lazy private val materialLogic: MaterialLogic) :
|
class DefaultMixQuantityLogic(
|
||||||
BaseLogic<MixMaterialDto, MixMaterialService>(service, MixMaterial::class.simpleName!!), MixMaterialLogic {
|
@Lazy private val materialLogic: MaterialLogic,
|
||||||
override fun validateMixMaterials(mixMaterials: Set<MixMaterialDto>) {
|
private val mixTypeLogic: MixTypeLogic
|
||||||
|
) : MixQuantityLogic {
|
||||||
|
override fun validateMixQuantities(mixMaterials: List<MixQuantityDto>) {
|
||||||
if (mixMaterials.isEmpty()) return
|
if (mixMaterials.isEmpty()) return
|
||||||
|
|
||||||
val sortedMixMaterials = mixMaterials.sortedBy { it.position }
|
val sortedMixMaterials = mixMaterials.sortedBy { it.position }
|
||||||
|
@ -38,24 +37,35 @@ class DefaultMixMaterialLogic(service: MixMaterialService, @Lazy private val mat
|
||||||
throw InvalidMixMaterialsPositionsException(ex.errors)
|
throw InvalidMixMaterialsPositionsException(ex.errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sortedMixMaterials[0].material.materialType.usePercentages) {
|
val firstMixMaterial = sortedMixMaterials[0]
|
||||||
|
if (firstMixMaterial is MixMaterialDto) {
|
||||||
|
if (firstMixMaterial.material.materialType.usePercentages) {
|
||||||
throw InvalidFirstMixMaterialException(sortedMixMaterials[0])
|
throw InvalidFirstMixMaterialException(sortedMixMaterials[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun validateAndSaveAll(dtos: List<MixMaterialSaveDto>): List<MixMaterialDto> {
|
override fun validateAndPrepareForMix(dtos: List<MixQuantitySaveDto>): MixQuantitiesDto {
|
||||||
val dtosWithMaterials = dtos.map {
|
val mixMixTypes = dtos.filter { it.isMixType }.map {
|
||||||
|
MixMixTypeDto(
|
||||||
|
id = it.id,
|
||||||
|
mixType = mixTypeLogic.getById(it.materialId),
|
||||||
|
quantity = it.quantity,
|
||||||
|
position = it.position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val mixMaterials = dtos.filter { !it.isMixType }.map {
|
||||||
MixMaterialDto(
|
MixMaterialDto(
|
||||||
id = it.id,
|
id = it.id,
|
||||||
material = materialLogic.getById(it.materialId),
|
material = materialLogic.getById(it.materialId),
|
||||||
quantity = it.quantity,
|
quantity = it.quantity,
|
||||||
position = it.position
|
position = it.position
|
||||||
)
|
)
|
||||||
}.toSet()
|
}
|
||||||
|
|
||||||
validateMixMaterials(dtosWithMaterials)
|
validateMixQuantities(mixMixTypes + mixMaterials)
|
||||||
|
return MixQuantitiesDto(mixMaterials, mixMixTypes)
|
||||||
return dtosWithMaterials.map(::save)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +83,7 @@ class InvalidMixMaterialsPositionsException(
|
||||||
)
|
)
|
||||||
|
|
||||||
class InvalidFirstMixMaterialException(
|
class InvalidFirstMixMaterialException(
|
||||||
val mixMaterial: MixMaterialDto
|
val mixMaterial: MixQuantityDto
|
||||||
) : RestException(
|
) : RestException(
|
||||||
"invalid-mixmaterial-first",
|
"invalid-mixmaterial-first",
|
||||||
"Invalid first mix material",
|
"Invalid first mix material",
|
|
@ -1,12 +1,10 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.MixType
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MixTypeService
|
import dev.fyloz.colorrecipesexplorer.service.MixTypeService
|
||||||
import org.springframework.context.annotation.Lazy
|
|
||||||
import org.springframework.transaction.annotation.Transactional
|
import org.springframework.transaction.annotation.Transactional
|
||||||
|
|
||||||
interface MixTypeLogic : Logic<MixTypeDto, MixTypeService> {
|
interface MixTypeLogic : Logic<MixTypeDto, MixTypeService> {
|
||||||
|
@ -22,11 +20,7 @@ interface MixTypeLogic : Logic<MixTypeDto, MixTypeService> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
class DefaultMixTypeLogic(
|
class DefaultMixTypeLogic(service: MixTypeService) : BaseLogic<MixTypeDto, MixTypeService>(service, Constants.ModelNames.MIX_TYPE), MixTypeLogic {
|
||||||
service: MixTypeService,
|
|
||||||
@Lazy private val materialLogic: MaterialLogic
|
|
||||||
) :
|
|
||||||
BaseLogic<MixTypeDto, MixTypeService>(service, MixType::class.simpleName!!), MixTypeLogic {
|
|
||||||
@Transactional
|
@Transactional
|
||||||
override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialTypeDto) =
|
override fun getOrCreateForNameAndMaterialType(name: String, materialType: MaterialTypeDto) =
|
||||||
service.getByNameAndMaterialType(name, materialType.id) ?: saveForNameAndMaterialType(name, materialType)
|
service.getByNameAndMaterialType(name, materialType.id) ?: saveForNameAndMaterialType(name, materialType)
|
||||||
|
@ -35,7 +29,9 @@ class DefaultMixTypeLogic(
|
||||||
mixType: MixTypeDto,
|
mixType: MixTypeDto,
|
||||||
name: String,
|
name: String,
|
||||||
materialType: MaterialTypeDto
|
materialType: MaterialTypeDto
|
||||||
) = if (service.isShared(mixType.id)) {
|
) = if (service.existsByNameAndMaterialType(name, materialType.id, mixType.id)) {
|
||||||
|
service.getByNameAndMaterialType(name, materialType.id)!!
|
||||||
|
} else if (service.isShared(mixType.id)) {
|
||||||
saveForNameAndMaterialType(name, materialType)
|
saveForNameAndMaterialType(name, materialType)
|
||||||
} else {
|
} else {
|
||||||
updateForNameAndMaterialType(mixType, name, materialType)
|
updateForNameAndMaterialType(mixType, name, materialType)
|
||||||
|
@ -50,16 +46,7 @@ class DefaultMixTypeLogic(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveForNameAndMaterialType(name: String, materialType: MaterialTypeDto): MixTypeDto {
|
private fun saveForNameAndMaterialType(name: String, materialType: MaterialTypeDto): MixTypeDto {
|
||||||
val material = materialLogic.save(
|
return save(MixTypeDto(name = name, materialType = materialType))
|
||||||
MaterialDto(
|
|
||||||
name = name,
|
|
||||||
inventoryQuantity = Float.MIN_VALUE,
|
|
||||||
isMixType = true,
|
|
||||||
materialType = materialType
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return save(MixTypeDto(name = name, material = material))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateForNameAndMaterialType(
|
private fun updateForNameAndMaterialType(
|
||||||
|
@ -67,7 +54,6 @@ class DefaultMixTypeLogic(
|
||||||
name: String,
|
name: String,
|
||||||
materialType: MaterialTypeDto
|
materialType: MaterialTypeDto
|
||||||
): MixTypeDto {
|
): MixTypeDto {
|
||||||
val material = materialLogic.update(mixType.material.copy(name = name, materialType = materialType))
|
return update(mixType.copy(name = name, materialType = materialType, material = mixType.material))
|
||||||
return update(mixType.copy(name = name, material = material))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@ import dev.fyloz.colorrecipesexplorer.dtos.*
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.users.GroupLogic
|
import dev.fyloz.colorrecipesexplorer.logic.users.GroupLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Recipe
|
|
||||||
import dev.fyloz.colorrecipesexplorer.service.RecipeService
|
import dev.fyloz.colorrecipesexplorer.service.RecipeService
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.collections.LazyMapList
|
import dev.fyloz.colorrecipesexplorer.utils.collections.LazyMapList
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.merge
|
import dev.fyloz.colorrecipesexplorer.utils.merge
|
||||||
|
@ -37,7 +36,7 @@ class DefaultRecipeLogic(
|
||||||
private val recipeStepLogic: RecipeStepLogic,
|
private val recipeStepLogic: RecipeStepLogic,
|
||||||
private val mixLogic: MixLogic,
|
private val mixLogic: MixLogic,
|
||||||
private val groupLogic: GroupLogic
|
private val groupLogic: GroupLogic
|
||||||
) : BaseLogic<RecipeDto, RecipeService>(service, Recipe::class.simpleName!!), RecipeLogic {
|
) : BaseLogic<RecipeDto, RecipeService>(service, Constants.ModelNames.RECIPE), RecipeLogic {
|
||||||
@Transactional
|
@Transactional
|
||||||
override fun getAllWithMixesAndGroupsInformation() =
|
override fun getAllWithMixesAndGroupsInformation() =
|
||||||
getAll().onEach { (it.mixes as LazyMapList<*, *>).initialize() }
|
getAll().onEach { (it.mixes as LazyMapList<*, *>).initialize() }
|
||||||
|
@ -155,8 +154,8 @@ interface RecipeImageLogic {
|
||||||
/** Saves the given [image] and associate it to the recipe with the given [recipeId]. Returns the id of the saved image. */
|
/** Saves the given [image] and associate it to the recipe with the given [recipeId]. Returns the id of the saved image. */
|
||||||
fun download(image: MultipartFile, recipeId: Long): String
|
fun download(image: MultipartFile, recipeId: Long): String
|
||||||
|
|
||||||
/** Deletes the image with the given [path] for the given [recipeId]. */
|
/** Deletes the image with the given [id] for the given [recipeId]. */
|
||||||
fun delete(recipeId: Long, path: String)
|
fun delete(recipeId: Long, id: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
|
@ -167,7 +166,13 @@ class DefaultRecipeImageLogic(val fileLogic: WriteableFileLogic) : RecipeImageLo
|
||||||
override fun download(image: MultipartFile, recipeId: Long): String {
|
override fun download(image: MultipartFile, recipeId: Long): String {
|
||||||
/** Gets the next id available for a new image for the given [recipeId]. */
|
/** Gets the next id available for a new image for the given [recipeId]. */
|
||||||
fun getNextAvailableId(): String = with(getAllImages(recipeId)) {
|
fun getNextAvailableId(): String = with(getAllImages(recipeId)) {
|
||||||
(if (isEmpty()) 0 else maxOf { it.toLong() } + 1L).toString()
|
val currentIds = mapNotNull { it.toLongOrNull() }
|
||||||
|
if (currentIds.isEmpty()) {
|
||||||
|
return 0.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextId = currentIds.maxOf { it } + 1L
|
||||||
|
return nextId.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
return getNextAvailableId().also {
|
return getNextAvailableId().also {
|
||||||
|
@ -176,8 +181,8 @@ class DefaultRecipeImageLogic(val fileLogic: WriteableFileLogic) : RecipeImageLo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun delete(recipeId: Long, path: String) =
|
override fun delete(recipeId: Long, id: String) =
|
||||||
fileLogic.deleteFromDirectory(path, getRecipeImagesDirectory(recipeId))
|
fileLogic.deleteFromDirectory(getImagePath(recipeId, id), getRecipeImagesDirectory(recipeId))
|
||||||
|
|
||||||
private fun getImagePath(recipeId: Long, id: String) = "${getRecipeImagesDirectory(recipeId)}/$id"
|
private fun getImagePath(recipeId: Long, id: String) = "${getRecipeImagesDirectory(recipeId)}/$id"
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.RecipeGroupInformationDto
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeGroupInformationDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
import dev.fyloz.colorrecipesexplorer.dtos.RecipeStepDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
import dev.fyloz.colorrecipesexplorer.exception.RestException
|
||||||
import dev.fyloz.colorrecipesexplorer.model.RecipeStep
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
import dev.fyloz.colorrecipesexplorer.model.account.Group
|
||||||
import dev.fyloz.colorrecipesexplorer.service.RecipeStepService
|
import dev.fyloz.colorrecipesexplorer.service.RecipeStepService
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
||||||
|
@ -19,7 +19,7 @@ interface RecipeStepLogic : Logic<RecipeStepDto, RecipeStepService> {
|
||||||
|
|
||||||
@LogicComponent
|
@LogicComponent
|
||||||
class DefaultRecipeStepLogic(recipeStepService: RecipeStepService) :
|
class DefaultRecipeStepLogic(recipeStepService: RecipeStepService) :
|
||||||
BaseLogic<RecipeStepDto, RecipeStepService>(recipeStepService, RecipeStep::class.simpleName!!), RecipeStepLogic {
|
BaseLogic<RecipeStepDto, RecipeStepService>(recipeStepService, Constants.ModelNames.RECIPE_STEP), RecipeStepLogic {
|
||||||
override fun validateGroupInformationSteps(groupInformation: RecipeGroupInformationDto) {
|
override fun validateGroupInformationSteps(groupInformation: RecipeGroupInformationDto) {
|
||||||
try {
|
try {
|
||||||
PositionUtils.validate(groupInformation.steps.map { it.position }.toList())
|
PositionUtils.validate(groupInformation.steps.map { it.position }.toList())
|
||||||
|
|
|
@ -1,33 +1,21 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
|
import dev.fyloz.colorrecipesexplorer.config.annotations.LogicComponent
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.TouchUpKitDto
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.config.ConfigurationLogic
|
import dev.fyloz.colorrecipesexplorer.logic.config.ConfigurationLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
|
import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
|
||||||
import dev.fyloz.colorrecipesexplorer.model.touchupkit.*
|
import dev.fyloz.colorrecipesexplorer.service.TouchUpKitService
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository
|
|
||||||
import dev.fyloz.colorrecipesexplorer.rest.TOUCH_UP_KIT_CONTROLLER_PATH
|
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.*
|
import dev.fyloz.colorrecipesexplorer.utils.*
|
||||||
import org.springframework.core.io.ByteArrayResource
|
import org.springframework.core.io.ByteArrayResource
|
||||||
import org.springframework.core.io.Resource
|
import org.springframework.core.io.Resource
|
||||||
import org.springframework.stereotype.Service
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.Period
|
|
||||||
|
|
||||||
private const val TOUCH_UP_KIT_FILES_PATH = "pdf/touchupkits"
|
|
||||||
|
|
||||||
const val TOUCH_UP_TEXT_FR = "KIT DE RETOUCHE"
|
|
||||||
const val TOUCH_UP_TEXT_EN = "TOUCH UP KIT"
|
|
||||||
|
|
||||||
interface TouchUpKitLogic :
|
|
||||||
ExternalModelService<TouchUpKit, TouchUpKitSaveDto, TouchUpKitUpdateDto, TouchUpKitOutputDto, TouchUpKitRepository> {
|
|
||||||
fun isExpired(touchUpKit: TouchUpKit): Boolean
|
|
||||||
|
|
||||||
|
interface TouchUpKitLogic : Logic<TouchUpKitDto, TouchUpKitService> {
|
||||||
|
/** Sets the touch up kit with the given [id] as complete. */
|
||||||
fun complete(id: Long)
|
fun complete(id: Long)
|
||||||
|
|
||||||
/** Generates and returns a [PdfDocument] for the given [job]. */
|
|
||||||
fun generateJobPdf(job: String): PdfDocument
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates and returns a [PdfDocument] for the given [job] as a [ByteArrayResource].
|
* Generates and returns a [PdfDocument] for the given [job] as a [ByteArrayResource].
|
||||||
*
|
*
|
||||||
|
@ -36,68 +24,37 @@ interface TouchUpKitLogic :
|
||||||
*/
|
*/
|
||||||
fun generateJobPdfResource(job: String): Resource
|
fun generateJobPdfResource(job: String): Resource
|
||||||
|
|
||||||
/** Writes the given [document] to the [FileService] if TOUCH_UP_KIT_CACHE_PDF is enabled. */
|
/** Generates and returns a [PdfDocument] for the given [job]. */
|
||||||
fun String.cachePdfDocument(document: PdfDocument)
|
fun generateJobPdf(job: String): PdfDocument
|
||||||
|
|
||||||
|
/** Writes the given [pdf] to the disk if TOUCH_UP_KIT_CACHE_PDF is enabled. */
|
||||||
|
fun cacheJobPdf(job: String, pdf: PdfDocument)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Service
|
@LogicComponent
|
||||||
@RequireDatabase
|
|
||||||
class DefaultTouchUpKitLogic(
|
class DefaultTouchUpKitLogic(
|
||||||
private val fileService: WriteableFileLogic,
|
service: TouchUpKitService,
|
||||||
private val configService: ConfigurationLogic,
|
private val fileLogic: WriteableFileLogic,
|
||||||
touchUpKitRepository: TouchUpKitRepository
|
private val configLogic: ConfigurationLogic
|
||||||
) : AbstractExternalModelService<TouchUpKit, TouchUpKitSaveDto, TouchUpKitUpdateDto, TouchUpKitOutputDto, TouchUpKitRepository>(
|
) : BaseLogic<TouchUpKitDto, TouchUpKitService>(service, Constants.ModelNames.TOUCH_UP_KIT), TouchUpKitLogic {
|
||||||
touchUpKitRepository
|
|
||||||
), TouchUpKitLogic {
|
|
||||||
private val cacheGeneratedFiles by lazy {
|
private val cacheGeneratedFiles by lazy {
|
||||||
configService.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) == true.toString()
|
configLogic.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) == true.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun idNotFoundException(id: Long) = touchUpKitIdNotFoundException(id)
|
override fun complete(id: Long) = service.updateCompletionDateById(id, LocalDate.now())
|
||||||
override fun idAlreadyExistsException(id: Long) = touchUpKitIdAlreadyExistsException(id)
|
|
||||||
|
|
||||||
override fun TouchUpKit.toOutput() = TouchUpKitOutputDto(
|
override fun generateJobPdfResource(job: String): Resource {
|
||||||
this.id!!,
|
if (cacheGeneratedFiles) {
|
||||||
this.project,
|
val pdfPath = jobPdfPath(job)
|
||||||
this.buggy,
|
if (fileLogic.exists(pdfPath)) {
|
||||||
this.company,
|
return fileLogic.read(pdfPath)
|
||||||
this.quantity,
|
}
|
||||||
this.shippingDate,
|
|
||||||
this.completionDate != null,
|
|
||||||
this.completionDate,
|
|
||||||
isExpired(this),
|
|
||||||
this.finish,
|
|
||||||
this.material,
|
|
||||||
this.content,
|
|
||||||
this.pdfUrl()
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun update(entity: TouchUpKitUpdateDto): TouchUpKit {
|
|
||||||
val persistedKit by lazy { getById(entity.id) }
|
|
||||||
|
|
||||||
return super.update(with(entity) {
|
|
||||||
touchUpKit(
|
|
||||||
id = id,
|
|
||||||
project = project ?: persistedKit.project,
|
|
||||||
buggy = buggy ?: persistedKit.buggy,
|
|
||||||
company = company ?: persistedKit.company,
|
|
||||||
quantity = quantity ?: persistedKit.quantity,
|
|
||||||
shippingDate = shippingDate ?: persistedKit.shippingDate,
|
|
||||||
completionDate = completionDate ?: persistedKit.completionDate,
|
|
||||||
finish = finish ?: persistedKit.finish,
|
|
||||||
material = material ?: persistedKit.material,
|
|
||||||
content = content?.map { touchUpKitProduct(it) }?.toSet() ?: persistedKit.content
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isExpired(touchUpKit: TouchUpKit) =
|
val pdf = generateJobPdf(job)
|
||||||
with(Period.parse(configService.getContent(ConfigurationType.TOUCH_UP_KIT_EXPIRATION))) {
|
cacheJobPdf(job, pdf)
|
||||||
touchUpKit.completed && touchUpKit.completionDate!!.plus(this) < LocalDate.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun complete(id: Long) {
|
return pdf.toByteArrayResource()
|
||||||
update(touchUpKitUpdateDto(id = id, completionDate = LocalDate.now()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateJobPdf(job: String) = pdf {
|
override fun generateJobPdf(job: String) = pdf {
|
||||||
|
@ -122,29 +79,17 @@ class DefaultTouchUpKitLogic(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateJobPdfResource(job: String): Resource {
|
override fun cacheJobPdf(job: String, pdf: PdfDocument) {
|
||||||
if (cacheGeneratedFiles) {
|
|
||||||
with(job.pdfDocumentPath()) {
|
|
||||||
if (fileService.exists(this)) {
|
|
||||||
return fileService.read(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateJobPdf(job).apply {
|
|
||||||
job.cachePdfDocument(this)
|
|
||||||
}.toByteArrayResource()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun String.cachePdfDocument(document: PdfDocument) {
|
|
||||||
if (!cacheGeneratedFiles) return
|
if (!cacheGeneratedFiles) return
|
||||||
|
|
||||||
fileService.write(document.toByteArrayResource(), this.pdfDocumentPath(), true)
|
fileLogic.write(pdf.toByteArrayResource(), jobPdfPath(job), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.pdfDocumentPath() =
|
private fun jobPdfPath(job: String) =
|
||||||
"$TOUCH_UP_KIT_FILES_PATH/$this.pdf"
|
"${Constants.FilePaths.TOUCH_UP_KITS}/$job.pdf"
|
||||||
|
|
||||||
private fun TouchUpKit.pdfUrl() =
|
companion object {
|
||||||
"${configService.getContent(ConfigurationType.INSTANCE_URL)}$TOUCH_UP_KIT_CONTROLLER_PATH/pdf?job=$project"
|
const val TOUCH_UP_TEXT_FR = "KIT DE RETOUCHE"
|
||||||
|
const val TOUCH_UP_TEXT_EN = "TOUCH UP KIT"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@ class TouchUpKitRemover(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeExpiredKits() {
|
private fun removeExpiredKits() {
|
||||||
with(touchUpKitLogic.getAll().filter(touchUpKitLogic::isExpired)) {
|
with(touchUpKitLogic.getAll().filter { it.expired }) {
|
||||||
this.forEach {
|
this.forEach {
|
||||||
logger.debug("Removed expired touch up kit ${it.id} (${it.project} ${it.buggy})")
|
logger.debug("Removed expired touch up kit ${it.id} (${it.project} ${it.buggy})")
|
||||||
touchUpKitLogic.delete(it)
|
touchUpKitLogic.deleteById(it.id)
|
||||||
}
|
}
|
||||||
logger.info("Removed ${this.size} expired touch up kits")
|
logger.info("Removed ${this.size} expired touch up kits")
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import dev.fyloz.colorrecipesexplorer.config.security.blacklistedJwtTokens
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.AbstractExternalModelService
|
import dev.fyloz.colorrecipesexplorer.logic.AbstractExternalModelService
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.ExternalModelService
|
import dev.fyloz.colorrecipesexplorer.logic.ExternalModelService
|
||||||
import dev.fyloz.colorrecipesexplorer.model.account.*
|
import dev.fyloz.colorrecipesexplorer.model.account.*
|
||||||
import dev.fyloz.colorrecipesexplorer.model.validation.or
|
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
|
import dev.fyloz.colorrecipesexplorer.repository.UserRepository
|
||||||
import org.springframework.context.annotation.Lazy
|
import org.springframework.context.annotation.Lazy
|
||||||
import org.springframework.context.annotation.Profile
|
import org.springframework.context.annotation.Profile
|
||||||
|
@ -136,8 +135,8 @@ class DefaultUserLogic(
|
||||||
return update(with(entity) {
|
return update(with(entity) {
|
||||||
User(
|
User(
|
||||||
id = id,
|
id = id,
|
||||||
firstName = firstName or persistedUser.firstName,
|
firstName = firstName ?: persistedUser.firstName,
|
||||||
lastName = lastName or persistedUser.lastName,
|
lastName = lastName ?: persistedUser.lastName,
|
||||||
password = persistedUser.password,
|
password = persistedUser.password,
|
||||||
isDefaultGroupUser = false,
|
isDefaultGroupUser = false,
|
||||||
isSystemUser = false,
|
isSystemUser = false,
|
||||||
|
|
|
@ -16,9 +16,9 @@ data class Mix(
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "mix_type_id")
|
@JoinColumn(name = "mix_type_id")
|
||||||
var mixType: MixType,
|
val mixType: MixType,
|
||||||
|
|
||||||
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
||||||
@JoinColumn(name = "mix_id")
|
@JoinColumn(name = "mix_id")
|
||||||
var mixMaterials: List<MixMaterial>,
|
val mixMaterials: List<MixMaterial>
|
||||||
) : ModelEntity
|
) : ModelEntity
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.model
|
||||||
|
|
||||||
|
import javax.persistence.*
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "mix_mix_type")
|
||||||
|
data class MixMixType(
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
override val id: Long,
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "mix_type_id")
|
||||||
|
val mixType: MixType,
|
||||||
|
|
||||||
|
val quantity: Float,
|
||||||
|
|
||||||
|
val position: Int
|
||||||
|
) : ModelEntity
|
|
@ -9,10 +9,13 @@ data class MixType(
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
override val id: Long?,
|
override val id: Long?,
|
||||||
|
|
||||||
@Column(unique = true)
|
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "material_type_id")
|
||||||
|
val materialType: MaterialType,
|
||||||
|
|
||||||
@OneToOne(cascade = [CascadeType.ALL])
|
@OneToOne(cascade = [CascadeType.ALL])
|
||||||
@JoinColumn(name = "material_id")
|
@JoinColumn(name = "material_id")
|
||||||
var material: Material
|
val material: Material?
|
||||||
) : ModelEntity
|
) : ModelEntity
|
|
@ -1,17 +1,8 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.model.touchupkit
|
package dev.fyloz.colorrecipesexplorer.model.touchupkit
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.Constants
|
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.AlreadyExistsException
|
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.NotFoundException
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.EntityDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.ModelEntity
|
import dev.fyloz.colorrecipesexplorer.model.ModelEntity
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.persistence.*
|
import javax.persistence.*
|
||||||
import javax.validation.constraints.Min
|
|
||||||
import javax.validation.constraints.NotBlank
|
|
||||||
import javax.validation.constraints.NotEmpty
|
|
||||||
|
|
||||||
const val TOUCH_UP_KIT_DELIMITER = ';'
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "touch_up_kit")
|
@Table(name = "touch_up_kit")
|
||||||
|
@ -35,24 +26,15 @@ data class TouchUpKit(
|
||||||
val completionDate: LocalDate?,
|
val completionDate: LocalDate?,
|
||||||
|
|
||||||
@Column(name = "finish")
|
@Column(name = "finish")
|
||||||
private val finishConcatenated: String,
|
val finish: String,
|
||||||
|
|
||||||
@Column(name = "material")
|
@Column(name = "material")
|
||||||
private val materialConcatenated: String,
|
val material: String,
|
||||||
|
|
||||||
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
@OneToMany(cascade = [CascadeType.ALL], fetch = FetchType.EAGER, orphanRemoval = true)
|
||||||
@JoinColumn(name = "touch_up_kit_id")
|
@JoinColumn(name = "touch_up_kit_id")
|
||||||
val content: Set<TouchUpKitProduct>
|
val content: List<TouchUpKitProduct>
|
||||||
) : ModelEntity {
|
) : ModelEntity
|
||||||
val finish
|
|
||||||
get() = finishConcatenated.split(TOUCH_UP_KIT_DELIMITER)
|
|
||||||
|
|
||||||
val material
|
|
||||||
get() = materialConcatenated.split(TOUCH_UP_KIT_DELIMITER)
|
|
||||||
|
|
||||||
val completed
|
|
||||||
get() = completionDate != null
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "touch_up_kit_product")
|
@Table(name = "touch_up_kit_product")
|
||||||
|
@ -69,174 +51,3 @@ data class TouchUpKitProduct(
|
||||||
|
|
||||||
val ready: Boolean
|
val ready: Boolean
|
||||||
) : ModelEntity
|
) : ModelEntity
|
||||||
|
|
||||||
data class TouchUpKitSaveDto(
|
|
||||||
@field:NotBlank
|
|
||||||
val project: String,
|
|
||||||
|
|
||||||
@field:NotBlank
|
|
||||||
val buggy: String,
|
|
||||||
|
|
||||||
@field:NotBlank
|
|
||||||
val company: String,
|
|
||||||
|
|
||||||
@field:Min(1, message = Constants.ValidationMessages.SIZE_GREATER_OR_EQUALS_ONE)
|
|
||||||
val quantity: Int,
|
|
||||||
|
|
||||||
val shippingDate: LocalDate,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val finish: List<String>,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val material: List<String>,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val content: Set<TouchUpKitProductDto>
|
|
||||||
) : EntityDto<TouchUpKit> {
|
|
||||||
override fun toEntity() = touchUpKit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class TouchUpKitUpdateDto(
|
|
||||||
val id: Long,
|
|
||||||
|
|
||||||
@field:NotBlank
|
|
||||||
val project: String?,
|
|
||||||
|
|
||||||
@field:NotBlank
|
|
||||||
val buggy: String?,
|
|
||||||
|
|
||||||
@field:NotBlank
|
|
||||||
val company: String?,
|
|
||||||
|
|
||||||
@field:Min(1, message = Constants.ValidationMessages.SIZE_GREATER_OR_EQUALS_ONE)
|
|
||||||
val quantity: Int?,
|
|
||||||
|
|
||||||
val shippingDate: LocalDate?,
|
|
||||||
|
|
||||||
val completionDate: LocalDate?,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val finish: List<String>?,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val material: List<String>?,
|
|
||||||
|
|
||||||
@field:NotEmpty
|
|
||||||
val content: Set<TouchUpKitProductDto>?
|
|
||||||
) : EntityDto<TouchUpKit>
|
|
||||||
|
|
||||||
data class TouchUpKitOutputDto(
|
|
||||||
override val id: Long,
|
|
||||||
val project: String,
|
|
||||||
val buggy: String,
|
|
||||||
val company: String,
|
|
||||||
val quantity: Int,
|
|
||||||
val shippingDate: LocalDate,
|
|
||||||
val completed: Boolean,
|
|
||||||
val completionDate: LocalDate?,
|
|
||||||
val expired: Boolean,
|
|
||||||
val finish: List<String>,
|
|
||||||
val material: List<String>,
|
|
||||||
val content: Set<TouchUpKitProduct>,
|
|
||||||
val pdfUrl: String
|
|
||||||
) : ModelEntity
|
|
||||||
|
|
||||||
data class TouchUpKitProductDto(
|
|
||||||
val name: String,
|
|
||||||
val description: String?,
|
|
||||||
val quantity: Float,
|
|
||||||
val ready: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
// ==== DSL ====
|
|
||||||
fun touchUpKit(
|
|
||||||
id: Long? = null,
|
|
||||||
project: String = "project",
|
|
||||||
buggy: String = "buggy",
|
|
||||||
company: String = "company",
|
|
||||||
quantity: Int = 1,
|
|
||||||
shippingDate: LocalDate = LocalDate.now(),
|
|
||||||
completionDate: LocalDate? = null,
|
|
||||||
finish: List<String>,
|
|
||||||
material: List<String>,
|
|
||||||
content: Set<TouchUpKitProduct>,
|
|
||||||
op: TouchUpKit.() -> Unit = {}
|
|
||||||
) = TouchUpKit(
|
|
||||||
id,
|
|
||||||
project,
|
|
||||||
buggy,
|
|
||||||
company,
|
|
||||||
quantity,
|
|
||||||
shippingDate,
|
|
||||||
completionDate,
|
|
||||||
finish.reduce { acc, f -> "$acc$TOUCH_UP_KIT_DELIMITER$f" },
|
|
||||||
material.reduce { acc, f -> "$acc$TOUCH_UP_KIT_DELIMITER$f" },
|
|
||||||
content
|
|
||||||
).apply(op)
|
|
||||||
|
|
||||||
fun touchUpKit(touchUpKitSaveDto: TouchUpKitSaveDto) =
|
|
||||||
with(touchUpKitSaveDto) {
|
|
||||||
touchUpKit(
|
|
||||||
project = project,
|
|
||||||
buggy = buggy,
|
|
||||||
company = company,
|
|
||||||
quantity = quantity,
|
|
||||||
shippingDate = shippingDate,
|
|
||||||
finish = finish,
|
|
||||||
material = material,
|
|
||||||
content = content.map { touchUpKitProduct(it) }.toSet()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun touchUpKitProduct(
|
|
||||||
id: Long? = null,
|
|
||||||
name: String = "product",
|
|
||||||
description: String? = "description",
|
|
||||||
quantity: Float = 1f,
|
|
||||||
ready: Boolean = false,
|
|
||||||
op: TouchUpKitProduct.() -> Unit = {}
|
|
||||||
) = TouchUpKitProduct(id, name, description, quantity, ready)
|
|
||||||
.apply(op)
|
|
||||||
|
|
||||||
fun touchUpKitUpdateDto(
|
|
||||||
id: Long = 0L,
|
|
||||||
project: String? = null,
|
|
||||||
buggy: String? = null,
|
|
||||||
company: String? = null,
|
|
||||||
quantity: Int? = null,
|
|
||||||
shippingDate: LocalDate? = null,
|
|
||||||
completionDate: LocalDate? = null,
|
|
||||||
finish: List<String>? = null,
|
|
||||||
material: List<String>? = null,
|
|
||||||
content: Set<TouchUpKitProductDto>? = null
|
|
||||||
) = TouchUpKitUpdateDto(id, project, buggy, company, quantity, shippingDate, completionDate, finish, material, content)
|
|
||||||
|
|
||||||
fun touchUpKitProduct(touchUpKitProductDto: TouchUpKitProductDto) =
|
|
||||||
touchUpKitProduct(
|
|
||||||
name = touchUpKitProductDto.name,
|
|
||||||
description = touchUpKitProductDto.description,
|
|
||||||
quantity = touchUpKitProductDto.quantity,
|
|
||||||
ready = touchUpKitProductDto.ready
|
|
||||||
)
|
|
||||||
|
|
||||||
// ==== Exceptions ====
|
|
||||||
private const val TOUCH_UP_KIT_NOT_FOUND_EXCEPTION_TITLE = "Touch up kit not found"
|
|
||||||
private const val TOUCH_UP_KIT_ALREADY_EXISTS_EXCEPTION_TITLE = "Touch up kit already exists"
|
|
||||||
private const val TOUCH_UP_KIT_EXCEPTION_ERROR_CODE = "touchupkit"
|
|
||||||
|
|
||||||
fun touchUpKitIdNotFoundException(id: Long) =
|
|
||||||
NotFoundException(
|
|
||||||
TOUCH_UP_KIT_EXCEPTION_ERROR_CODE,
|
|
||||||
TOUCH_UP_KIT_NOT_FOUND_EXCEPTION_TITLE,
|
|
||||||
"A touch up kit with the id $id could not be found",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
|
|
||||||
fun touchUpKitIdAlreadyExistsException(id: Long) =
|
|
||||||
AlreadyExistsException(
|
|
||||||
TOUCH_UP_KIT_EXCEPTION_ERROR_CODE,
|
|
||||||
TOUCH_UP_KIT_ALREADY_EXISTS_EXCEPTION_TITLE,
|
|
||||||
"A touch up kit with the id $id already exists",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.model.validation
|
|
||||||
|
|
||||||
import javax.validation.Constraint
|
|
||||||
import javax.validation.ConstraintValidator
|
|
||||||
import javax.validation.ConstraintValidatorContext
|
|
||||||
import javax.validation.Payload
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
private const val MESSAGE = "must be null or not blank"
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FIELD)
|
|
||||||
@MustBeDocumented
|
|
||||||
@Constraint(validatedBy = [NullOrNotBlankValidator::class])
|
|
||||||
annotation class NullOrNotBlank(
|
|
||||||
val message: String = MESSAGE,
|
|
||||||
val groups: Array<KClass<*>> = [],
|
|
||||||
@Suppress("unused") val payload: Array<KClass<out Payload>> = []
|
|
||||||
)
|
|
||||||
|
|
||||||
class NullOrNotBlankValidator : ConstraintValidator<NullOrNotBlank, String> {
|
|
||||||
var message = MESSAGE
|
|
||||||
|
|
||||||
override fun initialize(constraintAnnotation: NullOrNotBlank) {
|
|
||||||
message = constraintAnnotation.message
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean {
|
|
||||||
return value.isNullOrNotBlank().apply {
|
|
||||||
if (!this) context.buildConstraintViolationWithTemplate(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String?.isNullOrNotBlank(): Boolean = this == null || isNotBlank()
|
|
||||||
|
|
||||||
/** Checks if the given string [value] is not null and not blank. */
|
|
||||||
@ExperimentalContracts
|
|
||||||
fun isNotNullAndNotBlank(value: String?): Boolean {
|
|
||||||
contract { returns(true) implies (value != null) }
|
|
||||||
return value != null && value.isNotBlank()
|
|
||||||
}
|
|
||||||
|
|
||||||
infix fun String?.or(alternative: String): String = if (isNotNullAndNotBlank(this)) this else alternative
|
|
|
@ -1,46 +0,0 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.model.validation
|
|
||||||
|
|
||||||
import javax.validation.Constraint
|
|
||||||
import javax.validation.ConstraintValidator
|
|
||||||
import javax.validation.ConstraintValidatorContext
|
|
||||||
import javax.validation.Payload
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
private const val MIN_SIZE = Long.MIN_VALUE
|
|
||||||
private const val MAX_SIZE = Long.MAX_VALUE
|
|
||||||
private const val MESSAGE = "must be null or have a correct length"
|
|
||||||
|
|
||||||
@Target(AnnotationTarget.FIELD)
|
|
||||||
@MustBeDocumented
|
|
||||||
@Constraint(validatedBy = [NullOrSizeValidator::class])
|
|
||||||
annotation class NullOrSize(
|
|
||||||
val min: Long = MIN_SIZE,
|
|
||||||
val max: Long = MAX_SIZE,
|
|
||||||
val message: String = MESSAGE,
|
|
||||||
val groups: Array<KClass<*>> = [],
|
|
||||||
@Suppress("unused") val payload: Array<KClass<out Payload>> = []
|
|
||||||
)
|
|
||||||
|
|
||||||
class NullOrSizeValidator : ConstraintValidator<NullOrSize, Any> {
|
|
||||||
var min = MIN_SIZE
|
|
||||||
var max = MAX_SIZE
|
|
||||||
var message = MESSAGE
|
|
||||||
|
|
||||||
override fun initialize(constraintAnnotation: NullOrSize) {
|
|
||||||
min = constraintAnnotation.min
|
|
||||||
max = constraintAnnotation.max
|
|
||||||
message = constraintAnnotation.message
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isValid(value: Any?, context: ConstraintValidatorContext): Boolean {
|
|
||||||
if (value == null) return true
|
|
||||||
return when (value) {
|
|
||||||
is Number -> value.toLong() in min..max
|
|
||||||
is String -> value.length in min..max
|
|
||||||
is Collection<*> -> value.size in min..max
|
|
||||||
else -> throw IllegalStateException("Cannot use @NullOrSize on type ${value::class}")
|
|
||||||
}.apply {
|
|
||||||
if (!this) context.buildConstraintViolationWithTemplate(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package dev.fyloz.colorrecipesexplorer.repository
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.MixMaterial
|
import dev.fyloz.colorrecipesexplorer.model.MixMaterial
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
import org.springframework.stereotype.Repository
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.repository
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.MixMixType
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.data.jpa.repository.Modifying
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
|
import org.springframework.stereotype.Repository
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
interface MixMixTypeRepository : JpaRepository<MixMixType, Long> {
|
||||||
|
@Query(
|
||||||
|
nativeQuery = true, value = """
|
||||||
|
SELECT * FROM mix_mix_type mmt WHERE mmt.mix_id = :mixId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun findAllByMixId(mixId: Long): List<MixMixType>
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(
|
||||||
|
nativeQuery = true, value = """
|
||||||
|
INSERT INTO mix_mix_type (id, mix_type_id, mix_id, quantity, position)
|
||||||
|
VALUES (:id, :mixTypeId, :mixId, :quantity, :position)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun saveForMixId(id: Long?, mixTypeId: Long, mixId: Long, quantity: Float, position: Int)
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(
|
||||||
|
nativeQuery = true, value = """
|
||||||
|
DELETE FROM mix_mix_type mmt WHERE mmt.mix_id = :mixId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun deleteAllByMixId(mixId: Long)
|
||||||
|
}
|
|
@ -10,11 +10,12 @@ interface MixTypeRepository : JpaRepository<MixType, Long> {
|
||||||
/** Checks if a mix type with the given [name], [materialTypeId] and a different [id] exists. */
|
/** Checks if a mix type with the given [name], [materialTypeId] and a different [id] exists. */
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
SELECT CASE WHEN(COUNT(m) > 0) THEN TRUE ELSE FALSE END
|
SELECT CASE WHEN(COUNT(mt.id)) > 1 THEN TRUE ELSE FALSE END
|
||||||
FROM MixType m WHERE m.name = :name AND m.material.materialType.id = :materialTypeId AND m.id <> :id
|
FROM MixType mt
|
||||||
|
WHERE mt.name = :name AND mt.materialType.id = :materialTypeId AND mt.id <> :id
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long): Boolean
|
fun existsByNameAndMaterialTypeAndIdNot(name: String, materialTypeId: Long, id: Long): Boolean
|
||||||
|
|
||||||
/** Finds the mix type with the given [name] and [materialTypeId]. */
|
/** Finds the mix type with the given [name] and [materialTypeId]. */
|
||||||
@Query("SELECT m FROM MixType m WHERE m.name = :name AND m.material.materialType.id = :materialTypeId")
|
@Query("SELECT m FROM MixType m WHERE m.name = :name AND m.material.materialType.id = :materialTypeId")
|
||||||
|
|
|
@ -2,5 +2,13 @@ package dev.fyloz.colorrecipesexplorer.repository
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKit
|
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKit
|
||||||
import org.springframework.data.jpa.repository.JpaRepository
|
import org.springframework.data.jpa.repository.JpaRepository
|
||||||
|
import org.springframework.data.jpa.repository.Modifying
|
||||||
|
import org.springframework.data.jpa.repository.Query
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
interface TouchUpKitRepository : JpaRepository<TouchUpKit, Long>
|
interface TouchUpKitRepository : JpaRepository<TouchUpKit, Long> {
|
||||||
|
/** Updates the [completionDate] of the touch up kit with the given [id]. */
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE TouchUpKit t SET t.completionDate = :completionDate WHERE t.id = :id")
|
||||||
|
fun updateCompletionDateById(id: Long, completionDate: LocalDate)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.rest
|
package dev.fyloz.colorrecipesexplorer.rest
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewCatalog
|
import dev.fyloz.colorrecipesexplorer.config.annotations.PreAuthorizeViewCatalog
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.CompanyDto
|
import dev.fyloz.colorrecipesexplorer.dtos.CompanyDto
|
||||||
|
@ -8,10 +9,8 @@ import org.springframework.security.access.prepost.PreAuthorize
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import javax.validation.Valid
|
import javax.validation.Valid
|
||||||
|
|
||||||
private const val COMPANY_CONTROLLER_PATH = "api/company"
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(COMPANY_CONTROLLER_PATH)
|
@RequestMapping(Constants.ControllerPaths.COMPANY)
|
||||||
@RequireDatabase
|
@RequireDatabase
|
||||||
@PreAuthorizeViewCatalog
|
@PreAuthorizeViewCatalog
|
||||||
class CompanyController(private val companyLogic: CompanyLogic) {
|
class CompanyController(private val companyLogic: CompanyLogic) {
|
||||||
|
@ -26,7 +25,7 @@ class CompanyController(private val companyLogic: CompanyLogic) {
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('EDIT_COMPANIES')")
|
@PreAuthorize("hasAuthority('EDIT_COMPANIES')")
|
||||||
fun save(@Valid @RequestBody company: CompanyDto) =
|
fun save(@Valid @RequestBody company: CompanyDto) =
|
||||||
created<CompanyDto>(COMPANY_CONTROLLER_PATH) {
|
created<CompanyDto>(Constants.ControllerPaths.COMPANY) {
|
||||||
companyLogic.save(company)
|
companyLogic.save(company)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,6 @@ class MaterialController(
|
||||||
fun getAll() =
|
fun getAll() =
|
||||||
ok(materialLogic.getAll())
|
ok(materialLogic.getAll())
|
||||||
|
|
||||||
@GetMapping("notmixtype")
|
|
||||||
fun getAllNotMixType() =
|
|
||||||
ok(materialLogic.getAllNotMixType())
|
|
||||||
|
|
||||||
@GetMapping("{id}")
|
@GetMapping("{id}")
|
||||||
fun getById(@PathVariable id: Long) =
|
fun getById(@PathVariable id: Long) =
|
||||||
ok(materialLogic.getById(id))
|
ok(materialLogic.getById(id))
|
||||||
|
@ -54,10 +50,10 @@ class MaterialController(
|
||||||
|
|
||||||
@GetMapping("mix/create/{recipeId}")
|
@GetMapping("mix/create/{recipeId}")
|
||||||
fun getAllForMixCreation(@PathVariable recipeId: Long) =
|
fun getAllForMixCreation(@PathVariable recipeId: Long) =
|
||||||
ok(materialLogic.getAllForMixCreation(recipeId))
|
ok(materialLogic.getAllForRecipe(recipeId))
|
||||||
|
|
||||||
@GetMapping("mix/update/{mixId}")
|
@GetMapping("mix/update/{mixId}")
|
||||||
fun getAllForMixUpdate(@PathVariable mixId: Long) =
|
fun getAllForMixUpdate(@PathVariable mixId: Long) =
|
||||||
ok(materialLogic.getAllForMixUpdate(mixId))
|
ok(materialLogic.getAllForMix(mixId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import dev.fyloz.colorrecipesexplorer.logic.RecipeImageLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.RecipeLogic
|
import dev.fyloz.colorrecipesexplorer.logic.RecipeLogic
|
||||||
import org.springframework.context.annotation.Profile
|
import org.springframework.context.annotation.Profile
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.http.ResponseEntity
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize
|
import org.springframework.security.access.prepost.PreAuthorize
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.multipart.MultipartFile
|
import org.springframework.web.multipart.MultipartFile
|
||||||
|
@ -60,20 +59,19 @@ class RecipeController(private val recipeLogic: RecipeLogic, private val recipeI
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("{recipeId}/image")
|
@GetMapping("{recipeId}/image")
|
||||||
fun getAllImages(@PathVariable recipeId: Long) = ok {
|
fun getAllImages(@PathVariable recipeId: Long) =
|
||||||
recipeImageLogic.getAllImages(recipeId)
|
ok(recipeImageLogic.getAllImages(recipeId))
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("{recipeId}/image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
@PutMapping("{recipeId}/image", consumes = [MediaType.MULTIPART_FORM_DATA_VALUE])
|
||||||
@PreAuthorizeEditRecipes
|
@PreAuthorizeEditRecipes
|
||||||
fun downloadImage(@PathVariable recipeId: Long, image: MultipartFile): ResponseEntity<RecipeDto> {
|
fun downloadImage(@PathVariable recipeId: Long, image: MultipartFile) =
|
||||||
|
fileCreated("images/recipes/$recipeId") {
|
||||||
recipeImageLogic.download(image, recipeId)
|
recipeImageLogic.download(image, recipeId)
|
||||||
return getById(recipeId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("{recipeId}/image/{path}")
|
@DeleteMapping("{recipeId}/image/{id}")
|
||||||
@PreAuthorizeEditRecipes
|
@PreAuthorizeEditRecipes
|
||||||
fun deleteImage(@PathVariable recipeId: Long, @PathVariable path: String) = noContent {
|
fun deleteImage(@PathVariable recipeId: Long, @PathVariable id: String) = noContent {
|
||||||
recipeImageLogic.delete(recipeId, path)
|
recipeImageLogic.delete(recipeId, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.rest
|
package dev.fyloz.colorrecipesexplorer.rest
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.EntityDto
|
import dev.fyloz.colorrecipesexplorer.dtos.EntityDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.ModelEntity
|
import dev.fyloz.colorrecipesexplorer.model.ModelEntity
|
||||||
|
@ -35,6 +36,14 @@ fun okFile(file: Resource, mediaType: String? = null): ResponseEntity<Resource>
|
||||||
.contentType(MediaType.parseMediaType(mediaType ?: DEFAULT_MEDIA_TYPE))
|
.contentType(MediaType.parseMediaType(mediaType ?: DEFAULT_MEDIA_TYPE))
|
||||||
.body(file)
|
.body(file)
|
||||||
|
|
||||||
|
/** Creates a HTTP CREATED [ResponseEntity] for the file created by the given [producer]. */
|
||||||
|
fun fileCreated(basePath: String, producer: () -> String): ResponseEntity<String> {
|
||||||
|
val fileName = producer()
|
||||||
|
val path = "${Constants.ControllerPaths.FILE}?path=$basePath/$fileName"
|
||||||
|
|
||||||
|
return ResponseEntity.created(URI.create(path)).body(fileName)
|
||||||
|
}
|
||||||
|
|
||||||
/** Creates a HTTP CREATED [ResponseEntity] from the given [body] with the location set to [controllerPath]/id. */
|
/** Creates a HTTP CREATED [ResponseEntity] from the given [body] with the location set to [controllerPath]/id. */
|
||||||
fun <T : ModelEntity> created(controllerPath: String, body: T): ResponseEntity<T> =
|
fun <T : ModelEntity> created(controllerPath: String, body: T): ResponseEntity<T> =
|
||||||
created(controllerPath, body, body.id!!)
|
created(controllerPath, body, body.id!!)
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.rest
|
package dev.fyloz.colorrecipesexplorer.rest
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.TouchUpKitDto
|
||||||
import dev.fyloz.colorrecipesexplorer.logic.TouchUpKitLogic
|
import dev.fyloz.colorrecipesexplorer.logic.TouchUpKitLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitOutputDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitSaveDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitUpdateDto
|
|
||||||
import org.springframework.context.annotation.Profile
|
import org.springframework.context.annotation.Profile
|
||||||
import org.springframework.core.io.Resource
|
import org.springframework.core.io.Resource
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
|
@ -12,35 +11,29 @@ import org.springframework.security.access.prepost.PreAuthorize
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import javax.validation.Valid
|
import javax.validation.Valid
|
||||||
|
|
||||||
const val TOUCH_UP_KIT_CONTROLLER_PATH = "/api/touchupkit"
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping(TOUCH_UP_KIT_CONTROLLER_PATH)
|
@RequestMapping(Constants.ControllerPaths.TOUCH_UP_KIT)
|
||||||
@Profile("!emergency")
|
@Profile("!emergency")
|
||||||
@PreAuthorize("hasAuthority('VIEW_TOUCH_UP_KITS')")
|
@PreAuthorize("hasAuthority('VIEW_TOUCH_UP_KITS')")
|
||||||
class TouchUpKitController(
|
class TouchUpKitController(
|
||||||
private val touchUpKitLogic: TouchUpKitLogic
|
private val touchUpKitLogic: TouchUpKitLogic
|
||||||
) {
|
) {
|
||||||
@GetMapping
|
@GetMapping
|
||||||
fun getAll() =
|
fun getAll() = ok(touchUpKitLogic.getAll())
|
||||||
ok(touchUpKitLogic.getAllForOutput())
|
|
||||||
|
|
||||||
@GetMapping("{id}")
|
@GetMapping("{id}")
|
||||||
fun getById(@PathVariable id: Long) =
|
fun getById(@PathVariable id: Long) = ok(touchUpKitLogic.getById(id))
|
||||||
ok(touchUpKitLogic.getByIdForOutput(id))
|
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
@PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')")
|
@PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')")
|
||||||
fun save(@Valid @RequestBody touchUpKit: TouchUpKitSaveDto) =
|
fun save(@Valid @RequestBody touchUpKit: TouchUpKitDto) =
|
||||||
created<TouchUpKitOutputDto>(TOUCH_UP_KIT_CONTROLLER_PATH) {
|
created<TouchUpKitDto>(Constants.ControllerPaths.TOUCH_UP_KIT) {
|
||||||
with(touchUpKitLogic) {
|
touchUpKitLogic.save(touchUpKit)
|
||||||
save(touchUpKit).toOutput()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
@PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')")
|
@PreAuthorize("hasAuthority('EDIT_TOUCH_UP_KITS')")
|
||||||
fun update(@Valid @RequestBody touchUpKit: TouchUpKitUpdateDto) = noContent {
|
fun update(@Valid @RequestBody touchUpKit: TouchUpKitDto) = noContent {
|
||||||
touchUpKitLogic.update(touchUpKit)
|
touchUpKitLogic.update(touchUpKit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,11 @@ import dev.fyloz.colorrecipesexplorer.logic.files.FileLogic
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Material
|
import dev.fyloz.colorrecipesexplorer.model.Material
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository
|
import dev.fyloz.colorrecipesexplorer.repository.MaterialRepository
|
||||||
import org.springframework.beans.factory.annotation.Qualifier
|
import org.springframework.beans.factory.annotation.Qualifier
|
||||||
import java.net.URLEncoder
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
interface MaterialService : Service<MaterialDto, Material, MaterialRepository> {
|
interface MaterialService : Service<MaterialDto, Material, MaterialRepository> {
|
||||||
/** Checks if a material with the given [name] and a different [id] exists. */
|
/** Checks if a material with the given [name] and a different [id] exists. */
|
||||||
fun existsByName(name: String, id: Long?): Boolean
|
fun existsByName(name: String, id: Long?): Boolean
|
||||||
|
|
||||||
/** Gets all non mix type materials. */
|
|
||||||
fun getAllNotMixType(): Collection<MaterialDto>
|
|
||||||
|
|
||||||
/** Updates the [inventoryQuantity] of the [Material] with the given [id]. */
|
/** Updates the [inventoryQuantity] of the [Material] with the given [id]. */
|
||||||
fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float)
|
fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float)
|
||||||
|
|
||||||
|
@ -32,7 +27,7 @@ class DefaultMaterialService(
|
||||||
) :
|
) :
|
||||||
BaseService<MaterialDto, Material, MaterialRepository>(repository), MaterialService {
|
BaseService<MaterialDto, Material, MaterialRepository>(repository), MaterialService {
|
||||||
override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0)
|
override fun existsByName(name: String, id: Long?) = repository.existsByNameAndIdNot(name, id ?: 0)
|
||||||
override fun getAllNotMixType() = repository.findAllByIsMixTypeIsFalse().map(::toDto)
|
override fun getAll() = repository.findAllByIsMixTypeIsFalse().map(::toDto)
|
||||||
override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) =
|
override fun updateInventoryQuantityById(id: Long, inventoryQuantity: Float) =
|
||||||
repository.updateInventoryQuantityById(id, inventoryQuantity)
|
repository.updateInventoryQuantityById(id, inventoryQuantity)
|
||||||
|
|
||||||
|
@ -45,20 +40,12 @@ class DefaultMaterialService(
|
||||||
entity.inventoryQuantity,
|
entity.inventoryQuantity,
|
||||||
entity.isMixType,
|
entity.isMixType,
|
||||||
materialTypeService.toDto(entity.materialType!!),
|
materialTypeService.toDto(entity.materialType!!),
|
||||||
getSimdutUrl(entity)
|
hasSimdut(entity)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun toEntity(dto: MaterialDto) =
|
override fun toEntity(dto: MaterialDto) =
|
||||||
Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialTypeService.toEntity(dto.materialType))
|
Material(dto.id, dto.name, dto.inventoryQuantity, dto.isMixType, materialTypeService.toEntity(dto.materialType))
|
||||||
|
|
||||||
private fun getSimdutUrl(material: Material): String? {
|
private fun hasSimdut(material: Material) =
|
||||||
val filePath = "${Constants.FilePaths.SIMDUT}/${material.name}.pdf"
|
fileLogic.exists("${Constants.FilePaths.SIMDUT}/${material.name}.pdf")
|
||||||
|
|
||||||
if (!fileLogic.exists(filePath)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val encodedPath = URLEncoder.encode(filePath, StandardCharsets.UTF_8)
|
|
||||||
return "${Constants.ControllerPaths.FILE}?path=$encodedPath"
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.service
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.MixMixTypeDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.MixMixType
|
||||||
|
import dev.fyloz.colorrecipesexplorer.repository.MixMixTypeRepository
|
||||||
|
|
||||||
|
interface MixMixTypeService : Service<MixMixTypeDto, MixMixType, MixMixTypeRepository> {
|
||||||
|
fun getAllByMixId(mixId: Long): List<MixMixTypeDto>
|
||||||
|
|
||||||
|
fun saveAllForMixId(mixMixTypes: List<MixMixTypeDto>, mixId: Long): List<MixMixTypeDto>
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceComponent
|
||||||
|
class DefaultMixMixTypeService(repository: MixMixTypeRepository, private val mixTypeService: MixTypeService) :
|
||||||
|
BaseService<MixMixTypeDto, MixMixType, MixMixTypeRepository>(repository), MixMixTypeService {
|
||||||
|
override fun getAllByMixId(mixId: Long) = repository.findAllByMixId(mixId).map(::toDto)
|
||||||
|
|
||||||
|
override fun saveAllForMixId(mixMixTypes: List<MixMixTypeDto>, mixId: Long): List<MixMixTypeDto> {
|
||||||
|
repository.deleteAllByMixId(mixId)
|
||||||
|
|
||||||
|
mixMixTypes.forEach { saveForMixId(it, mixId) }
|
||||||
|
return getAllByMixId(mixId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveForMixId(mixMixType: MixMixTypeDto, mixId: Long) =
|
||||||
|
repository.saveForMixId(mixMixType.id, mixMixType.mixType.id, mixId, mixMixType.quantity, mixMixType.position)
|
||||||
|
|
||||||
|
override fun toDto(entity: MixMixType) =
|
||||||
|
MixMixTypeDto(entity.id, mixTypeService.toDto(entity.mixType), entity.quantity, entity.position)
|
||||||
|
|
||||||
|
override fun toEntity(dto: MixMixTypeDto) =
|
||||||
|
MixMixType(dto.id, mixTypeService.toEntity(dto.mixType), dto.quantity, dto.position)
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package dev.fyloz.colorrecipesexplorer.service
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.MixQuantitiesDto
|
||||||
import dev.fyloz.colorrecipesexplorer.model.Mix
|
import dev.fyloz.colorrecipesexplorer.model.Mix
|
||||||
import dev.fyloz.colorrecipesexplorer.repository.MixRepository
|
import dev.fyloz.colorrecipesexplorer.repository.MixRepository
|
||||||
|
|
||||||
|
@ -17,18 +18,29 @@ interface MixService : Service<MixDto, Mix, MixRepository> {
|
||||||
class DefaultMixService(
|
class DefaultMixService(
|
||||||
repository: MixRepository,
|
repository: MixRepository,
|
||||||
private val mixTypeService: MixTypeService,
|
private val mixTypeService: MixTypeService,
|
||||||
private val mixMaterialService: MixMaterialService
|
private val mixMaterialService: MixMaterialService,
|
||||||
|
private val mixMixTypeService: MixMixTypeService
|
||||||
) : BaseService<MixDto, Mix, MixRepository>(repository), MixService {
|
) : BaseService<MixDto, Mix, MixRepository>(repository), MixService {
|
||||||
override fun getAllByMixTypeId(mixTypeId: Long) = repository.findAllByMixTypeId(mixTypeId).map(::toDto)
|
override fun getAllByMixTypeId(mixTypeId: Long) = repository.findAllByMixTypeId(mixTypeId).map(::toDto)
|
||||||
override fun updateLocationById(id: Long, location: String?) = repository.updateLocationById(id, location)
|
override fun updateLocationById(id: Long, location: String?) = repository.updateLocationById(id, location)
|
||||||
|
|
||||||
|
override fun save(dto: MixDto): MixDto {
|
||||||
|
val savedMix = super.save(dto)
|
||||||
|
val savedMixMixTypes = mixMixTypeService.saveAllForMixId(dto.mixQuantities.mixTypes, savedMix.id)
|
||||||
|
|
||||||
|
return savedMix.copy(mixQuantities = savedMix.mixQuantities.copy(mixTypes = savedMixMixTypes))
|
||||||
|
}
|
||||||
|
|
||||||
override fun toDto(entity: Mix) =
|
override fun toDto(entity: Mix) =
|
||||||
MixDto(
|
MixDto(
|
||||||
entity.id!!,
|
entity.id!!,
|
||||||
entity.location,
|
entity.location,
|
||||||
entity.recipeId,
|
entity.recipeId,
|
||||||
mixTypeService.toDto(entity.mixType),
|
mixTypeService.toDto(entity.mixType),
|
||||||
entity.mixMaterials.map(mixMaterialService::toDto)
|
MixQuantitiesDto(
|
||||||
|
entity.mixMaterials.filter { !it.material.isMixType }.map(mixMaterialService::toDto),
|
||||||
|
mixMixTypeService.getAllByMixId(entity.id)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun toEntity(dto: MixDto) =
|
override fun toEntity(dto: MixDto) =
|
||||||
|
@ -37,6 +49,6 @@ class DefaultMixService(
|
||||||
dto.location,
|
dto.location,
|
||||||
dto.recipeId,
|
dto.recipeId,
|
||||||
mixTypeService.toEntity(dto.mixType),
|
mixTypeService.toEntity(dto.mixType),
|
||||||
dto.mixMaterials.map(mixMaterialService::toEntity)
|
dto.mixQuantities.materials.map(mixMaterialService::toEntity)
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -20,10 +20,14 @@ interface MixTypeService : Service<MixTypeDto, MixType, MixTypeRepository> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ServiceComponent
|
@ServiceComponent
|
||||||
class DefaultMixTypeService(repository: MixTypeRepository, val materialService: MaterialService) :
|
class DefaultMixTypeService(
|
||||||
|
repository: MixTypeRepository,
|
||||||
|
val materialService: MaterialService,
|
||||||
|
val materialTypeService: MaterialTypeService
|
||||||
|
) :
|
||||||
BaseService<MixTypeDto, MixType, MixTypeRepository>(repository), MixTypeService {
|
BaseService<MixTypeDto, MixType, MixTypeRepository>(repository), MixTypeService {
|
||||||
override fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long?) =
|
override fun existsByNameAndMaterialType(name: String, materialTypeId: Long, id: Long?) =
|
||||||
repository.existsByNameAndMaterialType(name, materialTypeId, id ?: 0L)
|
repository.existsByNameAndMaterialTypeAndIdNot(name, materialTypeId, id ?: 0L)
|
||||||
|
|
||||||
override fun getByNameAndMaterialType(name: String, materialTypeId: Long) =
|
override fun getByNameAndMaterialType(name: String, materialTypeId: Long) =
|
||||||
repository.findByNameAndMaterialType(name, materialTypeId)?.let(::toDto)
|
repository.findByNameAndMaterialType(name, materialTypeId)?.let(::toDto)
|
||||||
|
@ -32,8 +36,17 @@ class DefaultMixTypeService(repository: MixTypeRepository, val materialService:
|
||||||
override fun isShared(id: Long) = repository.isShared(id)
|
override fun isShared(id: Long) = repository.isShared(id)
|
||||||
|
|
||||||
override fun toDto(entity: MixType) =
|
override fun toDto(entity: MixType) =
|
||||||
MixTypeDto(entity.id!!, entity.name, materialService.toDto(entity.material))
|
MixTypeDto(
|
||||||
|
entity.id!!,
|
||||||
|
entity.name,
|
||||||
|
materialTypeService.toDto(entity.materialType),
|
||||||
|
if (entity.material != null) materialService.toDto(entity.material) else null
|
||||||
|
)
|
||||||
|
|
||||||
override fun toEntity(dto: MixTypeDto) =
|
override fun toEntity(dto: MixTypeDto) =
|
||||||
MixType(dto.id, dto.name, materialService.toEntity(dto.material))
|
MixType(
|
||||||
|
dto.id, dto.name,
|
||||||
|
materialTypeService.toEntity(dto.materialType),
|
||||||
|
if (dto.material != null) materialService.toEntity(dto.material) else null
|
||||||
|
)
|
||||||
}
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.service
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.config.annotations.ServiceComponent
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.TouchUpKitDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.dtos.TouchUpKitProductDto
|
||||||
|
import dev.fyloz.colorrecipesexplorer.logic.config.ConfigurationLogic
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKit
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.touchupkit.TouchUpKitProduct
|
||||||
|
import dev.fyloz.colorrecipesexplorer.repository.TouchUpKitRepository
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.Period
|
||||||
|
|
||||||
|
interface TouchUpKitService : Service<TouchUpKitDto, TouchUpKit, TouchUpKitRepository> {
|
||||||
|
/** Updates the [completionDate] of the touch up kit with the given [id]. */
|
||||||
|
fun updateCompletionDateById(id: Long, completionDate: LocalDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceComponent
|
||||||
|
class DefaultTouchUpKitService(repository: TouchUpKitRepository, private val configurationLogic: ConfigurationLogic) :
|
||||||
|
BaseService<TouchUpKitDto, TouchUpKit, TouchUpKitRepository>(repository), TouchUpKitService {
|
||||||
|
override fun updateCompletionDateById(id: Long, completionDate: LocalDate) =
|
||||||
|
repository.updateCompletionDateById(id, completionDate)
|
||||||
|
|
||||||
|
override fun toDto(entity: TouchUpKit) =
|
||||||
|
TouchUpKitDto(
|
||||||
|
entity.id!!,
|
||||||
|
entity.project,
|
||||||
|
entity.buggy,
|
||||||
|
entity.company,
|
||||||
|
entity.quantity,
|
||||||
|
entity.shippingDate,
|
||||||
|
entity.completionDate,
|
||||||
|
entity.completionDate != null,
|
||||||
|
isExpired(entity),
|
||||||
|
entity.finish.split(LIST_DELIMITER),
|
||||||
|
entity.material.split(LIST_DELIMITER),
|
||||||
|
entity.content.map(::touchUpKitProductToDto)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun touchUpKitProductToDto(entity: TouchUpKitProduct) =
|
||||||
|
TouchUpKitProductDto(entity.id!!, entity.name, entity.description, entity.quantity, entity.ready)
|
||||||
|
|
||||||
|
override fun toEntity(dto: TouchUpKitDto) =
|
||||||
|
TouchUpKit(
|
||||||
|
dto.id,
|
||||||
|
dto.project,
|
||||||
|
dto.buggy,
|
||||||
|
dto.company,
|
||||||
|
dto.quantity,
|
||||||
|
dto.shippingDate,
|
||||||
|
dto.completionDate,
|
||||||
|
dto.finish.joinToString(LIST_DELIMITER),
|
||||||
|
dto.material.joinToString(LIST_DELIMITER),
|
||||||
|
dto.content.map(::touchUpKitProductToEntity)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun touchUpKitProductToEntity(dto: TouchUpKitProductDto) =
|
||||||
|
TouchUpKitProduct(dto.id, dto.name, dto.description, dto.quantity, dto.ready)
|
||||||
|
|
||||||
|
private fun isExpired(touchUpKit: TouchUpKit) =
|
||||||
|
with(Period.parse(configurationLogic.getContent(ConfigurationType.TOUCH_UP_KIT_EXPIRATION))) {
|
||||||
|
touchUpKit.completionDate != null && touchUpKit.completionDate.plus(this) < LocalDate.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val LIST_DELIMITER = ";"
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,9 +99,9 @@ class DefaultInventoryLogicTest {
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
listOf()
|
listOf()
|
||||||
)
|
)
|
||||||
val mixType = MixTypeDto(1L, "Unit test mix type", material)
|
val mixType = MixTypeDto(1L, "Unit test mix type", materialType)
|
||||||
val mixMaterial = MixMaterialDto(1L, material, 1000f, 1)
|
val mixMaterial = MixMaterialDto(1L, material, 1000f, 1)
|
||||||
val mix = MixDto(1L, null, recipe.id, mixType, listOf(mixMaterial))
|
val mix = MixDto(1L, null, recipe.id, mixType, MixQuantitiesDto(listOf(mixMaterial), listOf()))
|
||||||
|
|
||||||
val dto = MixDeductDto(mix.id, 2f)
|
val dto = MixDeductDto(mix.id, 2f)
|
||||||
val expectedQuantities = listOf(MaterialQuantityDto(material.id, mixMaterial.quantity * dto.ratio))
|
val expectedQuantities = listOf(MaterialQuantityDto(material.id, mixMaterial.quantity * dto.ratio))
|
||||||
|
@ -136,9 +136,9 @@ class DefaultInventoryLogicTest {
|
||||||
mutableListOf(),
|
mutableListOf(),
|
||||||
listOf()
|
listOf()
|
||||||
)
|
)
|
||||||
val mixType = MixTypeDto(1L, "Unit test mix type", material)
|
val mixType = MixTypeDto(1L, "Unit test mix type", materialType)
|
||||||
val mixMaterial = MixMaterialDto(1L, material, 1000f, 1)
|
val mixMaterial = MixMaterialDto(1L, material, 1000f, 1)
|
||||||
val mix = MixDto(1L, null, recipe.id, mixType, listOf(mixMaterial))
|
val mix = MixDto(1L, null, recipe.id, mixType, MixQuantitiesDto(listOf(mixMaterial), listOf()))
|
||||||
|
|
||||||
val dto = MixDeductDto(mix.id, 2f)
|
val dto = MixDeductDto(mix.id, 2f)
|
||||||
val expectedQuantities = listOf(MaterialQuantityDto(material.id, mixMaterial.quantity * dto.ratio))
|
val expectedQuantities = listOf(MaterialQuantityDto(material.id, mixMaterial.quantity * dto.ratio))
|
||||||
|
|
|
@ -50,7 +50,7 @@ class DefaultMaterialLogicTest {
|
||||||
listOf()
|
listOf()
|
||||||
)
|
)
|
||||||
private val mix = MixDto(
|
private val mix = MixDto(
|
||||||
1L, "location", recipe.id, mixType = MixTypeDto(1L, "Unit test mix type", materialMixType), listOf()
|
1L, "location", recipe.id, mixType = MixTypeDto(1L, "Unit test mix type", materialType), MixQuantitiesDto()
|
||||||
)
|
)
|
||||||
private val mix2 = mix.copy(id = 2L, mixType = mix.mixType.copy(id = 2L, material = materialMixType2))
|
private val mix2 = mix.copy(id = 2L, mixType = mix.mixType.copy(id = 2L, material = materialMixType2))
|
||||||
|
|
||||||
|
@ -89,18 +89,6 @@ class DefaultMaterialLogicTest {
|
||||||
assertFalse(exists)
|
assertFalse(exists)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun getAllNotMixType_normalBehavior_returnsMaterialsFromService() {
|
|
||||||
// Arrange
|
|
||||||
every { materialServiceMock.getAllNotMixType() } returns listOf(material)
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val materials = materialLogic.getAllNotMixType()
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertContains(materials, material)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun getAllForMixCreation_normalBehavior_returnsNonMixTypeMaterials() {
|
fun getAllForMixCreation_normalBehavior_returnsNonMixTypeMaterials() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
@ -108,7 +96,7 @@ class DefaultMaterialLogicTest {
|
||||||
every { recipeLogicMock.getById(any()) } returns recipe
|
every { recipeLogicMock.getById(any()) } returns recipe
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val materials = materialLogic.getAllForMixCreation(recipe.id)
|
val materials = materialLogic.getAllForRecipe(recipe.id)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertContains(materials, material)
|
assertContains(materials, material)
|
||||||
|
@ -123,7 +111,7 @@ class DefaultMaterialLogicTest {
|
||||||
every { recipeLogicMock.getById(any()) } returns recipe
|
every { recipeLogicMock.getById(any()) } returns recipe
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val materials = materialLogic.getAllForMixCreation(recipe.id)
|
val materials = materialLogic.getAllForRecipe(recipe.id)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertContains(materials, materialMixType2)
|
assertContains(materials, materialMixType2)
|
||||||
|
@ -137,7 +125,7 @@ class DefaultMaterialLogicTest {
|
||||||
every { mixLogicMock.getById(any()) } returns mix
|
every { mixLogicMock.getById(any()) } returns mix
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val materials = materialLogic.getAllForMixUpdate(mix.id)
|
val materials = materialLogic.getAllForMix(mix.id)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertContains(materials, material)
|
assertContains(materials, material)
|
||||||
|
@ -154,26 +142,12 @@ class DefaultMaterialLogicTest {
|
||||||
every { mixLogicMock.getById(any()) } returns mix
|
every { mixLogicMock.getById(any()) } returns mix
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val materials = materialLogic.getAllForMixUpdate(mix.id)
|
val materials = materialLogic.getAllForMix(mix.id)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertContains(materials, materialMixType2)
|
assertContains(materials, materialMixType2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun getAllForMixUpdate_normalBehavior_excludesGivenMixTypeMaterial() {
|
|
||||||
// Arrange
|
|
||||||
every { materialLogic.getAll() } returns listOf(material, materialMixType, materialMixType2)
|
|
||||||
every { recipeLogicMock.getById(any()) } returns recipe
|
|
||||||
every { mixLogicMock.getById(any()) } returns mix
|
|
||||||
|
|
||||||
// Act
|
|
||||||
val materials = materialLogic.getAllForMixUpdate(mix.id)
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
assertFalse { materialMixType in materials }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun save_materialSaveDto_normalBehavior_callsSave() {
|
fun save_materialSaveDto_normalBehavior_callsSave() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
@ -11,7 +11,7 @@ class DefaultMixLogicTest {
|
||||||
private val recipeLogicMock = mockk<RecipeLogic>()
|
private val recipeLogicMock = mockk<RecipeLogic>()
|
||||||
private val materialTypeLogicMock = mockk<MaterialTypeLogic>()
|
private val materialTypeLogicMock = mockk<MaterialTypeLogic>()
|
||||||
private val mixTypeLogicMock = mockk<MixTypeLogic>()
|
private val mixTypeLogicMock = mockk<MixTypeLogic>()
|
||||||
private val mixMaterialLogicMock = mockk<MixMaterialLogic>()
|
private val mixQuantityLogicMock = mockk<MixQuantityLogic>()
|
||||||
|
|
||||||
private val mixLogic = spyk(
|
private val mixLogic = spyk(
|
||||||
DefaultMixLogic(
|
DefaultMixLogic(
|
||||||
|
@ -19,7 +19,7 @@ class DefaultMixLogicTest {
|
||||||
recipeLogicMock,
|
recipeLogicMock,
|
||||||
materialTypeLogicMock,
|
materialTypeLogicMock,
|
||||||
mixTypeLogicMock,
|
mixTypeLogicMock,
|
||||||
mixMaterialLogicMock
|
mixQuantityLogicMock
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,10 +40,11 @@ class DefaultMixLogicTest {
|
||||||
)
|
)
|
||||||
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
||||||
private val mixType =
|
private val mixType =
|
||||||
MixTypeDto(1L, "Unit test mix type", MaterialDto(1L, "Unit test mix type material", 1000f, true, materialType))
|
MixTypeDto(1L, "Unit test mix type", materialType)
|
||||||
private val mixMaterial =
|
private val mixMaterial =
|
||||||
MixMaterialDto(1L, MaterialDto(2L, "Unit test material", 1000f, false, materialType), 50f, 1)
|
MixMaterialDto(1L, MaterialDto(2L, "Unit test material", 1000f, false, materialType), 50f, 1)
|
||||||
private val mix = MixDto(recipeId = recipe.id, mixType = mixType, mixMaterials = listOf(mixMaterial))
|
private val mixQuantities = MixQuantitiesDto(listOf(mixMaterial))
|
||||||
|
private val mix = MixDto(recipeId = recipe.id, mixType = mixType, mixQuantities = mixQuantities)
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
internal fun afterEach() {
|
internal fun afterEach() {
|
||||||
|
@ -54,7 +55,7 @@ class DefaultMixLogicTest {
|
||||||
every { recipeLogicMock.getById(any()) } returns recipe
|
every { recipeLogicMock.getById(any()) } returns recipe
|
||||||
every { materialTypeLogicMock.getById(any()) } returns materialType
|
every { materialTypeLogicMock.getById(any()) } returns materialType
|
||||||
every { mixTypeLogicMock.getOrCreateForNameAndMaterialType(any(), any()) } returns mixType
|
every { mixTypeLogicMock.getOrCreateForNameAndMaterialType(any(), any()) } returns mixType
|
||||||
every { mixMaterialLogicMock.validateAndSaveAll(any()) } returns listOf(mixMaterial)
|
every { mixQuantityLogicMock.validateAndPrepareForMix(any()) } returns mixQuantities
|
||||||
every { mixLogic.save(any<MixDto>()) } returnsArgument 0
|
every { mixLogic.save(any<MixDto>()) } returnsArgument 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ class DefaultMixLogicTest {
|
||||||
every { recipeLogicMock.getById(any()) } returns recipe
|
every { recipeLogicMock.getById(any()) } returns recipe
|
||||||
every { materialTypeLogicMock.getById(any()) } returns materialType
|
every { materialTypeLogicMock.getById(any()) } returns materialType
|
||||||
every { mixTypeLogicMock.updateOrCreateForNameAndMaterialType(any(), any(), any()) } returns mixType
|
every { mixTypeLogicMock.updateOrCreateForNameAndMaterialType(any(), any(), any()) } returns mixType
|
||||||
every { mixMaterialLogicMock.validateAndSaveAll(any()) } returns listOf(mixMaterial)
|
every { mixQuantityLogicMock.validateAndPrepareForMix(any()) } returns mixQuantities
|
||||||
every { mixLogic.getById(any()) } returns mix
|
every { mixLogic.getById(any()) } returns mix
|
||||||
every { mixLogic.update(any<MixDto>()) } returnsArgument 0
|
every { mixLogic.update(any<MixDto>()) } returnsArgument 0
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ class DefaultMixLogicTest {
|
||||||
setup_save_normalBehavior()
|
setup_save_normalBehavior()
|
||||||
|
|
||||||
val mixMaterialDto =
|
val mixMaterialDto =
|
||||||
MixMaterialSaveDto(mixMaterial.id, mixMaterial.material.id, mixMaterial.quantity, mixMaterial.position)
|
MixQuantitySaveDto(mixMaterial.id, mixMaterial.material.id, mixMaterial.quantity, mixMaterial.position, false)
|
||||||
val saveDto = MixSaveDto(0L, mixType.name, recipe.id, materialType.id, listOf(mixMaterialDto))
|
val saveDto = MixSaveDto(0L, mixType.name, recipe.id, materialType.id, listOf(mixMaterialDto))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
@ -92,11 +93,12 @@ class DefaultMixLogicTest {
|
||||||
|
|
||||||
val mixMaterialDtos =
|
val mixMaterialDtos =
|
||||||
listOf(
|
listOf(
|
||||||
MixMaterialSaveDto(
|
MixQuantitySaveDto(
|
||||||
mixMaterial.id,
|
mixMaterial.id,
|
||||||
mixMaterial.material.id,
|
mixMaterial.material.id,
|
||||||
mixMaterial.quantity,
|
mixMaterial.quantity,
|
||||||
mixMaterial.position
|
mixMaterial.position,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val saveDto = MixSaveDto(0L, mixType.name, recipe.id, materialType.id, mixMaterialDtos)
|
val saveDto = MixSaveDto(0L, mixType.name, recipe.id, materialType.id, mixMaterialDtos)
|
||||||
|
@ -106,9 +108,9 @@ class DefaultMixLogicTest {
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
verify {
|
verify {
|
||||||
mixMaterialLogicMock.validateAndSaveAll(mixMaterialDtos)
|
mixQuantityLogicMock.validateAndPrepareForMix(mixMaterialDtos)
|
||||||
}
|
}
|
||||||
confirmVerified(mixMaterialLogicMock)
|
confirmVerified(mixQuantityLogicMock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -117,7 +119,7 @@ class DefaultMixLogicTest {
|
||||||
setup_update_normalBehavior()
|
setup_update_normalBehavior()
|
||||||
|
|
||||||
val mixMaterialDto =
|
val mixMaterialDto =
|
||||||
MixMaterialSaveDto(mixMaterial.id, mixMaterial.material.id, mixMaterial.quantity, mixMaterial.position)
|
MixQuantitySaveDto(mixMaterial.id, mixMaterial.material.id, mixMaterial.quantity, mixMaterial.position, false)
|
||||||
val saveDto = MixSaveDto(mix.id, mixType.name, recipe.id, materialType.id, listOf(mixMaterialDto))
|
val saveDto = MixSaveDto(mix.id, mixType.name, recipe.id, materialType.id, listOf(mixMaterialDto))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
@ -135,11 +137,12 @@ class DefaultMixLogicTest {
|
||||||
setup_update_normalBehavior()
|
setup_update_normalBehavior()
|
||||||
|
|
||||||
val mixMaterialDtos = listOf(
|
val mixMaterialDtos = listOf(
|
||||||
MixMaterialSaveDto(
|
MixQuantitySaveDto(
|
||||||
mixMaterial.id,
|
mixMaterial.id,
|
||||||
mixMaterial.material.id,
|
mixMaterial.material.id,
|
||||||
mixMaterial.quantity,
|
mixMaterial.quantity,
|
||||||
mixMaterial.position
|
mixMaterial.position,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val saveDto = MixSaveDto(mix.id, mixType.name, recipe.id, materialType.id, mixMaterialDtos)
|
val saveDto = MixSaveDto(mix.id, mixType.name, recipe.id, materialType.id, mixMaterialDtos)
|
||||||
|
@ -149,9 +152,9 @@ class DefaultMixLogicTest {
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
verify {
|
verify {
|
||||||
mixMaterialLogicMock.validateAndSaveAll(mixMaterialDtos)
|
mixQuantityLogicMock.validateAndPrepareForMix(mixMaterialDtos)
|
||||||
}
|
}
|
||||||
confirmVerified(mixMaterialLogicMock)
|
confirmVerified(mixQuantityLogicMock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -5,18 +5,17 @@ import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixMaterialDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionError
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
import dev.fyloz.colorrecipesexplorer.exception.InvalidPositionsException
|
||||||
import dev.fyloz.colorrecipesexplorer.service.MixMaterialService
|
|
||||||
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
import dev.fyloz.colorrecipesexplorer.utils.PositionUtils
|
||||||
import io.mockk.*
|
import io.mockk.*
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.assertThrows
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
|
||||||
class DefaultMixMaterialLogicTest {
|
class DefaultMixQuantityLogicTest {
|
||||||
private val mixMaterialServiceMock = mockk<MixMaterialService>()
|
|
||||||
private val materialLogicMock = mockk<MaterialLogic>()
|
private val materialLogicMock = mockk<MaterialLogic>()
|
||||||
|
private val mixTypeLogicMock = mockk<MixTypeLogic>()
|
||||||
|
|
||||||
private val mixMaterialLogic = DefaultMixMaterialLogic(mixMaterialServiceMock, materialLogicMock)
|
private val mixMaterialLogic = DefaultMixQuantityLogic(materialLogicMock, mixTypeLogicMock)
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
internal fun afterEach() {
|
internal fun afterEach() {
|
||||||
|
@ -35,7 +34,7 @@ class DefaultMixMaterialLogicTest {
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
// Assert
|
// Assert
|
||||||
mixMaterialLogic.validateMixMaterials(setOf(mixMaterial))
|
mixMaterialLogic.validateMixQuantities(listOf(mixMaterial))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -43,7 +42,7 @@ class DefaultMixMaterialLogicTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
// Act
|
// Act
|
||||||
// Assert
|
// Assert
|
||||||
mixMaterialLogic.validateMixMaterials(setOf())
|
mixMaterialLogic.validateMixQuantities(listOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -58,7 +57,7 @@ class DefaultMixMaterialLogicTest {
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
// Assert
|
// Assert
|
||||||
assertThrows<InvalidFirstMixMaterialException> { mixMaterialLogic.validateMixMaterials(setOf(mixMaterial)) }
|
assertThrows<InvalidFirstMixMaterialException> { mixMaterialLogic.validateMixQuantities(listOf(mixMaterial)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -75,6 +74,6 @@ class DefaultMixMaterialLogicTest {
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
// Assert
|
// Assert
|
||||||
assertThrows<InvalidMixMaterialsPositionsException> { mixMaterialLogic.validateMixMaterials(setOf(mixMaterial)) }
|
assertThrows<InvalidMixMaterialsPositionsException> { mixMaterialLogic.validateMixQuantities(listOf(mixMaterial)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.fyloz.colorrecipesexplorer.logic
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialDto
|
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MaterialTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
import dev.fyloz.colorrecipesexplorer.dtos.MixTypeDto
|
||||||
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
import dev.fyloz.colorrecipesexplorer.exception.CannotDeleteException
|
||||||
|
@ -9,18 +8,15 @@ import io.mockk.*
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.assertThrows
|
import org.junit.jupiter.api.assertThrows
|
||||||
import kotlin.math.exp
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class DefaultMixTypeLogicTest {
|
class DefaultMixTypeLogicTest {
|
||||||
private val mixTypeServiceMock = mockk<MixTypeService>()
|
private val mixTypeServiceMock = mockk<MixTypeService>()
|
||||||
private val materialLogicMock = mockk<MaterialLogic>()
|
|
||||||
|
|
||||||
private val mixTypeLogic = spyk(DefaultMixTypeLogic(mixTypeServiceMock, materialLogicMock))
|
private val mixTypeLogic = spyk(DefaultMixTypeLogic(mixTypeServiceMock))
|
||||||
|
|
||||||
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
private val materialType = MaterialTypeDto(1L, "Unit test material type", "UTMT", false)
|
||||||
private val material = MaterialDto(1L, "Unit test material", 1000f, true, materialType)
|
private val mixType = MixTypeDto(id = 1L, name = "Unit test mix type", materialType)
|
||||||
private val mixType = MixTypeDto(id = 1L, name = "Unit test mix type", material)
|
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun afterEach() {
|
fun afterEach() {
|
||||||
|
@ -43,7 +39,6 @@ class DefaultMixTypeLogicTest {
|
||||||
fun getOrCreateForNameAndMaterialType_notFound_callsSave() {
|
fun getOrCreateForNameAndMaterialType_notFound_callsSave() {
|
||||||
// Arrange
|
// Arrange
|
||||||
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns null
|
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns null
|
||||||
every { materialLogicMock.save(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(id = 0L)
|
val expectedMixType = mixType.copy(id = 0L)
|
||||||
|
@ -61,7 +56,6 @@ class DefaultMixTypeLogicTest {
|
||||||
fun getOrCreateForNameAndMaterialType_notFound_returnsFromSave() {
|
fun getOrCreateForNameAndMaterialType_notFound_returnsFromSave() {
|
||||||
// Arrange
|
// Arrange
|
||||||
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns null
|
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns null
|
||||||
every { materialLogicMock.save(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(id = 0L)
|
val expectedMixType = mixType.copy(id = 0L)
|
||||||
|
@ -73,11 +67,26 @@ class DefaultMixTypeLogicTest {
|
||||||
assertEquals(expectedMixType, actualMixType)
|
assertEquals(expectedMixType, actualMixType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun updateOrCreateForNameAndMaterialType_alreadyExists_returnsFromgetByNameAndMaterialType() {
|
||||||
|
// Arrange
|
||||||
|
every { mixTypeServiceMock.existsByNameAndMaterialType(any(), any(), any()) } returns true
|
||||||
|
every { mixTypeServiceMock.isShared(any()) } returns true
|
||||||
|
every { mixTypeServiceMock.getByNameAndMaterialType(any(), any()) } returns mixType
|
||||||
|
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val actualMixType = mixTypeLogic.updateOrCreateForNameAndMaterialType(mixType, mixType.name, materialType)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(mixType, actualMixType)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun updateOrCreateForNameAndMaterialType_mixTypeShared_callsSave() {
|
fun updateOrCreateForNameAndMaterialType_mixTypeShared_callsSave() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
every { mixTypeServiceMock.existsByNameAndMaterialType(any(), any(), any()) } returns false
|
||||||
every { mixTypeServiceMock.isShared(any()) } returns true
|
every { mixTypeServiceMock.isShared(any()) } returns true
|
||||||
every { materialLogicMock.save(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(id = 0L, name = "${mixType.name} updated")
|
val expectedMixType = mixType.copy(id = 0L, name = "${mixType.name} updated")
|
||||||
|
@ -94,8 +103,8 @@ class DefaultMixTypeLogicTest {
|
||||||
@Test
|
@Test
|
||||||
fun updateOrCreateForNameAndMaterialType_mixTypeShared_returnsFromSave() {
|
fun updateOrCreateForNameAndMaterialType_mixTypeShared_returnsFromSave() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
every { mixTypeServiceMock.existsByNameAndMaterialType(any(), any(), any()) } returns false
|
||||||
every { mixTypeServiceMock.isShared(any()) } returns true
|
every { mixTypeServiceMock.isShared(any()) } returns true
|
||||||
every { materialLogicMock.save(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.save(any()) } returnsArgument 0
|
every { mixTypeLogic.save(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(id = 0L, name = "${mixType.name} updated")
|
val expectedMixType = mixType.copy(id = 0L, name = "${mixType.name} updated")
|
||||||
|
@ -110,8 +119,8 @@ class DefaultMixTypeLogicTest {
|
||||||
@Test
|
@Test
|
||||||
fun updateOrCreateForNameAndMaterialType_mixTypeNotShared_callsUpdate() {
|
fun updateOrCreateForNameAndMaterialType_mixTypeNotShared_callsUpdate() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
every { mixTypeServiceMock.existsByNameAndMaterialType(any(), any(), any()) } returns false
|
||||||
every { mixTypeServiceMock.isShared(any()) } returns false
|
every { mixTypeServiceMock.isShared(any()) } returns false
|
||||||
every { materialLogicMock.update(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.update(any()) } returnsArgument 0
|
every { mixTypeLogic.update(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(name = "${mixType.name} updated")
|
val expectedMixType = mixType.copy(name = "${mixType.name} updated")
|
||||||
|
@ -128,8 +137,8 @@ class DefaultMixTypeLogicTest {
|
||||||
@Test
|
@Test
|
||||||
fun updateOrCreateForNameAndMaterialType_mixTypeNotShared_returnsFromUpdate() {
|
fun updateOrCreateForNameAndMaterialType_mixTypeNotShared_returnsFromUpdate() {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
every { mixTypeServiceMock.existsByNameAndMaterialType(any(), any(), any()) } returns false
|
||||||
every { mixTypeServiceMock.isShared(any()) } returns false
|
every { mixTypeServiceMock.isShared(any()) } returns false
|
||||||
every { materialLogicMock.update(any<MaterialDto>()) } returns material
|
|
||||||
every { mixTypeLogic.update(any()) } returnsArgument 0
|
every { mixTypeLogic.update(any()) } returnsArgument 0
|
||||||
|
|
||||||
val expectedMixType = mixType.copy(name = "${mixType.name} updated")
|
val expectedMixType = mixType.copy(name = "${mixType.name} updated")
|
||||||
|
|
|
@ -88,10 +88,11 @@ class DefaultRecipeImageLogicTest {
|
||||||
every { fileLogicMock.deleteFromDirectory(any(), any()) } just runs
|
every { fileLogicMock.deleteFromDirectory(any(), any()) } just runs
|
||||||
|
|
||||||
val recipeImagesDirectoryPath = "${Constants.FilePaths.RECIPE_IMAGES}/$recipeId"
|
val recipeImagesDirectoryPath = "${Constants.FilePaths.RECIPE_IMAGES}/$recipeId"
|
||||||
val imagePath = "$recipeImagesDirectoryPath/1"
|
val imageId = 1.toString()
|
||||||
|
val imagePath = "$recipeImagesDirectoryPath/$imageId"
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
recipeImageLogic.delete(recipeId, imagePath)
|
recipeImageLogic.delete(recipeId, imageId)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
verify {
|
verify {
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
package dev.fyloz.colorrecipesexplorer.logic
|
||||||
|
|
||||||
|
import dev.fyloz.colorrecipesexplorer.Constants
|
||||||
|
import dev.fyloz.colorrecipesexplorer.logic.config.ConfigurationLogic
|
||||||
|
import dev.fyloz.colorrecipesexplorer.logic.files.WriteableFileLogic
|
||||||
|
import dev.fyloz.colorrecipesexplorer.model.ConfigurationType
|
||||||
|
import dev.fyloz.colorrecipesexplorer.service.TouchUpKitService
|
||||||
|
import dev.fyloz.colorrecipesexplorer.utils.PdfDocument
|
||||||
|
import dev.fyloz.colorrecipesexplorer.utils.toByteArrayResource
|
||||||
|
import io.mockk.*
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.springframework.core.io.ByteArrayResource
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class DefaultTouchUpKitLogicTest {
|
||||||
|
private val touchUpKitServiceMock = mockk<TouchUpKitService>()
|
||||||
|
private val fileLogicMock = mockk<WriteableFileLogic>()
|
||||||
|
private val configLogicMock = mockk<ConfigurationLogic>()
|
||||||
|
|
||||||
|
private val touchUpKitLogic = spyk(DefaultTouchUpKitLogic(touchUpKitServiceMock, fileLogicMock, configLogicMock))
|
||||||
|
|
||||||
|
private val pdfMockData = mockk<ByteArrayResource>()
|
||||||
|
private val pdfMock = mockk<PdfDocument> {
|
||||||
|
mockkStatic(PdfDocument::toByteArrayResource)
|
||||||
|
mockkStatic(PdfDocument::toByteArrayResource)
|
||||||
|
every { toByteArrayResource() } returns pdfMockData
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
internal fun afterEach() {
|
||||||
|
clearAllMocks()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun complete_normalBehavior_callsUpdateCompletionDateByIdInService() {
|
||||||
|
// Arrange
|
||||||
|
every { touchUpKitServiceMock.updateCompletionDateById(any(), any()) } just runs
|
||||||
|
|
||||||
|
val touchUpKitId = 1L
|
||||||
|
|
||||||
|
// Act
|
||||||
|
touchUpKitLogic.complete(touchUpKitId)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify {
|
||||||
|
touchUpKitServiceMock.updateCompletionDateById(touchUpKitId, any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateJobPdfResource_normalBehavior_returnsGeneratedPdf() {
|
||||||
|
// Arrange
|
||||||
|
every { touchUpKitLogic.generateJobPdf(any()) } returns pdfMock
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns false.toString()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val generatedPdfData = touchUpKitLogic.generateJobPdfResource("Unit test job")
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(pdfMockData, generatedPdfData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateJobPdfResource_normalBehavior_callsCacheJobPdf() {
|
||||||
|
// Arrange
|
||||||
|
every { touchUpKitLogic.generateJobPdf(any()) } returns pdfMock
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns false.toString()
|
||||||
|
|
||||||
|
val job = "Unit test job"
|
||||||
|
|
||||||
|
// Act
|
||||||
|
touchUpKitLogic.generateJobPdfResource(job)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify {
|
||||||
|
touchUpKitLogic.cacheJobPdf(job, pdfMock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateJobPdfResource_cacheEnabledAndPdfExists_returnsCachedJobPdf() {
|
||||||
|
// Arrange
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns true.toString()
|
||||||
|
every { fileLogicMock.exists(any()) } returns true
|
||||||
|
every { fileLogicMock.read(any()) } returns pdfMockData
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val pdfData = touchUpKitLogic.generateJobPdfResource("Unit test job")
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertEquals(pdfMockData, pdfData)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateJobPdfResource_cacheEnabledAndPdfNotExists_generatesPdf() {
|
||||||
|
// Arrange
|
||||||
|
every { touchUpKitLogic.generateJobPdf(any()) } returns pdfMock
|
||||||
|
every { touchUpKitLogic.cacheJobPdf(any(), any()) } just runs
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns true.toString()
|
||||||
|
every { fileLogicMock.exists(any()) } returns false
|
||||||
|
|
||||||
|
// Act
|
||||||
|
touchUpKitLogic.generateJobPdfResource("Unit test job")
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify {
|
||||||
|
touchUpKitLogic.cacheJobPdf(any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cacheJobPdf_normalBehavior_callsWriteInFileLogic() {
|
||||||
|
// Arrange
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns true.toString()
|
||||||
|
every { fileLogicMock.write(any<ByteArrayResource>(), any(), any()) } just runs
|
||||||
|
|
||||||
|
val job = "Unit test job"
|
||||||
|
val pdfPath = "${Constants.FilePaths.TOUCH_UP_KITS}/$job.pdf"
|
||||||
|
|
||||||
|
// Act
|
||||||
|
touchUpKitLogic.cacheJobPdf(job, pdfMock)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify {
|
||||||
|
fileLogicMock.write(pdfMockData, pdfPath, true)
|
||||||
|
}
|
||||||
|
confirmVerified(fileLogicMock)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cacheJobPdf_cacheDisabled_doNothing() {
|
||||||
|
// Arrange
|
||||||
|
every { configLogicMock.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns false.toString()
|
||||||
|
every { fileLogicMock.write(any<ByteArrayResource>(), any(), any()) } just runs
|
||||||
|
|
||||||
|
// Act
|
||||||
|
touchUpKitLogic.cacheJobPdf("Unit test job", pdfMock)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(exactly = 0) {
|
||||||
|
fileLogicMock.write(any<ByteArrayResource>(), any(), any())
|
||||||
|
}
|
||||||
|
confirmVerified(fileLogicMock)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,126 +13,126 @@ import org.junit.jupiter.api.Test
|
||||||
import org.springframework.core.io.ByteArrayResource
|
import org.springframework.core.io.ByteArrayResource
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
private class TouchUpKitServiceTestContext {
|
//private class TouchUpKitServiceTestContext {
|
||||||
val touchUpKitRepository = mockk<TouchUpKitRepository>()
|
// val touchUpKitRepository = mockk<TouchUpKitRepository>()
|
||||||
val fileService = mockk<WriteableFileLogic> {
|
// val fileService = mockk<WriteableFileLogic> {
|
||||||
every { write(any<ByteArrayResource>(), any(), any()) } just Runs
|
// every { write(any<ByteArrayResource>(), any(), any()) } just Runs
|
||||||
}
|
// }
|
||||||
val configService = mockk<ConfigurationLogic>(relaxed = true)
|
// val configService = mockk<ConfigurationLogic>(relaxed = true)
|
||||||
val touchUpKitService = spyk(DefaultTouchUpKitLogic(fileService, configService, touchUpKitRepository))
|
// val touchUpKitService = spyk(DefaultTouchUpKitLogic(fileService, configService, touchUpKitRepository))
|
||||||
val pdfDocumentData = mockk<ByteArrayResource>()
|
// val pdfDocumentData = mockk<ByteArrayResource>()
|
||||||
val pdfDocument = mockk<PdfDocument> {
|
// val pdfDocument = mockk<PdfDocument> {
|
||||||
mockkStatic(PdfDocument::toByteArrayResource)
|
// mockkStatic(PdfDocument::toByteArrayResource)
|
||||||
mockkStatic(PdfDocument::toByteArrayResource)
|
// mockkStatic(PdfDocument::toByteArrayResource)
|
||||||
every { toByteArrayResource() } returns pdfDocumentData
|
// every { toByteArrayResource() } returns pdfDocumentData
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
class TouchUpKitLogicTest {
|
class TouchUpKitLogicTest {
|
||||||
private val job = "job"
|
// private val job = "job"
|
||||||
|
//
|
||||||
@AfterEach
|
// @AfterEach
|
||||||
internal fun afterEach() {
|
// internal fun afterEach() {
|
||||||
clearAllMocks()
|
// clearAllMocks()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// generateJobPdf()
|
// // generateJobPdf()
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
fun `generateJobPdf() generates a valid PdfDocument for the given job`() {
|
// fun `generateJobPdf() generates a valid PdfDocument for the given job`() {
|
||||||
test {
|
// test {
|
||||||
val generatedPdfDocument = touchUpKitService.generateJobPdf(job)
|
// val generatedPdfDocument = touchUpKitService.generateJobPdf(job)
|
||||||
|
//
|
||||||
setOf(0, 1).forEach {
|
// setOf(0, 1).forEach {
|
||||||
assertEquals(TOUCH_UP_TEXT_FR, generatedPdfDocument.containers[it].texts[0].text)
|
// assertEquals(TOUCH_UP_TEXT_FR, generatedPdfDocument.containers[it].texts[0].text)
|
||||||
assertEquals(TOUCH_UP_TEXT_EN, generatedPdfDocument.containers[it].texts[1].text)
|
// assertEquals(TOUCH_UP_TEXT_EN, generatedPdfDocument.containers[it].texts[1].text)
|
||||||
assertEquals(job, generatedPdfDocument.containers[it].texts[2].text)
|
// assertEquals(job, generatedPdfDocument.containers[it].texts[2].text)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// generateJobPdfResource()
|
// // generateJobPdfResource()
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
fun `generateJobPdfResource() generates and returns a ByteArrayResource for the given job then cache it`() {
|
// fun `generateJobPdfResource() generates and returns a ByteArrayResource for the given job then cache it`() {
|
||||||
test {
|
// test {
|
||||||
every { touchUpKitService.generateJobPdf(any()) } returns pdfDocument
|
// every { touchUpKitService.generateJobPdf(any()) } returns pdfDocument
|
||||||
with(touchUpKitService) {
|
// with(touchUpKitService) {
|
||||||
every { job.cachePdfDocument(pdfDocument) } just Runs
|
// every { job.cachePdfDocument(pdfDocument) } just Runs
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
val generatedResource = touchUpKitService.generateJobPdfResource(job)
|
// val generatedResource = touchUpKitService.generateJobPdfResource(job)
|
||||||
|
//
|
||||||
assertEquals(pdfDocumentData, generatedResource)
|
// assertEquals(pdfDocumentData, generatedResource)
|
||||||
|
//
|
||||||
verify {
|
// verify {
|
||||||
with(touchUpKitService) {
|
// with(touchUpKitService) {
|
||||||
job.cachePdfDocument(pdfDocument)
|
// job.cachePdfDocument(pdfDocument)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
fun `generateJobPdfResource() returns a cached ByteArrayResource from the FileService when caching is enabled and a cached file eixsts for the given job`() {
|
// fun `generateJobPdfResource() returns a cached ByteArrayResource from the FileService when caching is enabled and a cached file eixsts for the given job`() {
|
||||||
test {
|
// test {
|
||||||
enableCachePdf()
|
// enableCachePdf()
|
||||||
every { fileService.exists(any()) } returns true
|
// every { fileService.exists(any()) } returns true
|
||||||
every { fileService.read(any()) } returns pdfDocumentData
|
// every { fileService.read(any()) } returns pdfDocumentData
|
||||||
every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(
|
// every { configService.get(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns configuration(
|
||||||
ConfigurationType.TOUCH_UP_KIT_CACHE_PDF,
|
// ConfigurationType.TOUCH_UP_KIT_CACHE_PDF,
|
||||||
"true"
|
// "true"
|
||||||
)
|
// )
|
||||||
|
//
|
||||||
val redResource = touchUpKitService.generateJobPdfResource(job)
|
// val redResource = touchUpKitService.generateJobPdfResource(job)
|
||||||
|
//
|
||||||
assertEquals(pdfDocumentData, redResource)
|
// assertEquals(pdfDocumentData, redResource)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// String.cachePdfDocument()
|
// // String.cachePdfDocument()
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
fun `cachePdfDocument() does nothing when caching is disabled`() {
|
// fun `cachePdfDocument() does nothing when caching is disabled`() {
|
||||||
test {
|
// test {
|
||||||
disableCachePdf()
|
// disableCachePdf()
|
||||||
|
//
|
||||||
with(touchUpKitService) {
|
// with(touchUpKitService) {
|
||||||
job.cachePdfDocument(pdfDocument)
|
// job.cachePdfDocument(pdfDocument)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
verify(exactly = 0) {
|
// verify(exactly = 0) {
|
||||||
fileService.write(any<ByteArrayResource>(), any(), any())
|
// fileService.write(any<ByteArrayResource>(), any(), any())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
fun `cachePdfDocument() writes the given document to the FileService when cache is enabled`() {
|
// fun `cachePdfDocument() writes the given document to the FileService when cache is enabled`() {
|
||||||
test {
|
// test {
|
||||||
enableCachePdf()
|
// enableCachePdf()
|
||||||
|
//
|
||||||
with(touchUpKitService) {
|
// with(touchUpKitService) {
|
||||||
job.cachePdfDocument(pdfDocument)
|
// job.cachePdfDocument(pdfDocument)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
verify {
|
// verify {
|
||||||
fileService.write(pdfDocumentData, any(), true)
|
// fileService.write(pdfDocumentData, any(), true)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private fun TouchUpKitServiceTestContext.enableCachePdf() =
|
// private fun TouchUpKitServiceTestContext.enableCachePdf() =
|
||||||
this.setCachePdf(true)
|
// this.setCachePdf(true)
|
||||||
|
//
|
||||||
private fun TouchUpKitServiceTestContext.disableCachePdf() =
|
// private fun TouchUpKitServiceTestContext.disableCachePdf() =
|
||||||
this.setCachePdf(false)
|
// this.setCachePdf(false)
|
||||||
|
//
|
||||||
private fun TouchUpKitServiceTestContext.setCachePdf(enabled: Boolean) {
|
// private fun TouchUpKitServiceTestContext.setCachePdf(enabled: Boolean) {
|
||||||
every { configService.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns enabled.toString()
|
// every { configService.getContent(ConfigurationType.TOUCH_UP_KIT_CACHE_PDF) } returns enabled.toString()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private fun test(test: TouchUpKitServiceTestContext.() -> Unit) {
|
// private fun test(test: TouchUpKitServiceTestContext.() -> Unit) {
|
||||||
TouchUpKitServiceTestContext().test()
|
// TouchUpKitServiceTestContext().test()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue