diff --git a/CHANGELOG.md b/CHANGELOG.md index c28e2b784..cb869bc1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.5.3] - 2022-08-16 + +### Added + - Aggiunta email di warning per assenze per missione non inserite a causa di assenze preesistenti + - Aggiunta possibilità per il responsabile di sede di vedere i flussi di richiesta ferie anche + di personale non del proprio istituto ma di cui si è responsabile di sede (utile per le Aree di Ricerca) + - Aggiunte alcune label per gli screenreader per migliorare l'accessibilità + - Aggiunta possibilità di vedere nel menù la gestione dei telework se questi sono abilitati per + l'utente + - Aggiunta possibilità di vedere il menu con i flussi di lavoro al personale che è responsabile di + sede, anche sulle sedi diversa dalla propria di assegnazione +### Changed + - Corretto controllo dei buoni pasto inviati ad Attestati (funzionalità solo per CNR) + - La cancellazione dei servizi di reperibilità adesso è possibile anche se il servizio + ha associato delle persone ma non ha nessun giorno di reperibilità assegnato + - Modificato il comportamento del codice 39LA per l'assegnazione del buono pasto (non lo assegna) + - Rimosso codice 40LA dalla lista dei codici prendibili + - Modificata lista dei flussi attivi evitando di mostrare quelli relativi alla propria sede se non + si hanno gli opportuni permessi + - Rimossa scritta SSO nella form di login tramite OAuth + - Modificato messaggio di errore in caso di richiesta riposo compensativo senza ore sufficienti + ## [2.5.2] - 2022-07-06 ### Added @@ -15,7 +37,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Aggiunta possibilità di cedere un giorno di reperibilità - Aggiunto controllo sulla visibilità della presenza giornaliera dei dipendenti appartenenti ad un gruppo da parte del responsabile di gruppo - ### Changed - Corretto messaggio di errore in caso di attestato di fine mese non calcolato correttamente - Modificato il conteggio dei giorni di presenza in sede quando un dipendente è in telelavoro e @@ -223,4 +244,4 @@ e tipologia di orario di lavoro e aggiornamento del corrispondente externalId ## [2.0.0] - 2021-02-25 ### Added -- Prima relaase pubblica open source \ No newline at end of file +- Prima relaase pubblica open source diff --git a/VERSION b/VERSION index 21b159dc8..a4db534a2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.2 \ No newline at end of file +2.5.3 \ No newline at end of file diff --git a/app/controllers/AbsenceRequests.java b/app/controllers/AbsenceRequests.java index 4f9745229..0db0407da 100644 --- a/app/controllers/AbsenceRequests.java +++ b/app/controllers/AbsenceRequests.java @@ -35,6 +35,7 @@ import helpers.TemplateExtensions; import helpers.Web; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; @@ -59,6 +60,7 @@ import models.UsersRolesOffices; import models.absences.AbsenceType; import models.absences.GroupAbsenceType; +import models.absences.GroupAbsenceType.GroupAbsenceTypePattern; import models.absences.JustifiedType; import models.absences.definitions.DefaultAbsenceType; import models.absences.definitions.DefaultGroup; @@ -248,17 +250,18 @@ public static void listToApprove(AbsenceRequestType type) { fromDate); List groups = groupDao.groupsByOffice(person.office, Optional.absent(), Optional.of(false)); - List roleList = uroDao.getUsersRolesOfficesByUser(person.user); + List roleList = + uroDao.getAdministrativeUsersRolesOfficesByUser(person.user); List results = absenceRequestDao.allResults(roleList, fromDate, Optional.absent(), type, groups, person); - List myResults = absenceRequestDao.toApproveResults(roleList, Optional.absent(), + Set myResults = absenceRequestDao.toApproveResults(roleList, Optional.absent(), Optional.absent(), type, groups, person); List approvedResults = absenceRequestDao.totallyApproved(roleList, fromDate, Optional.absent(), type, groups, person); val config = absenceRequestManager.getConfiguration(type, person); val onlyOwn = false; - log.debug("Preparate richieste da approvare per {} in {} ms. Da approvare = {}", - person.getFullname(), System.currentTimeMillis() - start, myResults.size()); + log.debug("Preparate richieste da approvare per {} in {} ms. Da approvare = {}. Flussi attivi = {}", + person.getFullname(), System.currentTimeMillis() - start, myResults.size(), results.size()); render(config, results, type, onlyOwn, approvedResults, myResults); } @@ -302,8 +305,13 @@ public static void blank(Optional personId, LocalDate from, AbsenceRequest val absenceRequest = new AbsenceRequest(); absenceRequest.type = type; absenceRequest.person = person; + + PeriodChain periodChain = null; + GroupAbsenceType groupAbsenceType = null; if (type.equals(AbsenceRequestType.COMPENSATORY_REST) && person.isTopQualification()) { + groupAbsenceType = absenceComponentDao + .groupAbsenceTypeByName(DefaultGroup.RIPOSI_CNR.name()).get(); PersonStampingRecap psDto = stampingsRecapFactory.create(person, LocalDate.now().getYear(), LocalDate.now().getMonthOfYear(), true); int maxDays = (Integer) configurationManager.configValue(person.office, @@ -312,26 +320,32 @@ public static void blank(Optional personId, LocalDate from, AbsenceRequest handleCompensatoryRestSituation = true; } if (type.equals(AbsenceRequestType.VACATION_REQUEST)) { - GroupAbsenceType vacationGroup = + groupAbsenceType = absenceComponentDao.groupAbsenceTypeByName(DefaultGroup.FERIE_CNR.name()).get(); IWrapperPerson wperson = wrapperFactory.create(person); for (Contract contract : wperson.orderedYearContracts(LocalDate.now().getYear())) { VacationSituation vacationSituation = absenceService.buildVacationSituation(contract, - LocalDate.now().getYear(), vacationGroup, Optional.absent(), false); + LocalDate.now().getYear(), groupAbsenceType, Optional.absent(), false); vacationSituations.add(vacationSituation); } + periodChain = absenceService + .residual(person, groupAbsenceType, LocalDate.now()); + } + if (type.equals(AbsenceRequestType.PERSONAL_PERMISSION)) { + groupAbsenceType = absenceComponentDao + .groupAbsenceTypeByName(DefaultGroup.G_661.name()).get(); + periodChain = absenceService + .residual(person, groupAbsenceType, LocalDate.now()); } - GroupAbsenceType permissionGroup = absenceComponentDao - .groupAbsenceTypeByName(DefaultGroup.G_661.name()).get(); - PeriodChain periodChain = absenceService - .residual(person, permissionGroup, LocalDate.now()); + boolean showVacationPeriods = false; boolean retroactiveAbsence = false; absenceRequest.startAt = absenceRequest.endTo = LocalDateTime.now().plusDays(1); boolean insertable = true; - GroupAbsenceType groupAbsenceType = absenceRequestManager.getGroupAbsenceType(absenceRequest); + groupAbsenceType = absenceRequestManager.getGroupAbsenceType(absenceRequest); + AbsenceType absenceType = null; AbsenceForm absenceForm = absenceService.buildAbsenceForm(absenceRequest.person, absenceRequest.startAtAsDate(), null, absenceRequest.endToAsDate(), null, groupAbsenceType, @@ -340,6 +354,7 @@ public static void blank(Optional personId, LocalDate from, AbsenceRequest absenceService.insert(absenceRequest.person, absenceForm.groupSelected, absenceForm.from, absenceForm.to, absenceForm.absenceTypeSelected, absenceForm.justifiedTypeSelected, null, null, false, absenceManager); + render("@edit", absenceRequest, insertable, insertReport, vacationSituations, compensatoryRestAvailable, handleCompensatoryRestSituation, showVacationPeriods, @@ -358,10 +373,12 @@ public static void edit(final AbsenceRequest absenceRequest, boolean retroactive rules.checkIfPermitted(absenceRequest); boolean insertable = true; - GroupAbsenceType permissionGroup = absenceComponentDao - .groupAbsenceTypeByName(DefaultGroup.G_661.name()).get(); - PeriodChain periodChain = absenceService - .residual(absenceRequest.person, permissionGroup, LocalDate.now()); + GroupAbsenceType permissionGroup = groupAbsenceType; + PeriodChain periodChain = null; + if (!groupAbsenceType.name.equals(DefaultGroup.RIPOSI_CNR.name())) { + periodChain = absenceService + .residual(absenceRequest.person, permissionGroup, LocalDate.now()); + } if (absenceRequest.startAt == null || absenceRequest.endTo == null) { Validation.addError("absenceRequest.startAt", diff --git a/app/controllers/Competences.java b/app/controllers/Competences.java index 07ae806e7..790ff930a 100755 --- a/app/controllers/Competences.java +++ b/app/controllers/Competences.java @@ -986,14 +986,24 @@ public static void evaluateReperibility(Long reperibilityTypeId, boolean confirm flash.success("Riabilitato servizio %s", type.description); activateServices(type.office.id); } - if (!type.personReperibilities.isEmpty()) { + + log.debug("Reperibility type = {}, personReperibilities.size = {}, " + + "personReperibilitiesDays.size = {}", + type, type.personReperibilities.size(), + type.personReperibilities.stream().flatMap(pr -> pr.personReperibilityDays.stream()).collect(Collectors.toList()).size()); + + if (!type.personReperibilities.isEmpty() && + !type.personReperibilities.stream().flatMap(pr -> pr.personReperibilityDays.stream()).collect(Collectors.toList()).isEmpty()) { type.disabled = true; type.save(); + log.info("Disabilitato servizio {}", type); flash.success("Il servizio è stato disabilitato e non rimosso perchè legato con informazioni " + "importanti presenti in altre tabelle"); } else { + type.personReperibilities.stream().forEach(pr -> pr.delete()); type.delete(); + log.info("Rimosso servizio {}", type); flash.success("Servizio rimosso con successo"); } activateServices(type.office.id); diff --git a/app/controllers/InformationRequests.java b/app/controllers/InformationRequests.java index c7ca5c564..3863547db 100644 --- a/app/controllers/InformationRequests.java +++ b/app/controllers/InformationRequests.java @@ -46,6 +46,7 @@ import manager.flows.InformationRequestManager; import manager.recaps.personstamping.PersonStampingRecap; import manager.recaps.personstamping.PersonStampingRecapFactory; +import models.Contract; import models.Person; import models.Role; import models.TeleworkValidation; @@ -73,7 +74,7 @@ @Slf4j @With(Resecure.class) public class InformationRequests extends Controller { - + @Inject static InformationRequestManager informationRequestManager; @Inject @@ -100,23 +101,23 @@ public class InformationRequests extends Controller { public static void teleworks() { list(InformationType.TELEWORK_INFORMATION); } - + public static void illness() { list(InformationType.ILLNESS_INFORMATION); } - + public static void serviceExit() { list(InformationType.SERVICE_INFORMATION); } - + public static void teleworksToApprove() { listToApprove(InformationType.TELEWORK_INFORMATION); } - + public static void illnessToApprove() { listToApprove(InformationType.ILLNESS_INFORMATION); } - + public static void serviceExitToApprove() { listToApprove(InformationType.SERVICE_INFORMATION); } @@ -149,34 +150,33 @@ public static void list(InformationType type) { List illnessClosed = Lists.newArrayList(); switch (type) { case TELEWORK_INFORMATION: - teleworks = informationRequestDao.teleworksByPersonAndDate(person, fromDate, + teleworks = informationRequestDao.teleworksByPersonAndDate(person, fromDate, Optional.absent(), InformationType.TELEWORK_INFORMATION, true); - teleworksClosed = informationRequestDao.teleworksByPersonAndDate(person, fromDate, + teleworksClosed = informationRequestDao.teleworksByPersonAndDate(person, fromDate, Optional.absent(), InformationType.TELEWORK_INFORMATION, false); break; case ILLNESS_INFORMATION: - illness = informationRequestDao.illnessByPersonAndDate(person, fromDate, + illness = informationRequestDao.illnessByPersonAndDate(person, fromDate, Optional.absent(), InformationType.ILLNESS_INFORMATION, true); - illnessClosed = informationRequestDao.illnessByPersonAndDate(person, fromDate, + illnessClosed = informationRequestDao.illnessByPersonAndDate(person, fromDate, Optional.absent(), InformationType.ILLNESS_INFORMATION, false); break; case SERVICE_INFORMATION: - services = informationRequestDao.servicesByPersonAndDate(person, fromDate, + services = informationRequestDao.servicesByPersonAndDate(person, fromDate, Optional.absent(), InformationType.SERVICE_INFORMATION, true); - servicesClosed = informationRequestDao.servicesByPersonAndDate(person, fromDate, + servicesClosed = informationRequestDao.servicesByPersonAndDate(person, fromDate, Optional.absent(), InformationType.SERVICE_INFORMATION, false); break; default: log.info("Passato argomento non conosciuto"); break; } - render(teleworks, services, illness, teleworksClosed, + render(teleworks, services, illness, teleworksClosed, illnessClosed, servicesClosed, config, type); } - + /** - * Genera la pagina con tutte le richieste di flusso informativo del tipo passato - * come parametro. + * Genera la pagina con tutte le richieste di flusso informativo del tipo passato come parametro. * * @param type la tipologia di flusso informativo. */ @@ -187,15 +187,15 @@ public static void listToApprove(InformationType type) { val fromDate = LocalDateTime.now().withDayOfYear(1).withMonth(1).minusMonths(1); log.debug("Prelevo le richieste da approvare di assenze di tipo {} a partire da {}", type, fromDate); - + List roleList = uroDao.getUsersRolesOfficesByUser(person.user); - List myResults = + List myResults = informationRequestDao.toApproveResults(roleList, Optional.absent(), - Optional.absent(), type, person); - List approvedResults = + Optional.absent(), type, person); + List approvedResults = informationRequestDao.totallyApproved(roleList, fromDate, - Optional.absent(), type, person); - + Optional.absent(), type, person); + List idMyResults = myResults.stream().map(ir -> ir.id).collect(Collectors.toList()); List idApprovedResults = approvedResults.stream().map(ir -> ir.id) .collect(Collectors.toList()); @@ -224,20 +224,20 @@ public static void listToApprove(InformationType type) { val config = informationRequestManager.getConfiguration(type, person); val onlyOwn = false; - render(config, type, onlyOwn, approvedResults, myResults, + render(config, type, onlyOwn, approvedResults, myResults, myIllnessResult, illnessApprovedResult, myServiceResult, serviceApprovedResult, myTeleworkResult, teleworkApprovedResult); } - + /** * Crea la pagina di inserimento di una nuova richiesta di flusso informativo. * * @param personId l'identificativo della persona - * @param type la tipologia di flusso informativo da generare + * @param type la tipologia di flusso informativo da generare */ public static void blank(Optional personId, InformationType type) { Verify.verifyNotNull(type); - + Person person; if (personId.isPresent()) { rules.check("AbsenceRequests.blank4OtherPerson"); @@ -252,7 +252,7 @@ public static void blank(Optional personId, InformationType type) { } } notFoundIfNull(person); - + val configurationProblems = informationRequestManager.checkconfiguration(type, person); if (!configurationProblems.isEmpty()) { flash.error(Joiner.on(" ").join(configurationProblems)); @@ -272,33 +272,32 @@ public static void blank(Optional personId, InformationType type) { illnessRequest.informationType = type; render("@editIllnessRequest", illnessRequest, type, person); break; - default: + default: break; } } - + public static void editServiceRequest(ServiceRequest serviceRequest, InformationType type, @CheckWith(StringIsTime.class) String begin, @CheckWith(StringIsTime.class) String finish) { - - + } - + public static void editIllnessRequest(IllnessRequest illnessRequest, boolean retroactiveAbsence) { - + } - + /** * Persiste la richiesta di uscita di servizio e avvia il flusso approvativo. * * @param serviceRequest la richiesta di uscita di servizio - * @param begin l'orario di inizio - * @param finish l'orario di fine + * @param begin l'orario di inizio + * @param finish l'orario di fine */ public static void saveServiceRequest(ServiceRequest serviceRequest, @CheckWith(StringIsTime.class) String begin, @CheckWith(StringIsTime.class) String finish) { InformationType type = serviceRequest.informationType; boolean insertable = true; - if (Validation.hasErrors()) { + if (Validation.hasErrors()) { response.status = 400; insertable = false; render("@editServiceRequest", serviceRequest, insertable, begin, finish, type); @@ -315,25 +314,25 @@ public static void saveServiceRequest(ServiceRequest serviceRequest, render("@editServiceRequest", serviceRequest, insertable, begin, finish, type); } if (serviceRequest.beginAt.isAfter(serviceRequest.finishTo)) { - Validation.addError("serviceRequest.beginAt", + Validation.addError("serviceRequest.beginAt", "L'orario di inizio non può essere successivo all'orario di fine"); response.status = 400; insertable = false; render("@editServiceRequest", serviceRequest, insertable, begin, finish, type); } - informationRequestManager.configure(Optional.absent(), + informationRequestManager.configure(Optional.absent(), Optional.of(serviceRequest), Optional.absent()); serviceRequest.startAt = LocalDateTime.now(); serviceRequest.save(); - + boolean isNewRequest = !serviceRequest.isPersistent(); if (isNewRequest || !serviceRequest.flowStarted) { - informationRequestManager.executeEvent(Optional.fromNullable(serviceRequest), + informationRequestManager.executeEvent(Optional.fromNullable(serviceRequest), Optional.absent(), Optional.absent(), serviceRequest.person, InformationRequestEventType.STARTING_APPROVAL_FLOW, Optional.absent()); if (serviceRequest.autoApproved()) { - informationRequestManager.executeEvent(Optional.fromNullable(serviceRequest), - Optional.absent(), Optional.absent(), serviceRequest.person, + informationRequestManager.executeEvent(Optional.fromNullable(serviceRequest), + Optional.absent(), Optional.absent(), serviceRequest.person, InformationRequestEventType.COMPLETE, Optional.absent()); } if (serviceRequest.person.isSeatSupervisor()) { @@ -350,9 +349,9 @@ public static void saveServiceRequest(ServiceRequest serviceRequest, } flash.success("Operazione effettuata correttamente"); InformationRequests.list(serviceRequest.informationType); - + } - + /** * Persiste la richiesta e avvia il flusso informativo. * @@ -366,22 +365,22 @@ public static void saveIllnessRequest(IllnessRequest illnessRequest) { Validation.addError("illnessRequest.endDate", "Entrambi i campi data devono essere valorizzati"); response.status = 400; - + render("@editIllnessRequest", illnessRequest, type); } if (illnessRequest.beginDate.isAfter(illnessRequest.endDate)) { - Validation.addError("illnessRequest.beginDate", + Validation.addError("illnessRequest.beginDate", "La data di inizio non può essere successiva alla data di fine"); response.status = 400; render("@editIllnessRequest", illnessRequest, type); } - informationRequestManager.configure(Optional.of(illnessRequest), + informationRequestManager.configure(Optional.of(illnessRequest), Optional.absent(), Optional.absent()); illnessRequest.startAt = LocalDateTime.now(); illnessRequest.save(); boolean isNewRequest = !illnessRequest.isPersistent(); if (isNewRequest || !illnessRequest.flowStarted) { - informationRequestManager.executeEvent(Optional.absent(), + informationRequestManager.executeEvent(Optional.absent(), Optional.fromNullable(illnessRequest), Optional.absent(), illnessRequest.person, InformationRequestEventType.STARTING_APPROVAL_FLOW, Optional.absent()); if (illnessRequest.person.isSeatSupervisor()) { @@ -399,27 +398,36 @@ public static void saveIllnessRequest(IllnessRequest illnessRequest) { flash.success("Operazione effettuata correttamente"); InformationRequests.list(illnessRequest.informationType); } - + /** * Persiste la richiesta di telelavoro e avvia il flusso informativo. * * @param personId l'identificativo della persona - * @param year l'anno di riferimento - * @param month il mese di riferimento + * @param year l'anno di riferimento + * @param month il mese di riferimento */ public static void saveTeleworkRequest(Long personId, int year, int month) { Person person = personDao.getPersonById(personId); notFoundIfNull(person); - TeleworkRequest teleworkRequest = new TeleworkRequest(); - teleworkRequest.year = year; - teleworkRequest.month = month; - teleworkRequest.person = person; - teleworkRequest.startAt = LocalDateTime.now(); - teleworkRequest.informationType = InformationType.TELEWORK_INFORMATION; - teleworkRequest.save(); - informationRequestManager.configure(Optional.absent(), - Optional.absent(), Optional.of(teleworkRequest)); + + TeleworkRequest teleworkRequest; + + val teleworkRequestInPeriod = informationRequestDao.personTeleworkInPeriod(person, month, year); + if (!teleworkRequestInPeriod.isPresent()) { + teleworkRequest = new TeleworkRequest(); + teleworkRequest.year = year; + teleworkRequest.month = month; + teleworkRequest.person = person; + teleworkRequest.startAt = LocalDateTime.now(); + teleworkRequest.informationType = InformationType.TELEWORK_INFORMATION; + teleworkRequest.save(); + informationRequestManager.configure(Optional.absent(), Optional.absent(), + Optional.of(teleworkRequest)); + } else { + teleworkRequest = teleworkRequestInPeriod.get(); + } boolean isNewRequest = !teleworkRequest.isPersistent(); + if (isNewRequest || !teleworkRequest.flowStarted) { informationRequestManager.executeEvent(Optional.absent(), Optional.absent(), Optional.fromNullable(teleworkRequest), teleworkRequest.person, @@ -438,10 +446,10 @@ public static void saveTeleworkRequest(Long personId, int year, int month) { } flash.success("Operazione effettuata correttamente"); InformationRequests.list(teleworkRequest.informationType); - + } - - + + /** * Metodo dispatcher che chiama il corretto metodo per approvare la richiesta. * @@ -468,11 +476,11 @@ public static void approval(long id) { notFoundIfNull(request); User user = Security.getUser().get(); - boolean approved = informationRequestManager.approval(serviceRequest, + boolean approved = informationRequestManager.approval(serviceRequest, illnessRequest, teleworkRequest, user); if (approved) { - notificationManager.sendEmailToUser(Optional.absent(), Optional.absent(), + notificationManager.sendEmailToUser(Optional.absent(), Optional.absent(), Optional.fromNullable(request), true); log.debug("Inviata mail con approvazione"); flash.success("Operazione conclusa correttamente"); @@ -483,10 +491,10 @@ public static void approval(long id) { InformationRequests.listToApprove(request.informationType); } else { InformationRequests.list(request.informationType); - } + } } - + /** * Dispatcher che instrada al corretto metodo l'operazione da fare sulla richiesta a seconda dei * parametri. @@ -524,25 +532,25 @@ public static void disapproval(long id, boolean disapproval, String reason) { break; default: break; - } - - if (informationRequest.officeHeadApprovalRequired + } + + if (informationRequest.officeHeadApprovalRequired && informationRequest.officeHeadApproved == null && user.hasRoles(Role.SEAT_SUPERVISOR)) { // caso di approvazione da parte del responsabile di sede informationRequestManager.officeHeadDisapproval(id, reason); flash.error("Richiesta respinta"); InformationType type = informationRequest.informationType; - render("@show", informationRequest, type, serviceRequest, + render("@show", informationRequest, type, serviceRequest, illnessRequest, teleworkRequest, user); } render("@show", informationRequest, user); } - + /** * Mostra al template la richiesta. * - * @param id l'id della richiesta da visualizzare + * @param id l'id della richiesta da visualizzare * @param type la tipologia di richiesta */ public static void show(long id, InformationType type) { @@ -555,23 +563,23 @@ public static void show(long id, InformationType type) { TeleworkRequest teleworkRequest = null; switch (type) { case SERVICE_INFORMATION: - serviceRequest = informationRequestDao.getServiceById(id).get(); + serviceRequest = informationRequestDao.getServiceById(id).get(); break; case ILLNESS_INFORMATION: - illnessRequest = informationRequestDao.getIllnessById(id).get(); + illnessRequest = informationRequestDao.getIllnessById(id).get(); break; case TELEWORK_INFORMATION: - teleworkRequest = informationRequestDao.getTeleworkById(id).get(); + teleworkRequest = informationRequestDao.getTeleworkById(id).get(); break; default: log.info("Passato argomento non conosciuto"); break; } boolean disapproval = false; - render(informationRequest, teleworkRequest, illnessRequest, serviceRequest, + render(informationRequest, teleworkRequest, illnessRequest, serviceRequest, type, user, disapproval); } - + /** * Form di cancellazione di un flusso informativo. * @@ -594,34 +602,34 @@ public static void delete(long id) { case TELEWORK_INFORMATION: teleworkRequest = Optional.fromNullable(informationRequestDao.getTeleworkById(id).get()); break; - default: + default: break; } - informationRequestManager.executeEvent(serviceRequest, illnessRequest, + informationRequestManager.executeEvent(serviceRequest, illnessRequest, teleworkRequest, Security.getUser().get().person, InformationRequestEventType.DELETE, Optional.absent()); flash.success(Web.msgDeleted(InformationRequest.class)); list(informationRequest.informationType); } - + /** * Ritorna il riepilogo del telelavoro dell'anno/mese della persona in oggetto. * * @param personId l'identificativo della persona - * @param year l'anno di riferimento - * @param month il mese di riferimento + * @param year l'anno di riferimento + * @param month il mese di riferimento * @throws NoSuchFieldException eccezione di mancanza di campo - * @throws ExecutionException eccezione in esecuzione + * @throws ExecutionException eccezione in esecuzione */ - public static void generateTeleworkReport(Long personId, int year, int month) + public static void generateTeleworkReport(Long personId, int year, int month) throws NoSuchFieldException, ExecutionException { - + Person person = personDao.getPersonById(personId); notFoundIfNull(person); IWrapperPerson wrperson = wrapperFactory.create(person); - + List list = Lists.newArrayList(); - + if (!wrperson.isActiveInMonth(new YearMonth(year, month))) { flash.error("Non esiste situazione mensile per il mese di %s %s", DateUtility.fromIntToStringMonth(month), year); @@ -631,38 +639,38 @@ public static void generateTeleworkReport(Long personId, int year, int month) } PersonStampingRecap psDto = stampingsRecapFactory .create(wrperson.getValue(), year, month, true); - + log.debug("Chiedo la lista delle timbrature in telelavoro ad applicazione esterna."); - + list = manager.stampingsForReport(psDto); render(list, person); } - + /** * Ritorna la form di gestione approvazioni di telelavoro. * - * @param personId l'identificativo della persona di cui gestire le richieste/approvazioni - * di telelavoro + * @param personId l'identificativo della persona di cui gestire le richieste/approvazioni di + * telelavoro */ public static void handleTeleworkApproval(Long personId) { PersonLite p = null; Person person = personDao.getPersonById(personId); - if (person.personConfigurations.stream().noneMatch(pc -> + if (person.personConfigurations.stream().noneMatch(pc -> pc.epasParam.equals(EpasParam.TELEWORK_STAMPINGS) && pc.fieldValue.equals("true"))) { @SuppressWarnings("unchecked") List persons = (List) renderArgs.get("navPersons"); if (persons.isEmpty()) { flash.error("Non ci sono persone abilitate al telelavoro!!"); - Stampings.personStamping(personId, Integer.parseInt(session.get("yearSelected")), + Stampings.personStamping(personId, Integer.parseInt(session.get("yearSelected")), Integer.parseInt(session.get("monthSelected"))); } p = persons.get(0); - + } if (p != null) { - person = personDao.getPersonById(p.id); + person = personDao.getPersonById(p.id); } - + Preconditions.checkNotNull(person); rules.checkIfPermitted(person.office); List dtoList = Lists.newArrayList(); @@ -684,7 +692,7 @@ public static void handleTeleworkApproval(Long personId) { } render(dtoList); } - + /** * Revoca la validazione ad un telelavoro. * @@ -694,12 +702,12 @@ public static void revokeValidation(Long validationId) { Optional validation = validationDao.getValidationById(validationId); if (validation.isPresent()) { validation.get().delete(); - flash.success("Validazione rimossa. Effettuare nuova richiesta di approvazione"); + flash.success("Validazione rimossa. Effettuare nuova richiesta di approvazione"); } else { flash.error("Validazione sconosciuta! Verificare l'identificativo."); } - Stampings.personStamping(Security.getUser().get().person.id, - Integer.parseInt(session.get("yearSelected")), - Integer.parseInt(session.get("monthSelected"))); + Stampings.personStamping(Security.getUser().get().person.id, + Integer.parseInt(session.get("yearSelected")), + Integer.parseInt(session.get("monthSelected"))); } } diff --git a/app/controllers/rest/Missions.java b/app/controllers/rest/Missions.java index f120b7db9..f8d95cca3 100644 --- a/app/controllers/rest/Missions.java +++ b/app/controllers/rest/Missions.java @@ -26,10 +26,12 @@ import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; import manager.MissionManager; +import manager.NotificationManager; import manager.configurations.ConfigurationManager; import manager.configurations.EpasParam; import models.Office; import models.exports.MissionFromClient; +import play.cache.Cache; import play.data.binding.As; import play.mvc.Controller; import play.mvc.With; @@ -48,6 +50,8 @@ public class Missions extends Controller { private static ConfigurationManager configurationManager; @Inject private static OfficeDao officeDao; + @Inject + private static NotificationManager notificationManager; private static void logInfo(String description, MissionFromClient body) { log.info(MissionManager.LOG_PREFIX + "{}. Messaggio: {}", description, body); @@ -73,12 +77,12 @@ public static void amqpreceiver(@As(binder = JsonMissionBinder.class)MissionFrom JsonResponse.badRequest(); return; } - + if (body.dataInizio.isAfter(body.dataFine)) { logWarn("Data di inizio successiva alla data di fine, messaggio scartato", body); JsonResponse.badRequest(); } - + // person not present (404) if (!missionManager.linkToPerson(body).isPresent()) { logWarn("Dipendente riferito nel messaggio non trovato, messaggio scartato", body); @@ -89,7 +93,7 @@ public static void amqpreceiver(@As(binder = JsonMissionBinder.class)MissionFrom Optional officeByMessage = officeDao.byCodeId(body.codiceSede); //Ufficio associato alla persona prelevata tramite la matricola passata nel JSON Office office = body.person.office; - + if (!officeByMessage.isPresent()) { logWarn( String.format("Attenzione il codice sede %s non è presente su ePAS ed il dipendente %s " @@ -103,7 +107,7 @@ public static void amqpreceiver(@As(binder = JsonMissionBinder.class)MissionFrom body.codiceSede, office.name, office.codeId, body.person.getFullname()), body); } - + // Check if integration ePAS-Missions is enabled if (!(Boolean) configurationManager .configValue(office, EpasParam.ENABLE_MISSIONS_INTEGRATION)) { @@ -112,7 +116,7 @@ public static void amqpreceiver(@As(binder = JsonMissionBinder.class)MissionFrom office.name, body.person.fullName()), body); JsonResponse.ok(); } - + boolean success = false; switch (body.tipoMissione) { case "ORDINE": @@ -131,7 +135,17 @@ public static void amqpreceiver(@As(binder = JsonMissionBinder.class)MissionFrom if (success) { logInfo("Messaggio inserito con successo", body); } else { - logWarn("Non è stato possibile inserire il messaggio", body); + logWarn("Problemi durante l'inserimento del messaggio", body); + String problematicMissionCacheKey = + String.format("mission.problematic.%s.%s.%s.%s", + body.tipoMissione, body.id, body.anno, body.numero); + if (Cache.get(problematicMissionCacheKey) == null) { + log.debug("Imposto la cache di missione problematica con valore true per {}", body); + notificationManager.sendEmailMissionFromClientProblems(body); + Cache.set(problematicMissionCacheKey, true, "1d"); + } else { + logInfo("Missione problematica già segnalata all'utente. Email non inviata.", body); + } JsonResponse.conflict(); } diff --git a/app/dao/AbsenceRequestDao.java b/app/dao/AbsenceRequestDao.java index 377f49f69..1388e44f6 100644 --- a/app/dao/AbsenceRequestDao.java +++ b/app/dao/AbsenceRequestDao.java @@ -20,6 +20,7 @@ import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.google.inject.Provider; import com.querydsl.core.BooleanBuilder; import com.querydsl.jpa.JPQLQuery; @@ -27,6 +28,7 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import javax.persistence.EntityManager; @@ -120,7 +122,7 @@ public List findByPersonAndDate(Person person, * @param absenceRequestType il tipo di richiesta da cercare * @return la lista di tutti i flussi attivi da approvare. */ - public List toApproveResults(List uros, + public Set toApproveResults(List uros, Optional fromDate, Optional toDate, AbsenceRequestType absenceRequestType, List groups, Person signer) { Preconditions.checkNotNull(fromDate); @@ -134,7 +136,7 @@ public List toApproveResults(List uros, if (uros.stream().noneMatch(uro -> uro.role.name.equals(Role.GROUP_MANAGER) || uro.role.name.equals(Role.PERSONNEL_ADMIN) || uro.role.name.equals(Role.SEAT_SUPERVISOR))) { - return Lists.newArrayList(); + return Sets.newHashSet(); } if (fromDate.isPresent()) { conditions.and(absenceRequest.startAt.after(fromDate.get())); @@ -146,7 +148,7 @@ public List toApproveResults(List uros, .and(absenceRequest.flowStarted.isTrue()) .and(absenceRequest.flowEnded.isFalse())); - List results = new ArrayList<>(); + Set results = Sets.newHashSet(); if (uros.stream().anyMatch(uro -> uro.role.name.equals(Role.SEAT_SUPERVISOR))) { results.addAll( toApproveResultsAsSeatSuperVisor( @@ -253,7 +255,7 @@ public List allResults(List uros, conditions.and(absenceRequest.managerApprovalRequired.isTrue()) .and(absenceRequest.managerApproved.isNotNull()) .and(person.office.eq(signer.office)); - final QAffiliation affiliation = QAffiliation.affiliation; + final QAffiliation affiliation = QAffiliation.affiliation; query = getQueryFactory().selectFrom(absenceRequest) .join(absenceRequest.person, person).fetchJoin() .join(person.affiliations, affiliation) diff --git a/app/dao/InformationRequestDao.java b/app/dao/InformationRequestDao.java index da01ecec2..3e0c28096 100644 --- a/app/dao/InformationRequestDao.java +++ b/app/dao/InformationRequestDao.java @@ -50,7 +50,6 @@ * Dao per i flussi informativi. * * @author dario - * */ public class InformationRequestDao extends DaoBase { @@ -60,17 +59,16 @@ public class InformationRequestDao extends DaoBase { } /** - * Metodo che ritorna la lista delle richieste di flusso corrispondenti ai - * parametri passati. + * Metodo che ritorna la lista delle richieste di flusso corrispondenti ai parametri passati. * - * @param uroList la lista dei ruoli sulle sedi - * @param fromDate da quale data cercare le richieste (opzionale) - * @param toDate a quale data cercare le richieste (opzionale) + * @param uroList la lista dei ruoli sulle sedi + * @param fromDate da quale data cercare le richieste (opzionale) + * @param toDate a quale data cercare le richieste (opzionale) * @param informationType il tipo di richiesta - * @param signer chi deve firmare la richiesta + * @param signer chi deve firmare la richiesta * @return la lista delle richieste di flusso corrispondenti ai parametri passati. */ - public List toApproveResults(List uroList, + public List toApproveResults(List uroList, Optional fromDate, Optional toDate, InformationType informationType, Person signer) { final QInformationRequest informationRequest = QInformationRequest.informationRequest; @@ -86,7 +84,7 @@ public List toApproveResults(List uroList } if (toDate.isPresent()) { conditions.and(informationRequest.endTo.before(toDate.get())); - } + } conditions.and(informationRequest.informationType.eq(informationType) .and(informationRequest.flowStarted.isTrue()) .and(informationRequest.flowEnded.isFalse())); @@ -94,10 +92,10 @@ public List toApproveResults(List uroList List results = new ArrayList<>(); if (informationType.equals(InformationType.ILLNESS_INFORMATION) && uroList.stream().anyMatch(uro -> uro.role.name.equals(Role.PERSONNEL_ADMIN))) { - results.addAll(toApproveResultsAsPersonnelAdmin(uroList, + results.addAll(toApproveResultsAsPersonnelAdmin(uroList, informationType, signer, conditions)); } else { - results.addAll(toApproveResultsAsSeatSuperVisor(uroList, + results.addAll(toApproveResultsAsSeatSuperVisor(uroList, informationType, signer, conditions)); } @@ -107,9 +105,9 @@ public List toApproveResults(List uroList /** * Lista delle richiesta di assenza per persona e data. * - * @param person La persona della quale recuperare le richieste di assenza - * @param fromDate La data iniziale dell'intervallo temporale da considerare - * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) + * @param person La persona della quale recuperare le richieste di assenza + * @param fromDate La data iniziale dell'intervallo temporale da considerare + * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) * @param informationType Il tipo di richiesta di assenza specifico * @return La lista delle richieste di assenze sull'intervallo e la persona specificati. */ @@ -140,9 +138,9 @@ public List teleworksByPersonAndDate(Person person, /** * Lista delle richiesta di assenza per persona e data. * - * @param person La persona della quale recuperare le richieste di assenza - * @param fromDate La data iniziale dell'intervallo temporale da considerare - * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) + * @param person La persona della quale recuperare le richieste di assenza + * @param fromDate La data iniziale dell'intervallo temporale da considerare + * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) * @param informationType Il tipo di richiesta di assenza specifico * @return La lista delle richieste di assenze sull'intervallo e la persona specificati. */ @@ -173,9 +171,9 @@ public List illnessByPersonAndDate(Person person, /** * Lista delle richiesta di assenza per persona e data. * - * @param person La persona della quale recuperare le richieste di assenza - * @param fromDate La data iniziale dell'intervallo temporale da considerare - * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) + * @param person La persona della quale recuperare le richieste di assenza + * @param fromDate La data iniziale dell'intervallo temporale da considerare + * @param toDate La data finale dell'intervallo temporale da considerare (opzionale) * @param informationType Il tipo di richiesta di assenza specifico * @return La lista delle richieste di assenze sull'intervallo e la persona specificati. */ @@ -206,7 +204,7 @@ public List servicesByPersonAndDate(Person person, /** * Ritorna la richiesta informativa con id passato come parametro. * - * @param id l'identificativo della richiesta + * @param id l'identificativo della richiesta * @return La richiesta con l'id passato come parametro. */ public InformationRequest getById(Long id) { @@ -262,12 +260,11 @@ public Optional getTeleworkById(Long id) { } /** - * La lista delle richieste di malattia appartenenti alla lista di id passati - * come parametro. + * La lista delle richieste di malattia appartenenti alla lista di id passati come parametro. * * @param ids la lista di id di richieste di malattia - * @return la lista delle richieste di malattia appartenenti alla lista di id - * passati come parametro. + * @return la lista delle richieste di malattia appartenenti alla lista di id passati come + * parametro. */ public List illnessByIds(List ids) { final QIllnessRequest illnessRequest = QIllnessRequest.illnessRequest; @@ -276,12 +273,12 @@ public List illnessByIds(List ids) { } /** - * La lista delle richieste di uscita di servizio appartenenti alla lista - * di id passati come parametro. + * La lista delle richieste di uscita di servizio appartenenti alla lista di id passati come + * parametro. * * @param ids la lista di id di richieste di uscita di servizio - * @return la lista delle richieste di uscita di servizio appartenenti - * alla lista di id passati come parametro. + * @return la lista delle richieste di uscita di servizio appartenenti alla lista di id passati + * come parametro. */ public List servicesByIds(List ids) { final QServiceRequest serviceRequest = QServiceRequest.serviceRequest; @@ -290,12 +287,11 @@ public List servicesByIds(List ids) { } /** - * La lista delle richieste di telelavoro appartenenti alla lista di id passati - * come parametro. + * La lista delle richieste di telelavoro appartenenti alla lista di id passati come parametro. * * @param ids la lista di id di richieste di telelavoro - * @return la lista delle richieste di telelavoro appartenenti alla lista di id - * passati come parametro. + * @return la lista delle richieste di telelavoro appartenenti alla lista di id passati come + * parametro. */ public List teleworksByIds(List ids) { final QTeleworkRequest teleworkRequest = QTeleworkRequest.teleworkRequest; @@ -307,8 +303,8 @@ public List teleworksByIds(List ids) { * La lista di tutte le richieste di telelavoro effettuate dalla persona passata come parametro. * * @param person la persona di cui ricercare le richieste di telelavoro - * @return la lista di tutte le richieste di telelavoro effettuate dalla persona passata - * come parametro. + * @return la lista di tutte le richieste di telelavoro effettuate dalla persona passata come + * parametro. */ public List personTeleworkList(Person person) { final QTeleworkRequest teleworkRequest = QTeleworkRequest.teleworkRequest; @@ -316,21 +312,43 @@ public List personTeleworkList(Person person) { .orderBy(teleworkRequest.year.desc(), teleworkRequest.month.desc()).fetch(); } + /** + * La richiesta di telelavoro effettuate dalla persona del mese ed anno passati come parametro. + * + * @param person la persona di cui ricercare le richieste di telelavoro + * @param month il mese della richiesta di telelavoro + * @param year l'anno della richieste di telelavoro + * @return la richiesta di telelavoro effettuate dalla persona del mese ed anno passati come + * parametro. + */ + public Optional personTeleworkInPeriod(Person person, Integer month, + Integer year) { + final QTeleworkRequest teleworkRequest = QTeleworkRequest.teleworkRequest; + + final BooleanBuilder conditions = new BooleanBuilder(); + conditions.and(teleworkRequest.person.eq(person)) + .and(teleworkRequest.month.eq(month) + .and(teleworkRequest.year.eq(year))); + + final TeleworkRequest result = getQueryFactory().selectFrom(teleworkRequest) + .where(conditions).fetchFirst(); + return Optional.fromNullable(result); + } /** * Ritorna la lista delle informationRequest da approvare come responsabile di sede. * - * @param uros la lista degli userRoleOffice + * @param uros la lista degli userRoleOffice * @param informationType il tipo di richiesta - * @param signer il firmatario della richiesta - * @param conditions le condizioni da applicare alla ricerca + * @param signer il firmatario della richiesta + * @param conditions le condizioni da applicare alla ricerca * @return Lista delle InformationRequest da Approvare come responsabile di sede. */ private List toApproveResultsAsSeatSuperVisor(List uros, InformationType informationType, Person signer, BooleanBuilder conditions) { final QInformationRequest informationRequest = QInformationRequest.informationRequest; - if (uros.stream().anyMatch(uro -> uro.role.name.equals(Role.SEAT_SUPERVISOR) + if (uros.stream().anyMatch(uro -> uro.role.name.equals(Role.SEAT_SUPERVISOR) || uro.role.name.equals(Role.PERSONNEL_ADMIN))) { List officeList = uros.stream().map(u -> u.office).collect(Collectors.toList()); conditions = seatSupervisorQuery(officeList, conditions, signer); @@ -361,12 +379,11 @@ private List toApproveResultsAsPersonnelAdmin(List officeList, + private BooleanBuilder seatSupervisorQuery(List officeList, BooleanBuilder condition, Person signer) { final QInformationRequest informationRequest = QInformationRequest.informationRequest; @@ -381,12 +398,11 @@ private BooleanBuilder seatSupervisorQuery(List officeList, * Ritorna le condizioni con l'aggiunta di quelle relative al responsabile di sede. * * @param officeList la lista delle sedi - * @param condition le condizioni pregresse - * @param signer colui che deve firmare la richiesta + * @param condition le condizioni pregresse + * @param signer colui che deve firmare la richiesta * @return le condizioni per determinare se il responsabile di sede è coinvolto nell'approvazione. - * */ - private BooleanBuilder personnelAdminQuery(List officeList, + private BooleanBuilder personnelAdminQuery(List officeList, BooleanBuilder condition, Person signer) { final QInformationRequest informationRequest = QInformationRequest.informationRequest; @@ -400,9 +416,9 @@ private BooleanBuilder personnelAdminQuery(List officeList, /** * Metodo che ritorna la lista delle richieste già approvate per ruolo data e tipo. * - * @param uros la lista degli users_roles_offices - * @param fromDate la data da cui cercare le richieste - * @param toDate la data fino a cui cercare le richieste (opzionale) + * @param uros la lista degli users_roles_offices + * @param fromDate la data da cui cercare le richieste + * @param toDate la data fino a cui cercare le richieste (opzionale) * @param informationType il tipo della richiesta. * @return la lista delle richieste totalmente approvate. */ @@ -420,7 +436,7 @@ public List totallyApproved(List uros, conditions.and(informationRequest.startAt.after(fromDate)) .and(informationRequest.informationType.eq(informationType) .and(informationRequest.flowEnded.isTrue()) - .and(informationRequest.person.office.in(officeList))); + .and(informationRequest.person.office.in(officeList))); if (toDate.isPresent()) { conditions.and(informationRequest.endTo.before(toDate.get())); @@ -450,27 +466,27 @@ private List totallyApprovedOnRole(List u conditions.and(informationRequest.startAt.after(fromDate)) .and(informationRequest.informationType.eq(informationType) .and(informationRequest.flowEnded.isTrue()) - .and(informationRequest.person.office.eq(signer.office))); + .and(informationRequest.person.office.eq(signer.office))); if (toDate.isPresent()) { conditions.and(informationRequest.endTo.before(toDate.get())); } if (uros.stream().anyMatch(uro -> uro.role.name.equals(Role.SEAT_SUPERVISOR))) { conditions.and( - informationRequest.officeHeadApprovalRequired.isTrue() - .and(informationRequest.officeHeadApproved.isNotNull()) - .and(person.office.in(officeList))); + informationRequest.officeHeadApprovalRequired.isTrue() + .and(informationRequest.officeHeadApproved.isNotNull()) + .and(person.office.in(officeList))); } else { conditions.and( - informationRequest.administrativeApprovalRequired.isTrue() - .and(informationRequest.administrativeApproved.isNotNull()) - .and(person.office.in(officeList))); + informationRequest.administrativeApprovalRequired.isTrue() + .and(informationRequest.administrativeApproved.isNotNull()) + .and(person.office.in(officeList))); } return getQueryFactory().selectFrom(informationRequest) .join(informationRequest.person, person) .join(person.office, office) .where(office.in(uros.stream().map( - userRoleOffices -> userRoleOffices.office) + userRoleOffices -> userRoleOffices.office) .collect(Collectors.toSet())).and(conditions)) .orderBy(informationRequest.startAt.desc()) .fetch(); diff --git a/app/dao/UsersRolesOfficesDao.java b/app/dao/UsersRolesOfficesDao.java index 7819f15e4..da5ddefd9 100755 --- a/app/dao/UsersRolesOfficesDao.java +++ b/app/dao/UsersRolesOfficesDao.java @@ -101,6 +101,22 @@ public List getUsersRolesOfficesByUser(User user) { return getQueryFactory().selectFrom(uro).where(uro.user.eq(user)).fetch(); } + /** + * La lista di tutti gli userRoleOffice legati all'utente passato. + * + * @param user l'utente + * @return la lista di tutti gli usersRolesOffices associati al parametro passato. + */ + public List getAdministrativeUsersRolesOfficesByUser(User user) { + final QUsersRolesOffices uro = QUsersRolesOffices.usersRolesOffices; + return getQueryFactory().selectFrom(uro).where(uro.user.eq(user), uro.role.name.eq(Role.ABSENCE_MANAGER) + .or(uro.role.name.eq(Role.GROUP_MANAGER)) + .or(uro.role.name.eq(Role.PERSONNEL_ADMIN)) + .or(uro.role.name.eq(Role.PERSONNEL_ADMIN_MINI)) + .or(uro.role.name.eq(Role.SEAT_SUPERVISOR))).fetch(); + } + + /** * Metodo per effettuare check dello stato ruoli epas <-> perseo. */ diff --git a/app/helpers/TemplateUtility.java b/app/helpers/TemplateUtility.java index 9a56f23c1..37d7d42ce 100644 --- a/app/helpers/TemplateUtility.java +++ b/app/helpers/TemplateUtility.java @@ -241,7 +241,7 @@ public final int compensatoryRestRequests() { List roleList = uroDao.getUsersRolesOfficesByUser(user); List groups = groupDao.groupsByOffice(user.person.office, Optional.absent(), Optional.of(false)); - List results = absenceRequestDao + Set results = absenceRequestDao .toApproveResults(roleList, Optional.absent(), Optional.absent(), AbsenceRequestType.COMPENSATORY_REST, groups, user.person); return results.size(); @@ -261,7 +261,7 @@ public final int vacationRequests() { List roleList = uroDao.getUsersRolesOfficesByUser(user); List groups = groupDao.groupsByOffice(user.person.office, Optional.absent(), Optional.of(false)); - List results = absenceRequestDao + Set results = absenceRequestDao .toApproveResults(roleList, Optional.absent(), Optional.absent(), AbsenceRequestType.VACATION_REQUEST, groups, user.person); @@ -282,7 +282,7 @@ public final int personalPermissionRequests() { List roleList = uroDao.getUsersRolesOfficesByUser(user); List groups = groupDao.groupsByOffice(user.person.office, Optional.absent(), Optional.of(false)); - List results = absenceRequestDao + Set results = absenceRequestDao .toApproveResults(roleList, Optional.absent(), Optional.absent(), AbsenceRequestType.PERSONAL_PERMISSION, groups, user.person); @@ -303,7 +303,7 @@ public final int vacationPastYearAfterDeadlineRequests() { List roleList = uroDao.getUsersRolesOfficesByUser(user); List groups = groupDao.groupsByOffice(user.person.office, Optional.absent(), Optional.of(false)); - List results = absenceRequestDao + Set results = absenceRequestDao .toApproveResults(roleList, Optional.absent(), Optional.absent(), AbsenceRequestType.VACATION_PAST_YEAR_AFTER_DEADLINE_REQUEST, groups, user.person); diff --git a/app/manager/MissionManager.java b/app/manager/MissionManager.java index ffc4d0efa..a0d9636b9 100644 --- a/app/manager/MissionManager.java +++ b/app/manager/MissionManager.java @@ -143,7 +143,8 @@ public Optional linkToPerson(MissionFromClient mission) { public boolean createMissionFromClient(MissionFromClient body, boolean recompute) { String missionCacheKey = "createMission." + body.id; - + boolean managedMissionOk = true; + if (Cache.get(missionCacheKey) == null) { log.debug(LOG_PREFIX + "Imposto la cache {} con valore true", missionCacheKey); Cache.set(missionCacheKey, true, "1mn"); @@ -220,13 +221,15 @@ public boolean createMissionFromClient(MissionFromClient body, boolean recompute while (!actualDate.toLocalDate().isAfter(body.dataFine.toLocalDate())) { situation = getSituation(actualDate, body, workInterval); - atomicInsert(situation, body, actualDate); + if (!atomicInsert(situation, body, actualDate)) { + managedMissionOk = false; + }; actualDate = actualDate.plusDays(1); } recalculate(body, Optional.>absent()); Cache.delete(missionCacheKey); - return true; + return managedMissionOk; } /** @@ -318,6 +321,7 @@ private WorkingTimeTypeDay getFromDayOfMission(Person person, LocalDate actualDa * @return true se la gestione della missione è andata a buon fine, false altrimenti. */ public boolean manageMissionFromClient(MissionFromClient body, boolean recompute) { + boolean managedMissionOk = true; if (body.idOrdine == null) { return false; } @@ -369,7 +373,9 @@ public boolean manageMissionFromClient(MissionFromClient body, boolean recompute return false; } situation = getSituation(dateToConsider, body, workInterval); - atomicInsert(situation, body, dateToConsider); + if (!atomicInsert(situation, body, dateToConsider)) { + managedMissionOk = false; + }; } /* @@ -402,8 +408,8 @@ public boolean manageMissionFromClient(MissionFromClient body, boolean recompute //consistencyManager.updatePersonSituation(body.person.id, body.dataInizio.toLocalDate()); recalculate(body, Optional.fromNullable(missions)); - log.debug("Lanciati i ricalcoli"); - return true; + log.debug("Lanciati i ricalcoli per {} dal {}", body.person, body.dataInizio); + return managedMissionOk; } /** @@ -512,10 +518,10 @@ private boolean insertMission(String destination, Person person, person.getFullname(), numero, from, to, hours, minutes); return false; } - + log.debug(LOG_PREFIX + "Sto per inserire una missione per {}. Codice {}, {} - {}, " + "tempo {}:{}", person, mission.code, from, to, hours, minutes); - + Integer localHours = hours; Integer localMinutes = minutes; @@ -523,7 +529,9 @@ private boolean insertMission(String destination, Person person, absenceService.insert(person, group, from.toLocalDate(), to.toLocalDate(), mission, type, localHours, localMinutes, false, absenceManager); - if (insertReport.criticalErrors.isEmpty() || insertReport.warningsPreviousVersion.isEmpty()) { + log.debug("Insert Report = {}", insertReport); + if (insertReport.criticalErrors.isEmpty() && insertReport.warningsPreviousVersion.isEmpty() + && !insertReport.absencesToPersist.isEmpty()) { for (Absence absence : insertReport.absencesToPersist) { PersonDay personDay = personDayManager .getOrCreateAndPersistPersonDay(person, absence.getAbsenceDate()); @@ -537,9 +545,9 @@ private boolean insertMission(String destination, Person person, absence.note = "Missione: " + numero + '\n' + "Anno: " + anno + '\n' + "(Identificativo: " + absence.externalIdentifier + ")"; - + absence.save(); - + final Optional currentUser = Security.getUser(); if (currentUser.isPresent()) { notificationManager.notificationAbsencePolicy(currentUser.get(), @@ -559,6 +567,10 @@ private boolean insertMission(String destination, Person person, } JPA.em().flush(); return true; + } else { + log.info("Missione id={} di {}, insert Report con problemi di inserimento o " + + "nessuna assenza da inserire = {}", + id, person.getFullname(), insertReport); } return false; @@ -662,7 +674,7 @@ private LocalDateTime rightDate(LocalDate date, MissionFromClient body) { * @param body l'oggetto dto proveniente dal mission manager * @param actualDate la data attuale su cui lavorare */ - private void atomicInsert(Situation situation, MissionFromClient body, LocalDateTime actualDate) { + private boolean atomicInsert(Situation situation, MissionFromClient body, LocalDateTime actualDate) { boolean missionInserted = false; if (situation.isFirstOrLastDay) { @@ -701,6 +713,7 @@ > getFromDayOfMission(body.person, actualDate.toLocalDate()).workingTime) { + "idOrdine = {}, anno = {}, numero = {}", body.destinazioneMissione, actualDate, body.id, body.idOrdine, body.anno, body.numero); } + return missionInserted; } /** diff --git a/app/manager/NotificationManager.java b/app/manager/NotificationManager.java index 5bf11bb98..651b879c5 100644 --- a/app/manager/NotificationManager.java +++ b/app/manager/NotificationManager.java @@ -31,8 +31,8 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import lombok.extern.slf4j.Slf4j; import lombok.val; +import lombok.extern.slf4j.Slf4j; import manager.configurations.ConfigurationManager; import manager.configurations.EpasParam; import models.Competence; @@ -55,6 +55,7 @@ import models.enumerate.AccountRole; import models.enumerate.InformationType; import models.enumerate.NotificationSubject; +import models.exports.MissionFromClient; import models.flows.AbsenceRequest; import models.flows.CompetenceRequest; import models.flows.Group; @@ -1762,4 +1763,51 @@ private void sendEmailInformationRequestConfirmation(InformationRequest informat informationRequest, person.email, simpleEmail.getSubject(), mailBody); } + + /** + * Nel caso ci siano stati problemi nella gestione dei giorni + * @param mission + */ + public void sendEmailMissionFromClientProblems(MissionFromClient mission) { + Verify.verifyNotNull(mission.person); + final Person person = mission.person; + SimpleEmail simpleEmail = new SimpleEmail(); + String replayTo = (String) configurationManager + .configValue(person.office, EpasParam.EMAIL_TO_CONTACT); + try { + simpleEmail.addTo(person.email); + simpleEmail.addReplyTo(replayTo); + simpleEmail.addCc(replayTo); + } catch (EmailException e) { + log.error("Errore nell'invio dell'email per missione con problemi", e); + e.printStackTrace(); + } + + simpleEmail.setSubject( + String.format("ePas: verificare missione n. %s del %s di %s", + mission.numero, mission.anno, mission.person.getFullname())); + + final StringBuilder message = new StringBuilder() + .append(String.format("Gentile %s,\r\n", person.getName())); + message.append("ePAS ha ricevuto un messaggio dall'applicativo Missioni di tipo ") + .append(mission.tipoMissione).append(" missione.\r\n"); + message.append( String.format("La missione è la numero %s del %s dal %s al %s.\r\n\r\n", + mission.numero, mission.anno, TemplateExtensions.format(mission.dataInizio), + TemplateExtensions.format(mission.dataFine))); + message.append("Non è stato possibile inserire, modificare o cancellare tutti ") + .append("i giorni di missione previsti.\r\n\r\n"); + message.append("Si prega di verificare i dati della missione su ePAS con il proprio") + .append(" ufficio del personale."); + val mailBody = message.toString(); + try { + simpleEmail.setMsg(mailBody); + } catch (EmailException e) { + e.printStackTrace(); + } + Mail.send(simpleEmail); + log.info("Inviata email per problemi sulla missione n. {} del {} di {} dal {} al {}", + mission.numero, mission.anno, mission.person.getFullname(), + TemplateExtensions.format(mission.dataInizio), + TemplateExtensions.format(mission.dataFine)); + } } diff --git a/app/manager/ReperibilityManager2.java b/app/manager/ReperibilityManager2.java index 59240fc6e..ef6543fb6 100644 --- a/app/manager/ReperibilityManager2.java +++ b/app/manager/ReperibilityManager2.java @@ -35,6 +35,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; +import manager.configurations.ConfigurationManager; +import manager.configurations.EpasParam; import models.Competence; import models.CompetenceCode; import models.Person; @@ -66,6 +68,7 @@ public class ReperibilityManager2 { private final PersonDayManager personDayManager; private final CompetenceDao competenceDao; private final PersonReperibilityDayDao reperibilityDao; + private final ConfigurationManager configurationManager; /** * Injection. @@ -80,12 +83,13 @@ public class ReperibilityManager2 { public ReperibilityManager2(PersonReperibilityDayDao reperibilityDayDao, PersonDayDao personDayDao, PersonDayManager personDayManager, CompetenceDao competenceDao, - PersonReperibilityDayDao reperibilityDao) { + PersonReperibilityDayDao reperibilityDao, ConfigurationManager configurationManager) { this.reperibilityDayDao = reperibilityDayDao; this.personDayDao = personDayDao; this.personDayManager = personDayManager; this.competenceDao = competenceDao; this.reperibilityDao = reperibilityDao; + this.configurationManager = configurationManager; } /** @@ -310,10 +314,15 @@ public Map calculateReperibilityWorkDaysCompetences( final LocalDate lastDay; - if (to.isAfter(today)) { - lastDay = today; - } else { + if ((Boolean) configurationManager.configValue(reperibility.office, + EpasParam.ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH)) { lastDay = to; + } else { + if (to.isAfter(today)) { + lastDay = today; + } else { + lastDay = to; + } } CompetenceCode code = reperibility.monthlyCompetenceType.workdaysCode; involvedReperibilityWorkers(reperibility, from, to).forEach(person -> { @@ -390,10 +399,15 @@ public Map calculateReperibilityHolidaysCompetences( final LocalDate lastDay; - if (end.isAfter(today)) { - lastDay = today; - } else { + if ((Boolean) configurationManager.configValue(reperibility.office, + EpasParam.ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH)) { lastDay = end; + } else { + if (end.isAfter(today)) { + lastDay = today; + } else { + lastDay = end; + } } CompetenceCode code = reperibility.monthlyCompetenceType.holidaysCode; involvedReperibilityWorkers(reperibility, start, end).forEach(person -> { @@ -426,12 +440,19 @@ public void assignReperibilityCompetences(ReperibilityTypeMonth reperibilityType final LocalDate today = LocalDate.now(); final LocalDate lastDay; - - if (monthEnd.isAfter(today)) { - lastDay = today; - } else { + + if ((Boolean) configurationManager.configValue(reperibilityTypeMonth + .personReperibilityType.office, + EpasParam.ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH)) { lastDay = monthEnd; + } else { + if (monthEnd.isAfter(today)) { + lastDay = today; + } else { + lastDay = monthEnd; + } } + //cerco le persone reperibili nel periodo di interesse final List involvedReperibilityPeople = involvedReperibilityWorkers( reperibilityTypeMonth.personReperibilityType, monthBegin, monthEnd); diff --git a/app/manager/ShiftManager2.java b/app/manager/ShiftManager2.java index 4be04311f..c6e1586e8 100644 --- a/app/manager/ShiftManager2.java +++ b/app/manager/ShiftManager2.java @@ -767,8 +767,14 @@ public int calculatePersonShiftCompetencesInPeriod(ShiftType activity, Person pe break; case holiday: - timeInterval = Optional.fromNullable(daily); - timeInterval2 = Optional.absent(); + if (setting.holidayShiftInNightToo) { + timeInterval = Optional.of(new TimeInterval(new LocalTime(0, 0), new LocalTime(23, 59))); + timeInterval2 = Optional.absent(); + } else { + timeInterval = Optional.fromNullable(daily); + timeInterval2 = Optional.absent(); + } + list = shifts.stream().filter(day -> { return personDayManager.isHoliday(day.personShift.person, day.date, setting.saturdayHolidayShift); diff --git a/app/manager/attestati/dto/show/SeatCertification.java b/app/manager/attestati/dto/show/SeatCertification.java index 8edfed0eb..8229d7164 100644 --- a/app/manager/attestati/dto/show/SeatCertification.java +++ b/app/manager/attestati/dto/show/SeatCertification.java @@ -50,6 +50,7 @@ public static class PersonCertification { public String matricola; public boolean validato; public int numBuoniPasto; + public int numBuoniPastoElettronici; public List righeAssenza = Lists.newArrayList(); public List righeCompetenza = Lists.newArrayList(); //?? public List righeFormazione = Lists.newArrayList(); @@ -60,6 +61,7 @@ public String toString() { .add("matricola", matricola) .add("validato", validato) .add("numBuoniPasto", numBuoniPasto) + .add("numBuoniPastoElettronici", numBuoniPastoElettronici) .add("righeAssenza", righeAssenza) .toString(); } diff --git a/app/manager/attestati/service/CertificationService.java b/app/manager/attestati/service/CertificationService.java index b57f9859e..904233a1b 100644 --- a/app/manager/attestati/service/CertificationService.java +++ b/app/manager/attestati/service/CertificationService.java @@ -151,8 +151,8 @@ private Map personAttestatiCertifications(Person person, certification.year = year; certification.month = month; certification.certificationType = CertificationType.MEAL; - certification.content = personCertification.numBuoniPasto + ""; - + certification.content = String.format("%s;%s", + personCertification.numBuoniPasto, personCertification.numBuoniPastoElettronici); certifications.put(certification.aMapKey(), certification); return certifications; diff --git a/app/manager/configurations/ConfigurationManager.java b/app/manager/configurations/ConfigurationManager.java index 7c749d94c..97cabe046 100644 --- a/app/manager/configurations/ConfigurationManager.java +++ b/app/manager/configurations/ConfigurationManager.java @@ -48,11 +48,9 @@ import models.base.IPropertyInPeriod; import models.enumerate.BlockType; import models.query.QConfiguration; -import models.query.QPersonConfiguration; import org.joda.time.LocalDate; import org.joda.time.LocalTime; import org.joda.time.MonthDay; -import play.db.jpa.JPAPlugin; import play.jobs.Job; @@ -88,25 +86,8 @@ public class ConfigurationManager { public List configurationWithType(EpasParam epasParam) { final QConfiguration configuration = QConfiguration.configuration; - final JPQLQuery query = queryFactory.from(configuration) + final JPQLQuery query = queryFactory.selectFrom(configuration) .where(configuration.epasParam.eq(epasParam)); - return (List) query.fetch(); - } - - /** - * La lista delle configurazioni delle persone con parametro epasParam e valore value. - * - * @param epasParam il parametro da cercare - * @param value il valore da cercare - * @return la lista di configurazioni della persona. - */ - public List configurationWithTypeAndValue( - EpasParam epasParam, String value) { - final QPersonConfiguration configuration = QPersonConfiguration.personConfiguration; - - final JPQLQuery query = queryFactory.selectFrom(configuration) - .where(configuration.epasParam.eq(epasParam) - .and(configuration.fieldValue.eq(value))); return query.fetch(); } diff --git a/app/manager/configurations/EpasParam.java b/app/manager/configurations/EpasParam.java index e5e0d4b6f..ac9fd1e99 100644 --- a/app/manager/configurations/EpasParam.java +++ b/app/manager/configurations/EpasParam.java @@ -68,6 +68,14 @@ public enum EpasParam { EpasParamValueType.formatValue(true), Lists.newArrayList(), Office.class), + + ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH("enable_reperibility_approval_before_end_month", + EpasParamCategory.GENERAL, + EpasParamTimeType.GENERAL, + EpasParamValueType.BOOLEAN, + EpasParamValueType.formatValue(false), + Lists.newArrayList(), + Office.class), ENABLE_MISSIONS_INTEGRATION("enable_mission_integration", EpasParamCategory.GENERAL, diff --git a/app/manager/services/absences/AbsenceService.java b/app/manager/services/absences/AbsenceService.java index 0c1b78bc3..e8e1f1635 100644 --- a/app/manager/services/absences/AbsenceService.java +++ b/app/manager/services/absences/AbsenceService.java @@ -772,7 +772,13 @@ private InsertReport insertReportFromOldReport(AbsenceInsertReport absenceInsert templateRow.absenceErrors .add(AbsenceError.builder().absence(absenceResponse.getAbsenceAdded()) .absenceProblem(AbsenceProblem.NotOnHoliday).build()); - } else { + } + if (!absenceResponse.getWarning().isEmpty()) { + templateRow.absenceErrors.add(AbsenceError.builder().absence(absenceResponse.getAbsenceAdded()) + .absenceProblem(AbsenceProblem.MinimumTimeViolated).build()); + } + + else { templateRow.absenceErrors .add(AbsenceError.builder().absence(absenceResponse.getAbsenceAdded()) .absenceProblem(AbsenceProblem.LimitExceeded).build()); diff --git a/app/manager/services/absences/model/AbsencePeriod.java b/app/manager/services/absences/model/AbsencePeriod.java index d36e83b70..d91674a52 100644 --- a/app/manager/services/absences/model/AbsencePeriod.java +++ b/app/manager/services/absences/model/AbsencePeriod.java @@ -43,6 +43,7 @@ import models.absences.TakableAbsenceBehaviour.TakeCountBehaviour; import models.absences.definitions.DefaultAbsenceType; import models.enumerate.VacationCode; +import org.joda.time.DateTimeConstants; import org.joda.time.LocalDate; import org.testng.collections.Lists; @@ -213,6 +214,15 @@ public int computePeriodTakableAmount(TakeCountBehaviour countBehaviour, LocalDa if (countBehaviour.equals(TakeCountBehaviour.period)) { if (this.takableCodes.contains(absenceTypeDao .getAbsenceTypeByCode(DefaultAbsenceType.A_LAGILE.getCode()).get())) { + /* Caso di febbraio: secondo la nota del DG i giorni di lavoro agile a febbraio + * non possono essere più di 8. Quindi sottraggo 2 giorni nella modalità + * prevista dall'algoritmo (2 * 100) al quantitativo di giorni previsto per il + * gruppo del codice LAGILE (this.fixexPeriodTakableAmount). + */ + + if (from.monthOfYear().get() == DateTimeConstants.FEBRUARY) { + return this.fixedPeriodTakableAmount - 2 * 100; + } List workingDays = personDayManager.workingDaysInMonth(person, from, to); int count = (workingDays.size() * 100 / 2); if (count % 100 != 0) { diff --git a/app/models/GeneralSetting.java b/app/models/GeneralSetting.java index edc85e42c..a269a12ca 100644 --- a/app/models/GeneralSetting.java +++ b/app/models/GeneralSetting.java @@ -79,6 +79,8 @@ public class GeneralSetting extends BaseModel { public boolean roundingShiftQuantity = false; public boolean enableUniqueDailyShift = true; + + public boolean holidayShiftInNightToo = false; // Fine parametri gestione giorni di turno diff --git a/app/models/absences/definitions/DefaultAbsenceType.java b/app/models/absences/definitions/DefaultAbsenceType.java index 41e7b31d5..4c3f64d1f 100644 --- a/app/models/absences/definitions/DefaultAbsenceType.java +++ b/app/models/absences/definitions/DefaultAbsenceType.java @@ -51,7 +51,7 @@ public enum DefaultAbsenceType { A_LAGILE("L-AGILE", "Lavoro agile", false, - ImmutableSet.of(JustifiedTypeName.assign_all_day), 0, false, + ImmutableSet.of(JustifiedTypeName.complete_day_and_add_overtime), 0, false, MealTicketBehaviour.notAllowMealTicket, 0, null, Sets.newHashSet(), new LocalDate(2022, 3, 29), null, true, false, true), @@ -59,7 +59,7 @@ public enum DefaultAbsenceType { "Lavoro agile con maturazione buono pasto", false, ImmutableSet.of(JustifiedTypeName.assign_all_day), 0, false, MealTicketBehaviour.allowMealTicket, 0, null, Sets.newHashSet(), - new LocalDate(2022, 3, 29), null, true, false, true), + new LocalDate(2022, 3, 29), new LocalDate(2022, 6, 28), true, false, true), A_COVID19("COVID19", "Emergenza coronavirus, attività lavorativa presso il domicilio dei dipendenti", false, @@ -83,7 +83,7 @@ public enum DefaultAbsenceType { A_39LA("39LA", "Lavoro agile per dipendenti fragili o per assistenza a disabile/immunodepresso", false, ImmutableSet.of(JustifiedTypeName.assign_all_day), 0, false, - MealTicketBehaviour.allowMealTicket, 0, null, Sets.newHashSet(), + MealTicketBehaviour.notAllowMealTicket, 0, null, Sets.newHashSet(), null, null, true, false, true), A_39LANOBP("39LA", @@ -91,13 +91,13 @@ public enum DefaultAbsenceType { + "senza buono pasto", false, ImmutableSet.of(JustifiedTypeName.assign_all_day), 0, false, MealTicketBehaviour.notAllowMealTicket, 0, null, Sets.newHashSet(), null, - null, true, false, true), + new LocalDate(2022, 7, 1), true, false, true), A_40LA("40LA", "Lavoro agile per quarantena/isolamento fiduciario", false, ImmutableSet.of(JustifiedTypeName.assign_all_day), 0, false, MealTicketBehaviour.allowMealTicket, 0, null, Sets.newHashSet(), null, - null, true, false, true), + new LocalDate(2022, 7, 1), true, false, true), A_18M("18M", "Permesso assistenza parenti/affini disabili L. 104/92 in ore e minuti", true, ImmutableSet.of(JustifiedTypeName.specified_minutes), 0, false, diff --git a/app/models/absences/definitions/DefaultTakable.java b/app/models/absences/definitions/DefaultTakable.java index e8429da9a..d3e64fb56 100644 --- a/app/models/absences/definitions/DefaultTakable.java +++ b/app/models/absences/definitions/DefaultTakable.java @@ -122,13 +122,13 @@ public enum DefaultTakable { -1, null), T_LAGILE(AmountType.units, - ImmutableSet.of(DefaultAbsenceType.A_LAGILE, DefaultAbsenceType.A_LAGILEBP), - ImmutableSet.of(DefaultAbsenceType.A_LAGILE, DefaultAbsenceType.A_LAGILEBP), + ImmutableSet.of(DefaultAbsenceType.A_LAGILE), + ImmutableSet.of(DefaultAbsenceType.A_LAGILE), 10, null), T_39LA(AmountType.units, - ImmutableSet.of(DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_39LANOBP), - ImmutableSet.of(DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_39LANOBP), + ImmutableSet.of(DefaultAbsenceType.A_39LA), + ImmutableSet.of(DefaultAbsenceType.A_39LA), -1, null), T_SMART(AmountType.units, @@ -494,7 +494,7 @@ public enum DefaultTakable { DefaultAbsenceType.A_401, DefaultAbsenceType.A_412, DefaultAbsenceType.A_402, DefaultAbsenceType.A_62, DefaultAbsenceType.A_62A, DefaultAbsenceType.A_62D, DefaultAbsenceType.A_98CV, - DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_40LA, DefaultAbsenceType.A_46, + DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_46, DefaultAbsenceType.A_46RA, DefaultAbsenceType.A_VAC19), ImmutableSet.of(DefaultAbsenceType.A_102, DefaultAbsenceType.A_103, DefaultAbsenceType.A_103BP, @@ -539,7 +539,7 @@ public enum DefaultTakable { DefaultAbsenceType.A_401, DefaultAbsenceType.A_412, DefaultAbsenceType.A_402, DefaultAbsenceType.A_62, DefaultAbsenceType.A_62A, DefaultAbsenceType.A_62D, DefaultAbsenceType.A_98CV, - DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_40LA, DefaultAbsenceType.A_46, + DefaultAbsenceType.A_39LA, DefaultAbsenceType.A_46, DefaultAbsenceType.A_46RA, DefaultAbsenceType.A_VAC19), -1, null), diff --git a/app/views/AbsenceGroups/_configureSelectedGroup.html b/app/views/AbsenceGroups/_configureSelectedGroup.html index 73116f971..19d7f33f7 100644 --- a/app/views/AbsenceGroups/_configureSelectedGroup.html +++ b/app/views/AbsenceGroups/_configureSelectedGroup.html @@ -52,7 +52,7 @@ #{f.selectEnum 'minutes', items:absenceForm.selectableMinutes(), value:absenceForm.minutes, byEquals:'true' /} #{/if} - #{if absenceForm.absenceTypeSelected || !absenceForm.hasAbsenceTypeChoice() } + #{if absenceForm.absenceTypeSelected || !absenceForm.hasAbsenceTypeChoice()} #{secure.check 'Certifications.certifications'} #{f.checkbox 'forceInsert', value:forceInsert /} #{if forceInsert} diff --git a/app/views/AbsenceRequests/_form.html b/app/views/AbsenceRequests/_form.html index adf9609b5..6cd814fe7 100644 --- a/app/views/AbsenceRequests/_form.html +++ b/app/views/AbsenceRequests/_form.html @@ -21,12 +21,13 @@ #{f.hidden 'absenceRequest.person.id' /} #{f.hidden 'absenceRequest.type' /} #{f.hidden 'persist', value:persist /} + #{f.hidden 'groupAbsenceType.id', value:absenceForm.groupSelected.id /} #{f.date 'absenceRequest.startAt', required:true, class:'auto-submit-parent', value:absenceRequest.startAt?.format("dd/MM/yyyy") /} #{f.date 'absenceRequest.endTo', class:'auto-submit-parent', value:absenceRequest.endTo?.format("dd/MM/yyyy") /} - + #{if absenceRequest.type.equals(models.flows.enumerate.AbsenceRequestType.PERSONAL_PERMISSION)} #{if absenceForm != null} diff --git a/app/views/AbsenceRequests/_report.html b/app/views/AbsenceRequests/_report.html index a4b572fa1..b4781eaa0 100644 --- a/app/views/AbsenceRequests/_report.html +++ b/app/views/AbsenceRequests/_report.html @@ -24,6 +24,7 @@ #{alert color:'danger'}

