start quesitonnaire

This commit is contained in:
verboomp
2026-02-19 11:04:02 +01:00
parent 168fc986f2
commit 9b3446685a
47 changed files with 2456 additions and 97 deletions

View File

@@ -20,12 +20,10 @@ import jakarta.persistence.*;
@Entity
@Table(name = "customer")
@NamedQuery(name = Customer.FIND_ALL, query = "select c from Customer c order by c.name")
@NamedQuery(name = Customer.FIND_BY_NUMBER, query = "select c from Customer c where c.customerNumber = :cutomerNumber")
public class Customer extends AbstractDateEntity {
private static final long serialVersionUID = 1L;
public static final String SEQUENCE = "customer_seq";
public static final String FIND_ALL = "Customer.findAll";
public static final String FIND_BY_NUMBER = "Customer.findByNumber";
public static final String PARAM_NUMBER = "cutomerNumber";

View File

@@ -0,0 +1,181 @@
package marketing.heyday.hartmann.fotodocumentation.core.model;
import java.util.Date;
import org.apache.commons.lang.builder.HashCodeBuilder;
import jakarta.persistence.*;
/**
*
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 19 Jan 2026
*/
@Entity
@Table(name = "questionnaire")
public class Questionnaire extends AbstractDateEntity {
private static final long serialVersionUID = 1L;
public static final String SEQUENCE = "questionnaire_seq";
@Id
@Column(name = "questionnaire_id", length = 22)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
private Long questionnaireId;
// username from the person that shot the picture
@Column(name = "username")
private String username;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "questionnaire_date", nullable = false)
private Date questionnaireDate;
@Basic(fetch = FetchType.LAZY)
private String comment;
@Column(name = "evaluation")
private Integer evaluation;
@Column
private String category;
@Column(name = "questions")
@Basic(fetch = FetchType.LAZY)
private String questions;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id_fk")
private QuestionnaireCustomer customer;
public Long getQuestionnaireId() {
return questionnaireId;
}
public void setQuestionnaireId(Long questionnaireId) {
this.questionnaireId = questionnaireId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getQuestionnaireDate() {
return questionnaireDate;
}
public void setQuestionnaireDate(Date questionnaireDate) {
this.questionnaireDate = questionnaireDate;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Integer getEvaluation() {
return evaluation;
}
public void setEvaluation(Integer evaluation) {
this.evaluation = evaluation;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getQuestions() {
return questions;
}
public void setQuestions(String questions) {
this.questions = questions;
}
public QuestionnaireCustomer getCustomer() {
return customer;
}
public void setCustomer(QuestionnaireCustomer customer) {
this.customer = customer;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(questionnaireId).toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null || this.getClass() != obj.getClass() || questionnaireId == null) {
return false;
}
return this.questionnaireId.equals(((Questionnaire) obj).getQuestionnaireId());
}
public static class Builder {
private Questionnaire instance = new Questionnaire();
public Builder(){
instance.evaluation = 0;
}
public Builder username(String username) {
instance.setUsername(username);
return this;
}
public Builder questionnaireDate(Date date) {
instance.setQuestionnaireDate(date);
return this;
}
public Builder comment(String comment) {
instance.setComment(comment);
return this;
}
public Builder category(String category) {
instance.setCategory(category);
return this;
}
public Builder questions(String questions) {
instance.setQuestions(questions);
return this;
}
public Builder evaluation(Integer evaluation) {
instance.setEvaluation(evaluation);
return this;
}
public Builder customer(QuestionnaireCustomer customer) {
instance.setCustomer(customer);
return this;
}
public Questionnaire build() {
return instance;
}
}
}

View File

@@ -0,0 +1,140 @@
package marketing.heyday.hartmann.fotodocumentation.core.model;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.builder.HashCodeBuilder;
import jakarta.persistence.*;
/**
*
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 19 Jan 2026
*/
@Entity
@Table(name = "questionnaire_customer")
@NamedQuery(name = QuestionnaireCustomer.FIND_BY_NUMBER, query = "select c from QuestionnaireCustomer c where c.customerNumber = :customerNumber")
public class QuestionnaireCustomer extends AbstractDateEntity {
private static final long serialVersionUID = 1L;
public static final String SEQUENCE = "questionnaire_customer_seq";
public static final String FIND_BY_NUMBER = "QuestionnaireCustomer.findByNumber";
public static final String PARAM_NUMBER = "customerNumber";
@Id
@Column(name = "customer_id", length = 22)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE, allocationSize = 1)
private Long customerId;
@Column(name = "customer_number", unique = true, nullable = false)
private String customerNumber;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "city")
private String city;
@Column(name = "zip")
private String zip;
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Questionnaire> questionnaires = new HashSet<>();
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getCustomerNumber() {
return customerNumber;
}
public void setCustomerNumber(String customerNumber) {
this.customerNumber = customerNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public Set<Questionnaire> getQuestionnaires() {
return questionnaires;
}
public void setQuestionnaires(Set<Questionnaire> questionnaires) {
this.questionnaires = questionnaires;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(customerNumber).toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
return this.customerNumber.equals(((QuestionnaireCustomer) obj).getCustomerNumber());
}
public static class Builder {
private QuestionnaireCustomer instance = new QuestionnaireCustomer();
public Builder customerNumber(String customerNumber) {
instance.setCustomerNumber(customerNumber);
return this;
}
public Builder name(String name) {
instance.setName(name);
return this;
}
public Builder city(String city) {
instance.setCity(city);
return this;
}
public Builder zip(String zip) {
instance.setZip(zip);
return this;
}
public QuestionnaireCustomer build() {
return instance;
}
}
}

View File

@@ -0,0 +1,121 @@
package marketing.heyday.hartmann.fotodocumentation.core.service;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import jakarta.annotation.security.PermitAll;
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.*;
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
import marketing.heyday.hartmann.fotodocumentation.core.utils.CalendarUtil;
import marketing.heyday.hartmann.fotodocumentation.core.utils.ExcelUtils;
import marketing.heyday.hartmann.fotodocumentation.core.utils.PdfUtils;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerListValue;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.CustomerValue;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerListValue;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerValue;
/**
*
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 18 Feb 2026
*/
@Stateless
@LocalBean
@PermitAll
public class QuestionnaireCustomerService extends AbstractService {
@Inject
private ExcelUtils excelUtils;
@Inject
private CalendarUtil calendarUtil;
// query = search for name, number and date
public List<QuestionnaireCustomerListValue> getAll(String queryStr, String startsWith) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<QuestionnaireCustomer> criteriaQuery = builder.createQuery(QuestionnaireCustomer.class);
Root<QuestionnaireCustomer> customerRoot = criteriaQuery.from(QuestionnaireCustomer.class);
criteriaQuery = criteriaQuery.select(customerRoot).distinct(true);
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(startsWith)) {
String param = startsWith.toLowerCase() + "%";
var pred = builder.like(builder.lower(customerRoot.get("name")), param);
predicates.add(pred);
}
if (StringUtils.isNotBlank(queryStr)) {
// check if it contains a date
Date date = calendarUtil.parse(queryStr);
if (date != null) {
Date startOfDay = calendarUtil.getStartOfDay(date);
Date endOfDay = calendarUtil.getEndOfDay(date);
Fetch<QuestionnaireCustomer, Questionnaire> picturesFetch = customerRoot.fetch("pictures", JoinType.LEFT);
@SuppressWarnings("unchecked")
Join<QuestionnaireCustomer, Questionnaire> pictures = (Join<QuestionnaireCustomer, Questionnaire>) picturesFetch;
var predicateDate = builder.between(pictures.get("pictureDate"), startOfDay, endOfDay);
predicates.add(predicateDate);
} else {
String param = "%" + StringUtils.trimToEmpty(queryStr).toLowerCase() + "%";
var predicateName = builder.like(builder.lower(customerRoot.get("name")), param);
var predicateNr = builder.like(builder.lower(customerRoot.get("customerNumber")), param);
var pred = builder.or(predicateName, predicateNr);
predicates.add(pred);
}
}
if (predicates.size() == 1) {
criteriaQuery = criteriaQuery.where(predicates.getFirst());
} else if (predicates.size() > 1) {
criteriaQuery = criteriaQuery.where(builder.and(predicates.toArray(new Predicate[0])));
}
TypedQuery<QuestionnaireCustomer> typedQuery = entityManager.createQuery(criteriaQuery);
List<QuestionnaireCustomer> customers = typedQuery.getResultList();
customers.forEach(c -> c.getQuestionnaires().size());
return customers.parallelStream().map(QuestionnaireCustomerListValue::builder).toList();
}
public QuestionnaireCustomerValue get(Long id) {
QuestionnaireCustomer customer = entityManager.find(QuestionnaireCustomer.class, id);
if (customer == null) {
return null;
}
return QuestionnaireCustomerValue.builder(customer);
}
public byte[] getExport(Long id, Long questionnaireId) {
QuestionnaireCustomer customer = entityManager.find(QuestionnaireCustomer.class, id);
if (customer == null) {
return new byte[0];
}
List<Questionnaire> questionnaires = customer.getQuestionnaires().stream().sorted((x, y) -> x.getQuestionnaireDate().compareTo(y.getQuestionnaireDate())).toList();
if (questionnaireId != null) {
Optional<Questionnaire> pictureOpt = customer.getQuestionnaires().stream().filter(p -> p.getQuestionnaireId().equals(questionnaireId)).findFirst();
questionnaires = pictureOpt.map(Arrays::asList).orElse(questionnaires);
}
return excelUtils.create(customer, questionnaires);
}
}

