package de.stadtbote.votian; import de.stadtbote.votian.database.DatabaseService; import java.time.*; import java.util.*; import static de.stadtbote.votian.OrderEntryModels.*; import static de.stadtbote.votian.OrderEntryCatalog.invalid; import static de.stadtbote.votian.LegacyMapping.id; /** getNextOrderDate / isPublicHoliday: holidays are read from the maintained HQ calendar. */ final class OrderEntrySeries { static List dates(DatabaseService db,LegacyUser user,Request request) { if(!request.allOccurrences())return Collections.singletonList(request.pickupAt()); if(request.mode()!=Mode.RECURRING || request.recurrence()==null || request.recurrence().endDate()==null) throw invalid("Für die sofortige Serienanlage sind Dauerauftrag und Serienende erforderlich."); var start=request.pickupAt();var end=request.recurrence().endDate(); if(end.isAfter(start.toLocalDate().plusYears(2)))throw invalid("Die sofortige Serienanlage ist auf zwei Jahre begrenzt."); var holidays=new HashSet(); for(var row:db.queryForList("SELECT ph_year,ph_month,ph_day FROM publicholiday WHERE hq_id=? AND ph_year BETWEEN ? AND ?",user.headquartersId(),start.getYear(),end.getYear())) holidays.add(LocalDate.of((int)id(row,"ph_year"),(int)id(row,"ph_month"),(int)id(row,"ph_day"))); var dates=new ArrayList();dates.add(start); var date=start.toLocalDate(); while(true) { date=next(date,request.recurrence(),holidays,end); if(date.isAfter(end))break; var at=date.atTime(start.toLocalTime()); if(ZoneId.of("Europe/Berlin").getRules().getValidOffsets(at).size()!=1)throw invalid("Ein Serientermin fällt in eine ungültige oder mehrdeutige Uhrzeit bei der Zeitumstellung: "+at); dates.add(at); if(dates.size()*request.copies()>500 || dates.size()*request.copies()*request.stops().size()>10000) throw invalid("Die sofortige Serie ist zu groß (maximal 500 Aufträge bzw. 10.000 Stationen). Bitte das Serienende eingrenzen."); } return dates; } static LocalDate next(LocalDate previous,Recurrence recurrence,Set holidays,LocalDate end) { int rhythm=recurrence.rhythm();LocalDate next; if(rhythm<=3 || rhythm==9) { for(next=previous.plusDays(1);!next.isAfter(end);next=next.plusDays(1)) { boolean holiday=holidays.contains(next),weekday=recurrence.weekdays().charAt(next.getDayOfWeek().getValue()-1)=='1'; if((rhythm==9 && holiday) || (!holiday && weekday))return next; } return next; } boolean backwards=rhythm==7 || rhythm==8; next=switch(rhythm) { case 4->previous.plusWeeks(1);case 5->previous.plusWeeks(2);case 11->previous.plusWeeks(4); case 6->previous.plusMonths(1); case 7->previous.plusMonths(1).withDayOfMonth(previous.plusMonths(1).lengthOfMonth()); case 8->previous.getDayOfMonth()<20?previous.withDayOfMonth(previous.lengthOfMonth()):previous.plusMonths(1).withDayOfMonth(15); case 10->previous.getDayOfMonth()<16?previous.withDayOfMonth(16):previous.plusMonths(1).withDayOfMonth(1); default->throw invalid("Ungültiger Serienrhythmus."); }; while(holidays.contains(next))next=next.plusDays(backwards?-1:1); if(!next.isAfter(previous))throw invalid("Der Feiertagskalender lässt keinen folgenden Serientermin zu."); return next; } }