/** * Axelor Business Solutions * * Copyright (C) 2016 Axelor (<http://axelor.com>). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.axelor.apps.base.service.scheduler; import java.util.ArrayList; import org.joda.time.DateTimeConstants; import org.joda.time.LocalDate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.axelor.apps.base.db.IAdministration; import com.axelor.apps.base.db.Scheduler; import com.axelor.apps.base.db.SchedulerInstance; import com.axelor.apps.base.db.SchedulerInstanceHistory; import com.axelor.apps.base.db.repo.SchedulerInstanceRepository; import com.axelor.apps.base.exceptions.IExceptionMessage; import com.axelor.apps.base.service.administration.GeneralService; import com.axelor.exception.AxelorException; import com.axelor.exception.db.IException; import com.axelor.i18n.I18n; import com.google.inject.Inject; import com.google.inject.persist.Transactional; /** * SchedulerService est une classe implémentant l'ensemble des services des * planificateurs. * * @author P. Belloy * * @version 1.0 * */ public class SchedulerService { protected GeneralService generalService; private static final Logger LOG = LoggerFactory.getLogger(SchedulerService.class); private LocalDate today; @Inject private SchedulerInstanceRepository schedulerInstanceRepo; @Inject public SchedulerService(GeneralService generalService){ this.generalService = generalService; today = this.generalService.getTodayDate(); } /** * Méthode qui détermine si une instance de planificateur peut etre lancé * * @param schedulerI * Instance de planificateur * * @return boolean * True si il est temps de lancer, sinon False * * @throws AxelorException */ public boolean isSchedulerInstanceIsReady(SchedulerInstance schedulerI) throws AxelorException{ LocalDate firstExecution = schedulerI.getFirstExecutionDate(); LocalDate lastTheoreticalExecution = schedulerI.getLastTheoreticalExecutionDate(); //Si date de démarrage egal ou supérieur à date du jour if(lastTheoreticalExecution == null ){ LOG.debug("Date de dernière exécution absente"); if(firstExecution == null){ LOG.debug(String.format("Date de premiere executon absente")); } else if(firstExecution.equals(today) || firstExecution.isBefore(today)){ LOG.debug("Date de démarrage supérieur ou égal a date du jour"); return true; } else{ LOG.debug(String.format("La facturation ne peux pas encore être exécuté : %s < %s",today,firstExecution)); } } else{ //Sinon calcul le cycle return this.processScheduler(schedulerI); } return false; } /** * Méthode qui détermine si une instance de planificateur peut etre lancé * * @param schedulerI * Instance de planificateur * * @return boolean * True si il est temps de lancer, sinon False * * @throws AxelorException */ private boolean processScheduler(SchedulerInstance schedulerI) throws AxelorException{ LocalDate date = this.getComputeDate(schedulerI); LOG.debug("Date dernière éxécution théorique: "+schedulerI.getLastTheoreticalExecutionDate()); LOG.debug("Date calculé : "+date); LOG.debug("Date du jour : "+today); if(date != null && (date.equals(today) || date.isBefore(today))) return true; return false; } /** * Méthode qui calcule da date de prochaine éxécution d'une instance d planificateur * * @param schedulerI * Instance de planificateur * * @return LocalDate * Prochaine date d'éxécution * * @throws AxelorException */ public LocalDate getComputeDate(SchedulerInstance schedulerI) throws AxelorException{ return this.getComputeDate( schedulerI.getScheduler(), schedulerI.getLastTheoreticalExecutionDate()); } /** * Méthode qui calcule da date de prochaine éxécution d'une instance d planificateur * * @param schedulerI * Instance de planificateur * * @return LocalDate * Prochaine date d'éxécution * * @throws AxelorException */ public LocalDate getComputeDate(Scheduler scheduler, LocalDate date) throws AxelorException{ if(scheduler.getAnnually()) return this.getAnnualComputeDate(scheduler,date); else if(scheduler.getMonthly()) return this.getMonthlyComputeDate(scheduler,date); else if(scheduler.getWeekly()) return this.getWeeklyComputeDate(scheduler,date); else if(scheduler.getDaily()) return this.getDailyComputeDate(scheduler,date); else throw new AxelorException(String.format(I18n.get(IExceptionMessage.SCHEDULER_1),scheduler.getName()), IException.MISSING_FIELD); } /** * Méthode qui détermine la prochaine date d'éxécution d'un planificateur pour un rythme quotidien * * @param scheduler * Instance de planificateur * @param date * Derniere date d'éxécution théorique * * @return LocalDate * Prochaine date d'éxécution */ private LocalDate getDailyComputeDate(Scheduler scheduler,LocalDate date){ return date.plusDays(scheduler.getDayDaily()); } /** * Méthode qui détermine la prochaine date d'éxécution d'un planificateur pour un rythme hebdomadaire * * @param scheduler * Instance de planificateur * @param date * Derniere date d'éxécution théorique * * @return LocalDate * Prochaine date d'éxécution */ private LocalDate getWeeklyComputeDate(Scheduler scheduler,LocalDate date){ int weekDay = 0; if(scheduler.getMonday()) weekDay = DateTimeConstants.MONDAY; else if(scheduler.getTuesday()) weekDay = DateTimeConstants.TUESDAY; else if(scheduler.getWednesday()) weekDay = DateTimeConstants.WEDNESDAY; else if(scheduler.getThursday()) weekDay = DateTimeConstants.THURSDAY; else if(scheduler.getFriday()) weekDay = DateTimeConstants.FRIDAY; else if(scheduler.getSaturday()) weekDay = DateTimeConstants.SATURDAY; else if(scheduler.getSunday()) weekDay = DateTimeConstants.SUNDAY; if(weekDay == 0) weekDay = 1; return date.plusWeeks(scheduler.getWeekWeekly()).withDayOfWeek(weekDay); } /** * Méthode qui détermine la prochaine date d'éxécution d'un planificateur pour un rythme mensuel * * @param scheduler * Instance de planificateur * @param date * Derniere date d'éxécution théorique * * @return LocalDate * Prochaine date d'éxécution */ private LocalDate getMonthlyComputeDate(Scheduler scheduler,LocalDate date){ if(scheduler.getDayMonthly() == 0) return date.plusMonths(scheduler.getMonthMonthly()).withDayOfMonth(1); else { int start = date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().getMinimumValue(); int end = date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().getMaximumValue(); if(start <= scheduler.getDayMonthly() && scheduler.getDayMonthly() <= end) return date.plusMonths(scheduler.getMonthMonthly()).withDayOfMonth(scheduler.getDayMonthly()); else if(scheduler.getDayMonthly() < start) return date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().withMinimumValue(); else if(scheduler.getDayMonthly() > end) return date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().withMaximumValue(); return null; } } /** * Méthode qui détermine la prochaine date d'éxécution d'un planificateur pour un rythme annuel * * @param scheduler * Instance de planificateur * @param date * Derniere date d'éxécution théorique * * @return LocalDate * Prochaine date d'éxécution */ private LocalDate getAnnualComputeDate(Scheduler scheduler,LocalDate date){ int monthOfYear = 0; if(scheduler.getMonthAnnuallySelect().equals(IAdministration.JAN)) monthOfYear = DateTimeConstants.JANUARY; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.FEB)) monthOfYear = DateTimeConstants.FEBRUARY; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.MAR)) monthOfYear = DateTimeConstants.MARCH; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.APR)) monthOfYear = DateTimeConstants.APRIL; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.MAY)) monthOfYear = DateTimeConstants.MAY; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.JUN)) monthOfYear = DateTimeConstants.JUNE; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.JUL)) monthOfYear = DateTimeConstants.JULY; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.AUG)) monthOfYear = DateTimeConstants.AUGUST; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.SEP)) monthOfYear = DateTimeConstants.SEPTEMBER; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.OCT)) monthOfYear = DateTimeConstants.OCTOBER; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.NOV)) monthOfYear = DateTimeConstants.NOVEMBER; else if(scheduler.getMonthAnnuallySelect().equals(IAdministration.DEC)) monthOfYear = DateTimeConstants.DECEMBER; if(monthOfYear != 0){ int start = date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().getMinimumValue(); int end = date.plusWeeks(scheduler.getWeekWeekly()).dayOfMonth().getMaximumValue(); if(start <= scheduler.getDayAnnually() && scheduler.getDayAnnually() <= end) return date.plusYears(scheduler.getYearAnnually()).withMonthOfYear(monthOfYear).withDayOfMonth(scheduler.getDayAnnually()); else if(scheduler.getDayMonthly() < start) return date.plusYears(scheduler.getYearAnnually()).withMonthOfYear(monthOfYear).dayOfMonth().withMinimumValue(); else if(scheduler.getDayMonthly() > end) return date.plusYears(scheduler.getYearAnnually()).withMonthOfYear(monthOfYear).dayOfMonth().withMaximumValue(); } return null; } /** * Historise l'éxécution * * @param schedulerI * Instance de planificateur * @param currentDay * Date d'éxécution * @param isImmediate * Mettre a jour le cycle ? Dans le cas de facturation mémoire immédiate * * @throws AxelorException */ @Transactional(rollbackOn = {AxelorException.class, Exception.class}) public void addInHistory(SchedulerInstance schedulerI, LocalDate currentDay, boolean isImmediate) throws AxelorException { LocalDate date = this.getTheoricalExecutionDate(schedulerI); schedulerI.setLastExecutionDate(currentDay); if(!isImmediate){ schedulerI.setLastTheoreticalExecutionDate(date); } SchedulerInstanceHistory history = new SchedulerInstanceHistory(); history.setLastExecutionDate(currentDay); history.setLastThoereticalExecutionDate(date); history.setSchedulerInstance(schedulerI); if(schedulerI.getSchedulerInstanceHistoryList() == null){ schedulerI.setSchedulerInstanceHistoryList(new ArrayList<SchedulerInstanceHistory>()); } schedulerI.getSchedulerInstanceHistoryList().add(history); schedulerInstanceRepo.save(schedulerI); } /** * Méthode qui détermine la prochaine date d'éxécution théorique d'une instance de planificateur * * @param schedulerI * Instance de planificateur * * @return LocalDate * Prochaine date d'éxécution * * @throws AxelorException */ public LocalDate getTheoricalExecutionDate(SchedulerInstance schedulerI) throws AxelorException{ LocalDate theoricalExecutionDate = null; if(schedulerI.getLastTheoreticalExecutionDate() != null){ theoricalExecutionDate = this.getComputeDate(schedulerI); } else theoricalExecutionDate = schedulerI.getFirstExecutionDate(); return theoricalExecutionDate; } /** * Obtient le prochain jour du mois à partir d'une date * * @param localDate * Date de référence * @param dayMonthly * Jour du mois * * @return LocalDate * Date correspondant au prochain jour du mois */ public LocalDate getNextDayMonth(LocalDate localDate, int dayMonthly){ LocalDate date = null; int start = localDate.dayOfMonth().getMinimumValue(); int end = localDate.dayOfMonth().getMaximumValue(); if(localDate.dayOfMonth().get() <= dayMonthly){ if(start <= dayMonthly && dayMonthly <= end) date = localDate.withDayOfMonth(dayMonthly); else if(dayMonthly < start) date = localDate.dayOfMonth().withMinimumValue(); else if(dayMonthly > end) date = localDate.dayOfMonth().withMaximumValue(); } else{ int startNext = localDate.plusMonths(1).dayOfMonth().getMinimumValue(); int endNext = localDate.plusMonths(1).dayOfMonth().getMaximumValue(); if(startNext <= dayMonthly && dayMonthly <= endNext) date = localDate.plusMonths(1).withDayOfMonth(dayMonthly); else if(dayMonthly < startNext) date = localDate.plusMonths(1).dayOfMonth().withMinimumValue(); else if(dayMonthly > endNext) date = localDate.plusMonths(1).dayOfMonth().withMaximumValue(); } return date; } }