View File

@@ -0,0 +1,26 @@
package marketing.heyday.hartmann.fotodocumentation.core.service;
import jakarta.annotation.security.PermitAll;
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnairePublishValue;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 18 Feb 2026
*/
@Stateless
@LocalBean
@PermitAll
public class QuestionnairePublishService extends AbstractService {
public boolean publish(QuestionnairePublishValue value) {
// FIXME: implement me
return false;
}
}

View File

@@ -0,0 +1,44 @@
package marketing.heyday.hartmann.fotodocumentation.core.service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import jakarta.annotation.security.PermitAll;
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;
import jakarta.persistence.EntityNotFoundException;
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 19 Jan 2026
*/
@Stateless
@LocalBean
@PermitAll
public class QuestionnaireService extends AbstractService {
private static final Log LOG = LogFactory.getLog(QuestionnaireService.class);
public StorageState delete(Long id) {
return super.delete(Questionnaire.class, id);
}
public StorageState updateEvaluationStatus(Long id, Integer value) {
try {
Questionnaire entity = entityManager.getReference(Questionnaire.class, id);
entity.setEvaluation(value);
entityManager.flush();
return StorageState.OK;
} catch (EntityNotFoundException e) {
LOG.warn("Failed to update evaluation value not found " + id, e);
ejbContext.setRollbackOnly();
return StorageState.NOT_FOUND;
}
}
}

