Verschillende functionaliteit:
All checks were successful
Docker Image CI / build-and-push (push) Successful in 11m28s
Docker Image CI / deploy (push) Successful in 30s
Docker Image CI / notify-failure (push) Has been skipped

-een gebruiker kan meerdere bedrijven hebben
 -manier van afspraken maken veranderd in de backend ivm meerdere bedrijven gebruiker
 -bedrijven van een gebruiker zijn op te roepen
 -gebruikers zijn aan een bestaand bedrijf te koppelen via een REST call
 -email is aangepast op bedrijf en gebruiker
This commit is contained in:
2025-04-15 20:39:48 +02:00
parent bb9b5a93f9
commit ebce9820d2
12 changed files with 172 additions and 39 deletions

View File

@@ -2,6 +2,9 @@ package nl.veenm.paypoint.domain;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity @Entity
public class AppUser { public class AppUser {
@Id @Id
@@ -14,8 +17,13 @@ public class AppUser {
private String firstName; private String firstName;
private String lastName; private String lastName;
@ManyToOne(cascade = CascadeType.ALL) @ManyToMany
private Company company; @JoinTable(
name = "user_company",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "company_id")
)
private Set<Company> companies = new HashSet<>();
public Long getId() { public Long getId() {
return id; return id;
@@ -73,12 +81,12 @@ public class AppUser {
this.lastName = lastName; this.lastName = lastName;
} }
public Company getCompany() { public Set<Company> getCompanies() {
return company; return companies;
} }
public void setCompany(Company company) { public void setCompanies(Set<Company> companies) {
this.company = company; this.companies = companies;
} }
@Override @Override

View File

@@ -1,9 +1,10 @@
package nl.veenm.paypoint.domain; package nl.veenm.paypoint.domain;
import jakarta.persistence.Entity; import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.*;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import java.util.HashSet;
import java.util.Set;
@Entity @Entity
public class Company { public class Company {
@@ -17,6 +18,10 @@ public class Company {
private String postal_code; private String postal_code;
private String city; private String city;
@ManyToMany(mappedBy = "companies")
@JsonIgnore
private Set<AppUser> users = new HashSet<>();
public void setId(Long id) { public void setId(Long id) {
this.id = id; this.id = id;
} }
@@ -72,4 +77,12 @@ public class Company {
public void setCity(String city) { public void setCity(String city) {
this.city = city; this.city = city;
} }
public Set<AppUser> getUsers() {
return users;
}
public void setUsers(Set<AppUser> users) {
this.users = users;
}
} }

View File

@@ -13,12 +13,14 @@ public class Customer {
private String firstName; private String firstName;
private String lastName; private String lastName;
private String email; private String email;
private String phone;
public Customer(Long id, String firstName, String lastName, String email) { public Customer(Long id, String firstName, String lastName, String email, String phone) {
this.id = id; this.id = id;
this.firstName = firstName; this.firstName = firstName;
this.lastName = lastName; this.lastName = lastName;
this.email = email; this.email = email;
this.phone = phone;
} }
public Customer() { public Customer() {
@@ -55,4 +57,12 @@ public class Customer {
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
} }

View File

@@ -1,25 +1,36 @@
package nl.veenm.paypoint.helper; package nl.veenm.paypoint.helper;
import nl.veenm.paypoint.domain.Appointment;
import java.time.LocalDate; import java.time.LocalDate;
public class EmailHelper { public class EmailHelper {
public static String getIcs(String dtStamp, LocalDate date, String formattedTime, String formattedEndTime, String location) { public static String getIcs(String dtStamp, LocalDate date, String formattedTime, String formattedEndTime, String location, Appointment appointment) {
String organizer = appointment.getCompany().getName() + ":" + appointment.getCompany().getEmail();
String formatted = """ String formatted = """
BEGIN:VCALENDAR BEGIN:VCALENDAR
VERSION:2.0 VERSION:2.0
PRODID:-//PayPoint//Afspraak//NL PRODID:-//PayPoint//Afspraak//NL
BEGIN:VEVENT BEGIN:VEVENT
ORGANIZER;CN=Hairstyling By Daan:mailto:danielle@hairstylingbydaan.nl ORGANIZER;CN=%s
UID:12345 UID:12345
DTSTAMP:%s DTSTAMP:%s
DTSTART:%sT%s00 DTSTART:%sT%s00
DTEND:%sT%s00 DTEND:%sT%s00
SUMMARY:Afspraak bij Hairstyling By Daan SUMMARY:Afspraak bij %s
LOCATION:%s LOCATION:%s
DESCRIPTION:Bevestiging van uw afspraak bij Hairstyling By Daan DESCRIPTION:Bevestiging van uw afspraak bij %s
END:VEVENT END:VEVENT
END:VCALENDAR END:VCALENDAR
""".formatted(dtStamp, date.toString().replace("-", ""), formattedTime.replace(":", ""), date.toString().replace("-", ""), formattedEndTime.replace(":", ""), location.replace("<br>", " ")); """.formatted(organizer,
dtStamp,
date.toString().replace("-", ""),
formattedTime.replace(":", ""),
date.toString().replace("-", ""),
formattedEndTime.replace(":", ""),
appointment.getCompany().getName(),
location.replace("<br>", " "),
appointment.getCompany().getName());
System.out.println(formatted); System.out.println(formatted);
return formatted; return formatted;
} }

View File

@@ -1,6 +1,7 @@
package nl.veenm.paypoint.repository; package nl.veenm.paypoint.repository;
import io.quarkus.hibernate.orm.panache.PanacheRepository; import io.quarkus.hibernate.orm.panache.PanacheRepository;
import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import nl.veenm.paypoint.domain.Appointment; import nl.veenm.paypoint.domain.Appointment;
import nl.veenm.paypoint.domain.Company; import nl.veenm.paypoint.domain.Company;
@@ -8,6 +9,7 @@ import nl.veenm.paypoint.domain.Company;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
@ApplicationScoped @ApplicationScoped
public class AppointmentRepository implements PanacheRepository<Appointment> { public class AppointmentRepository implements PanacheRepository<Appointment> {
@@ -23,4 +25,11 @@ public class AppointmentRepository implements PanacheRepository<Appointment> {
public List<Appointment> findMostRecentByCompanyId(Company company) { public List<Appointment> findMostRecentByCompanyId(Company company) {
return find("company = ?1", company).list(); return find("company = ?1", company).list();
} }
public List<Appointment> findAppointmentsForCompanies(Set<Company> companies, LocalDateTime startDate, LocalDateTime endDate) {
return find("SELECT a FROM Appointment a WHERE a.company IN :companies AND a.startDate BETWEEN :start AND :end",
Parameters.with("companies", companies).and("start", startDate).and("end", endDate))
.list();
}
} }

View File

@@ -2,7 +2,6 @@ package nl.veenm.paypoint.resource;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.json.JsonNumber;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
@@ -32,12 +31,10 @@ public class AppointmentResource {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Path("/date") @Path("/date")
public List<Appointment> getAppointmentsByDate(@QueryParam("start") String start) { public List<Appointment> getAppointmentsByDate(@QueryParam("start") String start) {
JsonNumber companyIdJson = jwt.getClaim("company_id"); String user = jwt.getClaim("username");
Long companyId = companyIdJson.longValue(); return appointmentService.getAppointmentsByDate(start, user);
return appointmentService.getAppointmentsByDate(start, companyId);
} }
//TODO: Deze werkend maken //TODO: Deze werkend maken
// @GET // @GET
@@ -55,11 +52,11 @@ public class AppointmentResource {
} }
@POST @POST
@Path("/{companyId}")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
public Response addAppointment(Appointment appointment) { public Response addAppointment(@PathParam("companyId") Long companyId, Appointment appointment) {
JsonNumber companyIdJson = jwt.getClaim("company_id"); String user = jwt.getClaim("username");
Long companyId = companyIdJson.longValue(); return Response.ok(appointmentService.add(appointment, companyId, user)).build();
return Response.ok(appointmentService.add(appointment, companyId)).build();
} }
@DELETE @DELETE

View File

@@ -0,0 +1,34 @@
package nl.veenm.paypoint.resource;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import nl.veenm.paypoint.domain.Company;
import nl.veenm.paypoint.service.CompanyService;
import org.eclipse.microprofile.jwt.JsonWebToken;
import java.util.Set;
@Path("/company")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CompanyResource {
@Inject
CompanyService companyService;
@Inject
JsonWebToken jwt;
@GET
public Set<Company> getCompanies() {
return this.companyService.getCompanies(this.jwt.getClaim("username"));
}
@POST
public void linkCompany(@QueryParam("user") Long userId, @QueryParam("company") Long companyId) {
System.out.println(userId);
System.out.println(companyId);
this.companyService.linkCompany(userId, companyId);
}
}

View File

@@ -4,10 +4,12 @@ import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import nl.veenm.paypoint.domain.AppUser;
import nl.veenm.paypoint.domain.Appointment; import nl.veenm.paypoint.domain.Appointment;
import nl.veenm.paypoint.domain.Company; import nl.veenm.paypoint.domain.Company;
import nl.veenm.paypoint.repository.AppointmentRepository; import nl.veenm.paypoint.repository.AppointmentRepository;
import nl.veenm.paypoint.repository.CompanyRepository; import nl.veenm.paypoint.repository.CompanyRepository;
import nl.veenm.paypoint.repository.UserRepository;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -18,6 +20,9 @@ public class AppointmentService {
@Inject @Inject
AppointmentRepository appointmentRepository; AppointmentRepository appointmentRepository;
@Inject
UserRepository userRepository;
@Inject @Inject
CompanyRepository companyRepository; CompanyRepository companyRepository;
@@ -34,23 +39,25 @@ public class AppointmentService {
} }
@Transactional @Transactional
public Appointment add(Appointment appointment, Long companyId) { public Appointment add(Appointment appointment, Long companyId, String username) {
Company company = companyRepository.findById(companyId); Company company = companyRepository.findById(companyId);
AppUser user = userRepository.findByUsername(username);
appointment.setCompany(company); appointment.setCompany(company);
appointmentRepository.persist(appointment); appointmentRepository.persist(appointment);
emailService.stuurBevestiging(appointment); emailService.stuurBevestiging(appointment, user);
return appointment; return appointment;
} }
@Transactional @Transactional
public List<Appointment> getAppointmentsByDate(String start, Long companyId) { public List<Appointment> getAppointmentsByDate(String start, String username) {
LocalDate date = LocalDate.parse(start); LocalDate date = LocalDate.parse(start);
Company company = companyRepository.findById(companyId); AppUser user = userRepository.findByUsername(username);
LocalDateTime startOfDay = date.atStartOfDay(); LocalDateTime startOfDay = date.atStartOfDay();
LocalDateTime endOfDay = date.atTime(23, 59, 59); LocalDateTime endOfDay = date.atTime(23, 59, 59);
return appointmentRepository.find("startDate BETWEEN ?1 AND ?2 AND company = ?3", startOfDay, endOfDay, company).list(); return appointmentRepository.findAppointmentsForCompanies(user.getCompanies(), startOfDay, endOfDay);
} }
@Transactional @Transactional
@@ -93,9 +100,7 @@ public class AppointmentService {
List<Appointment> allAppointments = appointmentRepository.find("date BETWEEN ?1 AND ?2", startOfDay, endOfDay).list(); List<Appointment> allAppointments = appointmentRepository.find("date BETWEEN ?1 AND ?2", startOfDay, endOfDay).list();
allAppointments.forEach(appointment -> { allAppointments.forEach(emailService::stuurHerinnering);
emailService.stuurHerinnering(appointment);
});
} }
public Appointment getMostRecentByUserId(Long userId) { public Appointment getMostRecentByUserId(Long userId) {

View File

@@ -0,0 +1,40 @@
package nl.veenm.paypoint.service;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import nl.veenm.paypoint.domain.AppUser;
import nl.veenm.paypoint.domain.Company;
import nl.veenm.paypoint.repository.CompanyRepository;
import nl.veenm.paypoint.repository.UserRepository;
import java.util.Set;
@ApplicationScoped
public class CompanyService {
@Inject
CompanyRepository companyRepository;
@Inject
UserRepository userRepository;
public Set<Company> getCompanies(String username) {
AppUser user = this.userRepository.findByUsername(username);
return user.getCompanies();
}
@Transactional
public void linkCompany(Long userId, Long companyId) {
AppUser user = this.userRepository.findById(userId);
Company company = this.companyRepository.findById(companyId);
System.out.println(user.getCompanies());
user.getCompanies().add(company);
company.getUsers().add(user);
System.out.println(user.getCompanies());
userRepository.persist(user);
companyRepository.persist(company);
}
}

View File

@@ -3,6 +3,7 @@ package nl.veenm.paypoint.service;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import nl.veenm.paypoint.domain.Company;
import nl.veenm.paypoint.domain.Customer; import nl.veenm.paypoint.domain.Customer;
import nl.veenm.paypoint.repository.CustomerRepository; import nl.veenm.paypoint.repository.CustomerRepository;
@@ -32,4 +33,8 @@ public class CustomerService {
customerRepository.persist(customer); customerRepository.persist(customer);
return customer; return customer;
} }
public List<Company> getCompanies() {
return null;
}
} }

View File

@@ -3,6 +3,7 @@ package nl.veenm.paypoint.service;
import io.quarkus.mailer.Mail; import io.quarkus.mailer.Mail;
import io.quarkus.mailer.Mailer; import io.quarkus.mailer.Mailer;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import nl.veenm.paypoint.domain.AppUser;
import nl.veenm.paypoint.domain.Appointment; import nl.veenm.paypoint.domain.Appointment;
import nl.veenm.paypoint.domain.Company; import nl.veenm.paypoint.domain.Company;
import nl.veenm.paypoint.helper.EmailHelper; import nl.veenm.paypoint.helper.EmailHelper;
@@ -20,7 +21,7 @@ public class EmailService {
this.mailer = mailer; this.mailer = mailer;
} }
public void stuurBevestiging(Appointment appointment) { public void stuurBevestiging(Appointment appointment, AppUser user) {
Company company = appointment.getCompany(); Company company = appointment.getCompany();
String location = String.format("<br>%s<br>%s %s", company.getAddress(), company.getPostal_code(), company.getCity()); String location = String.format("<br>%s<br>%s %s", company.getAddress(), company.getPostal_code(), company.getCity());
String imageUrl = appointment.getCompany().getImg_href(); String imageUrl = appointment.getCompany().getImg_href();
@@ -98,14 +99,14 @@ public class EmailService {
</div> </div>
<div class="footer"> <div class="footer">
<img src="%s" alt="Afspraak bevestiging" class="image"> <img src="%s" alt="Afspraak bevestiging" class="image">
<h4>Met vriendelijke groet,<br>Danielle<br>Hairstyling By Daan</h4> <h4>Met vriendelijke groet,<br>%s<br>%s</h4>
<p>&copy; 2025 PayPoint. Alle rechten voorbehouden.</p> <p>&copy; 2025 PayPoint. Alle rechten voorbehouden.</p>
</div> </div>
</div> </div>
</body> </body>
</html> </html>
""".formatted(appointment.getCustomer().getFirstName(), formattedDate, formattedTime, """.formatted(appointment.getCustomer().getFirstName(), formattedDate, formattedTime,
location, imageUrl); location, imageUrl, user.getFirstName(), appointment.getCompany().getName());
String subject = String.format(" Afspraak bevestigd: %s", formattedDate); String subject = String.format(" Afspraak bevestigd: %s", formattedDate);
String recipient = appointment.getCustomer().getEmail(); String recipient = appointment.getCustomer().getEmail();
@@ -113,11 +114,12 @@ public class EmailService {
date = date.withHour(appointment.getStartHour()).withMinute(appointment.getStartMinute()); date = date.withHour(appointment.getStartHour()).withMinute(appointment.getStartMinute());
String dtStamp = date.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'")); String dtStamp = date.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
String sender = appointment.getCompany().getName() + " <paypoint@melvanveen.nl>";
mailer.send(Mail.withHtml(recipient, subject, emailBody) mailer.send(Mail.withHtml(recipient, subject, emailBody)
.setFrom("Hairstyling By Daan <paypoint@melvanveen.nl>") .setFrom(sender)
.setReplyTo(company.getEmail()) .setReplyTo(company.getEmail())
.addAttachment("afspraak.ics", EmailHelper.getIcs(dtStamp, date.toLocalDate(), formattedTime, formattedEndTime, location).getBytes(), "text/calendar")); .addAttachment("afspraak.ics", EmailHelper.getIcs(dtStamp, date.toLocalDate(), formattedTime, formattedEndTime, location, appointment).getBytes(), "text/calendar"));
} }
public void stuurVerwijdering(Appointment appointment) { public void stuurVerwijdering(Appointment appointment) {

View File

@@ -18,7 +18,6 @@ public class TokenService {
.claim("lastName", appUser.getLastName()) .claim("lastName", appUser.getLastName())
.claim("email", appUser.getEmail()) .claim("email", appUser.getEmail())
.claim("groups", appUser.getRole()) .claim("groups", appUser.getRole())
.claim("company_id", appUser.getCompany().getId())
.sign(); .sign();
} }
} }