diff --git a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java index 87614d4..3dddf8a 100644 --- a/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java +++ b/src/main/java/fr/titionfire/ffsaf/domain/service/MembreService.java @@ -38,6 +38,7 @@ import java.io.*; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; @WithSession @@ -138,6 +139,94 @@ public class MembreService { .map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList())); } + /* + + public Uni> getSimilar(String fname, String lname) { + return repository.listAll().map(membreModels -> membreModels.stream() + .filter(m -> StringSimilarity.similarity(m.getFname(), fname) <= 3 && + StringSimilarity.similarity(m.getLname(), lname) <= 3) + .map(SimpleMembre::fromModel).toList()); + }+ + */ + + public Uni allImporte(String subject, List data) { + if (data == null) + return Uni.createFrom().nullItem(); + final List data2 = data.stream() + .filter(dataIn -> dataIn.getNom() != null && !dataIn.getNom() + .isBlank() && dataIn.getPrenom() != null && !dataIn.getPrenom().isBlank()).toList(); + if (data2.isEmpty()) + return Uni.createFrom().nullItem(); + AtomicReference clubModel = new AtomicReference<>(); + + return repository.find("userId = ?1", subject).firstResult() + .chain(membreModel -> { + clubModel.set(membreModel.getClub()); + if (data2.stream().noneMatch(d -> d.getLicence() != null)) + return Uni.createFrom().item(new ArrayList()); + return repository.list("licence IN ?1 OR LOWER(lname || ' ' || fname) IN ?2", + data2.stream().map(SimpleMembreInOutData::getLicence).filter(Objects::nonNull).toList(), + data2.stream().map(o -> (o.getNom() + " " + o.getPrenom()).toLowerCase()).toList()); + }) + .call(Unchecked.function(membres -> { + for (MembreModel membreModel : membres) { + if (!Objects.equals(membreModel.getClub(), clubModel.get())) + throw new DForbiddenException( + "Le membre n°" + membreModel.getLicence() + " n'appartient pas à votre club"); + } + Uni uniResult = Uni.createFrom().voidItem(); + for (SimpleMembreInOutData dataIn : data2) { + MembreModel model = membres.stream() + .filter(m -> Objects.equals(m.getLicence(), dataIn.getLicence()) || m.getLname() + .equals(dataIn.getNom()) && m.getFname().equals(dataIn.getPrenom())).findFirst() + .orElseGet(() -> { + MembreModel mm = new MembreModel(); + mm.setClub(clubModel.get()); + mm.setLicences(new ArrayList<>()); + mm.setCountry("FR"); + return mm; + }); + + if ((model.getId() != null && StringSimilarity.similarity(model.getLname().toUpperCase(), + dataIn.getNom() + .toUpperCase()) > 3) || (model.getId() != null && StringSimilarity.similarity( + model.getFname().toUpperCase(), dataIn.getPrenom().toUpperCase()) > 3)) { + throw new DBadRequestException( + "Pour enregistrer un nouveau membre, veuillez laisser le champ licence vide."); + } + model.setLname(dataIn.getNom().toUpperCase()); + model.setFname(dataIn.getPrenom().toUpperCase().charAt(0) + dataIn.getPrenom().substring(1)); + + if (dataIn.getEmail() != null && !dataIn.getEmail().isBlank()) + model.setEmail(dataIn.getEmail()); + model.setGenre(Genre.fromString(dataIn.getGenre())); + if (dataIn.getBirthdate() != null) { + model.setBirth_date(dataIn.getBirthdate()); + model.setCategorie(Utils.getCategoryFormBirthDate(model.getBirth_date(), new Date())); + } + + uniResult = uniResult.call(() -> Panache.withTransaction(() -> repository.persist(model) + .chain(membreModel1 -> dataIn.isLicenceCurrent() ? licenceRepository.find( + "membre.id = ?1 AND saison = ?2", membreModel1.getId(), Utils.getSaison()) + .firstResult() + .call(l -> { + if (l == null) { + l = new LicenceModel(); + l.setMembre(membreModel1); + l.setValidate(false); + l.setSaison(Utils.getSaison()); + } + l.setCertificate(dataIn.getCertif()); + return licenceRepository.persist(l); + }) : licenceRepository.delete( + "membre = ?1 AND saison = ?2 AND validate = false", membreModel1, + Utils.getSaison())))); + } + return uniResult; + })) + .map(__ -> "OK"); + } + public Uni getById(long id) { return repository.findById(id); } diff --git a/src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java b/src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java index 81ef479..b8f50ec 100644 --- a/src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java +++ b/src/main/java/fr/titionfire/ffsaf/rest/MembreClubEndpoints.java @@ -71,6 +71,21 @@ public class MembreClubEndpoints { return membreService.getAllExport(securityCtx.getSubject()); } + @PUT + @Path("club/import") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Importer les membres du club", description = "Importer tout ou en partie les membres du club") + @APIResponses(value = { + @APIResponse(responseCode = "200", description = "Les membres du club ont été importés avec succès"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Uni importMembre(List dataIn) { + System.out.println("importMembre"); + return membreService.allImporte(securityCtx.getSubject(), dataIn); + } + @PUT @Path("club/{id}") @Produces(MediaType.TEXT_PLAIN) diff --git a/src/main/java/fr/titionfire/ffsaf/utils/Genre.java b/src/main/java/fr/titionfire/ffsaf/utils/Genre.java index 7b87357..c6bd8e9 100644 --- a/src/main/java/fr/titionfire/ffsaf/utils/Genre.java +++ b/src/main/java/fr/titionfire/ffsaf/utils/Genre.java @@ -14,6 +14,19 @@ public enum Genre { this.str = name; } + public static Genre fromString(String genre) { + if (genre == null) { + return NA; + } + if (genre.equalsIgnoreCase("Homme") || genre.equalsIgnoreCase("H")) { + return H; + } else if (genre.equalsIgnoreCase("Femme") || genre.equalsIgnoreCase("F")) { + return F; + } else { + return NA; + } + } + @Override public String toString() { return str; diff --git a/src/main/webapp/src/pages/MemberList.jsx b/src/main/webapp/src/pages/MemberList.jsx index 79bd089..fea91ba 100644 --- a/src/main/webapp/src/pages/MemberList.jsx +++ b/src/main/webapp/src/pages/MemberList.jsx @@ -193,8 +193,6 @@ function FileOutput() { function FileInput() { - const [data, setData] = useState(null); - const re = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i; function excelDateToJSDate(serial) { @@ -251,19 +249,30 @@ function FileInput() { if (line["Nom médecin certificat"] === undefined || line["Nom médecin certificat"] === "" || line["Date certificat"] === undefined || line["Date certificat"] === "") { cetifNotFill++; - } - try { - // noinspection JSNonASCIINames - tmp.certif = line["Nom médecin certificat"] + "¤" + excelDateToJSDate(line["Date certificat"]).toISOString() - } catch (e) { - toast.error("Format de la date de certificat invalide à la ligne " + (i + 2)) - error++; + } else { + try { + const date = excelDateToJSDate(line["Date certificat"]); + if (Number.isNaN(date.getFullYear())) { + toast.error("Format de la date de certificat invalide à la ligne " + (i + 2)) + error++; + } else { + // noinspection JSNonASCIINames + tmp.certif = line["Nom médecin certificat"] + "¤" + date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2); + } + } catch (e) { + toast.error("Format de la date de certificat invalide à la ligne " + (i + 2)) + error++; + } } if (tmp.birthdate === undefined || tmp.birthdate === "") { toast.error("Date de naissance vide à la ligne " + (i + 2)) error++; } + } + + if (tmp.birthdate !== undefined && tmp.birthdate !== "") { + console.log(tmp.birthdate); try { tmp.birthdate = excelDateToJSDate(tmp.birthdate).toISOString(); } catch (e) { @@ -271,6 +280,7 @@ function FileInput() { error++; } } + dataOut.push(tmp) } @@ -278,7 +288,22 @@ function FileInput() { toast.error(`${error} erreur(s) dans le fichier, opération annulée`) } else { console.log(dataOut); - setData(sheetData); + + toast.promise( + apiAxios.put(`/member/club/import`, dataOut), + { + pending: "Envoie des changement en cours", + success: "Changement envoyé avec succès 🎉", + error: { + render({data}) { + return errFormater(data, "Échec de l'envoie des changements") + } + } + } + ).then(_ => { + if (cetifNotFill > 0) + toast.warn(`${cetifNotFill} certificat(s) médical(aux) non rempli(s)`) + }) } };