View File

@@ -0,0 +1,29 @@
package marketing.heyday.hartmann.fotodocumentation.core.utils;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 2 Feb 2026
*/
@SuppressWarnings({ "java:S818", "squid:S818", "squid:S109" })
public class ExcelUtils {
private static final Log LOG = LogFactory.getLog(ExcelUtils.class);
public byte[] create(QuestionnaireCustomer customer, List<Questionnaire> questionnaires) {
// FIXME: implement excel export
return new byte[0];
}
}

View File

@@ -0,0 +1,88 @@
package marketing.heyday.hartmann.fotodocumentation.rest;
import static marketing.heyday.hartmann.fotodocumentation.rest.jackson.ApplicationConfigApi.JSON_OUT;
import java.io.OutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.resteasy.annotations.GZIP;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.ejb.EJB;
import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.core.StreamingOutput;
import marketing.heyday.hartmann.fotodocumentation.core.service.QuestionnaireCustomerService;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerListValue;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnaireCustomerValue;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 21 Jan 2026
*/
@RequestScoped
@Path("questionnairecustomer")
public class QuestionnaireCustomerResource {
private static final Log LOG = LogFactory.getLog(QuestionnaireCustomerResource.class);
@EJB
private QuestionnaireCustomerService questionnaireCustomerService;
@GZIP
@GET
@Path("")
@Produces(JSON_OUT)
@Operation(summary = "Get customer list")
@ApiResponse(responseCode = "200", description = "Successfully retrieved customer list", content = @Content(mediaType = JSON_OUT, array = @ArraySchema(schema = @Schema(implementation = QuestionnaireCustomerListValue.class))))
public Response doGetCustomerList(@QueryParam("query") String query, @QueryParam("startsWith") String startsWith) {
LOG.debug("Query customers for query " + query + " startsWith: " + startsWith);
var retVal = questionnaireCustomerService.getAll(query, startsWith);
return Response.ok().entity(retVal).build();
}
@GZIP
@GET
@Path("{id}")
@Produces(JSON_OUT)
@Operation(summary = "Get customer value")
@ApiResponse(responseCode = "200", description = "Successfully retrieved customer value", content = @Content(mediaType = JSON_OUT, array = @ArraySchema(schema = @Schema(implementation = QuestionnaireCustomerValue.class))))
public Response doGetDetailCustomer(@PathParam("id") Long id) {
LOG.debug("Get Customer details for id " + id);
var retVal = questionnaireCustomerService.get(id);
return Response.ok().entity(retVal).build();
}
@GZIP
@GET
@Path("export/{id}")
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@Operation(summary = "Get Export")
@ApiResponse(responseCode = "200", description = "Successfully retrieved export")
public Response doExport(@PathParam("id") Long id, @QueryParam("questionnaire") Long questionnaireId) {
LOG.debug("Create export for customer " + id + " with optional param " + questionnaireId);
byte[] pdf = questionnaireCustomerService.getExport(id, questionnaireId);
if (pdf.length == 0) {
return Response.status(Status.NOT_FOUND).build();
}
StreamingOutput streamingOutput = (OutputStream output) -> {
LOG.debug("Start writing content to OutputStream available bytes");
output.write(pdf);
};
return Response.status(Status.OK).entity(streamingOutput).build();
}
}