Elenco degli errori riscontrati


Si sta inserendo un'assenza in un giorno che già contiene un'assenza a giustificazione giornaliera + oppure non ci sono sufficienti ore a disposizione per fruire di un riposo compensativo #{/alert} #{/if} diff --git a/app/views/Administration/_generalSetting.html b/app/views/Administration/_generalSetting.html index 1a6fe58f6..f90ed3d70 100644 --- a/app/views/Administration/_generalSetting.html +++ b/app/views/Administration/_generalSetting.html @@ -144,7 +144,7 @@

&{actionSelected+'.table'}

#{/b.buttons} #{/form} #{/accordionItem} - #{accordionItem 'enableDailyPresenceForManagerParam', parent:'generalSetting', title:'Abilita visualizzazione presenza giornaliera gruppi', open:false} + #{accordionItem 'enableDailyPresenceForManagerParam', parent:'generalSetting', title:'Abilita visualizzazione presenza giornaliera gruppi', open:false} #{form action:@saveGeneralSetting(), method:'POST', class:'form form-horizontal'} #{f.hidden 'generalSetting.id' /} #{alert color:'info'}Abilita la visualizzazione della presenza giornaliera dei dipendenti appartenenti a un gruppo#{/alert} @@ -154,4 +154,14 @@

&{actionSelected+'.table'}

