Creation of appointment implemented
This commit is contained in:
@@ -11,6 +11,7 @@ import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -56,8 +57,8 @@ public class GarageApiController implements WerkstattApi {
|
||||
long garageId = Long.parseLong(werkstattId);
|
||||
Optional<Long> serviceId = NumberUtils.isParsable(leistungsId) ? Optional.of(Long.parseLong(leistungsId))
|
||||
: Optional.empty();
|
||||
Optional<Date> appointmentsFrom = Optional.ofNullable(parseLocalDateTime(von));
|
||||
Optional<Date> appointmentsTill = Optional.ofNullable(parseLocalDateTime(bis));
|
||||
Optional<Date> appointmentsFrom = Optional.ofNullable(parseDate(von));
|
||||
Optional<Date> appointmentsTill = Optional.ofNullable(parseDate(bis));
|
||||
|
||||
log.info("Filter appointments by garage {}, serviceId {}, from {}, till {}", garageId, serviceId,
|
||||
appointmentsFrom, appointmentsTill);
|
||||
@@ -76,15 +77,31 @@ public class GarageApiController implements WerkstattApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Termin> postTermin(String werkstattId, @Valid TerminRequest termin) {
|
||||
// TODO Auto-generated method stub
|
||||
return WerkstattApi.super.postTermin(werkstattId, termin);
|
||||
|
||||
// input validation
|
||||
Assert.isTrue(NumberUtils.isParsable(werkstattId), "werkstattId ungültig");
|
||||
Assert.isTrue(NumberUtils.isParsable(termin.getLeistungsId()), "leistungsId ungültig");
|
||||
Assert.notNull(parseDate(termin.getVon()), "von ungültig");
|
||||
Assert.notNull(parseDate(termin.getBis()), "bis ungültig");
|
||||
|
||||
long garageId = Long.parseLong(werkstattId);
|
||||
long serviceId = Long.parseLong(termin.getLeistungsId());
|
||||
Date appointmentFrom = parseDate(termin.getVon());
|
||||
Date appointmentTill = parseDate(termin.getBis());
|
||||
|
||||
// create appointment if possible
|
||||
Optional<Appointment> appointment = appointmentService.createAppointment(garageId, serviceId,
|
||||
appointmentFrom, appointmentTill);
|
||||
|
||||
return appointment.map(a -> ResponseEntity.ok(AppointmentTerminMapper.toTermin(a)))
|
||||
.orElse(ResponseEntity.status(HttpStatus.CONFLICT).build());
|
||||
}
|
||||
|
||||
private Date parseLocalDateTime(String dateTimeString) {
|
||||
private Date parseDate(String dateTimeString) {
|
||||
|
||||
if (StringUtils.isNotBlank(dateTimeString)) {
|
||||
try {
|
||||
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy hh:mm");
|
||||
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm");
|
||||
|
||||
return format.parse(dateTimeString);
|
||||
} catch (ParseException e) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package de.etecture.ga.dto.mapper;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import de.etecture.ga.dto.Termin;
|
||||
@@ -16,17 +13,13 @@ public class AppointmentTerminMapper {
|
||||
|
||||
log.info("Mapping Object {}", appointment);
|
||||
|
||||
LocalDateTime appointmentStart = Instant.ofEpochMilli(appointment.appointmentTime().getTime())
|
||||
.atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
LocalDateTime appointmentEnd = appointmentStart.plus(appointment.duration());
|
||||
|
||||
return new Termin()
|
||||
.id(Long.toString(appointment.id()))
|
||||
.leistungsId(Long.toString(appointment.serviceId().getId()))
|
||||
.leistung(appointment.serviceName())
|
||||
.werkstattName("to_be_set")
|
||||
.von(appointmentStart.format(DateTimeFormatter.ofPattern("dd.MM.yyyy hh:mm")))
|
||||
.bis(appointmentEnd.format(DateTimeFormatter.ofPattern("dd.MM.yyyy hh:mm")));
|
||||
.von(appointment.appointmentStart().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")))
|
||||
.bis(appointment.appointmentEnd().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package de.etecture.ga.model;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
@@ -32,4 +35,14 @@ public class Appointment {
|
||||
private Integer slot = 1;
|
||||
|
||||
private Duration duration = Duration.ZERO;
|
||||
|
||||
|
||||
public LocalDateTime appointmentStart() {
|
||||
return Instant.ofEpochMilli(this.appointmentTime().getTime())
|
||||
.atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
|
||||
public LocalDateTime appointmentEnd() {
|
||||
return this.appointmentStart().plus(this.duration());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package de.etecture.ga.service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import de.etecture.ga.model.Appointment;
|
||||
import de.etecture.ga.model.Garage;
|
||||
import de.etecture.ga.model.GarageServices;
|
||||
import de.etecture.ga.model.MDService;
|
||||
import de.etecture.ga.repository.AppointmentRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@@ -17,6 +25,10 @@ public class AppointmentService {
|
||||
|
||||
private final AppointmentRepository repository;
|
||||
|
||||
private final GarageService garageService;
|
||||
|
||||
private final MDServiceService serviceService;
|
||||
|
||||
public Optional<Appointment> getAppointment(long appointmentId) {
|
||||
|
||||
return repository.findById(appointmentId);
|
||||
@@ -47,4 +59,63 @@ public class AppointmentService {
|
||||
|
||||
return appointments.toList();
|
||||
}
|
||||
|
||||
public Optional<Appointment> createAppointment(long garageId, long serviceId, Date from, Date till) {
|
||||
|
||||
Optional<Garage> garage = garageService.getGarage(garageId);
|
||||
if (garage.isEmpty())
|
||||
throw new IllegalArgumentException("GarageId not valid");
|
||||
|
||||
Optional<MDService> service = serviceService.getMDService(serviceId)
|
||||
.filter(s -> garage.get().garageServices().stream().anyMatch(gs -> s.id() == gs.serviceId().getId()));
|
||||
Optional<GarageServices> garageService = garage.get().garageServices().stream()
|
||||
.filter(gs -> serviceId == gs.serviceId().getId()).findFirst();
|
||||
if (service.isEmpty() || garageService.isEmpty())
|
||||
throw new IllegalArgumentException("serviceId not valid");
|
||||
|
||||
LocalDateTime fromDateTime = Instant.ofEpochMilli(from.getTime()).atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime();
|
||||
LocalDateTime tillDateTime = Instant.ofEpochMilli(till.getTime()).atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime();
|
||||
|
||||
LocalDateTime validAppointmentTime = null;
|
||||
while (!fromDateTime.isAfter(tillDateTime)) {
|
||||
if (isSlotAvailable(garage.get(), fromDateTime)) {
|
||||
validAppointmentTime = fromDateTime;
|
||||
break;
|
||||
}
|
||||
fromDateTime = fromDateTime.plusMinutes(15);
|
||||
}
|
||||
|
||||
if (validAppointmentTime != null) {
|
||||
Appointment appointment = new Appointment().garageId(AggregateReference.to(garage.get().id()))
|
||||
.appointmentTime(Date.from(validAppointmentTime.atZone(ZoneId.systemDefault()).toInstant()))
|
||||
.serviceId(AggregateReference.to(service.get().id())).serviceCode(service.get().code())
|
||||
.serviceName(service.get().name()).duration(garageService.get().duration());
|
||||
|
||||
return Optional.of(repository.save(appointment));
|
||||
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSlotAvailable(Garage garage, LocalDateTime time) {
|
||||
|
||||
long appointments = garage.appointments().stream()
|
||||
.filter(a -> (a.appointmentStart().isBefore(time) || a.appointmentStart().isEqual(time))
|
||||
&& a.appointmentEnd().isAfter(time))
|
||||
.count();
|
||||
|
||||
return appointments < garage.maxAppointments();
|
||||
}
|
||||
|
||||
private LocalDateTime roundUpToQuarter(LocalDateTime datetime) {
|
||||
|
||||
if (datetime.getMinute() % 15 == 0)
|
||||
return datetime;
|
||||
|
||||
int minutesToAdd = 15 - (datetime.getMinute() % 15);
|
||||
return datetime.plusMinutes(minutesToAdd).truncatedTo(ChronoUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
|
||||
21
src/main/java/de/etecture/ga/service/GarageService.java
Normal file
21
src/main/java/de/etecture/ga/service/GarageService.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package de.etecture.ga.service;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import de.etecture.ga.model.Garage;
|
||||
import de.etecture.ga.repository.GarageRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class GarageService {
|
||||
|
||||
private final GarageRepository repository;
|
||||
|
||||
|
||||
public Optional<Garage> getGarage(long id) {
|
||||
return repository.findById(id);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.etecture.ga.service;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -16,6 +17,11 @@ public class MDServiceService {
|
||||
|
||||
private final MDServiceRepository serviceRepository;
|
||||
|
||||
|
||||
public Optional<MDService> getMDService(long id) {
|
||||
return serviceRepository.findById(id);
|
||||
}
|
||||
|
||||
public MDService storeMDService(String serviceCode) {
|
||||
|
||||
if (StringUtils.isBlank(serviceCode))
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package de.etecture.ga.api;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import de.etecture.ga.dto.TerminRequest;
|
||||
import de.etecture.ga.model.Appointment;
|
||||
import de.etecture.ga.model.Garage;
|
||||
import de.etecture.ga.repository.AppointmentRepository;
|
||||
@@ -114,7 +118,21 @@ class GarageApiControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPostTermin() {
|
||||
fail("Not yet implemented");
|
||||
void testPostTermin() throws Exception {
|
||||
|
||||
TerminRequest terminReq = new TerminRequest().leistungsId("3").von("02.01.2019 12:00").bis("02.01.2019 13:00");
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String terminJson = objectMapper.writeValueAsString(terminReq);
|
||||
|
||||
this.mockMvc
|
||||
.perform(MockMvcRequestBuilders.post("/werkstatt/{werkstattId}/termin", testGarage.id())
|
||||
.contentType(MediaType.APPLICATION_JSON).content(terminJson))
|
||||
.andDo(MockMvcResultHandlers.print()).andExpect(MockMvcResultMatchers.status().isOk())
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.id").exists())
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.leistung").value("Radwechsel"))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.leistungsId").value("3"))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.von").value("02.01.2019 12:00"))
|
||||
.andExpect(MockMvcResultMatchers.jsonPath("$.bis").value("02.01.2019 12:30"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user