View File

@@ -0,0 +1,46 @@
package marketing.heyday.hartmann.fotodocumentation.rest;
import org.jboss.resteasy.annotations.GZIP;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.ejb.EJB;
import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import marketing.heyday.hartmann.fotodocumentation.core.service.QuestionnairePublishService;
import marketing.heyday.hartmann.fotodocumentation.rest.jackson.JsonSchemaValidate;
import marketing.heyday.hartmann.fotodocumentation.rest.vo.QuestionnairePublishValue;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 18 Feb 2026
*/
@RequestScoped
@Path("questionnaire-publish")
public class QuestionnairePublishResource {
@EJB
private QuestionnairePublishService questionnairePublishService;
@GZIP
@POST
@Path("")
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Add questionnaire to database")
@ApiResponse(responseCode = "200", description = "Add successfull")
public Response doAddQuestionnaire(@JsonSchemaValidate("schema/questionnaire_publish.json") QuestionnairePublishValue value) {
boolean success = questionnairePublishService.publish(value);
return success ? Response.ok().build() : Response.status(Status.BAD_REQUEST).build();
}
}

View File

@@ -0,0 +1,70 @@
package marketing.heyday.hartmann.fotodocumentation.rest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import jakarta.ejb.EJB;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.ResponseBuilder;
import jakarta.ws.rs.core.Response.Status;
import marketing.heyday.hartmann.fotodocumentation.core.service.QuestionnaireService;
import marketing.heyday.hartmann.fotodocumentation.core.utils.EvaluationUtil;
import marketing.heyday.hartmann.fotodocumentation.core.utils.StorageUtils.StorageState;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 21 Jan 2026
*/
@RequestScoped
@Path("questionnaire")
public class QuestionnaireResource {
private static final Log LOG = LogFactory.getLog(QuestionnaireResource.class);
@EJB
private QuestionnaireService questionnaireService;
@Inject
private EvaluationUtil evaluationUtil;
@DELETE
@Path("{id}")
@Operation(summary = "Delete questionnaire from database")
@ApiResponse(responseCode = "200", description = "Task successfully deleted")
@ApiResponse(responseCode = "404", description = "Task not found")
@ApiResponse(responseCode = "403", description = "Insufficient permissions")
public Response doDelete(@PathParam("id") Long id) {
LOG.debug("Delete questionnaire with id " + id);
var state = questionnaireService.delete(id);
return deleteResponse(state).build();
}
@PUT
@Path("evaluation/{id}")
@Operation(summary = "Update evaluation for questionnaire data to database")
@ApiResponse(responseCode = "200", description = "Task successfully updated")
public Response doUpdateEvaluation(@PathParam("id") Long id, @QueryParam("evaluation") Integer value) {
if (evaluationUtil.isInValid(value)) {
return Response.status(Status.BAD_REQUEST).build();
}
StorageState state = questionnaireService.updateEvaluationStatus(id, value);
return deleteResponse(state).build();
}
protected ResponseBuilder deleteResponse(StorageState state) {
return switch (state) {
case OK -> Response.status(Status.OK);
case NOT_FOUND -> Response.status(Status.NOT_FOUND);
default -> Response.status(Status.INTERNAL_SERVER_ERROR);
};
}
}