#{/b.buttons} #{/form} #{/accordionItem} + #{accordionItem 'holidayShiftInNightToo', parent:'generalSetting', title:'Abilita conteggio festivo per tutti gli orari in giorno festivo', open:false} + #{form action:@saveGeneralSetting(), method:'POST', class:'form form-horizontal'} + #{f.hidden 'generalSetting.id' /} + #{alert color:'info'}Abilita al conteggio di turno festivo qualsiasi turno svolto nelle 24 ore di un giorno festivo (anche se dovrebbe essere notturno)#{/alert} + #{f.booleanRadio 'generalSetting.holidayShiftInNightToo' /} + #{b.buttons} + #{b.save /} + #{/b.buttons} + #{/form} + #{/accordionItem} #{/accordionGroup} diff --git a/app/views/Configurations/_tutorial.html b/app/views/Configurations/_tutorial.html index a0b8a1714..53215b6bd 100644 --- a/app/views/Configurations/_tutorial.html +++ b/app/views/Configurations/_tutorial.html @@ -22,6 +22,14 @@

#{/if} + #{if configuration.epasParam.equals(manager.configurations.EpasParam.ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH) } +

Impostando a sì questo parametro sarà possibile, per i responsabili dei servizi di reperibilità, procedere all'approvazione del calendario dell'intero + mese di reperibilità anche se il mese non è ancora terminato. Ad esempio, se il calendario fosse già completamente schedulato per l'intero mese + e si tentasse l'approvazione prima della fine del mese (es.: giorno 25 del mese), con questo parametro a SI verrebbero conteggiati tutti i giorni del mese + nell'approvazione e non solo quelli fino al giorno in cui si sta approvando il calendario del servizio. +

