feat: auto fill certificate fields on add licence
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 11m30s

This commit is contained in:
2026-08-06 16:16:47 +02:00
parent 8f0e3c0738
commit 2772e44456
7 changed files with 105 additions and 23 deletions

View File

@@ -216,7 +216,7 @@ public class MembreService {
public Uni<List<SimpleMembreInOutData>> getAllExport(String subject) {
return repository.find("userId = ?1", subject).firstResult()
.chain(membreModel -> repository.list("club = ?1", membreModel.getClub()))
.chain(membres -> licenceRepository.list("saison = ?1 AND membre IN ?2", Utils.getSaison(), membres)
.chain(membres -> licenceRepository.list("membre IN ?1", membres)
.map(l -> membres.stream().map(m -> SimpleMembreInOutData.fromModel(m, l)).toList()));
}

View File

@@ -2,10 +2,14 @@ package fr.titionfire.ffsaf.rest.data;
import fr.titionfire.ffsaf.data.model.LicenceModel;
import fr.titionfire.ffsaf.data.model.MembreModel;
import fr.titionfire.ffsaf.utils.Utils;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -24,7 +28,27 @@ public class SimpleMembreInOutData {
public static SimpleMembreInOutData fromModel(MembreModel membreModel, List<LicenceModel> lc) {
LicenceModel currentLicence = lc.stream().filter(l -> l.getMembre().getId().equals(membreModel.getId()))
.findFirst().orElse(null);
.max(Comparator.comparingInt(LicenceModel::getSaison)).orElse(null);
String certif = null;
if (currentLicence != null && currentLicence.getCertificate() != null) {
String[] strings = currentLicence.getCertificate().split("¤");
if (currentLicence.getSaison() == Utils.getSaison()) {
certif = currentLicence.getCertificate();
} else if (strings.length > 1) {
try {
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(strings[1]);
Calendar max = Utils.getFirstDateOfSaison();
max.add(Calendar.YEAR, -2);
if (max.getTime().compareTo(date) < 0) {
certif = currentLicence.getCertificate();
}
} catch (Exception ignored) {
}
}
}
return new SimpleMembreInOutData(
membreModel.getLicence(),
@@ -33,8 +57,8 @@ public class SimpleMembreInOutData {
membreModel.getEmail(),
membreModel.getGenre().str,
membreModel.getBirth_date(),
currentLicence != null,
currentLicence == null ? null : currentLicence.getCertificate()
currentLicence != null && currentLicence.getSaison() == Utils.getSaison(),
certif
);
}
}

View File

@@ -119,6 +119,15 @@ public class Utils {
}
}
public static Calendar getFirstDateOfSaison() {
Calendar calendar = Calendar.getInstance();
calendar.set(getSaison(), Calendar.SEPTEMBER, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar;
}
public static Calendar toCalendar(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);

View File

@@ -169,6 +169,7 @@ function FileOutput() {
toast.promise(
apiAxios.get(`/member/club/export`), getToastMessage("membre.toast.licences.export"))
.then(data => {
console.log(data)
const dataOut = []
for (const e of data.data) {
const tmp = {

View File

@@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faEuroSign, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {apiAxios, getFirstDateOfSaison, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
import {useTranslation} from "react-i18next";
@@ -51,13 +51,34 @@ export function LicenceCard({userData}) {
dispatch({type: 'SORT'})
}, [data]);
const handleAsk = () => {
const currentLicence = licences[0];
let certif = undefined;
if (currentLicence != null && currentLicence.certificate != null) {
const strings = currentLicence.certificate.split('¤');
if (currentLicence.saison === getSaison()) {
certif = currentLicence.certificate;
} else if (strings.length > 1) {
const date = new Date(strings[1]);
const max = getFirstDateOfSaison();
max.setFullYear(max.getFullYear() - 2);
if (max < date) {
certif = currentLicence.certificate;
}
}
}
setModal({id: -1, membre: userData.id, certificate: certif});
}
return <div className="card mb-4 mb-md-0">
<div className="card-header container-fluid">
<div className="row">
<div className="col">{t('licence')}</div>
<div className="col" style={{textAlign: 'right'}}>
<button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal"
onClick={_ => setModal({id: -1, membre: userData.id})}>{t('button.ajouter')}
onClick={handleAsk}>{t('button.ajouter')}
</button>
</div>
</div>
@@ -141,13 +162,6 @@ function ModalContent({licence, dispatch}) {
if (licence.id !== -1) {
setNew(false)
setSaison(licence.saison)
if (licence.certificate === null) {
setCertificateBy("")
setCertificateDate("")
} else {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
}
setValidate(licence.validate)
setPay(licence.pay);
} else {
@@ -158,6 +172,13 @@ function ModalContent({licence, dispatch}) {
setValidate(false)
setPay(false);
}
if (licence.certificate) {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
} else {
setCertificateBy("")
setCertificateDate("")
}
}, [licence]);
return <form onSubmit={e => sendLicence(e, dispatch)}>

View File

@@ -4,7 +4,7 @@ import {useEffect, useReducer, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faInfo, faPen} from "@fortawesome/free-solid-svg-icons";
import {AxiosError} from "../../../components/AxiosError.jsx";
import {apiAxios, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {apiAxios, getFirstDateOfSaison, getSaison, getToastMessage} from "../../../utils/Tools.js";
import {toast} from "react-toastify";
import {ColoredText} from "../../../components/ColoredCircle.jsx";
import {useTranslation} from "react-i18next";
@@ -32,7 +32,7 @@ function licenceReducer(licences, action) {
}
export function LicenceCard({userData}) {
const defaultLicence = {id: -1, membre: userData.id, validate: false, saison: getSaison(), certificate: false}
const defaultLicence = {id: -1, membre: userData.id, validate: false, saison: getSaison(), certificate: null}
const {t} = useTranslation();
const setLoading = useLoadingSwitcher()
@@ -48,13 +48,33 @@ export function LicenceCard({userData}) {
dispatch({type: 'SORT'})
}, [data]);
const handleAsk = () => {
const currentLicence = licences[0];
let certif = undefined;
if (currentLicence != null && currentLicence.certificate != null) {
const strings = currentLicence.certificate.split('¤');
if (currentLicence.saison === getSaison()) {
certif = currentLicence.certificate;
} else if (strings.length > 1) {
const date = new Date(strings[1]);
const max = getFirstDateOfSaison();
max.setFullYear(max.getFullYear() - 2);
if (max < date)
certif = currentLicence.certificate;
}
}
setModal({...defaultLicence, certificate: certif});
}
return <div className="card mb-4 mb-md-0">
<div className="card-header container-fluid">
<div className="row">
<div className="col">{t('licence')}</div>
<div className="col" style={{textAlign: 'right'}}>
<button className="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#LicenceModal"
onClick={() => setModal(defaultLicence)}
onClick={handleAsk}
disabled={licences.some(licence => licence.saison === getSaison())}>{t('demander')}
</button>
</div>
@@ -127,15 +147,14 @@ function ModalContent({licence, dispatch}) {
useEffect(() => {
if (licence.id !== -1) {
setNew(false)
if (licence.certificate === null) {
setCertificateBy("")
setCertificateDate("")
} else {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
}
} else {
setNew(true)
}
if (licence.certificate) {
setCertificateBy(licence.certificate.split('¤')[0])
setCertificateDate(licence.certificate.split('¤')[1])
} else {
setCertificateBy("")
setCertificateDate("")
}

View File

@@ -117,6 +117,14 @@ export function getSaison(currentDate = new Date()) {
}
}
export function getFirstDateOfSaison() {
const year = getSaison();
const firstDate = new Date(year, 8, 1); // 8 = septembre, 1 = premier jour
firstDate.setHours(0, 0, 0, 0); // Heure, minutes, secondes, millisecondes à 0
return firstDate;
}
export function getCatName(cat) {
switch (cat) {
case "SUPER_MINI":