View File

@@ -46,6 +46,10 @@ public class ApplicationConfigApi extends Application {
retVal.add(CustomerPictureResource.class);
retVal.add(CustomerResource.class);
retVal.add(PictureResource.class);
retVal.add(QuestionnairePublishResource.class);
retVal.add(QuestionnaireCustomerResource.class);
retVal.add(QuestionnaireResource.class);
LOG.info("returning rest api classes " + retVal);
return retVal;
}

View File

@@ -0,0 +1,29 @@
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
import java.util.Date;
import io.swagger.v3.oas.annotations.media.Schema;
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 19 Jan 2026
*/
@Schema(name = "QuestionnaireCustomerList")
public record QuestionnaireCustomerListValue(Long id, String name, String customerNumber, String zip, String city, Date lastUpdateDate) {
public static QuestionnaireCustomerListValue builder(QuestionnaireCustomer customer) {
if (customer == null) {
return null;
}
Date date = customer.getQuestionnaires().stream().map(Questionnaire::getQuestionnaireDate).sorted((p1, p2) -> p2.compareTo(p1)).findFirst().orElse(null);
return new QuestionnaireCustomerListValue(customer.getCustomerId(), customer.getName(), customer.getCustomerNumber(), customer.getZip(), customer.getCity(), date);
}
}

View File

@@ -0,0 +1,27 @@
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 22 Jan 2026
*/
@Schema(name = "QuestionnaireCustomer")
public record QuestionnaireCustomerValue(Long id, String name, String customerNumber, String city, String zip, List<QuestionnaireValue> questionnaires) {
public static QuestionnaireCustomerValue builder(QuestionnaireCustomer customer) {
if (customer == null) {
return null;
}
List<QuestionnaireValue> questionnaires = customer.getQuestionnaires().parallelStream().map(QuestionnaireValue::builder).filter(p -> p != null).toList();
return new QuestionnaireCustomerValue(customer.getCustomerId(), customer.getName(), customer.getCustomerNumber(), customer.getCity(), customer.getZip(), questionnaires);
}
}

View File

