# v1.1.1
### Corrections
* Désactivation de l'autocomplétion dans les étapes des recettes (permet d'éviter un bug qui affiche les suggestion par dessus toutes les étapes sur Edge)
* Correction d'un bug qui permettait d'envoyer les formulaires demandant des mots de passe sans donner un mot de passe valide.
* Amélioration des contrôlleurs et du service des mélanges.
* Correction d'un bug avec la création des mélanges.

### Ajouts
* L'onglet se ferme automatiquement lorsqu'un utilisateur tente d'accéder à un fichier SIMDUT inexistant.
* Meilleure sélection des produits dans l'éditeur de mélange.
* Retravail de l'affichage de la plupart des tables, les rendant moins chargées.
* Retravail de l'affichage des étapes et des images dans l'explorateur et l'éditeur de recette.
* Ajout de la page de l'historique des mises à jour.
* Ajout de la journalisation.

### Dépendances
* Ajout de Lombok
This commit is contained in:
FyloZ 2019-12-08 15:10:23 -05:00
parent dc1c0b8585
commit b66777aa23
177 changed files with 7131 additions and 5205 deletions

Binary file not shown.

46
backup.ps1 Normal file
View File

@ -0,0 +1,46 @@
param (
[string]$WorkDir = "./workdir",
[string]$BackupPath = "./backup",
[boolean]$OverWrite = $False
)
Write-Host "Démarrage de la sauvegarde..."
if (!(Test-Path -Path $WorkDir))
{
Write-Host "Le dossier de travail ($WorkDir) n'existe pas."
exit
}
if (!(Test-Path -Path $BackupPath))
{
Write-Host "Création du dossier des sauvegardes. ($BackupPath)"
New-Item -ItemType directory -Path $BackupPath | Out-Null
}
Write-Host "Début de la compression..."
$BackupFile = "$BackupPath/backup_$( Get-Date -f yyyyMMdd-HHmm ).zip"
$CompressArgs = @{
'Path' = $WorkDir
'DestinationPath' = $BackupFile
'Update' = $OverWrite
}
try
{
Compress-Archive @CompressArgs
Write-Host "Sauvegarde réussie! ($BackupFile)"
}
catch [System.IO.IOException]
{
Write-Output "Une erreur est survenue lors de la sauvegarde:"
if ($_.FullyQualifiedErrorId -like "ArchiveFileExists*")
{
Write-Host "L'archive de sauvegarde '$BackupFile' existe déjà. Ajoutez l'argument '-OverWrite `$true' pour écraser l'archive."
}
else
{
Write-Host $_
}
}

63
pom.xml
View File

@ -8,11 +8,10 @@
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>fyloz.trial</groupId>
<groupId>dev.fyloz.trial.colorrecipesexplorer</groupId>
<artifactId>ColorRecipesExplorer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ColorRecipesExplorer</name>
<description>Demo project for Spring Boot</description>
<version>1.1.1</version>
<name>Color Recipes Explorer</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@ -42,21 +41,26 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- H2 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.1.18</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!-- PDF -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
@ -67,6 +71,30 @@
<artifactId>pdfbox</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.13.1</version>
</dependency>
</dependencies>
<build>
@ -75,6 +103,23 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy file="start.bat" tofile="${basedir}/target/start.bat"/>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Binary file not shown.

View File

