Ergänzt Auftragserfassung und -bearbeitung, Unternehmerbelege, Stornos, Lagerkorrekturen, Stammdaten, Abrechnung, CRM, Formulare, Berichte und Preisverwaltung auf Basis der aktuellen PHP-Anwendung. Enthält Integrations- und Oberflächentests sowie dokumentierte Abgleiche und Prüfstände.
57 lines
3.5 KiB
Java
57 lines
3.5 KiB
Java
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<LocalDateTime> 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<LocalDate>();
|
|
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<LocalDateTime>();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<LocalDate> 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;
|
|
}
|
|
}
|