@@ -0,0 +1,21 @@
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
import java.util.Date;
import io.swagger.v3.oas.annotations.media.Schema;
import marketing.heyday.hartmann.fotodocumentation.rest.jackson.SchemaValidated;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 18 Feb 2026
*/
@Schema(name = "QuestionnairePublish")
public record QuestionnairePublishValue(String username, String pharmacyName, String customerNumber, Date date, String zip, String city, String comment, String category, String base64String) implements SchemaValidated {
}

View File

@@ -0,0 +1,28 @@
package marketing.heyday.hartmann.fotodocumentation.rest.vo;
import java.util.Date;
import io.swagger.v3.oas.annotations.media.Schema;
import marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire;
/**
*
* <p>Copyright: Copyright (c) 2024</p>
* <p>Company: heyday Marketing GmbH</p>
* @author <a href="mailto:p.verboom@heyday.marketing">Patrick Verboom</a>
* @version 1.0
*
* created: 22 Jan 2026
*/
@Schema(name = "Questionnaire")
public record QuestionnaireValue(Long id, String comment, String category, Date questionnaireDate, String username, Integer evaluation) {
public static QuestionnaireValue builder(Questionnaire questionnaire) {
if (questionnaire == null) {
return null;
}
return new QuestionnaireValue(questionnaire.getQuestionnaireId(), questionnaire.getComment(), questionnaire.getCategory(), questionnaire.getQuestionnaireDate(), questionnaire.getUsername(), questionnaire.getEvaluation());
}
}

View File

@@ -12,6 +12,9 @@
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Customer</class>
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Picture</class>
<class>marketing.heyday.hartmann.fotodocumentation.core.model.JwtRefreshToken</class>
<class>marketing.heyday.hartmann.fotodocumentation.core.model.QuestionnaireCustomer</class>
<class>marketing.heyday.hartmann.fotodocumentation.core.model.Questionnaire</class>
<properties>
<property name="hibernate.format_sql" value="false" />

View File

@@ -0,0 +1,37 @@
create sequence IF NOT EXISTS questionnaire_customer_seq start 25;
CREATE TABLE IF NOT EXISTS questionnaire_customer
(
customer_id bigint PRIMARY KEY,
customer_number varchar(150) NOT NULL,
name varchar(150) NOT NULL,
zip varchar(150),
city varchar(150),
jpa_active boolean NOT NULL,
jpa_created timestamp NOT NULL,
jpa_updated timestamp NOT NULL,
jpa_version integer NOT NULL,
CONSTRAINT unq_questionnaire_customer_number UNIQUE(customer_number)
);
create sequence IF NOT EXISTS questionnaire_seq start 25;
CREATE TABLE IF NOT EXISTS questionnaire
(
questionnaire_id bigint PRIMARY KEY,
username varchar(150),
questionnaire_date timestamp NOT NULL,
comment text,
questions text,
category varchar(250),
evaluation bigint NOT NULL,
jpa_active boolean NOT NULL,
jpa_created timestamp NOT NULL,
jpa_updated timestamp NOT NULL,
jpa_version integer NOT NULL,
customer_id_fk bigint REFERENCES questionnaire_customer
);

View File

@@ -0,0 +1,48 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Add Customer Picture",
"description": "Add a Customer Picture to the system",
"type": "object",
"properties": {
"username": {
"description": "The username from the user who uploads the picture",
"type": "string"
},
"pharmacyName": {
"description": "The Name from the pharmacy customer ",
"type": "string"
},
"customerNumber": {
"description": "The unique number from the pharmacy customer ",
"type": "string"
},
"date": {
"description": "The date when the picture is taken ",
"type": "string"
},
"comment": {
"description": "A free text comment field ",
"type": "string"
},
"zip": {
"description": "The zip from the customer",
"type": "string"
},
"city": {
"description": "The city from the customer",
"type": "string"
},
"base64String": {
"description": "The Picture content as base64 ",
"type": "string"
}
},
"required": [
"username",
"pharmacyName",
"customerNumber",
"date",
"comment",
"base64String"
]
}