@ -1,11 +1,13 @@
package fyloz.trial.ColorRecipesExplorer;
package dev.fyloz.trial.colorrecipesexplorer;
import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler;
import fyloz.trial.ColorRecipesExplorer.web.StringBank;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.FileHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.services.PasswordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import java.io.File;
import java.io.IOException;
@ -15,26 +17,38 @@ import java.util.List;
@SpringBootApplication
public class ColorRecipesExplorerApplication {
public static final Logger logger = LoggerFactory.getLogger(ColorRecipesExplorerApplication.class);
// Chemin du fichier contenant les utilisateurs
public static final Logger LOGGER = LoggerFactory.getLogger(ColorRecipesExplorerApplication.class);
public static String UPLOAD_LOCATION;
public static final String USERS_FILE_NAME = "passwords";
public static boolean USE_PORT;
public static ColorRecipesExplorerApplication CREApp;
private MessageSource messageSource;
public static void main(String[] args) {
logger.info("Le fichier des utilisateurs se situe à: " + new File(StringBank.UPLOAD_LOCATION + "/" + USERS_FILE_NAME).getAbsolutePath());
loadPasswords();
UPLOAD_LOCATION = args[0] != null ? args[0] : "./";
SpringApplication.run(ColorRecipesExplorerApplication.class, args);
}
@Autowired
public ColorRecipesExplorerApplication(MessageSource messageSource) {
this.messageSource = messageSource;
CREApp = this;
LOGGER.info("Le fichier des utilisateurs se situe à: " + new File(UPLOAD_LOCATION + "/" + USERS_FILE_NAME).getAbsolutePath());
loadPasswords();
}
/**
* Charge les mots de passes contenus dans le fichier.
* <p>
* Un mot de passe correspond à une ligne dans le fichier passwords.txt.
*
* @throws IOException
*/
private static void loadPasswords() {
FileHandler fileHandler = new FileHandler(USERS_FILE_NAME, FileHandler.FileContext.OTHERS, FileHandler.FileExtension.TXT);
private void loadPasswords() {
FileHandler fileHandler = new FileHandler(USERS_FILE_NAME, FileHandler.FileContext.OTHERS, FileHandler.FileExtension.TEXT);
if (!fileHandler.isValid()) {
fileHandler.createFile();
}
@ -43,15 +57,19 @@ public class ColorRecipesExplorerApplication {
List<String> fileContent = Files.readAllLines(fileHandler.getPath());
if (fileContent.size() < 1) {
logger.warn("Aucun mot de passe trouvé. Il sera impossible d'utiliser certaines fonctionnalitées de l'application.");
LOGGER.warn("Aucun mot de passe trouvé. Il sera impossible d'utiliser certaines fonctionnalitées de l'application.");
}
for (String line : fileContent) {
PasswordValidator.addPassword(line);
PasswordService.addPassword(line);
}
} catch (IOException e) {
logger.error("Une erreur est survenue lors du chargement du fichier des utilisateurs", e);
logger.warn("Il sera impossible d'utiliser certaines fonctionnalitées de l'application.");
LOGGER.error("Une erreur est survenue lors du chargement du fichier des utilisateurs", e);
LOGGER.warn("Il sera impossible d'utiliser certaines fonctionnalitées de l'application.");
}
}
public MessageSource getMessageSource() {
return messageSource;
}
}

View File

@ -0,0 +1,41 @@
package dev.fyloz.trial.colorrecipesexplorer.core.configuration;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class InitialDataLoader implements ApplicationListener<ApplicationReadyEvent> {
private MaterialTypeService materialTypeService;
@Autowired
public InitialDataLoader(MaterialTypeService materialTypeService) {
this.materialTypeService = materialTypeService;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
if (!materialTypeService.getByName(MaterialType.DEFAULT_MATERIAL_TYPE_NAME).isPresent()) {
createDefaultMaterialType();
}
}
private void createDefaultMaterialType() {
MaterialType defaultMaterialType = new MaterialType(MaterialType.DEFAULT_MATERIAL_TYPE_NAME, "", false);
Optional<MaterialType> optionalSavedMaterialType = materialTypeService.save(defaultMaterialType);
if (!optionalSavedMaterialType.isPresent()) {
ColorRecipesExplorerApplication.LOGGER.warn("Échec de la création du type de produit par défaut.");
}
}
}

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.core.configuration;
package dev.fyloz.trial.colorrecipesexplorer.core.configuration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
@ -18,19 +18,11 @@ public class LocaleConfiguration implements WebMvcConfigurer {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// messageSource.setBasenames("classpath:lang/messages", "classpath:lang/responses");
messageSource.setBasename("classpath:lang/responses");
messageSource.setBasenames("classpath:lang/messages", "classpath:lang/responses");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
// @Bean
// public MessageSource responsesMessageSource() {
// ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// messageSource.setBasename("classpath:lang/responses");
// return messageSource;
// }
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.core.configuration;
package dev.fyloz.trial.colorrecipesexplorer.core.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,19 @@
package dev.fyloz.trial.colorrecipesexplorer.core.configuration;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfiguration {
@Value("${response.useport}")
public boolean usePort;
@Bean
public void setUsePort() {
ColorRecipesExplorerApplication.USE_PORT = usePort;
}
}

View File

@ -1,6 +1,6 @@
package fyloz.trial.ColorRecipesExplorer.core.io;
package dev.fyloz.trial.colorrecipesexplorer.core.io.file;
import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import org.slf4j.Logger;
import java.io.File;
@ -9,12 +9,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static fyloz.trial.ColorRecipesExplorer.web.StringBank.UPLOAD_LOCATION;
public class FileHandler {
protected String name;
protected Logger logger = ColorRecipesExplorerApplication.logger;
protected Logger logger = ColorRecipesExplorerApplication.LOGGER;
private FileContext context;
private FileExtension extension;
@ -91,7 +89,7 @@ public class FileHandler {
}
public Path getPath() {
return Paths.get(String.format("%s/%s/%s%s", UPLOAD_LOCATION, context.getPath(), name, extension.getExtension()));
return Paths.get(String.format("%s/%s/%s%s", ColorRecipesExplorerApplication.UPLOAD_LOCATION, context.getPath(), name, extension.getExtension()));
}
public File getFile() {
@ -118,8 +116,9 @@ public class FileHandler {
public enum FileExtension {
JPEG("jpeg"),
PDF("pdf"),
TXT("txt"),
DB("db");
TEXT("txt"),
DATABASE("db"),
MARKDOWN("md");
private String extension;

View File

@ -1,13 +1,16 @@
package fyloz.trial.ColorRecipesExplorer.core.io;
package dev.fyloz.trial.colorrecipesexplorer.core.io.file;
import fyloz.trial.ColorRecipesExplorer.model.Recipe;
import fyloz.trial.ColorRecipesExplorer.services.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import java.util.List;
import java.util.stream.Collectors;
public class ImageHandler extends FileHandler {
public static final String IMAGES_LOCATION = ColorRecipesExplorerApplication.UPLOAD_LOCATION + "/images";
private Recipe recipe;
private int index = 0;
private RecipeService recipeService;
@ -22,6 +25,8 @@ public class ImageHandler extends FileHandler {
public ImageHandler(String name, RecipeService recipeService) {
super(name.replace(".jpeg", ""), FileContext.IMAGE, FileExtension.JPEG);
this.recipeService = recipeService;
String[] nameParts = name.split("-");
index = Integer.parseInt(nameParts[1].replace(".jpeg", ""));
}

View File

@ -0,0 +1,25 @@
package dev.fyloz.trial.colorrecipesexplorer.core.io.response;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import org.springframework.context.i18n.LocaleContextHolder;
import java.util.HashMap;
import java.util.Map;
public class JSONResponseBuilder extends ResponseBuilder<JSONResponseBuilder, Map<String, Object>> {
@Override
protected void addResponseCodeToAttribute(String responseCodeType, String responseMessagePath, String[] parameters) {
Map<String, Object> attributeContent = new HashMap<>();
// Récupère le message depuis le fichier des messages de la bonne language et rajoute ces paramètres.
String message = ColorRecipesExplorerApplication.CREApp.getMessageSource().getMessage(responseMessagePath, parameters, LocaleContextHolder.getLocale());
attributeContent.put("message", message);
addAttribute(responseCodeType, attributeContent);
}
public Map<String, Object> build() {
return attributes;
}
}

View File

@ -0,0 +1,50 @@
package dev.fyloz.trial.colorrecipesexplorer.core.io.response;
import org.springframework.web.servlet.ModelAndView;
public class ModelResponseBuilder extends ResponseBuilder<ModelResponseBuilder, ModelAndView> {
private static final String PATH_PARAMETER_PATTERN = "(\\{\\w+\\})";
private ModelAndView model;
public ModelResponseBuilder() {
this.model = new ModelAndView();
}
public ModelResponseBuilder(String view) {
this(new ModelAndView(view));
}
public ModelResponseBuilder(ModelAndView model) {
this.model = model == null ? new ModelAndView() : model;
}
public ModelResponseBuilder withView(String path) {
model.setViewName(path);
return this;
}
public ModelResponseBuilder withRedirect(String path, Object... parameters) {
for (Object parameter : parameters) path = path.replaceFirst(PATH_PARAMETER_PATTERN, parameter.toString());
model.setViewName("redirect:/" + path);
return this;
}
@Override
protected void addResponseCodeToAttribute(String responseCodeType, String responseMessagePath, String[] parameters) {
addAttribute(responseCodeType, responseMessagePath);
for (int i = 0; i < parameters.length; i++) {
addAttribute(String.format("responseArg%s", i + 1), parameters[i]);
}
}
public ModelAndView build() {
model.addAllObjects(attributes);
return model;
}
}

View File

@ -0,0 +1,112 @@
package dev.fyloz.trial.colorrecipesexplorer.core.io.response;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class ResponseBuilder<T extends ResponseBuilder, R> {
protected static final String RESPONSE_PREFIX = "response.";
protected static final String ERROR_ATTRIBUTE_NAME = "error";
protected static final String SUCCESS_ATTRIBUTE_NAME = "success";
protected Map<String, Object> attributes;
private boolean containsErrors = false;
public ResponseBuilder() {
attributes = new HashMap<>();
// Ajoute l'URL de base à toutes les réponses
attributes.put("baseUrl", ControllerUtils.getCurrentBaseUrl());
}
protected abstract void addResponseCodeToAttribute(String responseCodeType, String responseMessagePath, String[] parameters);
protected abstract R build();
public T addResponseCode(ResponseCode responseCode, Object... parameters) {
String[] parametersAsStr = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parametersAsStr[i] = parameters[i].toString();
}
return addResponseCode(responseCode, parametersAsStr);
}
public T addResponseCode(ResponseCode responseCode, String... parameters) {
int requiredParametersNumber = responseCode.getParametersNumber();
int givenParametersNumber = parameters.length;
if (requiredParametersNumber != givenParametersNumber) {
throw new IllegalArgumentException(String.format("Mauvais nombre de paramètre dans une réponse de modèle: %s requis, %s fournis", requiredParametersNumber, givenParametersNumber));
}
// Ajoute les paramètres, si nécessaire
if (requiredParametersNumber > 0) {
// Avertit s'il y a plus de paramètres que le nombre de paramètres supporté par le template thymeleaf (aussi MAX_PARAMETERS_NUMBER)
int maxParametersNumber = ResponseCode.MAX_PARAMETERS_NUMBER;
if (givenParametersNumber > maxParametersNumber) {
ColorRecipesExplorerApplication.LOGGER.warn(String.format("Trop de paramètres fournis pour le code de réponse %s: %s maximum, %s fournis", responseCode.name(), maxParametersNumber, givenParametersNumber));
}
}
String responseCodeType;
if (responseCode.getType() == ResponseCode.ResponseCodeType.ERROR) {
containsErrors = true;
responseCodeType = ERROR_ATTRIBUTE_NAME;
} else {
responseCodeType = SUCCESS_ATTRIBUTE_NAME;
}
String responseMessagePath = RESPONSE_PREFIX + responseCode.getCode();
addResponseCodeToAttribute(responseCodeType, responseMessagePath, parameters);
return (T) this;
}
public T addResponseData(ResponseDataType responseDataType, @NotNull Object data) {
Class requiredDataTypeClass = responseDataType.getDataType();
Class requiredListDataTypeClass = responseDataType.getListDataType();
Class givenDataTypeClass = data.getClass();
// Vérifie le type de l'objet
if (!requiredDataTypeClass.equals(givenDataTypeClass)) {
throw new IllegalArgumentException(String.format("L'objet passé en paramètre n'est pas du bon type. Requis: %s, fournis: %s", requiredDataTypeClass.getName(), givenDataTypeClass.getName()));
}
// Si l'objet est une liste, vérifie qu'elle n'est pas vide et qu'elle est du bon type
if (requiredDataTypeClass.equals(List.class) && requiredListDataTypeClass != null) {
List listData = (List) data;
if (listData.size() < 1) {
throw new IllegalArgumentException("La liste passée en paramètre ne contient aucun élément");
}
Class givenListDataTypeClass = listData.get(0).getClass();
if (givenDataTypeClass.equals(requiredListDataTypeClass)) {
throw new IllegalArgumentException(String.format("La liste passée en paramètre contient des éléments du mauvais type. Requis: %s, fournis: %s", requiredListDataTypeClass.getName(), givenListDataTypeClass.getName()));
}
}
// Ajoute l'attribut dans le Map
addAttribute(responseDataType.getDataTypeName(), data);
return (T) this;
}
public T addAttribute(String attributeName, Object content) {
attributes.put(attributeName, content);
return (T) this;
}
public boolean containsErrors() {
return containsErrors;
}
}

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.core;
package dev.fyloz.trial.colorrecipesexplorer.core.io.response;
public enum ResponseCode {
SUCCESS_USING_MATERIALS(1, ResponseCodeType.SUCCESS, 0),
@ -18,7 +18,21 @@ public enum ResponseCode {
COMPANY_LINKED(15, ResponseCodeType.ERROR, 1),
MIX_NOT_ASSOCIATED_WITH_RECIPE(16, ResponseCodeType.ERROR, 2),
NOT_ENOUGH_MATERIAL(17, ResponseCodeType.ERROR, 1),
MIX_TYPE_ALREADY_USED(18, ResponseCodeType.ERROR, 1);
MIX_TYPE_ALREADY_USED(18, ResponseCodeType.ERROR, 1),
MATERIAL_TYPE_NOT_FOUND(19, ResponseCodeType.ERROR, 1),
MATERIAL_TYPE_ALREADY_EXIST_PREFIX(20, ResponseCodeType.ERROR, 1),
MATERIAL_TYPE_LINKED(21, ResponseCodeType.ERROR, 1),
NO_COMPANY(22, ResponseCodeType.ERROR, 0),
NO_MATERIAL(23, ResponseCodeType.ERROR, 0),
FILE_NOT_IMAGE(24, ResponseCodeType.ERROR, 0),
RECIPE_NOT_FOUND_NO_PARAMS(25, ResponseCodeType.ERROR, 0),
MATERIAL_NOT_FOUND_BY_NAME(26, ResponseCodeType.ERROR, 1),
// HTTP Errors
_500(100, ResponseCodeType.ERROR, 0),
_404(101, ResponseCodeType.ERROR, 0),
_401(102, ResponseCodeType.ERROR, 0),
;
public static final int MAX_PARAMETERS_NUMBER = 2;
@ -48,4 +62,5 @@ public enum ResponseCode {
ERROR,
SUCCESS
}
}

View File

@ -1,12 +1,12 @@
package fyloz.trial.ColorRecipesExplorer.core;
package dev.fyloz.trial.colorrecipesexplorer.core.io.response;
import fyloz.trial.ColorRecipesExplorer.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.model.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.stream.Stream;
public enum ModelDataType {
public enum ResponseDataType {
MATERIAL("material", Material.class),
MATERIALS("materials", ArrayList.class, MATERIAL),
MATERIAL_ID("materialID", Integer.class),
@ -14,6 +14,7 @@ public enum ModelDataType {
MATERIAL_TYPE("materialType", MaterialType.class),
MATERIAL_TYPES("materialTypes", ArrayList.class, MATERIAL_TYPE),
MATERIAL_TYPE_NAME("materialTypeName", String.class),
RECIPE("recipe", Recipe.class),
RECIPES("recipes", ArrayList.class, RECIPE),
@ -40,17 +41,19 @@ public enum ModelDataType {
COMPANY_NAME("companyName", String.class),
IMAGE("image", String.class),
IMAGES("images", ArrayList.class, IMAGE);
IMAGES("images", ArrayList.class, IMAGE),
BLOCK_BUTTON("blockButton", Boolean.class);
private String dataTypeName;
private Class dataType;
private Class listDataType;
ModelDataType(String dataTypeName, Class dataType) {
ResponseDataType(String dataTypeName, Class dataType) {
this(dataTypeName, dataType, null);
}
ModelDataType(String dataTypeName, Class dataType, ModelDataType listDataType) {
ResponseDataType(String dataTypeName, Class dataType, ResponseDataType listDataType) {
this.dataTypeName = dataTypeName;
this.dataType = dataType;
@ -76,7 +79,7 @@ public enum ModelDataType {
return getDataTypeName();
}
public static ModelDataType getModelDataTypeFromDataType(Class dataType) {
public static ResponseDataType getModelDataTypeFromDataType(Class dataType) {
return Stream.of(values()).filter(r -> r.getDataType().equals(dataType)).findFirst().orElse(null);
}
}

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
public abstract class BeanModel {
public abstract Integer getID();

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import org.hibernate.validator.constraints.Length;

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import org.hibernate.annotations.ColumnDefault;

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import org.hibernate.annotations.ColumnDefault;
@ -11,6 +11,8 @@ import java.util.Objects;
@Entity
public class MaterialType extends BeanModel implements Serializable {
public static final String DEFAULT_MATERIAL_TYPE_NAME = "Aucun";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer materialTypeID;
@ -19,6 +21,7 @@ public class MaterialType extends BeanModel implements Serializable {
@NotNull
private String materialTypeName;
@Column(unique = true)
@NotNull
private String prefix;

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@ -23,7 +23,7 @@ public class Mix extends BeanModel implements Serializable {
@ManyToOne
private MixType mixType;
@OneToMany(mappedBy = "mix")
@OneToMany(mappedBy = "mix", cascade = CascadeType.ALL)
private List<MixQuantity> mixQuantities;
// Casier

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@ -6,7 +6,7 @@ import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "mixTypes")
@Table(name = "mixTypes") // TODO retirer @Table
public class MixType extends BeanModel implements Serializable {
@Id

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.validator.constraints.Length;
@ -6,6 +6,7 @@ import org.hibernate.validator.constraints.Length;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -38,10 +39,10 @@ public class Recipe extends BeanModel implements Serializable {
private String note;
@JsonIgnore
@OneToMany(mappedBy = "recipe")
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<Mix> recipeMixes;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<RecipeStep> recipeSteps;
public Recipe() {
@ -131,7 +132,7 @@ public class Recipe extends BeanModel implements Serializable {
}
public List<RecipeStep> getRecipeSteps() {
return recipeSteps;
return recipeSteps == null ? new ArrayList<>() : recipeSteps;
}
public void setRecipeSteps(List<RecipeStep> recipeSteps) {

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.model;
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
@ -15,7 +15,7 @@ public class RecipeStep extends BeanModel implements Serializable {
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private int stepID;
@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private Recipe recipe;

View File

@ -0,0 +1,21 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model.dto;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import lombok.Data;
import java.util.List;
@Data
public class MixCreationFormDto {
private Recipe recipe;
private String mixTypeName;
private List<String> materials;
private List<Float> quantities;
public MixCreationFormDto() {
}
}

View File

@ -1,27 +1,29 @@
package fyloz.trial.ColorRecipesExplorer.services;
package dev.fyloz.trial.colorrecipesexplorer.core.services;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication;
import fyloz.trial.ColorRecipesExplorer.model.BeanModel;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.model.BeanModel;
import org.slf4j.Logger;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.lang.NonNull;
import java.util.List;
import java.util.Optional;
public abstract class GenericService<T extends BeanModel> implements IGenericService<T> {
public abstract class GenericService<T extends BeanModel, R extends JpaRepository<T, Integer>> implements IGenericService<T> {
protected Logger logger = ColorRecipesExplorerApplication.logger;
protected Logger logger = ColorRecipesExplorerApplication.LOGGER;
protected JpaRepository<T, Integer> dao;
protected R dao;
public GenericService(JpaRepository<T, Integer> dao) {
public GenericService(R dao) {
this.dao = dao;
}
@Override
public T getByID(int id) {
return dao.findById(id).get();
public Optional<T> getByID(int id) {
return dao.findById(id);
}
@Override
@ -30,36 +32,42 @@ public abstract class GenericService<T extends BeanModel> implements IGenericSer
}
@Override
public T save(T entity) {
public Optional<T> save(T entity) {
if (isValidForCreation(entity)) {
return dao.save(entity);
return Optional.of(dao.save(entity));
}
return null;
return Optional.empty();
}
@Override
public boolean saveAll(List<T> entities) {
public boolean saveAll(Iterable<T> entities) {
if (entities == null) return false;
for (T e : entities) {
if (save(e) == null) {
if (!save(e).isPresent()) {
return false;
}
}
dao.saveAll(entities);
return true;
}
@Override
public T update(T entity) {
if (exists(entity)) {
return dao.save(entity);
public Optional<T> update(@NonNull T entity) {
if (isValidForUpdate(entity)) {
return Optional.of(dao.save(entity));
}
return null;
return Optional.empty();
}
@Override
public boolean delete(T entity) {
if (entity == null) return false;
if (exists(entity)) {
dao.delete(entity);
return true;
@ -70,12 +78,13 @@ public abstract class GenericService<T extends BeanModel> implements IGenericSer
@Override
public boolean deleteAll(List<T> entities) {
for (T e : entities) {
if (!delete(e)) {
return false;
}
if (entities == null) return false;
for (T entity : entities) {
if (!exists(entity)) return false;
}
dao.deleteAll(entities);
return true;
}
@ -89,6 +98,16 @@ public abstract class GenericService<T extends BeanModel> implements IGenericSer
return entity != null && !exists(entity);
}
/**
* Vérifie si une entité est valide pour la création dans la base de données.
*
* @param entity L'entité à vérifier.
* @return Si l'entité est valide pour l'édition.
*/
public boolean isValidForUpdate(@NonNull T entity) {
return exists(entity);
}
/**
* Vérifie si une entité existe dans la base de données.
*
@ -97,7 +116,12 @@ public abstract class GenericService<T extends BeanModel> implements IGenericSer
*/
@Override
public boolean exists(T entity) {
return dao.findById(entity.getID()).isPresent();
return entity != null && existsById(entity.getID());
}
@Override
public boolean existsById(int id) {
return dao.existsById(id);
}
/**

View File

@ -1,16 +1,17 @@
package fyloz.trial.ColorRecipesExplorer.services;
package dev.fyloz.trial.colorrecipesexplorer.core.services;
import fyloz.trial.ColorRecipesExplorer.model.BeanModel;
import dev.fyloz.trial.colorrecipesexplorer.core.model.BeanModel;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
public interface IGenericService<T extends BeanModel> {
/**
* Récupère toutes les entités du même type dans la base de données.
*
* @return Une liste de toutes les entités du même type.
* @return Une collection contenant toutes les entités du même type.
*/
List<T> getAll();
@ -20,7 +21,7 @@ public interface IGenericService<T extends BeanModel> {
* @param id L'identifiant de l'entité
* @return L'entité correspondant à l'identifiant.
*/
T getByID(int id);
Optional<T> getByID(int id);
/**
* Sauvegarde une entité dans la base de données.
@ -30,7 +31,7 @@ public interface IGenericService<T extends BeanModel> {
* @param entity L'entité à sauvegarder
* @return Si la sauvegarde a fonctionné.
*/
T save(T entity);
Optional<T> save(T entity);
/**
* Sauvegarde des entités dans la base de données.
@ -41,7 +42,7 @@ public interface IGenericService<T extends BeanModel> {
* @return Si la sauvegarde a fonctionné.
*/
@Transactional
boolean saveAll(List<T> entities);
boolean saveAll(Iterable<T> entities);
/**
* Met à jour une entité dans la base de données.
@ -51,7 +52,7 @@ public interface IGenericService<T extends BeanModel> {
* @param entity L'entité à mettre à jour
* @return Si la mise à jour a fonctionné.
*/
T update(T entity);
Optional<T> update(T entity);
/**
* Supprime une entité dans la base de données.
@ -75,4 +76,6 @@ public interface IGenericService<T extends BeanModel> {
boolean deleteAll(List<T> entities);
boolean exists(T entity);
boolean existsById(int id);
}

View File

@ -1,10 +1,12 @@
package fyloz.trial.ColorRecipesExplorer.services;
package dev.fyloz.trial.colorrecipesexplorer.core.services;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.model.Mix;
import fyloz.trial.ColorRecipesExplorer.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -32,12 +34,13 @@ public class InventoryService {
}
public boolean useMix(Mix mix, Map<Material, Float> quantities) {
for (Material material : mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList())) {
List<Material> materials = mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList());
for (Material material : materials) {
if (!material.isMixType()) {
float quantity = quantities.get(material);
material.setInventoryQuantity(material.getInventoryQuantity() - quantity);
if (materialService.update(material) == null) return false;
if (!materialService.update(material).isPresent()) return false;
}
}

View File

@ -1,9 +1,9 @@
package fyloz.trial.ColorRecipesExplorer;
package dev.fyloz.trial.colorrecipesexplorer.core.services;
import java.util.ArrayList;
import java.util.List;
public class PasswordValidator {
public class PasswordService {
private static List<String> passwords = new ArrayList<>();

View File

@ -0,0 +1,40 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.CompanyDao;
import dev.fyloz.trial.colorrecipesexplorer.dao.RecipeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CompanyService extends GenericService<Company, CompanyDao> {
private RecipeDao recipeDao; // Utilise le Dao pour éviter une erreur au démarrage, citant une récursion (CompanyService -> RecipeService -> CompanyService -> ...)
@Autowired
public CompanyService(CompanyDao companyDao, RecipeDao recipeDao) {
super(companyDao);
this.recipeDao = recipeDao;
}
@Override
public boolean isValidForCreation(Company entity) {
return super.isValidForCreation(entity) && !existsByName(entity.getCompanyName());
}
public boolean existsByName(String name) {
return dao.existsByCompanyName(name);
}
public boolean isLinkedToRecipes(Company company) {
return !recipeDao.findAllByCompany(company).isEmpty();
}
public boolean deleteIfNotLinked(Company company) {
if (company == null) return false;
if (isLinkedToRecipes(company)) return false;
return delete(company);
}
}

View File

@ -1,17 +1,23 @@
package fyloz.trial.ColorRecipesExplorer.services;
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler;
import fyloz.trial.ColorRecipesExplorer.dao.MaterialDao;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.FileHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MaterialDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class MaterialService extends GenericService<Material> {
public class MaterialService extends GenericService<Material, MaterialDao> {
private MixQuantityService mixQuantityService;
@ -21,14 +27,14 @@ public class MaterialService extends GenericService<Material> {
this.mixQuantityService = mixQuantityService;
}
/**
* Récupère la produit ayant le code spécifié dans la base de données.
*
* @param materialCode Le code du produit
* @return Le produit correspondant au code.
*/
public Material getByMaterialCode(String materialCode) {
return ((MaterialDao) dao).findByMaterialCode(materialCode);
public Optional<Material> getByMaterialCode(String materialCode) {
return dao.findByMaterialCode(materialCode);
}
public List<Material> getAllByMaterialType(MaterialType materialType) {
if (materialType == null) return new ArrayList<>();
return dao.findAllByMaterialType(materialType);
}
/**
@ -38,21 +44,43 @@ public class MaterialService extends GenericService<Material> {
* @return Si le produit est lié à d'autres mélanges.
*/
public boolean isLinkedToMixes(Material material) {
return !mixQuantityService.getByMaterial(material).isEmpty();
return mixQuantityService.existsByMaterial(material);
}
public boolean deleteIfNotLinked(Material material) {
if (!isLinkedToMixes(material)) {
return super.delete(material);
return delete(material);
}
return false;
}
@Override
public boolean exists(Material entity) {
Material material = getByMaterialCode(entity.getMaterialCode());
return super.exists(entity) || material != null;
public boolean exists(Material material) {
return material != null && (super.exists(material) || dao.existsByMaterialCode(material.getMaterialCode()));
}
@Override
public boolean isValidForUpdate(Material material) {
if (material == null) return false;
Optional<Material> materialByCode = dao.findByMaterialCode(material.getMaterialCode());
return super.isValidForUpdate(material) && (!materialByCode.isPresent() || material.getMaterialID().equals(materialByCode.get().getMaterialID()));
}
public List<Material> getAllBySearchString(String searchString) {
return dao.findAllByMaterialCodeContainingIgnoreCase(searchString).stream().filter(m -> !m.isMixType()).collect(Collectors.toList());
}
/**
* Crée un FileHandler pour le produit passé en paramètre.
*
* @param material Le produit dont on veut créer un FileHandler
* @return Le FileHandler correspondant au produit.
*/
private FileHandler getFileHandlerForMaterial(Material material) {
String filename = String.format("%s_%s", material.getMaterialID(), material.getMaterialCode());
return new FileHandler(filename, FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF);
}
/**
@ -66,9 +94,9 @@ public class MaterialService extends GenericService<Material> {
* @return Si le fichier SIMDUT à bien été créé ou non.
*/
public boolean addSimdut(MultipartFile simdut, Material material) {
if (simdut.getSize() <= 0) return true;
if (simdut.getSize() <= 0) return false;
FileHandler fileHandler = getFilesServiceForMaterial(material);
FileHandler fileHandler = getFileHandlerForMaterial(material);
if (!fileHandler.createFile()) {
return false;
@ -92,7 +120,7 @@ public class MaterialService extends GenericService<Material> {
* @return Si la suppression du fichier SIMDUT s'est effectuée.
*/
public boolean removeSimdut(Material material) {
FileHandler fileHandler = getFilesServiceForMaterial(material);
FileHandler fileHandler = getFileHandlerForMaterial(material);
if (fileHandler.getFile().exists()) {
return fileHandler.deleteFile();
@ -100,15 +128,4 @@ public class MaterialService extends GenericService<Material> {
return true;
}
/**
* Crée un FileHandler pour le produit passé en paramètre.
*
* @param material Le produit dont on veut créer un FileHandler
* @return Le FileHandler correspondant au produit.
*/
private FileHandler getFilesServiceForMaterial(Material material) {
String filename = String.format("%s_%s", material.getMaterialID(), material.getMaterialCode());
return new FileHandler(filename, FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF);
}
}

View File

@ -0,0 +1,69 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MaterialTypeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class MaterialTypeService extends GenericService<MaterialType, MaterialTypeDao> {
private MaterialService materialService;
@Autowired
public MaterialTypeService(MaterialTypeDao materialTypeDao, MaterialService materialService) {
super(materialTypeDao);
this.materialService = materialService;
}
public boolean isLinkedToMaterials(MaterialType materialType) {
return !materialService.getAllByMaterialType(materialType).isEmpty();
}
public boolean deleteIfNotLinked(MaterialType materialType) {
if (!isLinkedToMaterials(materialType)) {
return delete(materialType);
}
return false;
}
@Override
public boolean isValidForCreation(MaterialType entity) {
return entity != null && !existsByName(entity.getMaterialTypeName());
}
@Override
public boolean isValidForUpdate(MaterialType materialType) {
return super.isValidForUpdate(materialType)
&& isValidForUpdateName(materialType)
&& isValidForUpdatePrefix(materialType);
}
public boolean isValidForUpdateName(MaterialType materialType) {
Optional<MaterialType> materialTypeByName = dao.findByMaterialTypeName(materialType.getMaterialTypeName());
return !materialTypeByName.isPresent() || materialType.getMaterialTypeID().equals(materialTypeByName.get().getMaterialTypeID());
}
public boolean isValidForUpdatePrefix(MaterialType materialType) {
Optional<MaterialType> materialTypeByPrefix = dao.findByPrefix(materialType.getPrefix());
return !materialTypeByPrefix.isPresent() || materialType.getMaterialTypeID().equals(materialTypeByPrefix.get().getMaterialTypeID());
}
public Optional<MaterialType> getByName(String name) {
return dao.findByMaterialTypeName(name);
}
public Optional<MaterialType> getDefaultMaterialType() {
return getByName(MaterialType.DEFAULT_MATERIAL_TYPE_NAME);
}
public boolean existsByName(String name) {
return getByName(name).isPresent();
}
}

View File

@ -0,0 +1,21 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MixQuantityDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MixQuantityService extends GenericService<MixQuantity, MixQuantityDao> {
@Autowired
public MixQuantityService(MixQuantityDao mixQuantityDao) {
super(mixQuantityDao);
}
public boolean existsByMaterial(Material material) {
return dao.existsByMaterial(material);
}
}

View File

@ -0,0 +1,130 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import dev.fyloz.trial.colorrecipesexplorer.core.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MixDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
public class MixService extends GenericService<Mix, MixDao> {
private MaterialService materialService;
private MixQuantityService mixQuantityService;
private MixTypeService mixTypeService;
private RecipeService recipeService;
@Autowired
public MixService(MixDao mixDao, MaterialService materialService, MixQuantityService mixQuantityService, MixTypeService mixTypeService, RecipeService recipeService) {
super(mixDao);
this.materialService = materialService;
this.mixQuantityService = mixQuantityService;
this.mixTypeService = mixTypeService;
this.recipeService = recipeService;
}
@Transactional
public ModelResponseBuilder create(MixCreationFormDto formDto, @NotNull Recipe recipe) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
List<Material> materials = new ArrayList<>();
for (String materialCode : formDto.getMaterials()) {
Optional<Material> found = materialService.getByMaterialCode(materialCode);
if (!found.isPresent()) {
return modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND_BY_NAME, materialCode);
}
materials.add(found.get());
}
Optional<MixType> optionalMixType = mixTypeService.createByName(formDto.getMixTypeName());
if (!optionalMixType.isPresent()) return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
MixType mixType = optionalMixType.get();
if (recipeService.hasMixType(recipe, mixType))
return modelResponseBuilder.addResponseCode(ResponseCode.MIX_TYPE_ALREADY_USED, mixType.getTypeName());
// Crée le mélange en premier pour avoir accès à son ID pour les autres éléments
Mix mix = new Mix(recipe, mixType, null);
Optional<Mix> savedMix = save(mix);
if (savedMix.isPresent()) {
mix = savedMix.get();
List<MixQuantity> mixQuantities = createMixQuantities(savedMix.get(), materials, formDto.getQuantities());
mix.setMixQuantities(mixQuantities);
// Retourne aucune erreur s'il la mise à jour à lieu
if (update(mix).isPresent()) return null;
deleteMix(mix);
}
return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
@Transactional
public ModelResponseBuilder edit(Mix mix, MixCreationFormDto formDto) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
List<Material> materials = new ArrayList<>();
for (String materialCode : formDto.getMaterials()) {
Optional<Material> found = materialService.getByMaterialCode(materialCode);
if (!found.isPresent()) {
return modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND_BY_NAME, materialCode);
}
materials.add(found.get());
}
List<MixQuantity> mixQuantities = createMixQuantities(mix, materials, formDto.getQuantities());
// Supprime les anciens MixQuantity pour éviter les doublons et les entrées inutiles dans la base de données
if (!mixQuantityService.deleteAll(mix.getMixQuantities())) {
return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
mix.setMixQuantities(mixQuantities);
if (update(mix).isPresent()) return null;
else return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
public boolean deleteMix(Mix mix) {
if (mixQuantityService.deleteAll(mix.getMixQuantities())) {
return super.delete(mix);
}
return false;
}
/**
* Crée une liste de MixQuantity, pour la création d'un mélange
*
* @param mix Le mélange associé aux MixQuantity
* @param materials Les matériaux
* @param quantities Les quantités
* @return La liste des MixQuantity créés.
*/
public List<MixQuantity> createMixQuantities(Mix mix, List<Material> materials, List<Float> quantities) {
List<MixQuantity> mixQuantities = new ArrayList<>();
for (int i = 0; i < materials.size(); i++) {
Material material = materials.get(i);
float quantity = quantities.get(i);
MixQuantity mixQuantity = new MixQuantity(mix, material, quantity);
mixQuantities.add(mixQuantity);
}
return mixQuantities;
}
}

View File

@ -0,0 +1,46 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.MixTypeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class MixTypeService extends GenericService<MixType, MixTypeDao> {
private MaterialTypeService materialTypeService;
@Autowired
public MixTypeService(MixTypeDao mixTypeDao, MaterialTypeService materialTypeService) {
super(mixTypeDao);
this.materialTypeService = materialTypeService;
}
public Optional<MixType> getByName(String name) {
return dao.findByTypeName(name);
}
public Optional<MixType> getByMaterial(Material material) {
return dao.findByMaterial(material);
}
public Optional<MixType> createByName(String name) {
Optional<MaterialType> defaultType = materialTypeService.getDefaultMaterialType();
if (!defaultType.isPresent()) return Optional.empty();
Optional<MixType> mixTypeByName = getByName(name);
if (mixTypeByName.isPresent()) {
return mixTypeByName;
}
Material mixTypeMaterial = new Material(name, 0, true, defaultType.get());
MixType mixType = new MixType(name, mixTypeMaterial);
return save(mixType);
}
}

View File

@ -0,0 +1,196 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.FileHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.ImageHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.RecipeDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class RecipeService extends GenericService<Recipe, RecipeDao> {
private CompanyService companyService;
private StepService stepService;
@Autowired
public RecipeService(RecipeDao recipeDao, CompanyService companyService, StepService stepService) {
super(recipeDao);
this.companyService = companyService;
this.stepService = stepService;
}
/**
* Récupère la liste des recettes associées à une compagnie.
*
* @param company La compagnie.
* @return La liste des recettes associées à cette compagnie.
*/
public List<Recipe> getByCompany(Company company) {
return dao.findAllByCompany(company);
}
/**
* Récupère une liste triée des mélanges pour une recette.
*
* @param recipe La recette dont on veut récupérer les mélanges
* @return Une liste triée des mélanges.
*/
public List<Mix> getSortedMixes(Recipe recipe) {
List<Mix> mixes = recipe.getRecipeMixes();
mixes.sort(Comparator.comparing(Mix::getMixID));
return mixes;
}
/**
* Récupère toutes les recettes dans la base de données et les classes par compagnie.
*
* @return Un Map contenant les recettes classées par compagnie.
*/
public Map<Company, List<Recipe>> getRecipesByCompany() {
return mappedByCompany(getAll());
}
public Optional<Recipe> updateRecipe(Recipe newRecipe, Recipe storedRecipe, MultiValueMap<String, Object> form) {
storedRecipe.setRecipeCode(newRecipe.getRecipeCode());
storedRecipe.setCompany(newRecipe.getCompany());
storedRecipe.setRecipeDescription(newRecipe.getRecipeDescription());
storedRecipe.setSample(newRecipe.getSample());
storedRecipe.setApprobationDate(newRecipe.getApprobationDate());
storedRecipe.setRemark(newRecipe.getRemark());
storedRecipe.setNote(newRecipe.getNote());
Optional<Recipe> optionalRecipe = convertAndCreateSteps(storedRecipe, form);
if (!optionalRecipe.isPresent()) {
return Optional.empty();
}
return update(optionalRecipe.get());
}
@Transactional
public boolean deleteRecipe(Recipe recipe) {
if (!delete(recipe)) return false;
return removeAllFiles(recipe);
}
/**
* Récupère le nom des images liées à une recette.
*
* @param recipe La recette dont on veut récupérer les images
* @return Une liste contenant le nom des images liées à la recette.
*/
public List<String> getImageFiles(Recipe recipe) {
int recipeID = recipe.getRecipeID();
String recipeCode = recipe.getRecipeCode();
String fileName = String.format("%s_%s", recipeID, recipeCode);
File imageLocation = new File(ImageHandler.IMAGES_LOCATION);
File[] result = imageLocation.listFiles((d, n) -> n.startsWith(fileName) && n.endsWith("jpeg"));
if (result == null) return new ArrayList<>();
return Arrays.stream(result).map(File::getName).collect(Collectors.toList());
}
/**
* Récupère les types de mélanges associés à la recette.
*
* @param recipe La recette dont on veut récupérer les types de mélange
* @return Une liste contenant les types de mélanges.
*/
public List<MixType> getAssociatedMixesTypes(Recipe recipe) {
return recipe
.getRecipeMixes()
.stream()
.map(Mix::getMixType)
.collect(Collectors.toList());
}
/**
* Vérifie si une recette contient un mélange ayant le type de mélange spécifié.
*
* @param recipe La recette
* @param mixType Le type de mélange
* @return Si la recette contient le type de mélange
*/
public boolean hasMixType(Recipe recipe, MixType mixType) {
return getAssociatedMixesTypes(recipe)
.contains(mixType);
}
public Map<Company, List<Recipe>> getRecipesForSearchString(String searchString) {
List<Recipe> recipes = dao.findAllByRecipeDescriptionContainsOrRecipeCodeContains(searchString.toUpperCase());
return mappedByCompany(recipes);
}
private Map<Company, List<Recipe>> mappedByCompany(List<Recipe> recipes) {
List<Company> companies = companyService.getAll();
Map<Company, List<Recipe>> mappedRecipes = new HashMap<>();
for (Company c : companies) {
List<Recipe> recipesForCompany = recipes.stream().filter(r -> r.getCompany().equals(c)).collect(Collectors.toList());
if (recipesForCompany.size() > 0) {
mappedRecipes.put(c, recipesForCompany);
}
}
return mappedRecipes;
}
/**
* Crée et assigne des étapes à une recette, en se basant sur l'entrée de utilisateur.
*
* @param recipe La recette à assigner les étapes
* @param input L'entrée de l'utilisateur, contenant les étapes (key = "step_*")
* @return La recette avec les étapes.
*/
@Transactional
public Optional<Recipe> convertAndCreateSteps(Recipe recipe, MultiValueMap<String, Object> input) {
if (recipe == null || input == null) return Optional.empty();
// Convertit les étapes en RecipeSteps
List<RecipeStep> steps = new ArrayList<>();
if (input.containsKey("step")) {
Object field = input.get("step");
if (field instanceof LinkedList && ((LinkedList) field).getFirst() instanceof String) {
for (String step : (LinkedList<String>) field) {
steps.add(new RecipeStep(recipe, step));
}
}
}
return setSteps(recipe, steps);
}
private Optional<Recipe> setSteps(Recipe recipe, List<RecipeStep> steps) {
if (!stepService.deleteAll(recipe.getRecipeSteps())) return Optional.empty();
recipe.setRecipeSteps(null);
update(recipe);
recipe.setRecipeSteps(steps);
return Optional.of(recipe);
}
private boolean removeAllFiles(Recipe recipe) {
FileHandler fileHandler;
for (String image : getImageFiles(recipe)) {
fileHandler = new FileHandler(image.replace(".jpeg", ""), FileHandler.FileContext.IMAGE, FileHandler.FileExtension.JPEG);
if (!fileHandler.deleteFile()) return false;
}
return true;
}
}

View File

@ -0,0 +1,16 @@
package dev.fyloz.trial.colorrecipesexplorer.core.services.model;
import dev.fyloz.trial.colorrecipesexplorer.core.model.RecipeStep;
import dev.fyloz.trial.colorrecipesexplorer.core.services.GenericService;
import dev.fyloz.trial.colorrecipesexplorer.dao.StepDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StepService extends GenericService<RecipeStep, StepDao> {
@Autowired
public StepService(StepDao stepDao) {
super(stepDao);
}
}

View File

@ -0,0 +1,26 @@
package dev.fyloz.trial.colorrecipesexplorer.core.utils;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class ControllerUtils {
public static String redirect(String viewName) {
return String.format("redirect:/%s", viewName);
}
public static String getCurrentBaseUrl() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
return null;
}
HttpServletRequest request = attributes.getRequest();
String port = ":" + (ColorRecipesExplorerApplication.USE_PORT ? request.getServerPort() : "");
return String.format("%s://%s%s%s", request.getScheme(), request.getServerName(), port, request.getContextPath());
}
}

View File

@ -0,0 +1,22 @@
package dev.fyloz.trial.colorrecipesexplorer.core.utils;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class FileUtils {
public static String readClasspathFile(ResourceLoader loader, String path) {
try (InputStream stream = loader.getResource(path).getInputStream()) {
byte[] data = FileCopyUtils.copyToByteArray(stream);
return new String(data, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -1,13 +1,11 @@
package fyloz.trial.ColorRecipesExplorer.dao;
package dev.fyloz.trial.colorrecipesexplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Company;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CompanyDao extends JpaRepository<Company, Integer> {
void deleteByCompanyID(int companyID);
Company findByCompanyName(String companyName);
boolean existsByCompanyName(String companyName);
}

View File

@ -0,0 +1,20 @@
package dev.fyloz.trial.colorrecipesexplorer.dao;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface MaterialDao extends JpaRepository<Material, Integer> {
Optional<Material> findByMaterialCode(String materialCode);
List<Material> findAllByMaterialType(MaterialType materialType);
List<Material> findAllByMaterialCodeContainingIgnoreCase(String stringSearch);
boolean existsByMaterialCode(String materialCode);
}

View File

@ -0,0 +1,14 @@
package dev.fyloz.trial.colorrecipesexplorer.dao;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MaterialTypeDao extends JpaRepository<MaterialType, Integer> {
Optional<MaterialType> findByMaterialTypeName(String name);
Optional<MaterialType> findByPrefix(String prefix);
}

View File

@ -1,7 +1,7 @@
package fyloz.trial.ColorRecipesExplorer.dao;
package dev.fyloz.trial.colorrecipesexplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Mix;
import fyloz.trial.ColorRecipesExplorer.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

View File

@ -1,7 +1,7 @@
package fyloz.trial.ColorRecipesExplorer.dao;
package dev.fyloz.trial.colorrecipesexplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixQuantity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@ -9,7 +9,7 @@ import java.util.List;
@Repository
public interface MixQuantityDao extends JpaRepository<MixQuantity, Integer> {
MixQuantity findByMixQuantityID(int mixQuantityID);
List<MixQuantity> findAllByMaterial(Material material);
boolean existsByMaterial(Material material);
}

View File

@ -0,0 +1,15 @@
package dev.fyloz.trial.colorrecipesexplorer.dao;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface MixTypeDao extends JpaRepository<MixType, Integer> {
Optional<MixType> findByTypeName(String typeName);
Optional<MixType> findByMaterial(Material material);
}

View File

@ -0,0 +1,22 @@
package dev.fyloz.trial.colorrecipesexplorer.dao;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RecipeDao extends JpaRepository<Recipe, Integer> {
List<Recipe> findAllByCompany(Company company);
Recipe findByRecipeCode(String recipeCode);
Recipe findByRecipeID(int recipeID);
@Query("select r from Recipe r where upper(r.recipeCode) like %?1% or upper(r.recipeDescription) like %?1%")
List<Recipe> findAllByRecipeDescriptionContainsOrRecipeCodeContains(String searchWordUpperCase);
}

View File

@ -1,7 +1,7 @@
package fyloz.trial.ColorRecipesExplorer.dao;
package dev.fyloz.trial.colorrecipesexplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Recipe;
import fyloz.trial.ColorRecipesExplorer.model.RecipeStep;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.RecipeStep;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;

View File

@ -1,17 +1,25 @@
package fyloz.trial.ColorRecipesExplorer.web;
package dev.fyloz.trial.colorrecipesexplorer.web;
public class PagesPaths {
// Autres
public static final String INDEX = "index";
public static final String SEARCH = "search";
public static final String SEARCH_INVENTORY = "inventory/search";
public static final String SIMDUT_FILES = "simdut/{materialID}";
public static final String PASSWORD_VALIDATION = "password/valid";
public static final String TOUCHUP = "touchup";
public static final String RECIPE_XLS = "recipe/xls/{recipeID}";
public static final String ALL_RECIPES_XLS = "recipe/xls";
public static final String ERROR = "error";
public static final String CLOSE_TAB = "closeTab";
public static final String UPDATES = "updates";
public static final String UPDATES_GET = "updates/get";
// Images
public static final String IMAGES_FILES = "images/{image}";
public static final String ADD_IMAGE = "images/add";
public static final String ADD_IMAGE_SPECIFIC = "images/add/{recipeID}";
public static final String DELETE_IMAGE = "images/deleteIfNotLinked";
public static final String DELETE_IMAGE = "images/delete";
// Inventaire
public static final String INVENTORY = "inventory";
@ -47,6 +55,11 @@ public class PagesPaths {
// Types de matériaux
public static final String CREATOR_MATERIAL_TYPE = "materialType/creator";
public static final String REMOVER_MATERIAL_TYPE = "materialType/remover";
public static final String REMOVER_MATERIAL_TYPE_SPECIFIC = "materialType/remover/{materialTypeID}";
public static final String EDITOR_MATERIAL_TYPE = "materialType/editor";
public static final String EDITOR_MATERIAL_TYPE_SPECIFIC = "materialType/editor/{materialTypeID}";
public static final String EDITOR_MATERIAL_TYPE_EDITOR = "materialType/edit";
// Mélanges
public static final String CREATOR_MIX = "mix/creator";
@ -54,4 +67,6 @@ public class PagesPaths {
public static final String EDITOR_MIX = "mix/editor";
public static final String EDITOR_MIX_SPECIFIC = "mix/editor/{mixID}";
public static final String REMOVER_MIX_SPECIFIC = "mix/remover/{mixID}";
public static final String MATERIAL_SELECTOR_MIX = "mix/selector/{recipeID}/{mixID}";
public static final String MATERIAL_SELECTOR_FRAGMENT = "mix/selector.html :: materialSelector";
}

View File

@ -1,4 +1,4 @@
package fyloz.trial.ColorRecipesExplorer.web;
package dev.fyloz.trial.colorrecipesexplorer.web;
public class StringBank {
@ -48,8 +48,4 @@ public class StringBank {
public static final String COMPANY = "company";
public static final String COMPANY_ID = "companyID";
public static final String UPLOAD_LOCATION = System.getProperty("upload.location");
public static final String IMAGES_LOCATION = UPLOAD_LOCATION + "/images";
public static final String SIMDUTS_LOCATION = UPLOAD_LOCATION + "/simdut/";
}

View File

@ -0,0 +1,51 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseCode;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.ERROR;
@Controller
public class CREErrorController implements ErrorController {
@RequestMapping("/error")
public ModelAndView handleError(HttpServletRequest request) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("error");
responseBuilder.addAttribute("referer", request.getHeader("referer"));
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
ResponseCode responseCode = ResponseCode._500;
if (status != null) {
int statusCode = Integer.parseInt(status.toString());
switch (statusCode) {
case 401:
responseCode = ResponseCode._401;
break;
case 404:
responseCode = ResponseCode._404;
break;
default:
responseCode = ResponseCode._500;
break;
}
}
responseBuilder.addResponseCode(responseCode);
return responseBuilder.build();
}
@Override
public String getErrorPath() {
return ERROR;
}
}

View File

@ -1,12 +1,14 @@
package fyloz.trial.ColorRecipesExplorer.web.controller;
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication;
import fyloz.trial.ColorRecipesExplorer.core.ModelBuilder;
import fyloz.trial.ColorRecipesExplorer.core.ModelDataType;
import fyloz.trial.ColorRecipesExplorer.core.ResponseCode;
import fyloz.trial.ColorRecipesExplorer.core.io.ImageHandler;
import fyloz.trial.ColorRecipesExplorer.model.Recipe;
import fyloz.trial.ColorRecipesExplorer.services.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.ImageHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.JSONResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@ -17,15 +19,13 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static fyloz.trial.ColorRecipesExplorer.core.utils.ControllerUtils.redirect;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*;
import static fyloz.trial.ColorRecipesExplorer.web.StringBank.ERROR_SAVING_IMAGE;
import static fyloz.trial.ColorRecipesExplorer.web.StringBank.RESPONSE_ERROR;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class ImageFilesController {
@ -68,9 +68,9 @@ public class ImageFilesController {
*/
@GetMapping(ADD_IMAGE_SPECIFIC)
public ModelAndView showPage(ModelAndView model, @PathVariable int recipeID) {
return new ModelBuilder(model)
.setView(ADD_IMAGE)
.addData(ModelDataType.RECIPE_ID, recipeID)
return new ModelResponseBuilder(model)
.withView(ADD_IMAGE)
.addResponseData(ResponseDataType.RECIPE_ID, recipeID)
.build();
}
@ -95,37 +95,45 @@ public class ImageFilesController {
* @return La page à afficher.
*/
@PostMapping(ADD_IMAGE)
public ModelAndView addImage(int recipeID, MultipartFile image) {
ModelBuilder modelBuilder = new ModelBuilder(redirect(EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(recipeID))));
Recipe recipe = recipeService.getByID(recipeID);
public ModelAndView addImage(int recipeID, MultipartFile image) throws IOException {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(ControllerUtils.redirect(EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(recipeID))));
if (recipe == null) {
modelBuilder.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, String.valueOf(recipeID));
return showPage(modelBuilder.build(), recipeID);
// Vérifie que le fichier est bien une image
if (ImageIO.read(image.getInputStream()) == null) {
return showPage(modelResponseBuilder
.addResponseCode(ResponseCode.FILE_NOT_IMAGE)
.build(), recipeID);
}
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return showPage(modelResponseBuilder
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID)
.build(), recipeID);
}
Recipe recipe = optionalRecipe.get();
ImageHandler imageHandler = new ImageHandler(recipe, recipeService);
if (!imageHandler.createFile()) {
modelBuilder.addResponseCode(ResponseCode.ERROR_SAVING_IMAGE);
return showPage(modelBuilder.build(), recipeID);
return showPage(modelResponseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING_IMAGE)
.build(), recipeID);
}
modelBuilder
.addData(ModelDataType.RECIPE_CODE, recipe.getRecipeCode())
.addData(ModelDataType.RECIPE_ID, recipe.getRecipeID());
modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE_CODE, recipe.getRecipeCode())
.addResponseData(ResponseDataType.RECIPE_ID, recipe.getRecipeID());
try {
// Si je n'utilise pas le path, il cherche un fichier dans les tmp ?
image.transferTo(new File(imageHandler.getFile().getAbsolutePath()));
return modelBuilder.build();
return modelResponseBuilder.build();
} catch (IOException e) {
ColorRecipesExplorerApplication.logger.error("Erreur inconnue lors de la création d'une image", e);
modelBuilder.addResponseCode(ResponseCode.ERROR_SAVING_IMAGE);
ColorRecipesExplorerApplication.LOGGER.error("Erreur inconnue lors de la création d'une image", e);
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_IMAGE);
return showPage(modelBuilder.build(), recipeID);
return showPage(modelResponseBuilder.build(), recipeID);
}
}
@ -142,18 +150,18 @@ public class ImageFilesController {
* REQUIERT UNE AUTORISATION
*
* @param form Le formulaire contenant les informations sur l'opération
* @return La réponse sous forme de Map (-> Json)
* @return La réponse sous forme de Map (pour y accéder en Json)
*/
@PostMapping(value = DELETE_IMAGE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, String> deleteImage(@RequestBody Map<String, String> form) {
Map<String, String> response = new HashMap<>();
public Map<String, Object> deleteImage(@RequestBody Map<String, String> form) {
JSONResponseBuilder responseBuilder = new JSONResponseBuilder();
ImageHandler imageHandler = new ImageHandler(form.get("image"), recipeService);
if (!imageHandler.deleteFile()) {
response.put(RESPONSE_ERROR, ERROR_SAVING_IMAGE);
responseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_IMAGE);
}
return response;
return responseBuilder.build();
}
}

View File

@ -0,0 +1,81 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.JSONResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ResponseDataType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.PasswordService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.CompanyService;
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.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class IndexController {
private CompanyService companyService;
private RecipeService recipeService;
@Autowired
public IndexController(CompanyService companyService, RecipeService recipeService) {
this.companyService = companyService;
this.recipeService = recipeService;
}
/**
* Affiche la page d'acceuil.
*
* @return La page à afficher.
*/
@GetMapping({INDEX, "/"})
public ModelAndView showPage() {
List<Company> companies = companyService.getAll();
Map<Company, List<Recipe>> recipes = new HashMap<>();
for (Company company : companies) {
recipes.put(company, recipeService.getByCompany(company));
}
return new ModelResponseBuilder(INDEX)
.addResponseData(ResponseDataType.RECIPE_MAP, recipes)
.build();
}
@GetMapping(value = SEARCH, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, Object> searchWord(@RequestParam String searchString) {
Map<Company, List<Recipe>> searchResult = recipeService.getRecipesForSearchString(searchString);
Map<Integer, List<Integer>> outputResult = new HashMap<>();
for (Company c : searchResult.keySet()) {
outputResult.put(c.getCompanyID(), searchResult.get(c).stream().map(Recipe::getRecipeID).collect(Collectors.toList()));
}
return new JSONResponseBuilder()
.addAttribute("result", outputResult)
.build();
}
/**
* Valide un mot de passe reçu dans une requête HTTP POST, dans le champ "password".
*
* @param data Corps de la requête HTTP
* @return Si le mot de passe est valide.
*/
@PostMapping(value = PASSWORD_VALIDATION, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public boolean validatePassword(@RequestBody Map<String, Object> data) {
return PasswordService.isValid((String) data.get("password"));
}
}

View File

@ -1,33 +1,28 @@
package fyloz.trial.ColorRecipesExplorer.web.controller;
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import fyloz.trial.ColorRecipesExplorer.core.ModelBuilder;
import fyloz.trial.ColorRecipesExplorer.core.ModelDataType;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.model.Mix;
import fyloz.trial.ColorRecipesExplorer.model.MixQuantity;
import fyloz.trial.ColorRecipesExplorer.services.InventoryService;
import fyloz.trial.ColorRecipesExplorer.services.MaterialService;
import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService;
import fyloz.trial.ColorRecipesExplorer.services.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.JSONResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.services.InventoryService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.INVENTORY;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.USE_INVENTORY;
import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.StringBank.RESPONSE_REASON;
@Controller
public class InventoryController {
@ -45,19 +40,12 @@ public class InventoryController {
this.materialTypeService = materialTypeService;
}
/**
* Affiche la page de l'inventaire.
* <p>
* Modèle de la page:
* - materials: Contient la liste de tous les matériaux
*
* @return La page à afficher.
*/
@GetMapping(INVENTORY)
public ModelAndView getInventory(ModelAndView model) {
return new ModelBuilder(INVENTORY)
.addData(ModelDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
.addData(ModelDataType.MATERIAL_TYPES, materialTypeService.getAll())
return new ModelResponseBuilder(model)
.withView(INVENTORY)
.addResponseData(ResponseDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
}
@ -83,14 +71,14 @@ public class InventoryController {
* - success: Contient le message à affiche lors du succès de la méthode
*
* @param form Le JSON contenant les quantités à utiliser.
* @return Une réponse sous forme de Map (-> Json).
* @return Une réponse sous forme de Map (vers Json).
*/
@PostMapping(value = USE_INVENTORY, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@Transactional
// TODO traduits les méthodes JSON
public Map<String, String> consumeMaterials(@RequestBody Map<String, Object> form) {
Map<String, String> response = new HashMap<>();
// TODO traduit les méthodes JSON
public Map<String, Object> consumeMaterials(@RequestBody Map<String, Object> form) {
JSONResponseBuilder responseBuilder = new JSONResponseBuilder();
List<Mix> mixes = new ArrayList<>();
Map<Mix, Map<Material, Float>> quantities = new HashMap<>();
@ -98,12 +86,14 @@ public class InventoryController {
for (String mixIDStr : form.keySet()) {
int mixID = Integer.parseInt(mixIDStr);
Mix mix = mixService.getByID(mixID);
if (mix == null) {
response.put(RESPONSE_ERROR, String.format(MIX_NOT_FOUND, mixID));
return response;
Optional<Mix> optionalMix = mixService.getByID(mixID);
if (!optionalMix.isPresent()) {
return responseBuilder
.addResponseCode(ResponseCode.MIX_NOT_FOUND, mixID)
.build();
}
Mix mix = optionalMix.get();
mixes.add(mix);
Map<String, String> formMaterials = (Map<String, String>) form.get(mixIDStr);
@ -123,20 +113,36 @@ public class InventoryController {
for (Mix mix : mixes) {
String errorCode = inventoryService.checkQuantities(mix, quantities.get(mix));
if (errorCode != null) {
response.put(RESPONSE_ERROR, String.format(NOT_ENOUGH_MATERIAL, materialService.getByID(Integer.parseInt(errorCode.split("-")[1])).getMaterialCode()));
response.put(RESPONSE_REASON, errorCode);
return response;
String materialCode = materialService.getByID(Integer.parseInt(errorCode.split("-")[1])).orElse(new Material()).getMaterialCode();
return responseBuilder
.addResponseCode(ResponseCode.NOT_ENOUGH_MATERIAL, materialCode)
.addAttribute(RESPONSE_REASON, errorCode)
.build();
}
}
for (Mix mix : mixes) {
if (!inventoryService.useMix(mix, quantities.get(mix))) {
response.put(RESPONSE_ERROR, ERROR_SAVING);
return response;
return responseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build();
}
}
response.put(RESPONSE_SUCCESS, SUCCESS_USING_MATERIALS);
return response;
return responseBuilder
.addResponseCode(ResponseCode.SUCCESS_USING_MATERIALS)
.build();
}
@GetMapping(value = SEARCH_INVENTORY, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, Object> searchWordInventory(@RequestParam String searchString) {
List<Material> searchResult = materialService.getAllBySearchString(searchString);
List<Integer> outputResult = searchResult.stream().map(Material::getMaterialID).collect(Collectors.toList());
return new JSONResponseBuilder()
.addAttribute("result", outputResult)
.build();
}
}

View File

@ -0,0 +1,169 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.FileUtils;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.XlsxExporter;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.StringBank.MATERIALS;
@Controller
public class OthersController {
private RecipeService recipeService;
private MaterialService materialService;
private MixTypeService mixTypeService;
private MixService mixService;
private ResourceLoader resourceLoader;
@Autowired
public OthersController(RecipeService recipeService, MaterialService materialService, MixTypeService mixTypeService, MixService mixService, ResourceLoader resourceLoader) {
this.recipeService = recipeService;
this.materialService = materialService;
this.mixTypeService = mixTypeService;
this.mixService = mixService;
this.resourceLoader = resourceLoader;
}
@GetMapping(CLOSE_TAB)
public ModelAndView closeTab() {
return new ModelResponseBuilder(CLOSE_TAB).build();
}
@GetMapping(MATERIAL_SELECTOR_MIX)
public ModelAndView getMaterialSelectorFragment(@PathVariable int recipeID, @PathVariable int mixID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(MATERIAL_SELECTOR_FRAGMENT);
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return modelResponseBuilder
.withView("")
.build();
}
Optional<Mix> optionalMix = mixService.getByID(mixID);
boolean mixExist = optionalMix.isPresent();
List<MixType> associatedMixTypes = recipeService.getAssociatedMixesTypes(optionalRecipe.get());
// Récupère seulement les produits qui ne sont pas des types de mélange OU que ce type existe dans la recette
List<Material> materials = materialService
.getAll()
.stream()
.filter(m -> !m.isMixType() || (!mixExist || !m.equals(optionalMix.get().getMixType().getMaterial())) && associatedMixTypes.contains(mixTypeService.getByMaterial(m).get()))
.sorted(Comparator.comparing(Material::getMaterialCode))
.sorted(Comparator.comparing(m -> m.getMaterialType().getMaterialTypeName()))
.collect(Collectors.toList());
return modelResponseBuilder
.addAttribute(MATERIALS, materials)
.build();
}
@GetMapping(TOUCHUP)
public ModelAndView getTouchUpPdf() {
return new ModelResponseBuilder().withRedirect("pdf/touchup.pdf").build();
}
@GetMapping(RECIPE_XLS)
public ResponseEntity<byte[]> getXlsForRecipe(HttpServletRequest request, @PathVariable int recipeID) {
HttpHeaders headers = new HttpHeaders();
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
headers.add(HttpHeaders.LOCATION, request.getHeader("referer"));
return new ResponseEntity<>(headers, HttpStatus.FOUND);
}
byte[] recipeXLS = new XlsxExporter().generate(optionalRecipe.get());
if (recipeXLS.length <= 0) return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
return ResponseEntity.ok()
.headers(headers)
.contentLength(recipeXLS.length)
.contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.body(recipeXLS);
}
@GetMapping(value = ALL_RECIPES_XLS, produces = "application/zip")
public ResponseEntity<byte[]> getAllXls() throws IOException {
HttpHeaders headers = new HttpHeaders();
ColorRecipesExplorerApplication.LOGGER.info("Exportation de toutes les couleurs en XLS");
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(byteOS);
Collection<Recipe> recipes = recipeService.getAll();
for (Recipe recipe : recipes) {
byte[] recipeXLS = new XlsxExporter().generate(recipe);
if (recipeXLS.length <= 0) return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
out.putNextEntry(new ZipEntry(String.format("%s_%s.xlsx", recipe.getCompany().getCompanyName(), recipe.getRecipeCode())));
out.write(recipeXLS, 0, recipeXLS.length);
out.closeEntry();
}
out.close();
byte[] zipContent = byteOS.toByteArray();
byteOS.close();
return ResponseEntity.ok()
.headers(headers)
.contentLength(zipContent.length)
.contentType(MediaType.parseMediaType("application/zip"))
.body(zipContent);
}
@GetMapping(value = UPDATES)
public ModelAndView showUpdates() {
return new ModelResponseBuilder(UPDATES).build();
}
@GetMapping(value = UPDATES_GET, produces = MediaType.TEXT_HTML_VALUE)
@ResponseBody
public ResponseEntity<String> getUpdates() throws IOException {
String fileContent = FileUtils.readClasspathFile(resourceLoader, "classpath:updates.md");
if (fileContent == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
Parser parser = Parser.builder().build();
Node document = parser.parse(fileContent);
HtmlRenderer renderer = HtmlRenderer.builder().build();
return new ResponseEntity<>(renderer.render(document), HttpStatus.OK);
}
}

View File

@ -0,0 +1,112 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.JSONResponseBuilder;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
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.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class RecipeExplorerController {
private RecipeService recipeService;
private MixService mixService;
@Autowired
public RecipeExplorerController(RecipeService recipeService, MixService mixService) {
this.recipeService = recipeService;
this.mixService = mixService;
}
/**
* Affiche la recette.
* Cette méthode requiert l'identifiant de la recette à affiche dans l'URL.
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - recipe: Contient la recette
* - mixes: Contient les mélanges associées à la recette
* - images: Contient la liste des noms des images associées à la recette
*
* @param recipeID L'identifiant de la recette
* @return La page à afficher.
*/
@GetMapping(EXPLORER_RECIPE_SPECIFIC)
public ModelAndView showRecipe(@PathVariable int recipeID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(EXPLORER_RECIPE);
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return modelResponseBuilder
.withView(INDEX)
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID)
.build();
}
Recipe recipe = optionalRecipe.get();
List<Mix> mixes = new ArrayList<>(recipe.getRecipeMixes()); // Convertit le PersistentBag en ArrayList
mixes.sort(Comparator.comparing(Mix::getMixID));
return modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addResponseData(ResponseDataType.MIXES, mixes)
.addResponseData(ResponseDataType.IMAGES, recipeService.getImageFiles(recipe))
.build();
}
@PostMapping(value = EXPLORER_RECIPE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Map<String, Object> saveRecipeInformations(@RequestBody Map<String, Object> form) {
JSONResponseBuilder responseBuilder = new JSONResponseBuilder();
int recipeID = Integer.parseInt(form.get("recipeID").toString());
Map<String, String> location = (Map<String, String>) form.get("locations");
String note = form.get("note").toString();
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID);
} else {
Recipe recipe = optionalRecipe.get();
recipe.setNote(note);
recipeService.save(recipe);
}
for (String mixIDStr : location.keySet()) {
int mixID = Integer.parseInt(mixIDStr);
Optional<Mix> optionalMix = mixService.getByID(mixID);
if (!optionalMix.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.MIX_NOT_FOUND, mixID);
} else {
Mix mix = optionalMix.get();
if (mix.getRecipe().getRecipeID() != recipeID) {
responseBuilder.addResponseCode(ResponseCode.MIX_NOT_ASSOCIATED_WITH_RECIPE, mixID, recipeID);
} else {
mix.setLocation(location.get(mixIDStr));
mixService.update(mix);
}
}
}
if (!responseBuilder.containsErrors()) {
responseBuilder.addResponseCode(ResponseCode.SUCCESS_SAVING_RECIPE_INFORMATIONS);
}
return responseBuilder.build();
}
}

View File

@ -0,0 +1,62 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller;
import dev.fyloz.trial.colorrecipesexplorer.core.io.file.FileHandler;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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 javax.servlet.http.HttpServletRequest;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CLOSE_TAB;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.SIMDUT_FILES;
@Controller
public class SIMDUTFilesController {
private MaterialService materialService;
@Autowired
public SIMDUTFilesController(MaterialService materialService) {
this.materialService = materialService;
}
@GetMapping(SIMDUT_FILES)
public ResponseEntity<byte[]> getFile(HttpServletRequest request, @PathVariable int materialID) {
Optional<Material> optionalMaterial = materialService.getByID(materialID);
HttpHeaders headers = new HttpHeaders();
if (!optionalMaterial.isPresent()) {
headers.add("Location", request.getHeader("referer"));
return new ResponseEntity<>(headers, HttpStatus.FOUND);
}
FileHandler fileHandler = new FileHandler(String.format("%s_%s", materialID, optionalMaterial.get().getMaterialCode()), FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF);
if (!fileHandler.isValid()) {
headers.add("Location", "/" + CLOSE_TAB);
return new ResponseEntity<>(headers, HttpStatus.FOUND);
}
byte[] fileContent = fileHandler.readFile();
headers.setContentType(MediaType.APPLICATION_PDF);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
}
@PostMapping(SIMDUT_FILES)
public ResponseEntity<Void> getFile(@PathVariable int materialID) {
Optional<Material> optionalMaterial = materialService.getByID(materialID);
if (!optionalMaterial.isPresent()) return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
FileHandler fileHandler = new FileHandler(String.format("%s_%s", materialID, optionalMaterial.get().getMaterialCode()), FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF);
if (!fileHandler.isValid()) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
return ResponseEntity.status(HttpStatus.FOUND).build();
}
}

View File

@ -1,10 +1,10 @@
package fyloz.trial.ColorRecipesExplorer.web.controller.creators;
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import fyloz.trial.ColorRecipesExplorer.core.ModelBuilder;
import fyloz.trial.ColorRecipesExplorer.core.ModelDataType;
import fyloz.trial.ColorRecipesExplorer.core.ResponseCode;
import fyloz.trial.ColorRecipesExplorer.model.Company;
import fyloz.trial.ColorRecipesExplorer.services.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.CompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
@ -14,9 +14,10 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_COMPANY;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_COMPANY_SUCCESS;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_COMPANY;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_COMPANY_SUCCESS;
@Controller
public class CompanyCreatorController {
@ -28,16 +29,11 @@ public class CompanyCreatorController {
this.companyService = companyService;
}
/**
* Affiche la page de création d'une bannière.
*
* @return La page à afficher.
*/
@GetMapping(CREATOR_COMPANY)
public ModelAndView showCreationPage(ModelAndView model, Company company) {
return new ModelBuilder(model)
.setView(CREATOR_COMPANY)
.addData(ModelDataType.COMPANY, company)
return new ModelResponseBuilder(model)
.withView(CREATOR_COMPANY)
.addResponseData(ResponseDataType.COMPANY, company)
.build();
}
@ -60,20 +56,22 @@ public class CompanyCreatorController {
*/
@PostMapping(value = CREATOR_COMPANY, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView createCompany(@ModelAttribute @Valid Company company) {
ModelBuilder modelBuilder = new ModelBuilder(CREATOR_COMPANY_SUCCESS);
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(CREATOR_COMPANY_SUCCESS);
if (companyService.isValidForCreation(company)) {
if ((company = companyService.save(company)) != null) {
return modelBuilder
.addData(ModelDataType.COMPANY_NAME, company.getCompanyName())
Optional<Company> savedCompany = companyService.save(company);
if (savedCompany.isPresent()) {
return modelResponseBuilder
.addResponseData(ResponseDataType.COMPANY_NAME, savedCompany.get().getCompanyName())
.build();
} else {
modelBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
} else {
modelBuilder.addResponseCode(ResponseCode.MIX_NOT_ASSOCIATED_WITH_RECIPE, company.getCompanyName(), null);
modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_ALREADY_EXIST, company.getCompanyName());
}
return showCreationPage(modelBuilder.build(), company);
return showCreationPage(modelResponseBuilder.build(), company);
}
}

View File

@ -0,0 +1,89 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
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.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_MATERIAL;
@Controller
public class MaterialCreatorController {
private MaterialService materialService;
private MaterialTypeService materialTypeService;
@Autowired
public MaterialCreatorController(MaterialService materialService, MaterialTypeService materialTypeService) {
this.materialService = materialService;
this.materialTypeService = materialTypeService;
}
@GetMapping(CREATOR_MATERIAL)
public ModelAndView showCreationPage(ModelAndView model, Material material) {
return new ModelResponseBuilder(model)
.withView(CREATOR_MATERIAL)
.addResponseData(ResponseDataType.MATERIAL, material != null ? material : new Material())
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
}
/**
* Permet à l'utilisateur de créer un produit.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à effectuer cette action
* - Le produit existe déjà
* - Une erreur est survenue lors de la sauvegarde dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - materialCode: Contient le code de produit du produit créé
* <p>
* REQUIERT UNE AUTORISATION
*
* @param material Le produit à créer
* @param simdut Le fichier SIMDUT du produit (optionnel)
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_MATERIAL, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ModelAndView create(@Valid Material material, MultipartFile simdut) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
if (!materialService.exists(material)) {
Optional<Material> savedMaterial = materialService.save(material);
if (savedMaterial.isPresent()) {
material = savedMaterial.get();
modelResponseBuilder.addResponseData(ResponseDataType.MATERIAL_CODE, material.getMaterialCode());
if (simdut.getSize() > 0 && !materialService.addSimdut(simdut, material)) {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
}
return showCreationPage(modelResponseBuilder
.addResponseData(ResponseDataType.MATERIAL_CODE, material.getMaterialCode())
.build(), null);
} else {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
} else {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getMaterialCode());
}
return showCreationPage(modelResponseBuilder.build(), material);
}
}

View File

@ -0,0 +1,54 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_MATERIAL_TYPE;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.INDEX;
@Controller
public class MaterialTypeCreatorController {
private MaterialTypeService materialTypeService;
@Autowired
public MaterialTypeCreatorController(MaterialTypeService materialTypeService) {
this.materialTypeService = materialTypeService;
}
@GetMapping(CREATOR_MATERIAL_TYPE)
public ModelAndView showPage(ModelAndView model, MaterialType materialType) {
return new ModelResponseBuilder(model)
.withView(CREATOR_MATERIAL_TYPE)
.addResponseData(ResponseDataType.MATERIAL_TYPE, materialType == null ? new MaterialType() : materialType)
.build();
}
@PostMapping(CREATOR_MATERIAL_TYPE)
public ModelAndView create(@Valid MaterialType materialType) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(ControllerUtils.redirect(INDEX));
if (materialTypeService.isValidForCreation(materialType)) {
if (materialTypeService.save(materialType) != null) {
return modelResponseBuilder.build();
} else {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}
} else {
modelResponseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST, materialType.getMaterialTypeName());
}
return showPage(modelResponseBuilder.build(), materialType);
}
}

View File

@ -0,0 +1,128 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class MixCreatorController {
private MixService mixService;
private RecipeService recipeService;
private MaterialService materialService;
private MixTypeService mixTypeService;
@Autowired
public MixCreatorController(MixService mixService, RecipeService recipeService, MaterialService materialService, MixTypeService mixTypeService) {
this.mixService = mixService;
this.recipeService = recipeService;
this.materialService = materialService;
this.mixTypeService = mixTypeService;
}
/**
* Affiche la page de création d'un mélange.
* Cette méthode requiert l'identifiant d'une recette dans l'URL.
*
* @param model Le Model injecté par Thymeleaf
* @param recipeID L'identifiant de la recette
* @return La page à afficher.
*/
@GetMapping(CREATOR_MIX_SPECIFIC)
public ModelAndView showCreationPage(ModelAndView model, @PathVariable int recipeID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(model)
.withView(CREATOR_MIX);
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return modelResponseBuilder
.withView(ControllerUtils.redirect(EDITOR_RECIPE))
.build();
}
Recipe recipe = optionalRecipe.get();
List<MixType> associatedMixTypes = recipeService.getAssociatedMixesTypes(recipe);
// Récupère seulement les produits qui ne sont pas des types de mélange OU que ce type existe dans la recette
List<Material> materials = materialService
.getAll()
.stream()
.filter(m -> !m.isMixType() || associatedMixTypes.contains(mixTypeService.getByMaterial(m).get()))
.sorted(Comparator.comparing(Material::getMaterialCode))
.sorted(Comparator.comparing(m -> m.getMaterialType().getMaterialTypeName()))
.collect(Collectors.toList());
ModelResponseBuilder responseBuilder = modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addAttribute("materialsJson", materialService.asJson(materials));
if (materialService.getAll().isEmpty()) {
responseBuilder.addResponseCode(ResponseCode.NO_MATERIAL)
.addResponseData(ResponseDataType.BLOCK_BUTTON, true);
}
return responseBuilder.build();
}
/**
* Permet à l'utilisateur de créer un mélange.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - La recette n'existe pas
* - Un des produits n'existe pas
* - Une erreur est survenue lors de la sauvegarde du type de mélange
* - Une erreur est survenue lors de la sauvegarde du mélange
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param form La formulaire du mélange entré par l'utilisateur
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_MIX, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView createMix(@ModelAttribute @Valid MixCreationFormDto formDto) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
Recipe recipe = formDto.getRecipe();
if (recipe == null) return modelResponseBuilder
.withRedirect(EDITOR_RECIPE)
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND_NO_PARAMS)
.build();
ModelResponseBuilder mixCreationResponse = mixService.create(formDto, recipe);
if (mixCreationResponse != null)
return showCreationPage(mixCreationResponse.build(), formDto.getRecipe().getRecipeID());
return modelResponseBuilder
.withRedirect(EDITOR_RECIPE_SPECIFIC, recipe.getRecipeID())
.build();
}
}

View File

@ -0,0 +1,77 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.creators;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_RECIPE;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_RECIPE_SUCCESS;
@Controller
public class RecipeCreatorController {
private RecipeService recipeService;
private CompanyService companyService;
@Autowired
public RecipeCreatorController(RecipeService recipeService, CompanyService companyService) {
this.recipeService = recipeService;
this.companyService = companyService;
}
@GetMapping(CREATOR_RECIPE)
public ModelAndView showCreationPage(ModelAndView model, Recipe recipe) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model)
.withView(CREATOR_RECIPE)
.addResponseData(ResponseDataType.COMPANIES, companyService.getAll())
.addResponseData(ResponseDataType.RECIPE, recipe == null ? new Recipe() : recipe);
if (companyService.getAll().isEmpty()) {
responseBuilder.addResponseCode(ResponseCode.NO_COMPANY)
.addResponseData(ResponseDataType.BLOCK_BUTTON, true);
}
return responseBuilder.build();
}
/**
* Permet à l'utilisateur de créer une nouvelle recette.
* <p>
* La création échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - Une erreur est survenue lors de la sauvegarde dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param recipe La recette à créer
* @return La page à afficher.
*/
@PostMapping(value = CREATOR_RECIPE)
public ModelAndView createRecipe(@Valid Recipe recipe) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(CREATOR_RECIPE_SUCCESS);
Optional<Recipe> savedRecipe = recipeService.save(recipe);
if (savedRecipe.isPresent()) {
return modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, savedRecipe.get())
.build();
}
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
return showCreationPage(modelResponseBuilder.build(), recipe);
}
}

View File

@ -0,0 +1,178 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
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.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class MaterialEditorController {
private MaterialService materialService;
private MaterialTypeService materialTypeService;
@Autowired
public MaterialEditorController(MaterialService materialService, MaterialTypeService materialTypeService) {
this.materialService = materialService;
this.materialTypeService = materialTypeService;
}
/**
* Affiche la page de tous les produits.
* <p>
* Modèle de la page:
* - materials: La liste de tous les produits
*
* @param model Le Model injecté par Thymeleaf
* @return La page à afficher.
*/
@GetMapping(EDITOR_MATERIAL)
public ModelAndView listMaterials(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL)
.addResponseData(ResponseDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
.build();
}
@GetMapping(EDITOR_MATERIAL_SPECIFIC)
public ModelAndView showEditPage(ModelAndView model, @PathVariable int materialID, Material material) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_EDITOR);
if (material.getMaterialCode() == null) {
Optional<Material> optionalMaterial = materialService.getByID(materialID);
if (!optionalMaterial.isPresent()) {
return listMaterials(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, materialID)
.build()
);
}
material = optionalMaterial.get();
}
return responseBuilder
.addResponseData(ResponseDataType.MATERIAL, material)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
}
/**
* Permet à l'utilisateur de modifier un produit.
* <p>
* La modification échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - Il n'y a aucun produit avec l'identifiant passé en paramètre
* - Une erreur est survenue lors de la mise à jour dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - materialCode: Contient le code du produit mit à jour
* <p>
* REQUIERT UNE AUTORISATION
*
* @param material Le produit à mettre à jour
* @return La page à afficher.
*/
@PostMapping(value = EDITOR_MATERIAL, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView saveEditedMaterial(Material material) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
int materialID = material.getMaterialID();
if (!materialService.existsById(materialID)) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, materialID);
} else if (!materialService.isValidForUpdate(material)) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getMaterialCode());
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_ALREADY_EXIST, material.getMaterialCode())
.build(),
materialID, material);
} else {
Optional<Material> updatedMaterial = materialService.update(material);
if (updatedMaterial.isPresent()) {
responseBuilder.addResponseData(ResponseDataType.MATERIAL_CODE, updatedMaterial.get().getMaterialCode());
} else {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build(),
materialID, null);
}
}
return listMaterials(responseBuilder.build());
}
/**
* Affiche la page d'upload d'un fichier SIMDUT pour un produit
* Cette méthode requiert l'identifiant du produit dans l'URL
* <p>
* Modèle de la page:
* - materialID: Contient l'identifiant du produit
*
* @param model Le Model injecté par Thymeleaf
* @param materialID L'identifiant du produit à modifier le SIMDUT
* @return La page à afficher.
*/
@GetMapping(value = EDIT_MATERIAL_SIMDUT_SPECIFIC)
public ModelAndView chooseSIMDUTFile(ModelAndView model, @PathVariable int materialID) {
return new ModelResponseBuilder(model)
.withView(EDIT_MATERIAL_SIMDUT)
.addResponseData(ResponseDataType.MATERIAL_ID, materialID)
.build();
}
/**
* Sauvegarde le fichier SIMDUT d'un produit.
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param materialID L'identifiant du produit
* @param simdut Le fichier SIMDUT uploadé
* @return La page à afficher.
*/
@PostMapping(value = EDIT_MATERIAL_SIMDUT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ModelAndView saveSIMDUT(int materialID, MultipartFile simdut) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(ControllerUtils.redirect("material/editor/" + materialID));
Optional<Material> optionalMaterial = materialService.getByID(materialID);
if (!optionalMaterial.isPresent()) {
return chooseSIMDUTFile(modelResponseBuilder
.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, materialID)
.build(),
materialID);
}
Material material = optionalMaterial.get();
if (!materialService.removeSimdut(material) || !materialService.addSimdut(simdut, material)) {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
return chooseSIMDUTFile(modelResponseBuilder.build(), materialID);
}
return modelResponseBuilder.build();
}
}

View File

@ -0,0 +1,96 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
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.servlet.ModelAndView;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class MaterialTypeEditorController {
private MaterialTypeService materialTypeService;
@Autowired
public MaterialTypeEditorController(MaterialTypeService materialTypeService) {
this.materialTypeService = materialTypeService;
}
@GetMapping(EDITOR_MATERIAL_TYPE)
public ModelAndView listMaterialTypes(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_TYPE)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
}
@GetMapping(EDITOR_MATERIAL_TYPE_SPECIFIC)
public ModelAndView showEditPage(ModelAndView model, @PathVariable int materialTypeID, MaterialType materialType) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_MATERIAL_TYPE_EDITOR);
if (materialType.getMaterialTypeName() == null) {
Optional<MaterialType> optionalMaterialType = materialTypeService.getByID(materialTypeID);
if (!optionalMaterialType.isPresent()) {
return listMaterialTypes(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, materialTypeID)
.build()
);
}
materialType = optionalMaterialType.get();
}
return responseBuilder
.addResponseData(ResponseDataType.MATERIAL_TYPE, materialType)
.build();
}
@PostMapping(value = EDITOR_MATERIAL_TYPE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView saveEditedMaterialType(MaterialType materialType) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
int materialTypeID = materialType.getMaterialTypeID();
if (!materialTypeService.getByID(materialTypeID).isPresent()) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, materialTypeID);
} else if (!materialTypeService.isValidForUpdateName(materialType)) {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST, materialType.getMaterialTypeName())
.build(),
materialTypeID, materialType);
} else if (!materialTypeService.isValidForUpdatePrefix(materialType)) {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.MATERIAL_TYPE_ALREADY_EXIST_PREFIX, materialType.getPrefix())
.build(),
materialTypeID, materialType);
} else {
Optional<MaterialType> updatedMaterialType = materialTypeService.update(materialType);
if (updatedMaterialType.isPresent()) {
responseBuilder.addResponseData(ResponseDataType.MATERIAL_TYPE_NAME, updatedMaterialType.get().getMaterialTypeName());
} else {
return showEditPage(
responseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build(),
materialTypeID, null);
}
}
return listMaterialTypes(responseBuilder.build());
}
}

View File

@ -0,0 +1,154 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.Valid;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.StringBank.MIX_ID;
@Controller
public class MixEditorController {
private MixService mixService;
private MaterialService materialService;
private RecipeService recipeService;
private MixTypeService mixTypeService;
@Autowired
public MixEditorController(MixService mixService, MaterialService materialService, RecipeService recipeService, MixTypeService mixTypeService) {
this.mixService = mixService;
this.materialService = materialService;
this.recipeService = recipeService;
this.mixTypeService = mixTypeService;
}
/**
* Affiche la page d'édition d'un mélange.
* Cette méthode requiert l'identifiant du mélange à modifier dans l'URL.
*
* @param model Le Model injecté par Thymeleaf
* @param mixID L'identifiant du mélange à modifier
* @return La page à afficher.
*/
@GetMapping(EDITOR_MIX_SPECIFIC)
public ModelAndView showPage(ModelAndView model, @PathVariable int mixID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_MIX_SPECIFIC.replaceAll("/\\{" + MIX_ID + "}", ""));
Optional<Mix> optionalMix = mixService.getByID(mixID);
if (!optionalMix.isPresent()) {
return modelResponseBuilder
.withView(ControllerUtils.redirect(EDITOR_RECIPE))
.build();
}
Mix mix = optionalMix.get();
List<MixType> associatedMixTypes = recipeService.getAssociatedMixesTypes(mix.getRecipe());
// Récupère seulement les produits qui ne sont pas des types de mélange OU que ce type existe dans la recette
List<Material> materials = materialService
.getAll()
.stream()
.filter(m -> !m.isMixType() || (!m.equals(mix.getMixType().getMaterial()) && associatedMixTypes.contains(mixTypeService.getByMaterial(m).get())))
.sorted(Comparator.comparing(Material::getMaterialCode))
.sorted(Comparator.comparing(m -> m.getMaterialType().getMaterialTypeName()))
.collect(Collectors.toList());
return modelResponseBuilder
.addResponseData(ResponseDataType.MIX, mix)
.addResponseData(ResponseDataType.RECIPE_CODE, mix.getRecipe().getRecipeCode())
.addAttribute("mixJson", materialService.asJson(mix))
.addAttribute("materialsJson", materialService.asJson(materials))
.build();
}
/**
* Permet à l'utilisateur de modifier un mélange.
* <p>
* La mise à jour échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - Le mélange n'existe pas
* - Un des produits n'existe pas
* - Une erreur est survenue lors de la mise à jour dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* <p>
* REQUIERT UNE AUTORISATION
*
* @param form Le formulaire du mélange entré par l'utilisateur
* @return La page à afficher.
*/
@PostMapping(value = EDITOR_MIX, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView saveMix(@ModelAttribute @Valid MixCreationFormDto formDto, int mixID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
Optional<Mix> optionalMix = mixService.getByID(mixID);
if (!optionalMix.isPresent()) {
modelResponseBuilder.addResponseCode(ResponseCode.MIX_NOT_FOUND, mixID);
return showPage(modelResponseBuilder.build(), mixID);
}
Mix mix = optionalMix.get();
modelResponseBuilder.withRedirect(EDITOR_RECIPE_SPECIFIC, mix.getRecipe().getRecipeID());
ModelResponseBuilder editResult = mixService.edit(mix, formDto);
if (editResult != null) {
return showPage(
modelResponseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build(),
mixID);
}
return modelResponseBuilder.build();
}
@GetMapping(REMOVER_MIX_SPECIFIC)
public ModelAndView deleteMix(@PathVariable int mixID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder("");
Optional<Mix> optionalMix = mixService.getByID(mixID);
if (!optionalMix.isPresent()) {
return showPage(modelResponseBuilder
.addResponseCode(ResponseCode.MIX_NOT_FOUND, mixID)
.build(),
mixID);
}
Mix mix = optionalMix.get();
modelResponseBuilder.withView(ControllerUtils.redirect(EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(mix.getRecipe().getRecipeID()))));
if (!mixService.deleteMix(mix)) {
modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
return showPage(modelResponseBuilder.build(), mixID);
}
return modelResponseBuilder.build();
}
}

View File

@ -0,0 +1,125 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.editors;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.CompanyService;
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.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
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.RequestBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
@Controller
public class RecipeEditorController {
private RecipeService recipeService;
private CompanyService companyService;
@Autowired
public RecipeEditorController(RecipeService recipeService, CompanyService companyService) {
this.recipeService = recipeService;
this.companyService = companyService;
}
/**
* Affiche la page listant toutes les recettes.
* <p>
* Modèle de la page:
* - recipes: Contient un Map de toutes les recettes triées par compagnie
*
* @param model Le Model injecté par Thymeleaf
* @return La page à afficher.
*/
@GetMapping(EDITOR_RECIPE)
public ModelAndView listRecipes(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(EDITOR_RECIPE)
.addResponseData(ResponseDataType.RECIPE_MAP, recipeService.getRecipesByCompany())
.build();
}
@GetMapping(EDITOR_RECIPE_SPECIFIC)
public ModelAndView showEditPage(ModelAndView model, @PathVariable int recipeID, Recipe recipe) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(model)
.withView(EDITOR_RECIPE_EDITOR);
if (recipe.getRecipeCode() == null || recipe.getCompany() == null) {
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return listRecipes(
modelResponseBuilder
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID)
.build()
);
}
recipe = optionalRecipe.get();
}
return modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addResponseData(ResponseDataType.COMPANIES, companyService.getAll())
.addResponseData(ResponseDataType.MIXES, new ArrayList<>(recipeService.getSortedMixes(recipe))) // Convertit le PersistentBag en ArrayList
.addResponseData(ResponseDataType.IMAGES, recipeService.getImageFiles(recipe))
.addAttribute("recipeJSON", recipeService.asJson(recipe))
.build();
}
/**
* Permet à l'utilisateur de modifier une recette.
* <p>
* La mise à jour échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - La recette n'existe pas
* - Une erreur est survenue lors de la mise à jour dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - recipeCode: Contient la couleur d'une recette
* <p>
* REQUIERT UNE AUTORISATION
*
* @param recipe La recette à modifier
* @param form Le formulaire entré par l'utilisateur
* @return La page à afficher.
*/
@PostMapping(value = EDITOR_RECIPE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@Transactional
public ModelAndView saveRecipe(Recipe recipe, @RequestBody MultiValueMap<String, Object> form) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder("");
int recipeID = recipe.getRecipeID();
Optional<Recipe> optionalStoredRecipe = recipeService.getByID(recipeID);
if (!optionalStoredRecipe.isPresent()) {
return listRecipes(
modelResponseBuilder
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID)
.build());
}
Optional<Recipe> optionalRecipe = recipeService.updateRecipe(recipe, optionalStoredRecipe.get(), form);
if (optionalRecipe.isPresent()) {
modelResponseBuilder.addResponseData(ResponseDataType.RECIPE_CODE, optionalRecipe.get().getRecipeCode());
return listRecipes(modelResponseBuilder.build());
}
return listRecipes(
modelResponseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build());
}
}

View File

@ -1,10 +1,10 @@
package fyloz.trial.ColorRecipesExplorer.web.controller.removers;
package dev.fyloz.trial.colorrecipesexplorer.web.controller.removers;
import fyloz.trial.ColorRecipesExplorer.core.ModelBuilder;
import fyloz.trial.ColorRecipesExplorer.core.ModelDataType;
import fyloz.trial.ColorRecipesExplorer.core.ResponseCode;
import fyloz.trial.ColorRecipesExplorer.model.Company;
import fyloz.trial.ColorRecipesExplorer.services.CompanyService;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Company;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.CompanyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@ -12,8 +12,10 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_COMPANY;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_COMPANY_SPECIFIC;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_COMPANY;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_COMPANY_SPECIFIC;
@Controller
public class CompanyRemoverController {
@ -36,9 +38,9 @@ public class CompanyRemoverController {
*/
@GetMapping(REMOVER_COMPANY)
public ModelAndView showPage(ModelAndView model) {
return new ModelBuilder(model)
.setView(REMOVER_COMPANY)
.addData(ModelDataType.COMPANIES, companyService.getAll())
return new ModelResponseBuilder(model)
.withView(REMOVER_COMPANY)
.addResponseData(ResponseDataType.COMPANIES, companyService.getAll())
.build();
}
@ -63,19 +65,21 @@ public class CompanyRemoverController {
@PostMapping(REMOVER_COMPANY_SPECIFIC)
public ModelAndView removeCompany(@PathVariable int companyID) {
ModelBuilder modelBuilder = new ModelBuilder("");
Company company = companyService.getByID(companyID);
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder("");
Optional<Company> optionalCompany = companyService.getByID(companyID);
if (optionalCompany.isPresent()) {
Company company = optionalCompany.get();
if (companyService.exists(company)) {
if (companyService.deleteIfNotLinked(company)) {
modelBuilder.addData(ModelDataType.COMPANY_NAME, company.getCompanyName());
modelResponseBuilder.addResponseData(ResponseDataType.COMPANY_NAME, company.getCompanyName());
} else {
modelBuilder.addResponseCode(ResponseCode.COMPANY_LINKED, company.getCompanyName());
modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_LINKED, company.getCompanyName());
}
} else {
modelBuilder.addResponseCode(ResponseCode.COMPANY_NOT_FOUND, String.valueOf(companyID));
modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_NOT_FOUND, companyID);
}
return showPage(modelBuilder.build());
return showPage(modelResponseBuilder.build());
}
}

View File

@ -1,10 +1,10 @@
package fyloz.trial.ColorRecipesExplorer.web.controller.removers;
package dev.fyloz.trial.colorrecipesexplorer.web.controller.removers;
import fyloz.trial.ColorRecipesExplorer.core.ModelBuilder;
import fyloz.trial.ColorRecipesExplorer.core.ModelDataType;
import fyloz.trial.ColorRecipesExplorer.core.ResponseCode;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.services.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@ -12,10 +12,11 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.Optional;
import java.util.stream.Collectors;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_MATERIAL;
import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_MATERIAL_SPECIFIC;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_MATERIAL;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_MATERIAL_SPECIFIC;
@Controller
public class MaterialRemoverController {
@ -35,9 +36,9 @@ public class MaterialRemoverController {
*/
@GetMapping(REMOVER_MATERIAL)
public ModelAndView showPage(ModelAndView model) {
return new ModelBuilder(model)
.setView(REMOVER_MATERIAL)
.addData(ModelDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
return new ModelResponseBuilder(model)
.withView(REMOVER_MATERIAL)
.addResponseData(ResponseDataType.MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList()))
.build();
}
@ -61,23 +62,25 @@ public class MaterialRemoverController {
*/
@PostMapping(REMOVER_MATERIAL_SPECIFIC)
public ModelAndView removeMaterial(@PathVariable int materialID) {
ModelBuilder modelBuilder = new ModelBuilder("");
Material material = materialService.getByID(materialID);
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
if (material == null) {
modelBuilder.addResponseCode(ResponseCode.MIX_NOT_FOUND, String.valueOf(materialID));
Optional<Material> optionalMaterial = materialService.getByID(materialID);
if (!optionalMaterial.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_NOT_FOUND, materialID);
} else {
Material material = optionalMaterial.get();
if (materialService.deleteIfNotLinked(material)) {
modelBuilder.addData(ModelDataType.MATERIAL_CODE, material.getMaterialCode());
responseBuilder.addResponseData(ResponseDataType.MATERIAL_CODE, material.getMaterialCode());
if (!materialService.removeSimdut(material)) {
modelBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
responseBuilder.addResponseCode(ResponseCode.ERROR_SAVING_SIMDUT);
}
} else {
modelBuilder.addResponseCode(ResponseCode.MATERIAL_LINKED, material.getMaterialCode());
responseBuilder.addResponseCode(ResponseCode.MATERIAL_LINKED, material.getMaterialCode());
}
}
return showPage(modelBuilder.build());
return showPage(responseBuilder.build());
}
}

View File

@ -0,0 +1,58 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.removers;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialTypeService;
import org.springframework.beans.factory.annotation.Autowired;
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.servlet.ModelAndView;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_MATERIAL_TYPE;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_MATERIAL_TYPE_SPECIFIC;
@Controller
public class MaterialTypeRemoverController {
private MaterialTypeService materialTypeService;
@Autowired
public MaterialTypeRemoverController(MaterialTypeService materialTypeService) {
this.materialTypeService = materialTypeService;
}
@GetMapping(REMOVER_MATERIAL_TYPE)
public ModelAndView showPage(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(REMOVER_MATERIAL_TYPE)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.build();
}
@PostMapping(REMOVER_MATERIAL_TYPE_SPECIFIC)
public ModelAndView removeMaterialType(@PathVariable int materialTypeID) {
ModelResponseBuilder responseBuilder = new ModelResponseBuilder("");
Optional<MaterialType> optionalMaterialType = materialTypeService.getByID(materialTypeID);
if (!optionalMaterialType.isPresent()) {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_NOT_FOUND, materialTypeID);
} else {
MaterialType materialType = optionalMaterialType.get();
if (materialTypeService.deleteIfNotLinked(materialType)) {
responseBuilder.addResponseData(ResponseDataType.MATERIAL_TYPE_NAME, materialType.getMaterialTypeName());
} else {
responseBuilder.addResponseCode(ResponseCode.MATERIAL_TYPE_LINKED, materialType.getMaterialTypeName());
}
}
return showPage(responseBuilder.build());
}
}

View File

@ -0,0 +1,83 @@
package dev.fyloz.trial.colorrecipesexplorer.web.controller.removers;
import dev.fyloz.trial.colorrecipesexplorer.core.io.response.ModelResponseBuilder;
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.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
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.servlet.ModelAndView;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_RECIPE;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.REMOVER_RECIPE_SPECIFIC;
@Controller
public class RecipeRemoverController {
private RecipeService recipeService;
public RecipeRemoverController(RecipeService recipeService) {
this.recipeService = recipeService;
}
/**
* Affiche la liste de toutes les recettes.
*
* @param model Le Model injecté par Thymeleaf
* @return La page à afficher.
*/
@GetMapping(REMOVER_RECIPE)
public ModelAndView listRecipes(ModelAndView model) {
return new ModelResponseBuilder(model)
.withView(REMOVER_RECIPE)
.addResponseData(ResponseDataType.RECIPE_MAP, recipeService.getRecipesByCompany())
.build();
}
/**
* Permet à l'utilisateur de supprimer une recette.
* Cette méthode requiert l'identifiant de la recette dans l'URL.
* <p>
* La suppression échouera si:
* - L'utilisateur n'est pas autorisé à exécuter cette action
* - La recette n'existe pas
* - Une erreur est survenue lors de la suppression dans la base de données
* <p>
* Modèle de la page:
* - error: Contient le message d'erreur, s'il y a lieu
* - recipeCode: Contient la couleur de la recette
* <p>
* REQUIERT UNE AUTORISATION
*
* @param recipeID L'identifiant de la recette
* @return La page à afficher.
*/
@PostMapping(REMOVER_RECIPE_SPECIFIC)
public ModelAndView removeRecipe(@PathVariable int recipeID) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder("");
Optional<Recipe> optionalRecipe = recipeService.getByID(recipeID);
if (!optionalRecipe.isPresent()) {
return listRecipes(
modelResponseBuilder
.addResponseCode(ResponseCode.RECIPE_NOT_FOUND, recipeID)
.build());
}
Recipe recipe = optionalRecipe.get();
if (!recipeService.deleteRecipe(recipe)) {
return listRecipes(
modelResponseBuilder
.addResponseCode(ResponseCode.ERROR_SAVING)
.build());
}
modelResponseBuilder.addResponseData(ResponseDataType.RECIPE_CODE, recipe.getRecipeCode());
return listRecipes(modelResponseBuilder.build());
}
}

View File

@ -0,0 +1,101 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixQuantity;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.RecipeStep;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Catalog;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Sheet;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Table;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.DescriptionCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.NoteCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.SectionTitleCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.TitleCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.util.Position;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
public class XlsxExporter {
public byte[] generate(Recipe recipe) {
ColorRecipesExplorerApplication.LOGGER.info(String.format("Génération du XLS de la couleur %s (%s)", recipe.getRecipeCode(), recipe.getRecipeID()));
Document document = new Document(recipe.getRecipeCode());
Sheet sheet = document.getSheet();
registerCells(recipe, sheet);
document.buildSheet();
byte[] output;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
document.write(outputStream);
output = outputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
return output;
}
private void registerCells(Recipe recipe, Sheet sheet) {
// Header
sheet.registerCell(new TitleCell(recipe.getRecipeCode()));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.NAME, "Bannière"));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.VALUE_STR, recipe.getCompany().getCompanyName()));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.NAME, "Description"));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.VALUE_STR, recipe.getRecipeDescription()));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.NAME, "Échantillon"));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.VALUE_NUM, recipe.getSample()));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.NAME, "Date d'approbation"));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.VALUE_STR, recipe.getApprobationDate()));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.NAME, "Remarque"));
sheet.registerCell(new DescriptionCell(DescriptionCell.DescriptionCellType.VALUE_STR, recipe.getRemark()));
// Mélanges
Collection<Mix> recipeMixes = recipe.getRecipeMixes();
if (recipeMixes.size() > 0) {
sheet.registerCell(new SectionTitleCell("Recette"));
for (Mix mix : recipeMixes) {
Table mixTable = new Table(4, mix.getMixQuantities().size() + 1, mix.getMixType().getTypeName());
mixTable.setColumnName(0, "Quantité");
mixTable.setColumnName(2, "Unités");
int row = 0;
for (MixQuantity mixQuantity : mix.getMixQuantities()) {
mixTable.setRowName(row, mixQuantity.getMaterial().getMaterialCode());
mixTable.setContent(new Position(1, row + 1), mixQuantity.getQuantity());
mixTable.setContent(new Position(3, row + 1), mixQuantity.getMaterial().getMaterialType().getUsePercentages() ? "%" : "mL");
row++;
}
sheet.registerAllCells(mixTable.getCells());
}
}
// Étapes
Collection<RecipeStep> steps = recipe.getRecipeSteps();
if (steps.size() > 0) {
sheet.registerCell(new SectionTitleCell("Étapes"));
Catalog stepsCatalog = new Catalog();
for (RecipeStep step : recipe.getRecipeSteps()) stepsCatalog.addContent(step.getStepMessage());
sheet.registerAllCells(stepsCatalog.getCells());
}
// Note
String note = recipe.getNote();
if (note != null) {
sheet.registerCell(new SectionTitleCell("Note"));
sheet.registerCell(new NoteCell(recipe.getNote()));
}
}
}

View File

@ -0,0 +1,69 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.builder;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
public class CellBuilder {
private Document document;
private Cell cell;
private XSSFCell xcell;
public CellBuilder(Document document, Cell cell, int x, int y) {
this.document = document;
this.cell = cell;
xcell = document.getSheet().getCell(cell, x, y, true);
}
private void applyColor(IColoredCell cell, XSSFCellStyle style) {
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(cell.getBackgroundColor().getIndex());
}
private void applyFont(IFontCell cell, XSSFCellStyle style) {
XSSFFont font = document.createFont();
font.setBold(cell.isBold());
font.setColor(cell.getFontColor().getIndex());
font.setFontHeightInPoints(cell.getFontHeight());
style.setFont(font);
if (cell.isCentered()) style.setAlignment(HorizontalAlignment.CENTER);
}
private void applyStyle() {
XSSFCellStyle style = document.createCellStyle();
style.setWrapText(true);
if (cell instanceof IColoredCell) applyColor((IColoredCell) cell, style);
if (cell instanceof IFontCell) applyFont((IFontCell) cell, style);
xcell.setCellStyle(style);
}
private void applyContent() throws InvalidCellTypeException {
switch (cell.getType()) {
case STRING:
xcell.setCellValue(cell.getContentText());
break;
case NUMERIC:
xcell.setCellValue(cell.getContentNum());
break;
default:
throw new InvalidCellTypeException(cell.getType());
}
}
public void build() throws InvalidCellTypeException {
applyStyle();
applyContent();
}
}

View File

@ -0,0 +1,60 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.builder;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Sheet;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.util.CellUtils;
import org.apache.poi.ss.util.CellRangeAddress;
public class SheetBuilder {
private Sheet sheet;
private Iterable<Cell> cells;
public SheetBuilder(Sheet sheet) {
this.sheet = sheet;
cells = sheet.getRegisteredCells();
}
private void applyCellMerge(IBiggerCell cell, int x, int y) {
int width = cell.getWidth() - 1;
int height = cell.getHeight() - 1;
sheet.getXSheet().addMergedRegion(new CellRangeAddress(y, y + height, x, x + width));
}
public void build() throws InvalidCellTypeException {
int x = 0;
int y = 0;
for (Cell cell : cells) {
while (!CellUtils.isCellEmpty(sheet, x, y) || CellUtils.isCellInMergedCell(sheet, x, y)) {
x++;
if (x >= Document.WIDTH) {
x = 0;
y++;
}
}
new CellBuilder(sheet.getDocument(), cell, x, y).build();
if (cell instanceof IBiggerCell) {
IBiggerCell biggerCell = (IBiggerCell) cell;
applyCellMerge(biggerCell, x, y);
x += biggerCell.getWidth() - 1;
} else {
x++;
}
if (x >= Document.WIDTH) {
x = 0;
y++;
}
}
}
}

View File

@ -0,0 +1,73 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import org.apache.poi.ss.usermodel.CellType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Catalog {
private List<String> content;
public Catalog() {
content = new ArrayList<>();
}
public void addContent(String value) {
content.add(value);
}
public Collection<Cell> getCells() {
Collection<Cell> cells = new ArrayList<>();
int index = 1;
for (String value : content) {
cells.add(new CatalogValueCell(String.format("%s. %s", index, value)));
index++;
}
return cells;
}
private class CatalogValueCell extends Cell implements IBiggerCell, IFontCell {
private String value;
public CatalogValueCell(String value) {
super(CellType.STRING);
this.value = value;
}
@Override
public int getWidth() {
return Document.WIDTH;
}
@Override
public int getHeight() {
return 1;
}
@Override
public String getContentText() {
return value;
}
@Override
public boolean isCentered() {
return false;
}
@Override
public boolean isBold() {
return false;
}
}
}

View File

@ -0,0 +1,33 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component;
import dev.fyloz.trial.colorrecipesexplorer.ColorRecipesExplorerApplication;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.builder.SheetBuilder;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.exception.InvalidCellTypeException;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbookType;
public class Document extends XSSFWorkbook {
public static final int WIDTH = 8;
private static final XSSFWorkbookType WORKBOOK_TYPE = XSSFWorkbookType.XLSX;
private Sheet sheet;
public Document(String name) {
super(WORKBOOK_TYPE);
sheet = new Sheet(this, createSheet(name));
}
public void buildSheet() {
try {
new SheetBuilder(sheet).build();
} catch (InvalidCellTypeException e) {
ColorRecipesExplorerApplication.LOGGER.error("Une erreur est survenue lors de la génération du document: " + e.getLocalizedMessage());
}
}
public Sheet getSheet() {
return sheet;
}
}

View File

@ -0,0 +1,84 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import java.util.*;
public class Sheet {
private Document document;
private XSSFSheet xsheet;
private Map<Integer, XSSFRow> rows;
private List<Cell> registeredCells;
Sheet(Document document, XSSFSheet xsheet) {
this.document = document;
this.xsheet = xsheet;
xsheet.setDefaultColumnWidth(xsheet.getDefaultColumnWidth() * 2);
rows = new HashMap<>();
registeredCells = new ArrayList<>();
}
public void registerCell(Cell cell) {
registeredCells.add(cell);
}
public void registerAllCells(Collection<Cell> cells) {
registeredCells.addAll(cells);
}
public List<Cell> getRegisteredCells() {
return registeredCells;
}
public XSSFRow getRow(int rownum, boolean create) {
int rowSize = rows.keySet().size();
if (rowSize <= rownum) {
if (!create) return null;
for (int i = rowSize; i <= rownum; i++) {
rows.put(i, xsheet.createRow(i));
}
}
return rows.get(rownum);
}
public XSSFCell getCell(int rownum, int columnnum, CellType type, boolean create) {
XSSFRow row = getRow(rownum, create);
// List<XSSFCell> columns = rows.get(row);
int columnsSize = row.getLastCellNum();
if (columnsSize == -1) columnsSize = 0;
if (columnsSize <= columnnum) {
if (!create) return null;
for (int i = columnsSize; i <= columnnum; i++) {
row.createCell(i, type);
}
}
return row.getCell(columnnum);
}
public XSSFCell getCell(Cell cell, int x, int y, boolean create) {
return getCell(y, x, cell.getType(), create);
}
public XSSFSheet getXSheet() {
return xsheet;
}
public Document getDocument() {
return document;
}
}

View File

@ -0,0 +1,276 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.Cell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.exception.IndexOutOfTableBoundsException;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.util.Position;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.IndexedColors;
import java.text.DecimalFormat;
import java.util.*;
public class Table {
private static DecimalFormat format = new DecimalFormat("#.###");
private int width;
private int height;
private int cellWidth;
private Map<Position, Cell> content;
private List<TableSubNameCell> columns;
private List<TableSubNameCell> rows;
public Table(int width, int height, String name) {
this.width = width;
this.height = height;
cellWidth = Document.WIDTH / width;
content = new HashMap<>();
columns = new ArrayList<>();
rows = new ArrayList<>();
for (int row = 0; row < this.height; row++) {
for (int col = 0; col < this.width; col++) {
Position pos = new Position(col, row);
Cell cell;
if (col == 0 && row == 0) { // Titre
cell = new TableNameCell(name, cellWidth);
} else if (col == 0 || row == 0) { // Colonnes/Rangée
cell = new TableSubNameCell("", cellWidth);
if (col > 0) columns.add((TableSubNameCell) cell);
else rows.add((TableSubNameCell) cell);
} else {
cell = new TableStringValueCell("", cellWidth);
}
content.put(pos, cell);
}
}
}
public void setColumnName(int index, String name) {
int columnnum = columns.size() - 1;
if (columnnum < index) throw new IndexOutOfTableBoundsException(index, columnnum);
columns.get(index).setName(name);
}
/**
* @param index Index de la rangée (à partir de 0)
* @param name Nouveau nom de la rangée
* @throws IndexOutOfTableBoundsException La rangée à l'index défini est en dehors de la portée du tableau.
* @author
*/
public void setRowName(int index, String name) {
int rownum = rows.size() - 1;
if (rownum < index) throw new IndexOutOfTableBoundsException(index, rownum);
rows.get(index).setName(name);
}
public void setContent(Position position, String value) {
if (isInTableRange(position, this)) {
content.put(position, new TableStringValueCell(value, cellWidth));
}
}
public void setContent(Position position, double value) {
if (isInTableRange(position, this)) {
value = Double.parseDouble(format.format(value).replace(',', '.'));
content.put(position, new TableNumValueCell(value, cellWidth));
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Collection<Cell> getCells() {
int col = 0;
int row = 0;
Collection<Cell> cells = new ArrayList<>();
while (row < height) {
while (col < width) {
Position pos = new Position(col, row);
cells.add(content.get(pos));
col++;
}
col = 0;
row++;
}
return cells;
}
public static boolean isInTableRange(Position position, Table table) {
int col = position.getColumn();
int columnnum = table.getWidth() - 1;
if (col > columnnum) throw new IndexOutOfTableBoundsException(col, columnnum);
int row = position.getRow();
int rownum = table.getHeight() - 1;
if (row > rownum) throw new IndexOutOfTableBoundsException(row, rownum);
return true;
}
private class TableNameCell extends Cell implements IColoredCell, IFontCell, IBiggerCell {
private final IndexedColors COLOR = IndexedColors.GREY_40_PERCENT;
private String name;
private int width;
private int height = 2;
public TableNameCell(String name, int width) {
super(CellType.STRING);
this.name = name;
this.width = width;
}
@Override
public String getContentText() {
return name;
}
@Override
public IndexedColors getBackgroundColor() {
return COLOR;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
}
private class TableSubNameCell extends Cell implements IFontCell, IBiggerCell, IColoredCell {
private String name;
private int width;
private int height = 2;
public TableSubNameCell(String name, int width) {
super(CellType.STRING);
this.name = name;
this.width = width;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getContentText() {
return name;
}
@Override
public IndexedColors getBackgroundColor() {
return IndexedColors.GREY_25_PERCENT;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
}
private class TableStringValueCell extends Cell implements IFontCell, IBiggerCell {
private String value;
private int width;
private int height = 2;
public TableStringValueCell(String value, int width) {
super(CellType.STRING);
this.value = value;
this.width = width;
}
@Override
public String getContentText() {
return value;
}
@Override
public boolean isBold() {
return false;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
}
private class TableNumValueCell extends Cell implements IFontCell, IBiggerCell {
private double value;
private int width;
private int height = 2;
public TableNumValueCell(double value, int width) {
super(CellType.NUMERIC);
this.value = value;
this.width = width;
}
@Override
public double getContentNum() {
return value;
}
@Override
public boolean isBold() {
return false;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
}
}

View File

@ -0,0 +1,24 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells;
import org.apache.poi.ss.usermodel.CellType;
public class Cell {
protected CellType type;
public Cell(CellType type) {
this.type = type;
}
public CellType getType() {
return type;
}
public String getContentText() {
return "";
}
public double getContentNum() {
return 0.0d;
}
}

View File

@ -0,0 +1,68 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.IndexedColors;
public class DescriptionCell extends Cell implements IColoredCell, IBiggerCell, IFontCell {
private static final int WIDTH_NAME = 2;
private static final int WIDTH_VALUE = 6;
private static final int HEIGHT = 2;
private static final IndexedColors BG_COLOR_NAME = IndexedColors.GREY_40_PERCENT;
private static final short FONT_HEIGHT = 18;
private DescriptionCellType type;
private String valueStr;
private double valueNbr;
public DescriptionCell(DescriptionCellType type, String value) {
super(CellType.STRING);
this.type = type;
this.valueStr = value;
}
public DescriptionCell(DescriptionCellType type, double value) {
super(CellType.NUMERIC);
this.type = type;
this.valueNbr = value;
}
@Override
public int getWidth() {
return type == DescriptionCellType.NAME ? WIDTH_NAME : WIDTH_VALUE;
}
@Override
public int getHeight() {
return HEIGHT;
}
@Override
public IndexedColors getBackgroundColor() {
return type == DescriptionCellType.NAME ? BG_COLOR_NAME : IndexedColors.WHITE1;
}
@Override
public short getFontHeight() {
return FONT_HEIGHT;
}
@Override
public String getContentText() {
return type != DescriptionCellType.VALUE_NUM ? valueStr : "";
}
@Override
public double getContentNum() {
return type == DescriptionCellType.VALUE_NUM ? valueNbr : 0.0d;
}
public enum DescriptionCellType {
NAME, VALUE_STR, VALUE_NUM
}
}

View File

@ -0,0 +1,37 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import org.apache.poi.ss.usermodel.CellType;
public class NoteCell extends Cell implements IBiggerCell, IFontCell {
private String value;
public NoteCell(String value) {
super(CellType.STRING);
this.value = value;
}
@Override
public int getWidth() {
return Document.WIDTH;
}
@Override
public int getHeight() {
return Math.max(Math.abs(value.length() / 100), 1);
}
@Override
public boolean isBold() {
return false;
}
@Override
public String getContentText() {
return value.replaceAll("(.{100})", "$1\n");
}
}

View File

@ -0,0 +1,56 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.IndexedColors;
public class SectionTitleCell extends Cell implements IColoredCell, IBiggerCell, IFontCell {
private static final int WIDTH = Document.WIDTH;
private static final int HEIGHT = 2;
private static final IndexedColors BG_COLOR = IndexedColors.BLACK;
private static final IndexedColors FONT_COLOR = IndexedColors.WHITE;
private static final int FONT_HEIGHT = 24;
private String title;
public SectionTitleCell(String title) {
super(CellType.STRING);
this.title = title.toUpperCase();
}
@Override
public int getWidth() {
return WIDTH;
}
@Override
public int getHeight() {
return HEIGHT;
}
@Override
public IndexedColors getBackgroundColor() {
return BG_COLOR;
}
@Override
public IndexedColors getFontColor() {
return FONT_COLOR;
}
@Override
public short getFontHeight() {
return FONT_HEIGHT;
}
@Override
public String getContentText() {
return title;
}
}

View File

@ -0,0 +1,60 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Document;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IBiggerCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IColoredCell;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles.IFontCell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.IndexedColors;
public class TitleCell extends Cell implements IColoredCell, IBiggerCell, IFontCell {
private static final int WIDTH = Document.WIDTH;
private static final int HEIGHT = 3;
private static final CellType TYPE = CellType.STRING;
private static final IndexedColors FONT_COLOR = IndexedColors.WHITE;
private static IndexedColors BACKGROUND_COLOR = IndexedColors.BLACK;
private String title;
public TitleCell(String title) {
super(TYPE);
this.title = title;
}
@Override
public IndexedColors getBackgroundColor() {
return BACKGROUND_COLOR;
}
@Override
public IndexedColors getFontColor() {
return FONT_COLOR;
}
public Cell setColor(IndexedColors color) {
BACKGROUND_COLOR = color;
return this;
}
@Override
public short getFontHeight() {
return 36;
}
@Override
public int getWidth() {
return WIDTH;
}
@Override
public int getHeight() {
return HEIGHT;
}
@Override
public String getContentText() {
return title;
}
}

View File

@ -0,0 +1,7 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles;
public interface IBiggerCell {
int getWidth();
int getHeight();
}

View File

@ -0,0 +1,7 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles;
import org.apache.poi.ss.usermodel.IndexedColors;
public interface IColoredCell {
IndexedColors getBackgroundColor();
}

View File

@ -0,0 +1,23 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.component.cells.styles;
import org.apache.poi.ss.usermodel.IndexedColors;
public interface IFontCell {
default IndexedColors getFontColor() {
return IndexedColors.BLACK;
}
default short getFontHeight() {
return 14;
}
default boolean isBold() {
return true;
}
default boolean isCentered() {
return true;
}
}

View File

@ -0,0 +1,9 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.exception;
public class IndexOutOfTableBoundsException extends RuntimeException {
public IndexOutOfTableBoundsException(int current, int max) {
super(String.format("L'index '%s' se trouve hors de portée du tableau (maximum: %s)", current, max));
}
}

View File

@ -0,0 +1,11 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.exception;
import org.apache.poi.ss.usermodel.CellType;
public class InvalidCellTypeException extends Exception {
public InvalidCellTypeException(CellType type) {
super(String.format("Les recettes ne peuvent contenir que des cellules de type STRING et NUMERIC. Actuel: %s", type.name()));
}
}

View File

@ -0,0 +1,52 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.util;
import dev.fyloz.trial.colorrecipesexplorer.xlsx.component.Sheet;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.*;
public class CellUtils {
public static XSSFColor getXSSFColor(int[] rgb) {
return new XSSFColor(new byte[]{(byte) rgb[0], (byte) rgb[1], (byte) rgb[2]}, new DefaultIndexedColorMap());
}
public static boolean isCellEmpty(Sheet sheet, int x, int y) {
XSSFRow row = sheet.getRow(y, false);
if (row == null) {
return true;
}
XSSFCell cell = row.getCell(x);
if (cell == null) { // use row.getCell(x, Row.CREATE_NULL_AS_BLANK) to avoid null cells
return true;
}
if (cell.getCellType() == CellType.BLANK) {
return true;
}
if (cell.getCellType() == CellType.STRING && cell.getStringCellValue().trim().isEmpty()) {
return true;
}
return false;
}
public static boolean isCellInMergedCell(Sheet sheet, int x, int y) {
XSSFSheet xsheet = sheet.getXSheet();
int numberOfMergedRegions = xsheet.getNumMergedRegions();
for (int i = 0; i < numberOfMergedRegions; i++) {
CellRangeAddress mergedCell = xsheet.getMergedRegion(i);
if (mergedCell.isInRange(y, x)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,49 @@
package dev.fyloz.trial.colorrecipesexplorer.xlsx.util;
import java.util.Objects;
public class Position {
private int column;
private int row;
public Position(int column, int row) {
this.column = column;
this.row = row;
}
public int getColumn() {
return column;
}
public void setColumn(int column) {
this.column = column;
}
public int getRow() {
return row;
}
public void setRow(int row) {
this.row = row;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Position position = (Position) o;
return column == position.column &&
row == position.row;
}
@Override
public int hashCode() {
return Objects.hash(column, row);
}
@Override
public String toString() {
return String.format("%s, %s", column, row);
}
}

View File

@ -1,112 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.core;
import org.springframework.web.servlet.ModelAndView;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ModelBuilder {
private static String RESPONSE_PREFIX = "response.";
private static String ERROR_ATTRIBUTE_NAME = "error";
private static String SUCCESS_ATTRIBUTE_NAME = "success";
private ModelAndView model;
private Map<String, Object> modelAttributes;
public ModelBuilder(String view) {
this(new ModelAndView(view));
}
public ModelBuilder(ModelAndView model) {
this.model = model == null ? new ModelAndView() : model;
modelAttributes = new HashMap<>();
}
public ModelBuilder setView(String view) {
model.setViewName(view);
return this;
}
public ModelBuilder addAttribute(String attributeName, Object content) {
modelAttributes.put(attributeName, content);
return this;
}
public ModelBuilder addResponseCode(ResponseCode responseCode, String... parameters) {
int requiredParametersNumber = responseCode.getParametersNumber();
int givenParametersNumber = parameters.length;
StringBuilder builder = new StringBuilder();
if (requiredParametersNumber != givenParametersNumber) {
throw new IllegalArgumentException(String.format("Mauvais nombre de paramètre dans un réponse de modèle: %s requis, %s fournis", requiredParametersNumber, givenParametersNumber));
}
// Construit le code de réponse
builder.append(RESPONSE_PREFIX);
builder.append(responseCode.getCode());
// Ajoute les paramètres, si nécessaire
if (requiredParametersNumber > 0) {
builder.append("('");
for (int i = 0; i < givenParametersNumber; i++) {
builder.append(parameters[i]);
if (i < givenParametersNumber - 1) {
builder.append("','");
}
}
builder.append("')");
}
// Ajoute l'attribut dans le Map
addAttribute(responseCode.getType() == ResponseCode.ResponseCodeType.ERROR ? ERROR_ATTRIBUTE_NAME : SUCCESS_ATTRIBUTE_NAME, builder.toString());
addAttribute("arg1", "Bathia");
addAttribute("arg2", "test");
return this;
}
public ModelBuilder addData(ModelDataType modelDataType, @NotNull Object data) {
Class modelDataTypeClass = modelDataType.getDataType();
Class modelListDataTypeClass = modelDataType.getListDataType();
Class givenDataTypeClass = data.getClass();
// Vérifie le type de l'objet
if (!modelDataTypeClass.equals(givenDataTypeClass)) {
throw new IllegalArgumentException(String.format("L'objet passé en paramètre n'est pas du bon type. Requis: %s, fournis: %s", modelDataTypeClass.getName(), givenDataTypeClass.getName()));
}
// Si l'objet est une liste, vérifie qu'elle n'est pas vide et qu'elle est du bon type
if (modelDataTypeClass.equals(List.class) && modelListDataTypeClass != null) {
List listData = (List) data;
if (listData.size() < 1) {
throw new IllegalArgumentException("La liste passée en paramètre ne contient aucun élément");
}
Class givenListDataTypeClass = listData.get(0).getClass();
if (givenDataTypeClass.equals(modelListDataTypeClass)) {
throw new IllegalArgumentException(String.format("La liste passée en paramètre contient des éléments du mauvais type. Requis: %s, fournis: %s", modelListDataTypeClass.getName(), givenListDataTypeClass.getName()));
}
}
// Ajoute l'attribut dans le Map
addAttribute(modelDataType.getDataTypeName(), data);
return this;
}
public ModelAndView build() {
model.addAllObjects(modelAttributes);
return model;
}
}

View File

@ -1,8 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.core.utils;
public class ControllerUtils {
public static String redirect(String viewName) {
return String.format("redirect:/%s", viewName);
}
}

View File

@ -1,12 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MaterialDao extends JpaRepository<Material, Integer> {
Material findByMaterialID(int materialID);
Material findByMaterialCode(String materialCode);
}

View File

@ -1,10 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.MaterialType;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MaterialTypeDao extends JpaRepository<MaterialType, Integer> {
MaterialType findByMaterialTypeName(String name);
}

View File

@ -1,15 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Material;
import fyloz.trial.ColorRecipesExplorer.model.MixType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MixTypeDao extends JpaRepository<MixType, Integer> {
MixType findByTypeID(int id);
MixType findByTypeName(String typeName);
MixType findByMaterial(Material material);
}

View File

@ -1,18 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.dao;
import fyloz.trial.ColorRecipesExplorer.model.Company;
import fyloz.trial.ColorRecipesExplorer.model.Recipe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RecipeDao extends JpaRepository<Recipe, Integer> {
List<Recipe> findAllByCompany(Company company);
Recipe findByRecipeCode(String recipeCode);
Recipe findByRecipeID(int recipeID);
}

View File

@ -1,44 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.factory.files;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfWriter;
import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class PdfFactory {
protected Document document;
private String fileName;
private FileHandler fileHandler;
public PdfFactory(String fileName) {
this(fileName, FileHandler.FileContext.OTHERS);
}
public PdfFactory(String fileName, FileHandler.FileContext context) {
this.fileName = fileName;
fileHandler = new FileHandler(fileName, context, FileHandler.FileExtension.PDF);
}
public PdfFactory open() throws FileNotFoundException, DocumentException {
document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(fileHandler.getPath().toString()));
document.open();
return this;
}
public void write() {
document.close();
}
public FileHandler getFileHandler() {
return fileHandler;
}
}

View File

@ -1,59 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.factory.files;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfWriter;
import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class TouchUpKitPdfFactory {
private String kitName;
private ByteArrayOutputStream pdfContent;
private Document document;
public TouchUpKitPdfFactory(String kitName) {
this.kitName = kitName.toUpperCase();
pdfContent = new ByteArrayOutputStream();
document = new Document();
}
public TouchUpKitPdfFactory build() throws DocumentException {
ColorRecipesExplorerApplication.logger.info(String.format("Génération d'un PDF pour le kit de retouche '%s'", kitName));
PdfWriter.getInstance(document, pdfContent);
Font descFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 48, BaseColor.BLACK);
Font kitNameFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, kitName.length() > 22 ? 32 : 38, BaseColor.BLACK);
Paragraph descFr = new Paragraph("KIT DE RETOUCHE", descFont);
Paragraph descEn = new Paragraph("TOUCH UP KIT", descFont);
Paragraph touchUpKitName = new Paragraph(kitName, kitNameFont);
descFr.setAlignment(Element.ALIGN_CENTER);
descEn.setAlignment(Element.ALIGN_CENTER);
touchUpKitName.setAlignment(Element.ALIGN_CENTER);
document.open();
document.add(descFr);
document.add(descEn);
document.add(touchUpKitName);
document.close();
return this;
}
public byte[] toByteArray() {
byte[] content = pdfContent.toByteArray();
try {
pdfContent.close();
} catch (IOException e) {
ColorRecipesExplorerApplication.logger.error("Une erreur est survenue lors de la fermeture d'un ByteArrayOutputStream", e);
}
return content;
}
}

View File

@ -1,12 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.model.form;
import org.springframework.web.multipart.MultipartFile;
public class MaterialForm {
private String materialCode;
private int inventoryQuantity;
private MultipartFile file;
}

View File

@ -1,46 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.services;
import fyloz.trial.ColorRecipesExplorer.dao.CompanyDao;
import fyloz.trial.ColorRecipesExplorer.dao.RecipeDao;
import fyloz.trial.ColorRecipesExplorer.model.Company;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CompanyService extends GenericService<Company> {
private RecipeDao recipeDao; // Utilise le Dao pour éviter une erreur au démarrage, citant une récursion (CompanyService -> RecipeService -> CompanyService -> ...)
@Autowired
public CompanyService(CompanyDao companyDao, RecipeDao recipeDao) {
super(companyDao);
this.recipeDao = recipeDao;
}
@Override
public boolean isValidForCreation(Company entity) {
return super.isValidForCreation(entity) && getByName(entity.getCompanyName()) == null;
}
public Company getByName(String name) {
return ((CompanyDao) dao).findByCompanyName(name);
}
/**
* Vérifie si une bannière est liée à une ou plusieurs recettes en faisant une requête dans la base de données,
* pour retourner s'il y a des résultats ou non.
*
* @return Si la bannière est liée à une ou plusieurs recettes.
*/
public boolean isLinkedToRecipes(Company company) {
return !recipeDao.findAllByCompany(company).isEmpty();
}
public boolean deleteIfNotLinked(Company company) {
if (!isLinkedToRecipes(company)) {
return super.delete(company);
}
return false;
}
}

View File

@ -1,42 +0,0 @@
package fyloz.trial.ColorRecipesExplorer.services;
import fyloz.trial.ColorRecipesExplorer.dao.MaterialTypeDao;
import fyloz.trial.ColorRecipesExplorer.model.MaterialType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MaterialTypeService extends GenericService<MaterialType> {
private MaterialTypeDao materialTypeDao;
@Autowired
public MaterialTypeService(MaterialTypeDao materialTypeDao) {
super(materialTypeDao);
this.materialTypeDao = materialTypeDao;
}
@Override
public boolean isValidForCreation(MaterialType entity) {
return entity != null && !existsByName(entity);
}
public MaterialType getByName(String name) {
return materialTypeDao.findByMaterialTypeName(name);
}
public MaterialType getDefaultMaterialType() {
MaterialType defaultType = getByName("Aucun");
if (defaultType == null) {
defaultType = new MaterialType("Aucun", "", false);
save(defaultType);
}
return defaultType;
}
public boolean existsByName(MaterialType entity) {
return getByName(entity.getMaterialTypeName()) != null;
}
}

Some files were not shown because too many files have changed in this diff Show More