+ #{/if} + #{if configuration.epasParam.equals(manager.configurations.EpasParam.ENABLE_MISSIONS_INTEGRATION) }

Abilitando questa funzionalità, ePAS comunicherà con la piattaforma Missioni ricevendo in automatico i giorni di missione dei dipendenti
e inserendoli automaticamente nel loro cartellino. diff --git a/app/views/Secure/_login_idp.html b/app/views/Secure/_login_idp.html index 54154af92..f5abb704d 100644 --- a/app/views/Secure/_login_idp.html +++ b/app/views/Secure/_login_idp.html @@ -43,7 +43,7 @@

- Utilizza il bottone sottostante per accedere con le credenziali ${companyCode} (SSO) + Utilizza il bottone sottostante per accedere con le credenziali ${companyCode}

diff --git a/app/views/Stampings/personStamping.html b/app/views/Stampings/personStamping.html index 90b59a570..621fbaf51 100755 --- a/app/views/Stampings/personStamping.html +++ b/app/views/Stampings/personStamping.html @@ -11,7 +11,7 @@
- + diff --git a/app/views/Stampings/personStampingComponents/_mealTicketsTemplate.html b/app/views/Stampings/personStampingComponents/_mealTicketsTemplate.html index 27a796359..572291123 100644 --- a/app/views/Stampings/personStampingComponents/_mealTicketsTemplate.html +++ b/app/views/Stampings/personStampingComponents/_mealTicketsTemplate.html @@ -13,16 +13,16 @@ #{if dayRecap.mealTicket} webui-popover-hover #{if dayRecap.mealTicket.equals("NO")} -data-content="&{'mealTicket.notAquired'}"> +data-content="&{'mealTicket.notAquired'}">no #{/if} #{if dayRecap.mealTicket.equals("NOT_YET")} -data-content="&{'mealTicket.notYetAquired'}"> +data-content="&{'mealTicket.notYetAquired'}">da maturare #{/if} #{if dayRecap.mealTicket.equals("YES")} -data-content="&{'mealTicket.aquired'}"> +data-content="&{'mealTicket.aquired'}">si #{/if} #{if dayRecap.mealTicket.equals("YES_IF_EXIT_NOW")} -data-content="&{'mealTicket.aquiredExitingNow'}"> +data-content="&{'mealTicket.aquiredExitingNow'}">si uscendo adesso #{/if} #{/if} #{else} diff --git a/app/views/_employeeMenu.html b/app/views/_employeeMenu.html index 526d80da8..d00e905e4 100644 --- a/app/views/_employeeMenu.html +++ b/app/views/_employeeMenu.html @@ -58,6 +58,11 @@ #{secure.li @TeleworkStampings.teleworkStampings(session.get("yearSelected"), session.get("monthSelected")) } #{/secure.li} + + #{secure.li @InformationRequests.teleworks()} + + #{/secure.li} + #{/if} #{navbar.li @Absences.absences(session.get("yearSelected"), session.get("monthSelected")) } diff --git a/app/views/tags/absences/absence-popover.html b/app/views/tags/absences/absence-popover.html index daf3562a7..c4096db90 100755 --- a/app/views/tags/absences/absence-popover.html +++ b/app/views/tags/absences/absence-popover.html @@ -26,6 +26,7 @@ #{else} ${_absence.absenceType.code} #{/else} + ${_absence.absenceType.description} #{/if}
diff --git a/conf/messages.it b/conf/messages.it index a5d11b34e..e0f0d036d 100755 --- a/conf/messages.it +++ b/conf/messages.it @@ -448,6 +448,7 @@ generalSetting.maxDaysInPastForRestStampings =Numero massimo di giorni nel passa generalSetting.enableAutoconfigCovid19=Abilita visione auto inserimento covid19 in configurazione generalSetting.enableAutoconfigSmartworking=Abilita visione auto inserimento smartworking in configurazione generalSetting.enableDailyPresenceForManager=Abilita visualizzazione presenza giornaliera dipendenti appartenenti a gruppo +generalSetting.holidayShiftInNightToo=Considera come festivi i turni notturni effettuati nei festivi group.label =Nome gruppo group.limitType =Periodo di utililzzo limite @@ -1179,6 +1180,7 @@ TR_VACATIONS= Inserimento ferie e permessi per Tecnologi e Ricercatori TR_COMPENSATORY= Inserimento riposi compensativi per Tecnologi e Ricercatori ENABLE_CALENDARSHIFT= Abilitazione gestione calendario turni ENABLE_CALENDAR_REPERIBILITY= Abilitazione gestione calendario reperibilità +ENABLE_REPERIBILITY_APPROVAL_BEFORE_END_MONTH=Abilta approvazione reperibilità prima della fine del mese ENABLE_MISSIONS_INTEGRATION= Abilitazione integrazione con piattaforma Missioni SEND_FLOWS_NOTIFICATION=Invio notifica per terminazione flusso di approvazione SEND_MANAGER_NOTIFICATION_FOR_661=Invio notifica terminazione flusso permesso personale a responsabile di gruppo diff --git a/conf/permissions.drl b/conf/permissions.drl index 2f1f6c02c..c2f8c20a3 100755 --- a/conf/permissions.drl +++ b/conf/permissions.drl @@ -887,17 +887,17 @@ end rule showAbsenceRequest when - UsersRolesOffices(role.name == Role.SEAT_SUPERVISOR || role.name == Role.PERSONNEL_ADMIN) - $ar: AbsenceRequest(person != currentOperator.person, person.getInstitute() == currentOperator.person.getInstitute()) + $ar: AbsenceRequest(person != currentOperator.person) + UsersRolesOffices(role.name == Role.SEAT_SUPERVISOR || role.name == Role.PERSONNEL_ADMIN, office == $ar.person.office) $c: PermissionCheck(action == "AbsenceRequests.show", target==$ar, granted == false) then $c.grant(); end rule showInformationRequest -when - UsersRolesOffices(role.name == Role.SEAT_SUPERVISOR || role.name == Role.PERSONNEL_ADMIN) - $ir: InformationRequest(person != currentOperator.person) +when + $ir: InformationRequest(person != currentOperator.person) + UsersRolesOffices(role.name == Role.SEAT_SUPERVISOR || role.name == Role.PERSONNEL_ADMIN, office == $ir.person.office) $c: PermissionCheck(action == "InformationRequests.show", target==$ir, granted == false) then $c.grant(); @@ -3667,6 +3667,20 @@ then $c.grant(); end +/* + * Permesso per l'abilitazione alla visualizzazione dei flussi delle assenze per + * i responsabili di sede + */ +rule isFlowsVisibleForSeatSupervisor +when + $uro: UsersRolesOffices(role.name == Role.SEAT_SUPERVISOR) + $o: Office() from $uro.office + Configuration(epasParam == EpasParam.ENABLE_FLOWS, fieldValue == true) from $o.configurations + $c: PermissionCheck(action == "AbsenceRequests.enabled", granted == false, target == null) +then + $c.grant(); +end + /* * Permesso per l'abilitazione alla visualizzazione delle richieste di ferie anno passato dopo il 31/8 */ diff --git a/db/evolutions/191.sql b/db/evolutions/191.sql new file mode 100644 index 000000000..64513615a --- /dev/null +++ b/db/evolutions/191.sql @@ -0,0 +1,9 @@ +# ---!Ups + +ALTER TABLE general_setting ADD COLUMN holiday_shift_in_night_too BOOLEAN DEFAULT FALSE; +ALTER TABLE general_setting_history ADD COLUMN holiday_shift_in_night_too BOOLEAN DEFAULT FALSE; + +# ---!Downs + +ALTER TABLE general_setting DROP COLUMN holiday_shift_in_night_too; +ALTER TABLE general_setting_history DROP COLUMN holiday_shift_in_night_too; \ No newline at end of file
Riepilogo mensile &{'Month.'+ psDto.month} ${psDto.year}
Giorno Buono
pasto