Empêcher la création de mélanges avec le même nom qu'un produit

Les types de produit par défaut son maintenant définis dans les propriétés
This commit is contained in:
FyloZ 2020-02-26 10:27:57 -05:00
parent 58a60645b4
commit 5fa6cf1328
22 changed files with 114 additions and 3437 deletions

2
.gitignore vendored
View File

@ -29,6 +29,4 @@ HELP.md
.vscode/
/logs/
*.log
/workdir/
*.db

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
package dev.fyloz.trial.colorrecipesexplorer;
import dev.fyloz.trial.colorrecipesexplorer.core.model.config.CREProperties;
import dev.fyloz.trial.colorrecipesexplorer.core.model.config.MaterialTypeProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication
@EnableConfigurationProperties(MaterialTypeProperties.class)
@EnableConfigurationProperties({MaterialTypeProperties.class, CREProperties.class})
public class ColorRecipesExplorerApplication {
public static void main(String[] args) {

View File

@ -15,4 +15,6 @@ public class Preferences {
public static String passwordsFileName;
public static String baseMaterialTypeName;
}

View File

@ -2,15 +2,18 @@ package dev.fyloz.trial.colorrecipesexplorer.core.configuration;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.Preferences;
import dev.fyloz.trial.colorrecipesexplorer.core.model.config.CREProperties;
import dev.fyloz.trial.colorrecipesexplorer.core.model.config.MaterialTypeProperties;
import dev.fyloz.trial.colorrecipesexplorer.core.services.PasswordService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.files.FilesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.io.IOException;
import java.util.List;
@ -18,37 +21,46 @@ import java.util.List;
@Configuration
public class SpringConfiguration {
@Value("${url.useport}")
private boolean usePort;
@Value("${server.upload-directory}")
private String uploadDirectory;
@Value("${server.passwords.file-name}")
private String passwordsFileName;
private FilesService filesService;
private MessageSource messageSource;
private CREProperties creProperties;
private MaterialTypeProperties materialTypeProperties;
@Autowired
public SpringConfiguration(FilesService filesService, MessageSource messageSource) {
public void setFilesService(FilesService filesService) {
this.filesService = filesService;
}
@Autowired
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
@Autowired
@Qualifier("CREProperties")
public void setCreProperties(CREProperties creProperties) {
this.creProperties = creProperties;
}
@Autowired
public void setMaterialTypeProperties(MaterialTypeProperties materialTypeProperties) {
this.materialTypeProperties = materialTypeProperties;
}
@Bean
public void setPreferences() {
Preferences.urlUsePort = usePort;
Preferences.uploadDirectory = uploadDirectory;
Preferences.passwordsFileName = passwordsFileName;
Preferences.urlUsePort = creProperties.isUrlUsePort();
Preferences.uploadDirectory = creProperties.getUploadDirectory();
Preferences.passwordsFileName = creProperties.getPasswordFile();
Preferences.logger = LoggerFactory.getLogger(ColorRecipesExplorerApplication.class);
Preferences.messageSource = messageSource;
Preferences.baseMaterialTypeName = materialTypeProperties.getBaseName();
}
@Bean
public void initializePasswords() {
Logger logger = Preferences.logger;
String filePath = filesService.getPath(passwordsFileName);
String filePath = filesService.getPath(Preferences.passwordsFileName);
logger.info("Le fichier des utilisateurs se situe à: " + filesService.getFile(filePath).getAbsolutePath());

View File

@ -4,6 +4,8 @@ import dev.fyloz.trial.colorrecipesexplorer.core.model.IModel;
import lombok.Getter;
import lombok.NonNull;
import javax.swing.text.html.parser.Entity;
@Getter
public class EntityAlreadyExistsException extends ModelException {
@ -15,6 +17,10 @@ public class EntityAlreadyExistsException extends ModelException {
@NonNull
private Object requestedId;
public EntityAlreadyExistsException(EntityAlreadyExistsException ex) {
this(ex.type, ex.identifierType, ex.identifierName, ex.requestedId);
}
public EntityAlreadyExistsException(Class<? extends IModel> type, IdentifierType identifierType, Object requestedId) {
super(type);
this.identifierType = identifierType;

View File

@ -37,8 +37,4 @@ public class Material implements IModel {
@ManyToOne
private MaterialType materialType;
public boolean isMixType() {
return isMixType;
}
}

View File

@ -13,6 +13,8 @@ import java.util.List;
@NoArgsConstructor
public class Mix implements IModel {
public static final String IDENTIFIER_MIX_TYPE_NAME = "mixType";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Basic

View File

@ -0,0 +1,18 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "cre.server")
@Getter
@Setter
public class CREProperties {
private String passwordFile;
private String uploadDirectory;
private boolean urlUsePort;
}

View File

@ -16,4 +16,6 @@ public class MaterialTypeProperties {
private List<MaterialType> defaults;
private String baseName;
}

View File

@ -1,12 +1,15 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model.dto;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
public class MixFormDto {
private Recipe recipe;
@ -21,6 +24,4 @@ public class MixFormDto {
private List<Float> quantities;
public MixFormDto() {
}
}

View File

@ -82,11 +82,8 @@ public class MixService extends GenericService<Mix, MixDao> {
.withDto(formDto)
.build();
if (materialService.existsByName(formDto.getMixTypeName()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, "materialName", formDto.getMixTypeName());
if (mix.getRecipe().hasMixType(mix.getMixType()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, "mixType", mix.getMixType());
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, Mix.IDENTIFIER_MIX_TYPE_NAME, mix.getMixType().getName());
mixTypeService.save(mix.getMixType());
save(mix);
@ -99,10 +96,12 @@ public class MixService extends GenericService<Mix, MixDao> {
.build();
MixType mixType = mix.getMixType();
if (!formDto.getOldMixTypeName().equals(mixType.getName()) && mixTypeService.existsByName(formDto.getMixTypeName()) && mix.getRecipe().hasMixType(mixTypeService.getByName(formDto.getMixTypeName())))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, Mix.IDENTIFIER_MIX_TYPE_NAME, mix.getMixType().getName());
if (materialService.existsByName(mixType.getName()) && !materialService.getByName(mixType.getName()).equals(mixType.getMaterial()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, "materialName", mixType.getName());
if (!formDto.getOldMixTypeName().equals(mixType.getName()) && mix.getRecipe().hasMixType(mix.getMixType()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, "mixType", mix.getMixType());
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, MixType.IDENTIFIER_MATERIAL_NAME, mixType.getName());
update(mix);
}

View File

@ -11,6 +11,7 @@ import dev.fyloz.trial.colorrecipesexplorer.dao.MixTypeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.constraints.NotNull;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -26,6 +27,16 @@ public class MixTypeService extends GenericService<MixType, MixTypeDao> {
this.materialService = materialService;
}
/**
* Vérifie si un type de mélange correspondant à un nom existe.
*
* @param name Le nom du type de mélange
* @return Si un type de mélange correspondant au nom existe
*/
public boolean existsByName(String name) {
return dao.existsByName(name);
}
/**
* Récupère le type de mélange correspondant à un nom.
*
@ -65,6 +76,14 @@ public class MixTypeService extends GenericService<MixType, MixTypeDao> {
return new MixType(name, mixTypeMaterial);
}
@Override
public MixType save(@NotNull MixType entity) {
if (materialService.existsByName(entity.getName()))
throw new EntityAlreadyExistsException(type, ModelException.IdentifierType.OTHER, MixType.IDENTIFIER_MATERIAL_NAME, entity.getName());
return super.save(entity);
}
@Override
public MixType update(MixType mixType) {

View File

@ -94,7 +94,7 @@ public class RecipeService extends GenericService<Recipe, RecipeDao> {
// Note
recipe.setNote(note);
save(recipe);
update(recipe);
// Casiers
for (Map.Entry<Long, String> location : locations.entrySet()) {

View File

@ -9,6 +9,9 @@ import java.util.Optional;
@Repository
public interface MixTypeDao extends JpaRepository<MixType, Long> {
boolean existsByName(String name);
Optional<MixType> findByName(String name);
Optional<MixType> findByMaterial(Material material);

View File

@ -11,10 +11,7 @@ import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
@ -53,7 +50,7 @@ public class RecipeExplorerController {
@PostMapping(value = EXPLORER_RECIPE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, Object> saveRecipeInformations(RecipeExplorerFormDto form) {
public Map<String, Object> saveRecipeInformations(@RequestBody RecipeExplorerFormDto form) {
JSONResponseBuilder responseBuilder = new JSONResponseBuilder();
try {

View File

@ -6,6 +6,7 @@ import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilde
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
@ -64,15 +65,18 @@ public class MixEditorController {
Mix mix = mixService.getById(id);
mixService.update(mix, formDto);
return modelResponseBuilder.build();
} catch (EntityNotFoundException ex) {
return getPage(modelResponseBuilder.addResponseCode(ResponseCode.MIX_NOT_FOUND, id).build(), id);
modelResponseBuilder.addResponseCode(ResponseCode.MIX_NOT_FOUND, id).build();
} catch (EntityAlreadyExistsException ex) {
if (ex.getIdentifierName().equals("materialName"))
return getPage(modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_AND_MIX_TYPE_CANNOT_HAVE_SAME_NAME).build(), id);
if (ex.getIdentifierName().equals("mixType"))
if (ex.getIdentifierName().equals(MixType.IDENTIFIER_MATERIAL_NAME))
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_AND_MIX_TYPE_CANNOT_HAVE_SAME_NAME);
else if (ex.getIdentifierName().equals(Mix.IDENTIFIER_MIX_TYPE_NAME))
modelResponseBuilder.addResponseCode(ResponseCode.MIX_TYPE_ALREADY_USED, ex.getRequestedId());
else throw new EntityAlreadyExistsException(ex);
}
return modelResponseBuilder.build();
return getPage(modelResponseBuilder.build(), id);
}
}

View File

@ -21,10 +21,11 @@ spring.h2.console.settings.web-allow-others=true
server.port=9090
server.http2.enabled=true
server.error.whitelabel.enabled=false
server.upload-directory=./workdir
server.passwords.file-name=passwords.txt
url.useport=true
# CRE CONFIG
cre.server.upload-directory=./workdir
cre.server.password-file=passwords.txt
cre.server.url-use-port=true
# DEFAULT MATERIAL TYPES
entities.material-types.defaults[0].name=Aucun
@ -35,6 +36,8 @@ entities.material-types.defaults[1].name=Base
entities.material-types.defaults[1].prefix=BAS
entities.material-types.defaults[1].use-percentages=false
entities.material-types.base-name=Base
# DEBUG
spring.jpa.show-sql=true
spring.h2.console.enabled=true

View File

@ -7,7 +7,7 @@ response.14=Le produit {0} est lié à une ou plusieurs recettes, veuillez les m
response.15=La bannière {0} est liée à une ou plusieurs recettes, veuillez les supprimer d'abord
response.16=Le mélange ayant l''identifiant {0} n''est pas associé à la recette ayant l''identifiant {1}
response.17=Il n''y a pas assez de {0} en inventaire
response.18=Cette recette contient déjà un mélange du type {0}
response.18=Cette recette contient déjà un mélange de type {0}
response.19=Aucun type de produit ayant le nom {0} n''a été trouvée
response.2=Les informations de la recette ont été sauvegardées
response.3=Une erreur est survenue lors de l''enregistrement

View File

@ -19,6 +19,7 @@
<!-- Information nécessaire à la création des mélanges -->
<input id="recipeId" name="recipe" th:value="${recipe.id}" type="hidden"/>
<input id="mixId" name="mixId" th:value="${mix.id}" type="hidden"/>
<input id="oldMixTypeName" name="oldMixTypeName" th:value="${mix.mixType.name}" type="hidden"/>
<div class="content">
<div class="flexContent formWrap">

View File

@ -139,7 +139,7 @@
th:data-mixId="${mix.id}"
th:data-quantityML="${mixQuantity.quantity}"
th:data-usePercentages="${material.materialType.usePercentages}"
th:data-isMixType="${material.isMixType}"
th:data-isMixType="${material.isMixType()}"
th:data-defaultvalue="${mixQuantity.quantity}"
th:value="${mixQuantity.quantity}"
th:readonly="${material.materialType.usePercentages}"
@ -209,6 +209,7 @@
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script th:include="fragments.html :: printStrings"></script>
<script>
/*<![CDATA[*/
@ -251,7 +252,7 @@
$("#formSubmit").on({
click: function () {
let formData = {};
formData.recipeID = $("#recipeId").val();
formData.recipeId = $("#recipeId").val();
formData.locations = {};
$(".recipeLocation").each(function () {
@ -470,7 +471,6 @@
/*]]>*/
</script>
<script th:include="fragments.html :: printStrings"></script>
<script type="module">
/*<![CDATA[*/
@ -482,7 +482,7 @@
async function printMix(printButton) {
const mixContainer = $(printButton).parents(".mixContainer");
const allBases = $(mixContainer).find(".materialRow[data-materialtypename='[[${T(dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType).BASE_MATERIAL_TYPE.name}]]']");
const allBases = $(mixContainer).find(".materialRow[data-materialtypename='[[${T(dev.fyloz.trial.colorrecipesexplorer.core.Preferences).baseMaterialTypeName}]]']");
console.log(allBases.length + " bases trouvées");
if (allBases.length <= 0) {
showMessage(errorMsg, noBaseError);

Binary file not shown.