merge origin/main into morefiles, resolve all conflicts

This commit is contained in:
2026-04-07 20:29:54 -06:00
376 changed files with 30110 additions and 9105 deletions

View File

@@ -33,6 +33,7 @@ public class DevStackApplication {
docker.ensureDockerAvailable();
docker.startDatabase();
context = new SpringApplicationBuilder(BackendApplication.class)
.profiles("local")
.initializers(new FlywayContextInitializer())
.run(args);
context.addApplicationListener(event -> {

View File

@@ -11,7 +11,7 @@ final class RuntimeClasspathValidator {
if (!resourceExists("application.yml")) {
throw new IllegalStateException("Backend resources are missing from the runtime classpath. Reimport the Maven project in IntelliJ and run the shared Maven run configuration.");
}
if (!resourceExists("db/migration/V1__baseline_schema.sql")) {
if (!resourceExists("db/migration/V1__target_baseline.sql")) {
throw new IllegalStateException("Flyway migration files are missing from the runtime classpath. Reimport the Maven project in IntelliJ and run the shared Maven run configuration.");
}
}

View File

@@ -2,8 +2,6 @@ package com.petshop.backend.config;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.StoreAssignmentService;
import com.petshop.backend.service.UserBusinessLinkageService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@@ -13,14 +11,10 @@ public class DataInitializer implements CommandLineRunner {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final StoreAssignmentService storeAssignmentService;
public DataInitializer(UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, StoreAssignmentService storeAssignmentService) {
public DataInitializer(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeAssignmentService = storeAssignmentService;
}
@Override
@@ -34,16 +28,25 @@ public class DataInitializer implements CommandLineRunner {
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("admin123"));
admin.setEmail("admin@petshop.com");
admin.setFirstName("Admin");
admin.setLastName("User");
admin.setFullName("Admin User");
admin.setPhone("000-000-1000");
admin.setRole(User.Role.ADMIN);
admin.setActive(true);
admin = userRepository.save(admin);
userRepository.save(admin);
System.out.println("Admin user created successfully");
} else {
System.out.println("Admin user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (admin.getFirstName() == null || admin.getFirstName().isEmpty()) {
admin.setFirstName("Admin");
updated = true;
}
if (admin.getLastName() == null || admin.getLastName().isEmpty()) {
admin.setLastName("User");
updated = true;
}
if (admin.getFullName() == null || admin.getFullName().isEmpty()) {
admin.setFullName("Admin User");
updated = true;
@@ -65,12 +68,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
admin = userRepository.save(admin);
userRepository.save(admin);
System.out.println("Admin user normalized");
}
}
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(admin), 1L);
User staff = userRepository.findByUsername("staff").orElse(null);
if (staff == null) {
@@ -79,16 +80,25 @@ public class DataInitializer implements CommandLineRunner {
staff.setUsername("staff");
staff.setPassword(passwordEncoder.encode("staff123"));
staff.setEmail("staff@petshop.com");
staff.setFirstName("Staff");
staff.setLastName("User");
staff.setFullName("Staff User");
staff.setPhone("000-000-1001");
staff.setRole(User.Role.STAFF);
staff.setActive(true);
staff = userRepository.save(staff);
userRepository.save(staff);
System.out.println("Staff user created successfully");
} else {
System.out.println("Staff user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (staff.getFirstName() == null || staff.getFirstName().isEmpty()) {
staff.setFirstName("Staff");
updated = true;
}
if (staff.getLastName() == null || staff.getLastName().isEmpty()) {
staff.setLastName("User");
updated = true;
}
if (staff.getFullName() == null || staff.getFullName().isEmpty()) {
staff.setFullName("Staff User");
updated = true;
@@ -110,12 +120,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
staff = userRepository.save(staff);
userRepository.save(staff);
System.out.println("Staff user normalized");
}
}
// Ensure linked employee
storeAssignmentService.assignStoreIfMissing(userBusinessLinkageService.ensureLinkedEmployee(staff), 1L);
User customer = userRepository.findByUsername("customer").orElse(null);
if (customer == null) {
@@ -124,16 +132,25 @@ public class DataInitializer implements CommandLineRunner {
customer.setUsername("customer");
customer.setPassword(passwordEncoder.encode("customer123"));
customer.setEmail("customer@petshop.com");
customer.setFirstName("Test");
customer.setLastName("Customer");
customer.setFullName("Test Customer");
customer.setPhone("000-000-1002");
customer.setRole(User.Role.CUSTOMER);
customer.setActive(true);
customer = userRepository.save(customer);
userRepository.save(customer);
System.out.println("Customer user created successfully");
} else {
System.out.println("Customer user already exists");
// Normalize missing fields if needed
boolean updated = false;
if (customer.getFirstName() == null || customer.getFirstName().isEmpty()) {
customer.setFirstName("Test");
updated = true;
}
if (customer.getLastName() == null || customer.getLastName().isEmpty()) {
customer.setLastName("Customer");
updated = true;
}
if (customer.getFullName() == null || customer.getFullName().isEmpty()) {
customer.setFullName("Test Customer");
updated = true;
@@ -155,12 +172,10 @@ public class DataInitializer implements CommandLineRunner {
updated = true;
}
if (updated) {
customer = userRepository.save(customer);
userRepository.save(customer);
System.out.println("Customer user normalized");
}
}
// Ensure linked customer
userBusinessLinkageService.ensureLinkedCustomer(customer);
System.out.println("==== DataInitializer: Completed ====");
}

View File

@@ -35,13 +35,14 @@ public class FlywayContextInitializer implements ApplicationContextInitializer<C
RuntimeException lastFailure = null;
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
Flyway.configure()
Flyway flyway = Flyway.configure()
.dataSource(url, username, password)
.locations(locations)
.baselineOnMigrate(environment.getProperty("spring.flyway.baseline-on-migrate", Boolean.class, false))
.baselineVersion(MigrationVersion.fromVersion(environment.getProperty("spring.flyway.baseline-version", "1")))
.load()
.migrate();
.load();
flyway.repair();
flyway.migrate();
return;
} catch (RuntimeException ex) {
lastFailure = ex;

View File

@@ -0,0 +1,37 @@
package com.petshop.backend.config;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.ProductRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
@Profile("local")
public class LocalCatalogSeedInitializer implements CommandLineRunner {
private final DataSource dataSource;
private final PetRepository petRepository;
private final ProductRepository productRepository;
public LocalCatalogSeedInitializer(DataSource dataSource, PetRepository petRepository, ProductRepository productRepository) {
this.dataSource = dataSource;
this.petRepository = petRepository;
this.productRepository = productRepository;
}
@Override
public void run(String... args) {
if (petRepository.count() > 6 || productRepository.count() > 6) {
return;
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(false, false, "UTF-8",
new ClassPathResource("dev/expand_pet_product_seed.sql"));
populator.execute(dataSource);
}
}

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.adoption.AdoptionRequest;
import com.petshop.backend.dto.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AdoptionService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -18,24 +17,28 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
@RestController
@RequestMapping("/api/v1/adoptions")
public class AdoptionController {
private final AdoptionService adoptionService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public AdoptionController(AdoptionService adoptionService, UserRepository userRepository, CustomerRepository customerRepository) {
public AdoptionController(AdoptionService adoptionService, UserRepository userRepository) {
this.adoptionService = adoptionService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<Page<AdoptionResponse>> getAllAdoptions(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long customerId,
@RequestParam(required = false) String status,
@RequestParam(required = false) Long storeId,
@RequestParam(required = false) String date,
Pageable pageable) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
@@ -43,13 +46,15 @@ public class AdoptionController {
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
Long customerId = null;
Long effectiveCustomerId = customerId;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
effectiveCustomerId = user.getId();
}
return ResponseEntity.ok(adoptionService.getAllAdoptions(q, pageable, customerId));
LocalDate adoptionDate = (date != null && !date.isBlank()) ? LocalDate.parse(date) : null;
return ResponseEntity.ok(adoptionService.getAllAdoptions(q, effectiveCustomerId, status, storeId, adoptionDate, pageable));
}
@GetMapping("/{id}")
@@ -63,29 +68,16 @@ public class AdoptionController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(adoptionService.getAdoptionById(id, customerId));
}
@PostMapping
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<AdoptionResponse> createAdoption(@Valid @RequestBody AdoptionRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
if (!request.getCustomerId().equals(customer.getCustomerId())) {
throw new org.springframework.security.access.AccessDeniedException("You can only create adoptions for yourself");
}
}
return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request));
}

View File

@@ -1,26 +1,40 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.analytics.DashboardResponse;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AnalyticsService;
import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
@RestController
@RequestMapping("/api/v1/analytics")
@PreAuthorize("hasRole('ADMIN')")
@PreAuthorize("hasAnyRole('ADMIN', 'STAFF')")
public class AnalyticsController {
private final AnalyticsService analyticsService;
private final UserRepository userRepository;
public AnalyticsController(AnalyticsService analyticsService) {
public AnalyticsController(AnalyticsService analyticsService, UserRepository userRepository) {
this.analyticsService = analyticsService;
this.userRepository = userRepository;
}
@GetMapping("/dashboard")
public ResponseEntity<DashboardResponse> getDashboard(
@RequestParam(defaultValue = "30") int days,
@RequestParam(defaultValue = "10") int top) {
return ResponseEntity.ok(analyticsService.getDashboardData(days, top));
if (days < 1 || days > 365) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "days must be between 1 and 365");
}
if (top < 1 || top > 50) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "top must be between 1 and 50");
}
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
return ResponseEntity.ok(analyticsService.getDashboardData(days, top, user));
}
}

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.appointment.AppointmentRequest;
import com.petshop.backend.dto.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.AppointmentService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -27,32 +26,39 @@ public class AppointmentController {
private final AppointmentService appointmentService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public AppointmentController(AppointmentService appointmentService, UserRepository userRepository, CustomerRepository customerRepository) {
public AppointmentController(AppointmentService appointmentService, UserRepository userRepository) {
this.appointmentService = appointmentService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<Page<AppointmentResponse>> getAllAppointments(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long storeId,
@RequestParam(required = false) String status,
@RequestParam(required = false) String date,
@RequestParam(required = false) Long customerId,
@RequestParam(required = false) Long employeeId,
Pageable pageable) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
Long customerId = null;
Long effectiveCustomerId = customerId;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
effectiveCustomerId = user.getId();
}
return ResponseEntity.ok(appointmentService.getAllAppointments(q, pageable, customerId));
LocalDate appointmentDate = (date != null && !date.isBlank()) ? LocalDate.parse(date) : null;
return ResponseEntity.ok(appointmentService.getAllAppointments(
q, effectiveCustomerId, employeeId, storeId, status, appointmentDate, pageable));
}
@GetMapping("/{id}")
@@ -66,8 +72,8 @@ public class AppointmentController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(appointmentService.getAppointmentById(id, customerId));
@@ -83,8 +89,8 @@ public class AppointmentController {
.orElse(null);
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
if (!request.getCustomerId().equals(customer.getCustomerId())) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
if (!request.getCustomerId().equals(user.getId())) {
throw new org.springframework.security.access.AccessDeniedException("You can only create appointments for yourself");
}
}

View File

@@ -7,15 +7,13 @@ import com.petshop.backend.dto.auth.ProfileUpdateRequest;
import com.petshop.backend.dto.auth.RegisterRequest;
import com.petshop.backend.dto.auth.RegisterResponse;
import com.petshop.backend.dto.auth.UserInfoResponse;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.JwtUtil;
import com.petshop.backend.service.AvatarStorageService;
import com.petshop.backend.service.UserBusinessLinkageService;
import com.petshop.backend.util.AuthenticationHelper;
import com.petshop.backend.util.PhoneUtils;
import jakarta.validation.Valid;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
@@ -28,6 +26,7 @@ import org.springframework.security.authentication.InternalAuthenticationService
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -43,37 +42,35 @@ public class AuthController {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final AvatarStorageService avatarStorageService;
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository, AvatarStorageService avatarStorageService) {
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, AvatarStorageService avatarStorageService) {
this.authenticationManager = authenticationManager;
this.userRepository = userRepository;
this.jwtUtil = jwtUtil;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.avatarStorageService = avatarStorageService;
}
@PostMapping("/register")
public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
String username = trimToNull(request.getUsername());
String email = trimToNull(request.getEmail());
NameParts nameParts = splitFullName(request.getFullName());
String phone = normalizePhone(request.getPhone());
if (userRepository.findByUsername(username).isPresent()) {
Map<String, String> error = new HashMap<>();
error.put("message", "Username already exists");
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
if (userRepository.findByEmail(email).isPresent()) {
Map<String, String> error = new HashMap<>();
error.put("message", "Email already exists");
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
String phone = trimToNull(request.getPhone());
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
Map<String, String> error = new HashMap<>();
error.put("message", "Phone already exists");
@@ -81,19 +78,18 @@ public class AuthController {
}
User user = new User();
user.setUsername(request.getUsername());
user.setUsername(username);
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setEmail(request.getEmail());
user.setFullName(request.getFullName());
user.setEmail(email);
user.setFirstName(nameParts.firstName());
user.setLastName(nameParts.lastName());
user.setFullName(nameParts.fullName());
user.setPhone(phone);
user.setRole(User.Role.CUSTOMER);
user.setActive(true);
User savedUser = userRepository.save(user);
// Create or link customer record
userBusinessLinkageService.ensureLinkedCustomer(savedUser);
String token = jwtUtil.generateToken(savedUser);
return ResponseEntity.status(HttpStatus.CREATED).body(new RegisterResponse(
@@ -142,23 +138,11 @@ public class AuthController {
}
}
@Transactional(readOnly = true)
@GetMapping("/me")
public ResponseEntity<UserInfoResponse> getCurrentUser() {
User user = getAuthenticatedUser();
EmployeeStore employeeStore = resolveEmployeeStore(user);
return ResponseEntity.ok(new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
return ResponseEntity.ok(toUserInfoResponse(user));
}
@PutMapping("/me")
@@ -166,31 +150,36 @@ public class AuthController {
User user = getAuthenticatedUser();
boolean invalidateToken = false;
if (request.getUsername() != null && !request.getUsername().equals(user.getUsername())) {
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
String username = trimToNull(request.getUsername());
if (username != null && !username.equals(user.getUsername())) {
if (userRepository.findByUsername(username).isPresent()) {
Map<String, String> error = new HashMap<>();
error.put("message", "Username already exists");
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
user.setUsername(request.getUsername());
user.setUsername(username);
invalidateToken = true;
}
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
if (userRepository.findByEmail(request.getEmail()).isPresent()) {
String email = trimToNull(request.getEmail());
if (email != null && !email.equals(user.getEmail())) {
if (userRepository.findByEmail(email).isPresent()) {
Map<String, String> error = new HashMap<>();
error.put("message", "Email already exists");
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
user.setEmail(request.getEmail());
user.setEmail(email);
}
if (request.getFullName() != null) {
user.setFullName(request.getFullName());
NameParts nameParts = splitFullName(request.getFullName());
user.setFirstName(nameParts.firstName());
user.setLastName(nameParts.lastName());
user.setFullName(nameParts.fullName());
}
if (request.getPhone() != null) {
String phone = trimToNull(request.getPhone());
String phone = normalizePhone(request.getPhone());
if (!java.util.Objects.equals(phone, user.getPhone())) {
if (phone != null && userRepository.findByPhone(phone)
.filter(existing -> !existing.getId().equals(user.getId()))
@@ -213,31 +202,28 @@ public class AuthController {
}
User updatedUser = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(updatedUser);
EmployeeStore employeeStore = resolveEmployeeStore(updatedUser);
return ResponseEntity.ok(new UserInfoResponse(
updatedUser.getId(),
updatedUser.getUsername(),
updatedUser.getEmail(),
updatedUser.getFullName(),
updatedUser.getPhone(),
avatarStorageService.toOwnerAvatarUrl(updatedUser),
updatedUser.getRole().name(),
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
}
private EmployeeStore resolveEmployeeStore(User user) {
if (user.getRole() == User.Role.CUSTOMER) {
return null;
private UserInfoResponse toUserInfoResponse(User user) {
StoreLocation primaryStore = user.getPrimaryStore();
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
String fullName = user.getFullName();
if (fullName == null || fullName.isBlank()) {
fullName = joinFullName(user.getFirstName(), user.getLastName());
}
return employeeRepository.findByUserId(user.getId())
.flatMap(employee -> employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId()))
.orElse(null);
return new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
fullName,
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
customerId,
primaryStore != null ? primaryStore.getStoreId() : null,
primaryStore != null ? primaryStore.getStoreName() : null
);
}
private String trimToNull(String value) {
@@ -248,6 +234,36 @@ public class AuthController {
return trimmed.isEmpty() ? null : trimmed;
}
private String normalizePhone(String value) {
return trimToNull(PhoneUtils.normalize(trimToNull(value)));
}
private NameParts splitFullName(String value) {
String normalized = trimToNull(value);
if (normalized == null) {
throw new IllegalArgumentException("Full name is required");
}
String[] parts = normalized.split("\\s+", 2);
String firstName = parts[0];
String lastName = parts.length > 1 ? parts[1] : "";
return new NameParts(firstName, lastName, joinFullName(firstName, lastName));
}
private String joinFullName(String firstName, String lastName) {
String first = trimToNull(firstName);
String last = trimToNull(lastName);
if (first == null) {
return last == null ? null : last;
}
if (last == null) {
return first;
}
return first + " " + last;
}
private record NameParts(String firstName, String lastName, String fullName) {
}
@PostMapping("/me/avatar")
public ResponseEntity<?> uploadAvatar(@RequestParam("avatar") MultipartFile file) {
User user = getAuthenticatedUser();

View File

@@ -25,8 +25,9 @@ public class CategoryController {
@GetMapping
public ResponseEntity<Page<CategoryResponse>> getAllCategories(
@RequestParam(required = false) String q,
@RequestParam(required = false) String type,
Pageable pageable) {
return ResponseEntity.ok(categoryService.getAllCategories(q, pageable));
return ResponseEntity.ok(categoryService.getAllCategories(q, type, pageable));
}
@GetMapping("/{id}")

View File

@@ -4,8 +4,8 @@ import com.petshop.backend.dto.chat.ConversationRequest;
import com.petshop.backend.dto.chat.ConversationResponse;
import com.petshop.backend.dto.chat.MessageRequest;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.ChatRealtimeService;
import com.petshop.backend.service.ChatService;
@@ -26,13 +26,11 @@ public class ChatController {
private final ChatService chatService;
private final ChatRealtimeService chatRealtimeService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public ChatController(ChatService chatService, ChatRealtimeService chatRealtimeService, UserRepository userRepository, CustomerRepository customerRepository) {
public ChatController(ChatService chatService, ChatRealtimeService chatRealtimeService, UserRepository userRepository) {
this.chatService = chatService;
this.chatRealtimeService = chatRealtimeService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
private User getCurrentUser() {
@@ -96,4 +94,13 @@ public class ChatController {
chatRealtimeService.publishConversationUpdate(id);
return ResponseEntity.ok(conversation);
}
@PutMapping("/conversations/{id}")
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<ConversationResponse> updateConversation(@PathVariable Long id, @Valid @RequestBody UpdateConversationRequest request) {
User user = getCurrentUser();
ConversationResponse conversation = chatService.updateConversation(id, user.getId(), user.getRole(), request);
chatRealtimeService.publishConversationUpdate(id);
return ResponseEntity.ok(conversation);
}
}

View File

@@ -1,9 +1,9 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest;
import com.petshop.backend.dto.customer.CustomerResponse;
import com.petshop.backend.service.CustomerService;
import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -17,45 +17,45 @@ import org.springframework.web.bind.annotation.*;
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public class CustomerController {
private final CustomerService customerService;
private final UserService userService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
public CustomerController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<Page<CustomerResponse>> getAllCustomers(
public ResponseEntity<Page<UserResponse>> getAllCustomers(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(customerService.getAllCustomers(q, pageable));
return ResponseEntity.ok(userService.getAllUsers(q, "CUSTOMER", pageable));
}
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(customerService.getCustomerById(id));
public ResponseEntity<UserResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<CustomerResponse> createCustomer(@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerService.createCustomer(request));
public ResponseEntity<UserResponse> createCustomer(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
}
@PutMapping("/{id}")
public ResponseEntity<CustomerResponse> updateCustomer(
public ResponseEntity<UserResponse> updateCustomer(
@PathVariable Long id,
@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.ok(customerService.updateCustomer(id, request));
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) {
customerService.bulkDeleteCustomers(request);
userService.bulkDeleteUsers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,10 +1,12 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.DropdownOption;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.*;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -16,24 +18,25 @@ import java.util.stream.Collectors;
public class DropdownController {
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final ServiceRepository serviceRepository;
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
private final StoreRepository storeRepository;
private final SupplierRepository supplierRepository;
private final UserRepository userRepository;
public DropdownController(PetRepository petRepository, CustomerRepository customerRepository,
ServiceRepository serviceRepository, ProductRepository productRepository,
CategoryRepository categoryRepository, StoreRepository storeRepository,
SupplierRepository supplierRepository) {
public DropdownController(PetRepository petRepository,
ServiceRepository serviceRepository, ProductRepository productRepository,
CategoryRepository categoryRepository, StoreRepository storeRepository,
SupplierRepository supplierRepository,
UserRepository userRepository) {
this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.serviceRepository = serviceRepository;
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
this.storeRepository = storeRepository;
this.supplierRepository = supplierRepository;
this.userRepository = userRepository;
}
@GetMapping("/pets")
@@ -45,16 +48,36 @@ public class DropdownController {
);
}
@GetMapping("/adoption-pets")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getAdoptionPets() {
return ResponseEntity.ok(
petRepository.findAllByPetStatusIgnoreCaseOrderByPetNameAsc("Available").stream()
.map(p -> new DropdownOption(p.getPetId(), p.getPetName()))
.collect(Collectors.toList())
);
}
@GetMapping("/customers")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomers() {
return ResponseEntity.ok(
customerRepository.findAll().stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@GetMapping("/appointment-customers")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
return ResponseEntity.ok(
userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@GetMapping("/services")
public ResponseEntity<List<DropdownOption>> getServices() {
return ResponseEntity.ok(
@@ -82,6 +105,29 @@ public class DropdownController {
);
}
@GetMapping("/product-categories")
public ResponseEntity<List<DropdownOption>> getProductCategories() {
return ResponseEntity.ok(
categoryRepository.findAll().stream()
.filter(c -> "product".equalsIgnoreCase(c.getCategoryType()))
.map(c -> new DropdownOption(c.getCategoryId(), c.getCategoryName()))
.collect(Collectors.toList())
);
}
@GetMapping("/pet-species")
public ResponseEntity<List<DropdownOption>> getPetSpecies() {
return ResponseEntity.ok(
petRepository.findAll().stream()
.map(p -> p.getPetSpecies())
.filter(species -> species != null && !species.isBlank())
.distinct()
.sorted(String.CASE_INSENSITIVE_ORDER)
.map(species -> new DropdownOption(null, species))
.collect(Collectors.toList())
);
}
@GetMapping("/stores")
public ResponseEntity<List<DropdownOption>> getStores() {
return ResponseEntity.ok(
@@ -91,6 +137,32 @@ public class DropdownController {
);
}
@GetMapping({"/stores/{storeId}/employees", "/employees"})
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getStoreEmployees(@PathVariable(required = false) Long storeId) {
List<User> employees;
if (storeId == null || storeId == 0) {
employees = userRepository.findByRoleAndActiveTrue(User.Role.STAFF);
} else {
employees = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
}
return ResponseEntity.ok(
employees.stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@GetMapping("/customers/{customerId}/pets")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
return ResponseEntity.ok(
petRepository.findAllByOwner_IdOrderByPetNameAsc(customerId).stream()
.map(p -> new DropdownOption(p.getPetId(), p.getPetName()))
.collect(Collectors.toList())
);
}
@GetMapping("/suppliers")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<DropdownOption>> getSuppliers() {

View File

@@ -1,8 +1,8 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.employee.EmployeeRequest;
import com.petshop.backend.dto.employee.EmployeeResponse;
import com.petshop.backend.service.EmployeeService;
import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.service.UserService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -15,35 +15,40 @@ import org.springframework.web.bind.annotation.*;
@RequestMapping("/api/v1/employees")
@PreAuthorize("hasRole('ADMIN')")
public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
private final UserService userService;
public EmployeeController(UserService userService) {
this.userService = userService;
}
@GetMapping
public ResponseEntity<Page<EmployeeResponse>> getAllEmployees(@RequestParam(required = false) String q, Pageable pageable) {
return ResponseEntity.ok(employeeService.getAllEmployees(q, pageable));
public ResponseEntity<Page<UserResponse>> getAllEmployees(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(userService.getAllUsers(q, "STAFF", pageable));
}
@GetMapping("/{id}")
public ResponseEntity<EmployeeResponse> getEmployeeById(@PathVariable Long id) {
return ResponseEntity.ok(employeeService.getEmployeeById(id));
public ResponseEntity<UserResponse> getEmployeeById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<EmployeeResponse> createEmployee(@Valid @RequestBody EmployeeRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(employeeService.createEmployee(request));
public ResponseEntity<UserResponse> createEmployee(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
}
@PutMapping("/{id}")
public ResponseEntity<EmployeeResponse> updateEmployee(@PathVariable Long id, @Valid @RequestBody EmployeeRequest request) {
return ResponseEntity.ok(employeeService.updateEmployee(id, request));
public ResponseEntity<UserResponse> updateEmployee(
@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
employeeService.deleteEmployee(id);
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}

View File

@@ -26,8 +26,9 @@ public class InventoryController {
@GetMapping
public ResponseEntity<Page<InventoryResponse>> getAllInventory(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long storeId,
Pageable pageable) {
return ResponseEntity.ok(inventoryService.getAllInventory(q, pageable));
return ResponseEntity.ok(inventoryService.getAllInventory(q, storeId, pageable));
}
@GetMapping("/{id}")

View File

@@ -0,0 +1,76 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.pet.MyPetRequest;
import com.petshop.backend.dto.pet.MyPetResponse;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.PetService;
import com.petshop.backend.util.AuthenticationHelper;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/my-pets")
@PreAuthorize("isAuthenticated()")
public class MyPetController {
private final PetService petService;
private final UserRepository userRepository;
public MyPetController(PetService petService, UserRepository userRepository) {
this.petService = petService;
this.userRepository = userRepository;
}
@GetMapping
public ResponseEntity<List<MyPetResponse>> getMyPets() {
return ResponseEntity.ok(petService.getMyPets(currentUserId()));
}
@PostMapping
public ResponseEntity<MyPetResponse> createMyPet(@Valid @RequestBody MyPetRequest request) {
return ResponseEntity.ok(petService.createMyPet(currentUserId(), request));
}
@PutMapping("/{id}")
public ResponseEntity<MyPetResponse> updateMyPet(@PathVariable Long id, @Valid @RequestBody MyPetRequest request) {
return ResponseEntity.ok(petService.updateMyPet(currentUserId(), id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteMyPet(@PathVariable Long id) {
petService.deleteMyPet(currentUserId(), id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/image")
public ResponseEntity<?> uploadMyPetImage(@PathVariable Long id, @RequestParam("image") MultipartFile image) {
try {
return ResponseEntity.ok(petService.uploadMyPetImage(currentUserId(), id, image));
} catch (IllegalArgumentException ex) {
return ResponseEntity.badRequest().body(Map.of("message", ex.getMessage()));
} catch (IOException ex) {
return ResponseEntity.badRequest().body(Map.of("message", "Failed to upload pet image: " + ex.getMessage()));
}
}
private Long currentUserId() {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
return user.getId();
}
}

View File

@@ -25,8 +25,11 @@ public class PetController {
@GetMapping
public ResponseEntity<Page<PetResponse>> getAllPets(
@RequestParam(required = false) String q,
@RequestParam(required = false) String species,
@RequestParam(required = false) String status,
@RequestParam(required = false) Long storeId,
Pageable pageable) {
return ResponseEntity.ok(petService.getAllPets(q, pageable));
return ResponseEntity.ok(petService.getAllPets(q, species, status, storeId, pageable));
}
@GetMapping("/{id}")

View File

@@ -48,12 +48,8 @@ public class PetImageController {
@GetMapping("/{id}/image")
public ResponseEntity<Resource> getPetImage(@PathVariable Long id) {
try {
PetService.ImagePayload payload = petService.loadPetImage(id, currentUserId(), currentUserRole());
return ResponseEntity.ok().contentType(payload.mediaType()).body(payload.resource());
} catch (PetService.ForbiddenImageAccessException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
PetService.ImagePayload payload = petService.loadPetImage(id, currentUserId(), currentUserRole());
return ResponseEntity.ok().contentType(payload.mediaType()).body(payload.resource());
}
@DeleteMapping("/{id}/image")

View File

@@ -25,8 +25,9 @@ public class ProductController {
@GetMapping
public ResponseEntity<Page<ProductResponse>> getAllProducts(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long categoryId,
Pageable pageable) {
return ResponseEntity.ok(productService.getAllProducts(q, pageable));
return ResponseEntity.ok(productService.getAllProducts(q, categoryId, pageable));
}
@GetMapping("/{id}")

View File

@@ -26,8 +26,10 @@ public class ProductSupplierController {
@GetMapping
public ResponseEntity<Page<ProductSupplierResponse>> getAllProductSuppliers(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long productId,
@RequestParam(required = false) Long supplierId,
Pageable pageable) {
return ResponseEntity.ok(productSupplierService.getAllProductSuppliers(q, pageable));
return ResponseEntity.ok(productSupplierService.getAllProductSuppliers(q, productId, supplierId, pageable));
}
@GetMapping("/{productId}/{supplierId}")

View File

@@ -22,8 +22,9 @@ public class PurchaseOrderController {
@GetMapping
public ResponseEntity<Page<PurchaseOrderResponse>> getAllPurchaseOrders(
@RequestParam(required = false) String q,
@RequestParam(required = false) Long storeId,
Pageable pageable) {
return ResponseEntity.ok(purchaseOrderService.getAllPurchaseOrders(q, pageable));
return ResponseEntity.ok(purchaseOrderService.getAllPurchaseOrders(q, storeId, pageable));
}
@GetMapping("/{id}")

View File

@@ -3,8 +3,7 @@ package com.petshop.backend.controller;
import com.petshop.backend.dto.refund.RefundRequest;
import com.petshop.backend.dto.refund.RefundResponse;
import com.petshop.backend.dto.refund.RefundUpdateRequest;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.service.RefundService;
import com.petshop.backend.util.AuthenticationHelper;
@@ -16,9 +15,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/refunds")
@@ -26,37 +23,28 @@ public class RefundController {
private final RefundService refundService;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public RefundController(RefundService refundService, UserRepository userRepository, CustomerRepository customerRepository) {
public RefundController(RefundService refundService, UserRepository userRepository) {
this.refundService = refundService;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@PostMapping
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<?> createRefund(@Valid @RequestBody RefundRequest request) {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
public ResponseEntity<RefundResponse> createRefund(@Valid @RequestBody RefundRequest request) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
}
RefundResponse refund = refundService.createRefund(request, customerId);
return ResponseEntity.status(HttpStatus.CREATED).body(refund);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("message", e.getMessage());
return ResponseEntity.badRequest().body(error);
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.status(HttpStatus.CREATED).body(refundService.createRefund(request, customerId));
}
@GetMapping
@@ -70,8 +58,8 @@ public class RefundController {
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
List<RefundResponse> refunds = refundService.getAllRefunds(customerId);
@@ -80,54 +68,32 @@ public class RefundController {
@GetMapping("/{id}")
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<?> getRefundById(@PathVariable Long id) {
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
public ResponseEntity<RefundResponse> getRefundById(@PathVariable Long id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String role = authentication.getAuthorities().stream()
.findFirst()
.map(authority -> authority.getAuthority().replace("ROLE_", ""))
.orElse(null);
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
customerId = customer.getCustomerId();
}
RefundResponse refund = refundService.getRefundById(id, customerId);
return ResponseEntity.ok(refund);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("message", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
Long customerId = null;
if (role != null && role.equals("CUSTOMER")) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
customerId = user.getId();
}
return ResponseEntity.ok(refundService.getRefundById(id, customerId));
}
@PutMapping("/{id}")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<?> updateRefund(@PathVariable Long id, @Valid @RequestBody RefundUpdateRequest request) {
try {
RefundResponse refund = refundService.updateRefundStatus(id, request.getStatus());
return ResponseEntity.ok(refund);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("message", e.getMessage());
return ResponseEntity.badRequest().body(error);
}
public ResponseEntity<RefundResponse> updateRefund(@PathVariable Long id, @Valid @RequestBody RefundUpdateRequest request) {
return ResponseEntity.ok(refundService.updateRefundStatus(id, request.getStatus()));
}
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> deleteRefund(@PathVariable Long id) {
try {
refundService.deleteRefund(id);
Map<String, String> response = new HashMap<>();
response.put("message", "Refund deleted successfully");
return ResponseEntity.ok(response);
} catch (RuntimeException e) {
Map<String, String> error = new HashMap<>();
error.put("message", e.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
public ResponseEntity<Void> deleteRefund(@PathVariable Long id) {
refundService.deleteRefund(id);
return ResponseEntity.noContent().build();
}
}

View File

@@ -20,6 +20,10 @@ public class AdoptionRequest {
@NotBlank(message = "Adoption status is required")
private String adoptionStatus;
private Long employeeId;
private Long sourceStoreId;
public Long getPetId() {
return petId;
}
@@ -56,6 +60,22 @@ public class AdoptionRequest {
this.adoptionStatus = adoptionStatus;
}
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
public Long getSourceStoreId() {
return sourceStoreId;
}
public void setSourceStoreId(Long sourceStoreId) {
this.sourceStoreId = sourceStoreId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -64,12 +84,14 @@ public class AdoptionRequest {
return Objects.equals(petId, that.petId) &&
Objects.equals(customerId, that.customerId) &&
Objects.equals(adoptionDate, that.adoptionDate) &&
Objects.equals(adoptionStatus, that.adoptionStatus);
Objects.equals(adoptionStatus, that.adoptionStatus) &&
Objects.equals(employeeId, that.employeeId) &&
Objects.equals(sourceStoreId, that.sourceStoreId);
}
@Override
public int hashCode() {
return Objects.hash(petId, customerId, adoptionDate, adoptionStatus);
return Objects.hash(petId, customerId, adoptionDate, adoptionStatus, employeeId, sourceStoreId);
}
@Override
@@ -79,6 +101,8 @@ public class AdoptionRequest {
", customerId=" + customerId +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", employeeId=" + employeeId +
", sourceStoreId=" + sourceStoreId +
'}';
}
}

View File

@@ -11,152 +11,75 @@ public class AdoptionResponse {
private String petName;
private Long customerId;
private String customerName;
private Long employeeId;
private String employeeName;
private Long sourceStoreId;
private String sourceStoreName;
private LocalDate adoptionDate;
private String adoptionStatus;
private BigDecimal adoptionFee;
private Long employeeId;
private String employeeName;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public AdoptionResponse() {
}
public AdoptionResponse(Long adoptionId, Long petId, String petName, Long customerId, String customerName, LocalDate adoptionDate, String adoptionStatus, BigDecimal adoptionFee, LocalDateTime createdAt, LocalDateTime updatedAt) {
public AdoptionResponse(Long adoptionId, Long petId, String petName, Long customerId, String customerName, Long employeeId, String employeeName, Long sourceStoreId, String sourceStoreName, LocalDate adoptionDate, String adoptionStatus, BigDecimal adoptionFee, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.adoptionId = adoptionId;
this.petId = petId;
this.petName = petName;
this.customerId = customerId;
this.customerName = customerName;
this.adoptionDate = adoptionDate;
this.adoptionStatus = adoptionStatus;
this.adoptionFee = adoptionFee;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAdoptionId() {
return adoptionId;
}
public void setAdoptionId(Long adoptionId) {
this.adoptionId = adoptionId;
}
public Long getPetId() {
return petId;
}
public void setPetId(Long petId) {
this.petId = petId;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public LocalDate getAdoptionDate() {
return adoptionDate;
}
public void setAdoptionDate(LocalDate adoptionDate) {
this.sourceStoreId = sourceStoreId;
this.sourceStoreName = sourceStoreName;
this.adoptionDate = adoptionDate;
}
public String getAdoptionStatus() {
return adoptionStatus;
}
public void setAdoptionStatus(String adoptionStatus) {
this.adoptionStatus = adoptionStatus;
}
public BigDecimal getAdoptionFee() {
return adoptionFee;
}
public void setAdoptionFee(BigDecimal adoptionFee) {
this.adoptionFee = adoptionFee;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AdoptionResponse that = (AdoptionResponse) o;
return Objects.equals(adoptionId, that.adoptionId) && Objects.equals(petId, that.petId) && Objects.equals(petName, that.petName) && Objects.equals(customerId, that.customerId) && Objects.equals(customerName, that.customerName) && Objects.equals(adoptionDate, that.adoptionDate) && Objects.equals(adoptionStatus, that.adoptionStatus) && Objects.equals(adoptionFee, that.adoptionFee) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
public Long getAdoptionId() { return adoptionId; }
public void setAdoptionId(Long adoptionId) { this.adoptionId = adoptionId; }
@Override
public int hashCode() {
return Objects.hash(adoptionId, petId, petName, customerId, customerName, adoptionDate, adoptionStatus, adoptionFee, createdAt, updatedAt);
}
public Long getPetId() { return petId; }
public void setPetId(Long petId) { this.petId = petId; }
@Override
public String toString() {
return "AdoptionResponse{" +
"adoptionId=" + adoptionId +
", petId=" + petId +
", petName='" + petName + '\'' +
", customerId=" + customerId +
", customerName='" + customerName + '\'' +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", adoptionFee=" + adoptionFee +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
public String getPetName() { return petName; }
public void setPetName(String petName) { this.petName = petName; }
public Long getCustomerId() { return customerId; }
public void setCustomerId(Long customerId) { this.customerId = customerId; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public Long getEmployeeId() { return employeeId; }
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
public String getEmployeeName() { return employeeName; }
public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
public Long getSourceStoreId() { return sourceStoreId; }
public void setSourceStoreId(Long sourceStoreId) { this.sourceStoreId = sourceStoreId; }
public String getSourceStoreName() { return sourceStoreName; }
public void setSourceStoreName(String sourceStoreName) { this.sourceStoreName = sourceStoreName; }
public LocalDate getAdoptionDate() { return adoptionDate; }
public void setAdoptionDate(LocalDate adoptionDate) { this.adoptionDate = adoptionDate; }
public String getAdoptionStatus() { return adoptionStatus; }
public void setAdoptionStatus(String adoptionStatus) { this.adoptionStatus = adoptionStatus; }
public BigDecimal getAdoptionFee() { return adoptionFee; }
public void setAdoptionFee(BigDecimal adoptionFee) { this.adoptionFee = adoptionFee; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -9,15 +9,19 @@ public class DashboardResponse {
private InventorySummary inventorySummary;
private List<TopProduct> topProducts;
private List<DailySales> dailySales;
private List<PaymentMethodData> paymentMethods;
private List<EmployeePerformanceData> employeePerformance;
public DashboardResponse() {
}
public DashboardResponse(SalesSummary salesSummary, InventorySummary inventorySummary, List<TopProduct> topProducts, List<DailySales> dailySales) {
public DashboardResponse(SalesSummary salesSummary, InventorySummary inventorySummary, List<TopProduct> topProducts, List<DailySales> dailySales, List<PaymentMethodData> paymentMethods, List<EmployeePerformanceData> employeePerformance) {
this.salesSummary = salesSummary;
this.inventorySummary = inventorySummary;
this.topProducts = topProducts;
this.dailySales = dailySales;
this.paymentMethods = paymentMethods;
this.employeePerformance = employeePerformance;
}
public SalesSummary getSalesSummary() {
@@ -52,17 +56,33 @@ public class DashboardResponse {
this.dailySales = dailySales;
}
public List<PaymentMethodData> getPaymentMethods() {
return paymentMethods;
}
public void setPaymentMethods(List<PaymentMethodData> paymentMethods) {
this.paymentMethods = paymentMethods;
}
public List<EmployeePerformanceData> getEmployeePerformance() {
return employeePerformance;
}
public void setEmployeePerformance(List<EmployeePerformanceData> employeePerformance) {
this.employeePerformance = employeePerformance;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DashboardResponse that = (DashboardResponse) o;
return Objects.equals(salesSummary, that.salesSummary) && Objects.equals(inventorySummary, that.inventorySummary) && Objects.equals(topProducts, that.topProducts) && Objects.equals(dailySales, that.dailySales);
return Objects.equals(salesSummary, that.salesSummary) && Objects.equals(inventorySummary, that.inventorySummary) && Objects.equals(topProducts, that.topProducts) && Objects.equals(dailySales, that.dailySales) && Objects.equals(paymentMethods, that.paymentMethods) && Objects.equals(employeePerformance, that.employeePerformance);
}
@Override
public int hashCode() {
return Objects.hash(salesSummary, inventorySummary, topProducts, dailySales);
return Objects.hash(salesSummary, inventorySummary, topProducts, dailySales, paymentMethods, employeePerformance);
}
@Override
@@ -72,6 +92,8 @@ public class DashboardResponse {
", inventorySummary=" + inventorySummary +
", topProducts=" + topProducts +
", dailySales=" + dailySales +
", paymentMethods=" + paymentMethods +
", employeePerformance=" + employeePerformance +
'}';
}
@@ -80,15 +102,17 @@ public class DashboardResponse {
private Long totalSales;
private BigDecimal totalRefunds;
private Long totalRefundCount;
private Long totalItemsSold;
public SalesSummary() {
}
public SalesSummary(BigDecimal totalRevenue, Long totalSales, BigDecimal totalRefunds, Long totalRefundCount) {
public SalesSummary(BigDecimal totalRevenue, Long totalSales, BigDecimal totalRefunds, Long totalRefundCount, Long totalItemsSold) {
this.totalRevenue = totalRevenue;
this.totalSales = totalSales;
this.totalRefunds = totalRefunds;
this.totalRefundCount = totalRefundCount;
this.totalItemsSold = totalItemsSold;
}
public BigDecimal getTotalRevenue() {
@@ -123,17 +147,25 @@ public class DashboardResponse {
this.totalRefundCount = totalRefundCount;
}
public Long getTotalItemsSold() {
return totalItemsSold;
}
public void setTotalItemsSold(Long totalItemsSold) {
this.totalItemsSold = totalItemsSold;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SalesSummary that = (SalesSummary) o;
return Objects.equals(totalRevenue, that.totalRevenue) && Objects.equals(totalSales, that.totalSales) && Objects.equals(totalRefunds, that.totalRefunds) && Objects.equals(totalRefundCount, that.totalRefundCount);
return Objects.equals(totalRevenue, that.totalRevenue) && Objects.equals(totalSales, that.totalSales) && Objects.equals(totalRefunds, that.totalRefunds) && Objects.equals(totalRefundCount, that.totalRefundCount) && Objects.equals(totalItemsSold, that.totalItemsSold);
}
@Override
public int hashCode() {
return Objects.hash(totalRevenue, totalSales, totalRefunds, totalRefundCount);
return Objects.hash(totalRevenue, totalSales, totalRefunds, totalRefundCount, totalItemsSold);
}
@Override
@@ -143,10 +175,69 @@ public class DashboardResponse {
", totalSales=" + totalSales +
", totalRefunds=" + totalRefunds +
", totalRefundCount=" + totalRefundCount +
", totalItemsSold=" + totalItemsSold +
'}';
}
}
public static class PaymentMethodData {
private String paymentMethod;
private Long count;
public PaymentMethodData() {
}
public PaymentMethodData(String paymentMethod, Long count) {
this.paymentMethod = paymentMethod;
this.count = count;
}
public String getPaymentMethod() {
return paymentMethod;
}
public void setPaymentMethod(String paymentMethod) {
this.paymentMethod = paymentMethod;
}
public Long getCount() {
return count;
}
public void setCount(Long count) {
this.count = count;
}
}
public static class EmployeePerformanceData {
private String employeeName;
private BigDecimal revenue;
public EmployeePerformanceData() {
}
public EmployeePerformanceData(String employeeName, BigDecimal revenue) {
this.employeeName = employeeName;
this.revenue = revenue;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public BigDecimal getRevenue() {
return revenue;
}
public void setRevenue(BigDecimal revenue) {
this.revenue = revenue;
}
}
public static class InventorySummary {
private Long totalProducts;
private Long lowStockProducts;

View File

@@ -1,10 +1,8 @@
package com.petshop.backend.dto.appointment;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
public class AppointmentRequest {
@@ -26,8 +24,9 @@ public class AppointmentRequest {
@NotNull(message = "Appointment status is required")
private String appointmentStatus;
@NotEmpty(message = "At least one pet must be specified")
private List<Long> petIds;
private Long petId;
private Long employeeId;
// @NotNull(message = "Employee ID is required")
private Long employeeId;
@@ -80,12 +79,20 @@ public class AppointmentRequest {
this.appointmentStatus = appointmentStatus;
}
public List<Long> getPetIds() {
return petIds;
public Long getPetId() {
return petId;
}
public void setPetIds(List<Long> petIds) {
this.petIds = petIds;
public void setPetId(Long petId) {
this.petId = petId;
}
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
public Long getEmployeeId() {
@@ -107,12 +114,13 @@ public class AppointmentRequest {
Objects.equals(appointmentDate, that.appointmentDate) &&
Objects.equals(appointmentTime, that.appointmentTime) &&
Objects.equals(appointmentStatus, that.appointmentStatus) &&
Objects.equals(petIds, that.petIds);
Objects.equals(petId, that.petId) &&
Objects.equals(employeeId, that.employeeId);
}
@Override
public int hashCode() {
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petIds);
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petId, employeeId);
}
@Override
@@ -124,7 +132,8 @@ public class AppointmentRequest {
", appointmentDate=" + appointmentDate +
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", petIds=" + petIds +
", petId=" + petId +
", employeeId=" + employeeId +
'}';
}
}

View File

@@ -3,8 +3,6 @@ package com.petshop.backend.dto.appointment;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
public class AppointmentResponse {
private Long appointmentId;
@@ -19,8 +17,8 @@ public class AppointmentResponse {
private LocalDate appointmentDate;
private LocalTime appointmentTime;
private String appointmentStatus;
private List<String> petNames;
private List<Long> petIds;
private String petName;
private Long petId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -30,8 +28,8 @@ public class AppointmentResponse {
public AppointmentResponse(Long appointmentId, Long customerId, String customerName,
Long storeId, String storeName, Long serviceId, String serviceName,
LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus,
List<String> petNames, List<Long> petIds, LocalDateTime createdAt,
LocalDateTime updatedAt, Long employeeId, String employeeName) {
String petName, Long petId, LocalDateTime createdAt, LocalDateTime updatedAt,
Long employeeId, String employeeName) {
this.appointmentId = appointmentId;
this.customerId = customerId;
this.customerName = customerName;
@@ -42,170 +40,59 @@ public class AppointmentResponse {
this.appointmentDate = appointmentDate;
this.appointmentTime = appointmentTime;
this.appointmentStatus = appointmentStatus;
this.petNames = petNames;
this.petIds = petIds;
this.petName = petName;
this.petId = petId;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.employeeId = employeeId;
this.employeeName = employeeName;
}
public Long getAppointmentId() {
return appointmentId;
}
public void setAppointmentId(Long appointmentId) {
this.appointmentId = appointmentId;
}
public Long getAppointmentId() { return appointmentId; }
public void setAppointmentId(Long appointmentId) { this.appointmentId = appointmentId; }
public Long getCustomerId() {
return customerId;
}
public Long getCustomerId() { return customerId; }
public void setCustomerId(Long customerId) { this.customerId = customerId; }
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public String getCustomerName() {
return customerName;
}
public Long getStoreId() { return storeId; }
public void setStoreId(Long storeId) { this.storeId = storeId; }
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getStoreName() { return storeName; }
public void setStoreName(String storeName) { this.storeName = storeName; }
public Long getStoreId() {
return storeId;
}
public Long getServiceId() { return serviceId; }
public void setServiceId(Long serviceId) { this.serviceId = serviceId; }
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
public String getServiceName() { return serviceName; }
public void setServiceName(String serviceName) { this.serviceName = serviceName; }
public String getStoreName() {
return storeName;
}
public Long getEmployeeId() { return employeeId; }
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getEmployeeName() { return employeeName; }
public void setEmployeeName(String employeeName) { this.employeeName = employeeName; }
public Long getServiceId() {
return serviceId;
}
public LocalDate getAppointmentDate() { return appointmentDate; }
public void setAppointmentDate(LocalDate appointmentDate) { this.appointmentDate = appointmentDate; }
public void setServiceId(Long serviceId) {
this.serviceId = serviceId;
}
public LocalTime getAppointmentTime() { return appointmentTime; }
public void setAppointmentTime(LocalTime appointmentTime) { this.appointmentTime = appointmentTime; }
public String getServiceName() {
return serviceName;
}
public String getAppointmentStatus() { return appointmentStatus; }
public void setAppointmentStatus(String appointmentStatus) { this.appointmentStatus = appointmentStatus; }
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getPetName() { return petName; }
public void setPetName(String petName) { this.petName = petName; }
public Long getPetId() { return petId; }
public void setPetId(Long petId) { this.petId = petId; }
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDate getAppointmentDate() {
return appointmentDate;
}
public void setAppointmentDate(LocalDate appointmentDate) {
this.appointmentDate = appointmentDate;
}
public LocalTime getAppointmentTime() {
return appointmentTime;
}
public void setAppointmentTime(LocalTime appointmentTime) {
this.appointmentTime = appointmentTime;
}
public String getAppointmentStatus() {
return appointmentStatus;
}
public void setAppointmentStatus(String appointmentStatus) {
this.appointmentStatus = appointmentStatus;
}
public List<String> getPetNames() {
return petNames;
}
public void setPetNames(List<String> petNames) {
this.petNames = petNames;
}
public List<Long> getPetIds() {
return petIds;
}
public void setPetIds(List<Long> petIds) {
this.petIds = petIds;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AppointmentResponse that = (AppointmentResponse) o;
return Objects.equals(appointmentId, that.appointmentId) && Objects.equals(customerId, that.customerId) && Objects.equals(customerName, that.customerName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName) && Objects.equals(serviceId, that.serviceId) && Objects.equals(serviceName, that.serviceName) && Objects.equals(appointmentDate, that.appointmentDate) && Objects.equals(appointmentTime, that.appointmentTime) && Objects.equals(appointmentStatus, that.appointmentStatus) && Objects.equals(petNames, that.petNames) && Objects.equals(petIds, that.petIds) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(appointmentId, customerId, customerName, storeId, storeName, serviceId, serviceName, appointmentDate, appointmentTime, appointmentStatus, petNames, petIds, createdAt, updatedAt);
}
@Override
public String toString() {
return "AppointmentResponse{" +
"appointmentId=" + appointmentId +
", customerId=" + customerId +
", customerName='" + customerName + '\'' +
", storeId=" + storeId +
", storeName='" + storeName + '\'' +
", serviceId=" + serviceId +
", serviceName='" + serviceName + '\'' +
", appointmentDate=" + appointmentDate +
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", petNames=" + petNames +
", petIds=" + petIds +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -10,13 +10,14 @@ public class UserInfoResponse {
private String phone;
private String avatarUrl;
private String role;
private Long customerId;
private Long storeId;
private String storeName;
public UserInfoResponse() {
}
public UserInfoResponse(Long id, String username, String email, String fullName, String phone, String avatarUrl, String role, Long storeId, String storeName) {
public UserInfoResponse(Long id, String username, String email, String fullName, String phone, String avatarUrl, String role, Long customerId, Long storeId, String storeName) {
this.id = id;
this.username = username;
this.email = email;
@@ -24,6 +25,7 @@ public class UserInfoResponse {
this.phone = phone;
this.avatarUrl = avatarUrl;
this.role = role;
this.customerId = customerId;
this.storeId = storeId;
this.storeName = storeName;
}
@@ -84,6 +86,15 @@ public class UserInfoResponse {
this.role = role;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public Long getStoreId() {
return storeId;
}
@@ -112,13 +123,14 @@ public class UserInfoResponse {
Objects.equals(phone, that.phone) &&
Objects.equals(avatarUrl, that.avatarUrl) &&
Objects.equals(role, that.role) &&
Objects.equals(customerId, that.customerId) &&
Objects.equals(storeId, that.storeId) &&
Objects.equals(storeName, that.storeName);
}
@Override
public int hashCode() {
return Objects.hash(id, username, email, fullName, phone, avatarUrl, role, storeId, storeName);
return Objects.hash(id, username, email, fullName, phone, avatarUrl, role, customerId, storeId, storeName);
}
@Override
@@ -131,6 +143,7 @@ public class UserInfoResponse {
", phone='" + phone + '\'' +
", avatarUrl='" + avatarUrl + '\'' +
", role='" + role + '\'' +
", customerId=" + customerId +
", storeId=" + storeId +
", storeName='" + storeName + '\'' +
'}';

View File

@@ -1,18 +1,15 @@
package com.petshop.backend.dto.chat;
import jakarta.validation.constraints.NotBlank;
public class MessageRequest {
@NotBlank(message = "Message content is required")
private String content;
private String attachmentUrl;
private String attachmentName;
private String attachmentMimeType;
private Long attachmentSizeBytes;
public MessageRequest() {
}
public MessageRequest(String content) {
this.content = content;
}
public String getContent() {
return content;
}
@@ -20,4 +17,36 @@ public class MessageRequest {
public void setContent(String content) {
this.content = content;
}
public String getAttachmentUrl() {
return attachmentUrl;
}
public void setAttachmentUrl(String attachmentUrl) {
this.attachmentUrl = attachmentUrl;
}
public String getAttachmentName() {
return attachmentName;
}
public void setAttachmentName(String attachmentName) {
this.attachmentName = attachmentName;
}
public String getAttachmentMimeType() {
return attachmentMimeType;
}
public void setAttachmentMimeType(String attachmentMimeType) {
this.attachmentMimeType = attachmentMimeType;
}
public Long getAttachmentSizeBytes() {
return attachmentSizeBytes;
}
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
this.attachmentSizeBytes = attachmentSizeBytes;
}
}

View File

@@ -11,6 +11,10 @@ public class MessageResponse {
private String content;
private LocalDateTime timestamp;
private Boolean isRead;
private String attachmentUrl;
private String attachmentName;
private String attachmentMimeType;
private Long attachmentSizeBytes;
public MessageResponse() {
}
@@ -32,6 +36,10 @@ public class MessageResponse {
response.setContent(message.getContent());
response.setTimestamp(message.getTimestamp());
response.setIsRead(message.getIsRead());
response.setAttachmentUrl(message.getAttachmentUrl());
response.setAttachmentName(message.getAttachmentName());
response.setAttachmentMimeType(message.getAttachmentMimeType());
response.setAttachmentSizeBytes(message.getAttachmentSizeBytes());
return response;
}
@@ -82,4 +90,36 @@ public class MessageResponse {
public void setIsRead(Boolean isRead) {
this.isRead = isRead;
}
public String getAttachmentUrl() {
return attachmentUrl;
}
public void setAttachmentUrl(String attachmentUrl) {
this.attachmentUrl = attachmentUrl;
}
public String getAttachmentName() {
return attachmentName;
}
public void setAttachmentName(String attachmentName) {
this.attachmentName = attachmentName;
}
public String getAttachmentMimeType() {
return attachmentMimeType;
}
public void setAttachmentMimeType(String attachmentMimeType) {
this.attachmentMimeType = attachmentMimeType;
}
public Long getAttachmentSizeBytes() {
return attachmentSizeBytes;
}
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
this.attachmentSizeBytes = attachmentSizeBytes;
}
}

View File

@@ -0,0 +1,25 @@
package com.petshop.backend.dto.chat;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
public class UpdateConversationRequest {
@NotBlank(message = "Status is required")
@Pattern(regexp = "^(OPEN|CLOSED)$", message = "Status must be OPEN or CLOSED")
private String status;
public UpdateConversationRequest() {
}
public UpdateConversationRequest(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@@ -1,64 +0,0 @@
package com.petshop.backend.dto.customer;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import java.util.Objects;
public class CustomerRequest {
@NotBlank(message = "First name is required")
private String firstName;
@NotBlank(message = "Last name is required")
private String lastName;
@Email(message = "Invalid email format")
private String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomerRequest that = (CustomerRequest) o;
return Objects.equals(firstName, that.firstName) &&
Objects.equals(lastName, that.lastName) &&
Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, email);
}
@Override
public String toString() {
return "CustomerRequest{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
'}';
}
}

View File

@@ -1,98 +0,0 @@
package com.petshop.backend.dto.customer;
import java.time.LocalDateTime;
import java.util.Objects;
public class CustomerResponse {
private Long customerId;
private String firstName;
private String lastName;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public CustomerResponse() {
}
public CustomerResponse(Long customerId, String firstName, String lastName, String email, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerId = customerId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomerResponse that = (CustomerResponse) o;
return Objects.equals(customerId, that.customerId) && Objects.equals(firstName, that.firstName) && Objects.equals(lastName, that.lastName) && Objects.equals(email, that.email) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(customerId, firstName, lastName, email, createdAt, updatedAt);
}
@Override
public String toString() {
return "CustomerResponse{" +
"customerId=" + customerId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,51 +0,0 @@
package com.petshop.backend.dto.employee;
import com.petshop.backend.entity.User;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class EmployeeRequest {
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
private String username;
@Size(min = 6, message = "Password must be at least 6 characters")
private String password;
@NotBlank(message = "First name is required")
private String firstName;
@NotBlank(message = "Last name is required")
private String lastName;
@Email(message = "Invalid email format")
private String email;
@NotBlank(message = "Phone is required")
@Size(max = 20, message = "Phone must not exceed 20 characters")
private String phone;
@NotNull(message = "Role is required")
private User.Role role;
private Boolean active = true;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public User.Role getRole() { return role; }
public void setRole(User.Role role) { this.role = role; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
}

View File

@@ -1,43 +0,0 @@
package com.petshop.backend.dto.employee;
import java.time.LocalDateTime;
public class EmployeeResponse {
private Long employeeId;
private Long userId;
private String username;
private String firstName;
private String lastName;
private String fullName;
private String email;
private String phone;
private String role;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public Long getEmployeeId() { return employeeId; }
public void setEmployeeId(Long employeeId) { this.employeeId = employeeId; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getFullName() { return fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public Boolean getActive() { return active; }
public void setActive(Boolean active) { this.active = active; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}

View File

@@ -12,6 +12,8 @@ public class InventoryRequest {
@PositiveOrZero(message = "Quantity must be zero or positive")
private Integer quantity;
private Long storeId;
public Long getProdId() {
return prodId;
}
@@ -28,18 +30,27 @@ public class InventoryRequest {
this.quantity = quantity;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InventoryRequest that = (InventoryRequest) o;
return Objects.equals(prodId, that.prodId) &&
Objects.equals(quantity, that.quantity);
Objects.equals(quantity, that.quantity) &&
Objects.equals(storeId, that.storeId);
}
@Override
public int hashCode() {
return Objects.hash(prodId, quantity);
return Objects.hash(prodId, quantity, storeId);
}
@Override
@@ -47,6 +58,7 @@ public class InventoryRequest {
return "InventoryRequest{" +
"prodId=" + prodId +
", quantity=" + quantity +
", storeId=" + storeId +
'}';
}
}

View File

@@ -8,6 +8,8 @@ public class InventoryResponse {
private Long prodId;
private String productName;
private String categoryName;
private Long storeId;
private String storeName;
private Integer quantity;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -15,11 +17,13 @@ public class InventoryResponse {
public InventoryResponse() {
}
public InventoryResponse(Long inventoryId, Long prodId, String productName, String categoryName, Integer quantity, LocalDateTime createdAt, LocalDateTime updatedAt) {
public InventoryResponse(Long inventoryId, Long prodId, String productName, String categoryName, Long storeId, String storeName, Integer quantity, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.inventoryId = inventoryId;
this.prodId = prodId;
this.productName = productName;
this.categoryName = categoryName;
this.storeId = storeId;
this.storeName = storeName;
this.quantity = quantity;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
@@ -57,6 +61,22 @@ public class InventoryResponse {
this.categoryName = categoryName;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public Integer getQuantity() {
return quantity;
}
@@ -86,12 +106,12 @@ public class InventoryResponse {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InventoryResponse that = (InventoryResponse) o;
return Objects.equals(inventoryId, that.inventoryId) && Objects.equals(prodId, that.prodId) && Objects.equals(productName, that.productName) && Objects.equals(categoryName, that.categoryName) && Objects.equals(quantity, that.quantity) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
return Objects.equals(inventoryId, that.inventoryId) && Objects.equals(prodId, that.prodId) && Objects.equals(productName, that.productName) && Objects.equals(categoryName, that.categoryName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName) && Objects.equals(quantity, that.quantity) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(inventoryId, prodId, productName, categoryName, quantity, createdAt, updatedAt);
return Objects.hash(inventoryId, prodId, productName, categoryName, storeId, storeName, quantity, createdAt, updatedAt);
}
@Override
@@ -101,6 +121,8 @@ public class InventoryResponse {
", prodId=" + prodId +
", productName='" + productName + '\'' +
", categoryName='" + categoryName + '\'' +
", storeId=" + storeId +
", storeName='" + storeName + '\'' +
", quantity=" + quantity +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +

View File

@@ -0,0 +1,42 @@
package com.petshop.backend.dto.pet;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class MyPetRequest {
@NotBlank(message = "Pet name is required")
@Size(max = 50, message = "Pet name must not exceed 50 characters")
private String petName;
@NotBlank(message = "Species is required")
@Size(max = 50, message = "Species must not exceed 50 characters")
private String species;
@Size(max = 50, message = "Breed must not exceed 50 characters")
private String breed;
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
}

View File

@@ -0,0 +1,61 @@
package com.petshop.backend.dto.pet;
public class MyPetResponse {
private Long customerPetId;
private String petName;
private String species;
private String breed;
private String imageUrl;
public MyPetResponse() {
}
public MyPetResponse(Long customerPetId, String petName, String species, String breed, String imageUrl) {
this.customerPetId = customerPetId;
this.petName = petName;
this.species = species;
this.breed = breed;
this.imageUrl = imageUrl;
}
public Long getCustomerPetId() {
return customerPetId;
}
public void setCustomerPetId(Long customerPetId) {
this.customerPetId = customerPetId;
}
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}

View File

@@ -23,6 +23,10 @@ public class PetRequest {
private BigDecimal petPrice;
private Long customerId;
private Long storeId;
public String getPetName() {
return petName;
}
@@ -71,6 +75,22 @@ public class PetRequest {
this.petPrice = petPrice;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@@ -15,11 +15,15 @@ public class PetResponse {
private String imageUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private Long customerId;
private String customerName;
private Long storeId;
private String storeName;
public PetResponse() {
}
public PetResponse(Long petId, String petName, String petSpecies, String petBreed, Integer petAge, String petStatus, BigDecimal petPrice, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt) {
public PetResponse(Long petId, String petName, String petSpecies, String petBreed, Integer petAge, String petStatus, BigDecimal petPrice, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt, Long customerId, String customerName, Long storeId, String storeName) {
this.petId = petId;
this.petName = petName;
this.petSpecies = petSpecies;
@@ -30,6 +34,10 @@ public class PetResponse {
this.imageUrl = imageUrl;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.customerId = customerId;
this.customerName = customerName;
this.storeId = storeId;
this.storeName = storeName;
}
public Long getPetId() {
@@ -112,17 +120,49 @@ public class PetResponse {
this.updatedAt = updatedAt;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PetResponse that = (PetResponse) o;
return Objects.equals(petId, that.petId) && Objects.equals(petName, that.petName) && Objects.equals(petSpecies, that.petSpecies) && Objects.equals(petBreed, that.petBreed) && Objects.equals(petAge, that.petAge) && Objects.equals(petStatus, that.petStatus) && Objects.equals(petPrice, that.petPrice) && Objects.equals(imageUrl, that.imageUrl) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
return Objects.equals(petId, that.petId) && Objects.equals(petName, that.petName) && Objects.equals(petSpecies, that.petSpecies) && Objects.equals(petBreed, that.petBreed) && Objects.equals(petAge, that.petAge) && Objects.equals(petStatus, that.petStatus) && Objects.equals(petPrice, that.petPrice) && Objects.equals(imageUrl, that.imageUrl) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt) && Objects.equals(customerId, that.customerId) && Objects.equals(customerName, that.customerName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName);
}
@Override
public int hashCode() {
return Objects.hash(petId, petName, petSpecies, petBreed, petAge, petStatus, petPrice, imageUrl, createdAt, updatedAt);
return Objects.hash(petId, petName, petSpecies, petBreed, petAge, petStatus, petPrice, imageUrl, createdAt, updatedAt, customerId, customerName, storeId, storeName);
}
@Override
@@ -138,6 +178,10 @@ public class PetResponse {
", imageUrl='" + imageUrl + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", customerId=" + customerId +
", customerName='" + customerName + '\'' +
", storeId=" + storeId +
", storeName='" + storeName + '\'' +
'}';
}
}

View File

@@ -8,6 +8,8 @@ public class PurchaseOrderResponse {
private Long purchaseOrderId;
private Long supId;
private String supplierName;
private Long storeId;
private String storeName;
private LocalDate orderDate;
private String status;
private LocalDateTime createdAt;
@@ -16,10 +18,12 @@ public class PurchaseOrderResponse {
public PurchaseOrderResponse() {
}
public PurchaseOrderResponse(Long purchaseOrderId, Long supId, String supplierName, LocalDate orderDate, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
public PurchaseOrderResponse(Long purchaseOrderId, Long supId, String supplierName, Long storeId, String storeName, LocalDate orderDate, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.purchaseOrderId = purchaseOrderId;
this.supId = supId;
this.supplierName = supplierName;
this.storeId = storeId;
this.storeName = storeName;
this.orderDate = orderDate;
this.status = status;
this.createdAt = createdAt;
@@ -50,6 +54,22 @@ public class PurchaseOrderResponse {
this.supplierName = supplierName;
}
public Long getStoreId() {
return storeId;
}
public void setStoreId(Long storeId) {
this.storeId = storeId;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public LocalDate getOrderDate() {
return orderDate;
}
@@ -87,12 +107,12 @@ public class PurchaseOrderResponse {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PurchaseOrderResponse that = (PurchaseOrderResponse) o;
return Objects.equals(purchaseOrderId, that.purchaseOrderId) && Objects.equals(supId, that.supId) && Objects.equals(supplierName, that.supplierName) && Objects.equals(orderDate, that.orderDate) && Objects.equals(status, that.status) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
return Objects.equals(purchaseOrderId, that.purchaseOrderId) && Objects.equals(supId, that.supId) && Objects.equals(supplierName, that.supplierName) && Objects.equals(storeId, that.storeId) && Objects.equals(storeName, that.storeName) && Objects.equals(orderDate, that.orderDate) && Objects.equals(status, that.status) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(purchaseOrderId, supId, supplierName, orderDate, status, createdAt, updatedAt);
return Objects.hash(purchaseOrderId, supId, supplierName, storeId, storeName, orderDate, status, createdAt, updatedAt);
}
@Override
@@ -101,6 +121,8 @@ public class PurchaseOrderResponse {
"purchaseOrderId=" + purchaseOrderId +
", supId=" + supId +
", supplierName='" + supplierName + '\'' +
", storeId=" + storeId +
", storeName='" + storeName + '\'' +
", orderDate=" + orderDate +
", status='" + status + '\'' +
", createdAt=" + createdAt +

View File

@@ -0,0 +1,62 @@
package com.petshop.backend.dto.refund;
import java.math.BigDecimal;
public class RefundItemResponse {
private Long id;
private Long prodId;
private String prodName;
private Integer quantity;
private BigDecimal unitPrice;
public RefundItemResponse() {
}
public RefundItemResponse(Long id, Long prodId, String prodName, Integer quantity, BigDecimal unitPrice) {
this.id = id;
this.prodId = prodId;
this.prodName = prodName;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getProdId() {
return prodId;
}
public void setProdId(Long prodId) {
this.prodId = prodId;
}
public String getProdName() {
return prodName;
}
public void setProdName(String prodName) {
this.prodName = prodName;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
}

View File

@@ -1,7 +1,11 @@
package com.petshop.backend.dto.refund;
import com.petshop.backend.dto.sale.SaleItemRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;
public class RefundRequest {
@@ -11,6 +15,10 @@ public class RefundRequest {
@NotBlank(message = "Reason is required")
private String reason;
@NotEmpty(message = "At least one item is required")
@Valid
private List<SaleItemRequest> items;
public Long getSaleId() {
return saleId;
}
@@ -27,18 +35,27 @@ public class RefundRequest {
this.reason = reason;
}
public List<SaleItemRequest> getItems() {
return items;
}
public void setItems(List<SaleItemRequest> items) {
this.items = items;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RefundRequest that = (RefundRequest) o;
return Objects.equals(saleId, that.saleId) &&
Objects.equals(reason, that.reason);
Objects.equals(reason, that.reason) &&
Objects.equals(items, that.items);
}
@Override
public int hashCode() {
return Objects.hash(saleId, reason);
return Objects.hash(saleId, reason, items);
}
@Override
@@ -46,6 +63,7 @@ public class RefundRequest {
return "RefundRequest{" +
"saleId=" + saleId +
", reason='" + reason + '\'' +
", items=" + items +
'}';
}
}

View File

@@ -2,6 +2,7 @@ package com.petshop.backend.dto.refund;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
public class RefundResponse {
@@ -11,22 +12,32 @@ public class RefundResponse {
private BigDecimal amount;
private String reason;
private String status;
private List<RefundItemResponse> items;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public RefundResponse() {
}
public RefundResponse(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, String status, LocalDateTime createdAt, LocalDateTime updatedAt) {
public RefundResponse(Long id, Long saleId, Long customerId, BigDecimal amount, String reason, String status, List<RefundItemResponse> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.saleId = saleId;
this.customerId = customerId;
this.amount = amount;
this.reason = reason;
this.status = status;
this.items = items;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
// ...
public List<RefundItemResponse> getItems() {
return items;
}
public void setItems(List<RefundItemResponse> items) {
this.items = items;
}
public Long getId() {
return id;

View File

@@ -22,6 +22,12 @@ public class SaleRequest {
private Long customerId;
private String channel;
private Long couponId;
private Long cartId;
public Long getStoreId() {
return storeId;
}
@@ -70,6 +76,30 @@ public class SaleRequest {
this.customerId = customerId;
}
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
public Long getCouponId() {
return couponId;
}
public void setCouponId(Long couponId) {
this.couponId = couponId;
}
public Long getCartId() {
return cartId;
}
public void setCartId(Long cartId) {
this.cartId = cartId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -80,12 +110,15 @@ public class SaleRequest {
Objects.equals(items, that.items) &&
Objects.equals(isRefund, that.isRefund) &&
Objects.equals(originalSaleId, that.originalSaleId) &&
Objects.equals(customerId, that.customerId);
Objects.equals(customerId, that.customerId) &&
Objects.equals(channel, that.channel) &&
Objects.equals(couponId, that.couponId) &&
Objects.equals(cartId, that.cartId);
}
@Override
public int hashCode() {
return Objects.hash(storeId, paymentMethod, items, isRefund, originalSaleId, customerId);
return Objects.hash(storeId, paymentMethod, items, isRefund, originalSaleId, customerId, channel, couponId, cartId);
}
@Override
@@ -97,6 +130,9 @@ public class SaleRequest {
", isRefund=" + isRefund +
", originalSaleId=" + originalSaleId +
", customerId=" + customerId +
", channel='" + channel + '\'' +
", couponId=" + couponId +
", cartId=" + cartId +
'}';
}
}

View File

@@ -13,6 +13,13 @@ public class SaleResponse {
private Long storeId;
private String storeName;
private BigDecimal totalAmount;
private BigDecimal subtotalAmount;
private BigDecimal couponDiscountAmount;
private BigDecimal employeeDiscountAmount;
private Integer pointsEarned;
private String channel;
private Long couponId;
private Long cartId;
private String paymentMethod;
private Boolean isRefund;
private Long originalSaleId;
@@ -22,21 +29,6 @@ public class SaleResponse {
public SaleResponse() {
}
public SaleResponse(Long saleId, LocalDateTime saleDate, Long employeeId, String employeeName, Long storeId, String storeName, BigDecimal totalAmount, String paymentMethod, Boolean isRefund, Long originalSaleId, List<SaleItemResponse> items, LocalDateTime createdAt) {
this.saleId = saleId;
this.saleDate = saleDate;
this.employeeId = employeeId;
this.employeeName = employeeName;
this.storeId = storeId;
this.storeName = storeName;
this.totalAmount = totalAmount;
this.paymentMethod = paymentMethod;
this.isRefund = isRefund;
this.originalSaleId = originalSaleId;
this.items = items;
this.createdAt = createdAt;
}
public Long getSaleId() {
return saleId;
}
@@ -93,6 +85,62 @@ public class SaleResponse {
this.totalAmount = totalAmount;
}
public BigDecimal getSubtotalAmount() {
return subtotalAmount;
}
public void setSubtotalAmount(BigDecimal subtotalAmount) {
this.subtotalAmount = subtotalAmount;
}
public BigDecimal getCouponDiscountAmount() {
return couponDiscountAmount;
}
public void setCouponDiscountAmount(BigDecimal couponDiscountAmount) {
this.couponDiscountAmount = couponDiscountAmount;
}
public BigDecimal getEmployeeDiscountAmount() {
return employeeDiscountAmount;
}
public void setEmployeeDiscountAmount(BigDecimal employeeDiscountAmount) {
this.employeeDiscountAmount = employeeDiscountAmount;
}
public Integer getPointsEarned() {
return pointsEarned;
}
public void setPointsEarned(Integer pointsEarned) {
this.pointsEarned = pointsEarned;
}
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
public Long getCouponId() {
return couponId;
}
public void setCouponId(Long couponId) {
this.couponId = couponId;
}
public Long getCartId() {
return cartId;
}
public void setCartId(Long cartId) {
this.cartId = cartId;
}
public String getPaymentMethod() {
return paymentMethod;
}

View File

@@ -4,7 +4,9 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class ServiceRequest {
@NotBlank(message = "Service name is required")
@@ -19,6 +21,8 @@ public class ServiceRequest {
@Positive(message = "Duration must be positive")
private Integer serviceDuration;
private Set<String> species = new HashSet<>();
public String getServiceName() {
return serviceName;
}
@@ -51,6 +55,14 @@ public class ServiceRequest {
this.serviceDuration = serviceDuration;
}
public Set<String> getSpecies() {
return species;
}
public void setSpecies(Set<String> species) {
this.species = species;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -59,12 +71,13 @@ public class ServiceRequest {
return Objects.equals(serviceName, that.serviceName) &&
Objects.equals(serviceDesc, that.serviceDesc) &&
Objects.equals(servicePrice, that.servicePrice) &&
Objects.equals(serviceDuration, that.serviceDuration);
Objects.equals(serviceDuration, that.serviceDuration) &&
Objects.equals(species, that.species);
}
@Override
public int hashCode() {
return Objects.hash(serviceName, serviceDesc, servicePrice, serviceDuration);
return Objects.hash(serviceName, serviceDesc, servicePrice, serviceDuration, species);
}
@Override
@@ -74,6 +87,7 @@ public class ServiceRequest {
", serviceDesc='" + serviceDesc + '\'' +
", servicePrice=" + servicePrice +
", serviceDuration=" + serviceDuration +
", species=" + species +
'}';
}
}

View File

@@ -3,6 +3,7 @@ package com.petshop.backend.dto.service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Set;
public class ServiceResponse {
private Long serviceId;
@@ -10,18 +11,20 @@ public class ServiceResponse {
private String serviceDesc;
private BigDecimal servicePrice;
private Integer serviceDuration;
private Set<String> species;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public ServiceResponse() {
}
public ServiceResponse(Long serviceId, String serviceName, String serviceDesc, BigDecimal servicePrice, Integer serviceDuration, LocalDateTime createdAt, LocalDateTime updatedAt) {
public ServiceResponse(Long serviceId, String serviceName, String serviceDesc, BigDecimal servicePrice, Integer serviceDuration, Set<String> species, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.serviceId = serviceId;
this.serviceName = serviceName;
this.serviceDesc = serviceDesc;
this.servicePrice = servicePrice;
this.serviceDuration = serviceDuration;
this.species = species;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
@@ -66,6 +69,14 @@ public class ServiceResponse {
this.serviceDuration = serviceDuration;
}
public Set<String> getSpecies() {
return species;
}
public void setSpecies(Set<String> species) {
this.species = species;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
@@ -87,12 +98,12 @@ public class ServiceResponse {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceResponse that = (ServiceResponse) o;
return Objects.equals(serviceId, that.serviceId) && Objects.equals(serviceName, that.serviceName) && Objects.equals(serviceDesc, that.serviceDesc) && Objects.equals(servicePrice, that.servicePrice) && Objects.equals(serviceDuration, that.serviceDuration) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
return Objects.equals(serviceId, that.serviceId) && Objects.equals(serviceName, that.serviceName) && Objects.equals(serviceDesc, that.serviceDesc) && Objects.equals(servicePrice, that.servicePrice) && Objects.equals(serviceDuration, that.serviceDuration) && Objects.equals(species, that.species) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
}
@Override
public int hashCode() {
return Objects.hash(serviceId, serviceName, serviceDesc, servicePrice, serviceDuration, createdAt, updatedAt);
return Objects.hash(serviceId, serviceName, serviceDesc, servicePrice, serviceDuration, species, createdAt, updatedAt);
}
@Override
@@ -103,6 +114,7 @@ public class ServiceResponse {
", serviceDesc='" + serviceDesc + '\'' +
", servicePrice=" + servicePrice +
", serviceDuration=" + serviceDuration +
", species=" + species +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';

View File

@@ -18,6 +18,8 @@ public class StoreRequest {
@Email(message = "Email must be valid")
private String email;
private String imageUrl;
public String getStoreName() {
return storeName;
}
@@ -50,6 +52,14 @@ public class StoreRequest {
this.email = email;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -58,12 +68,13 @@ public class StoreRequest {
return Objects.equals(storeName, that.storeName) &&
Objects.equals(address, that.address) &&
Objects.equals(phone, that.phone) &&
Objects.equals(email, that.email);
Objects.equals(email, that.email) &&
Objects.equals(imageUrl, that.imageUrl);
}
@Override
public int hashCode() {
return Objects.hash(storeName, address, phone, email);
return Objects.hash(storeName, address, phone, email, imageUrl);
}
@Override
@@ -73,6 +84,7 @@ public class StoreRequest {
", address='" + address + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", imageUrl='" + imageUrl + '\'' +
'}';
}
}

View File

@@ -9,17 +9,19 @@ public class StoreResponse {
private String address;
private String phone;
private String email;
private String imageUrl;
private LocalDateTime createdAt;
public StoreResponse() {
}
public StoreResponse(Long storeId, String storeName, String address, String phone, String email, LocalDateTime createdAt) {
public StoreResponse(Long storeId, String storeName, String address, String phone, String email, String imageUrl, LocalDateTime createdAt) {
this.storeId = storeId;
this.storeName = storeName;
this.address = address;
this.phone = phone;
this.email = email;
this.imageUrl = imageUrl;
this.createdAt = createdAt;
}
@@ -63,6 +65,14 @@ public class StoreResponse {
this.email = email;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -8,14 +8,20 @@ import jakarta.validation.constraints.Size;
import java.util.Objects;
public class UserRequest {
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
private String username;
@Size(min = 6, message = "Password must be at least 6 characters")
private String password;
@NotBlank(message = "Full name is required")
@NotBlank(message = "First name is required")
@Size(max = 50)
private String firstName;
@NotBlank(message = "Last name is required")
@Size(max = 50)
private String lastName;
private String fullName;
@Email(message = "Invalid email format")
@@ -27,6 +33,10 @@ public class UserRequest {
@NotNull(message = "Role is required")
private User.Role role;
private String staffRole;
private Long primaryStoreId;
private Boolean active = true;
public String getUsername() {
@@ -45,6 +55,22 @@ public class UserRequest {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return fullName;
}
@@ -77,6 +103,22 @@ public class UserRequest {
this.role = role;
}
public String getStaffRole() {
return staffRole;
}
public void setStaffRole(String staffRole) {
this.staffRole = staffRole;
}
public Long getPrimaryStoreId() {
return primaryStoreId;
}
public void setPrimaryStoreId(Long primaryStoreId) {
this.primaryStoreId = primaryStoreId;
}
public Boolean getActive() {
return active;
}
@@ -91,29 +133,20 @@ public class UserRequest {
if (o == null || getClass() != o.getClass()) return false;
UserRequest that = (UserRequest) o;
return Objects.equals(username, that.username) &&
Objects.equals(password, that.password) &&
Objects.equals(fullName, that.fullName) &&
Objects.equals(firstName, that.firstName) &&
Objects.equals(lastName, that.lastName) &&
Objects.equals(email, that.email) &&
Objects.equals(phone, that.phone) &&
role == that.role &&
Objects.equals(active, that.active);
role == that.role;
}
@Override
public int hashCode() {
return Objects.hash(username, password, fullName, email, phone, role, active);
return Objects.hash(username, firstName, lastName, email, role);
}
@Override
public String toString() {
return "UserRequest{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", fullName='" + fullName + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", role=" + role +
", active=" + active +
'}';
return "UserRequest{username='" + username + "', firstName='" + firstName +
"', lastName='" + lastName + "', role=" + role + '}';
}
}

View File

@@ -6,10 +6,15 @@ import java.util.Objects;
public class UserResponse {
private Long id;
private String username;
private String firstName;
private String lastName;
private String fullName;
private String email;
private String phone;
private String role;
private String staffRole;
private Long primaryStoreId;
private Integer loyaltyPoints;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@@ -17,18 +22,6 @@ public class UserResponse {
public UserResponse() {
}
public UserResponse(Long id, String username, String fullName, String email, String phone, String role, Boolean active, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.username = username;
this.fullName = fullName;
this.email = email;
this.phone = phone;
this.role = role;
this.active = active;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getId() {
return id;
}
@@ -45,6 +38,22 @@ public class UserResponse {
this.username = username;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return fullName;
}
@@ -77,6 +86,30 @@ public class UserResponse {
this.role = role;
}
public String getStaffRole() {
return staffRole;
}
public void setStaffRole(String staffRole) {
this.staffRole = staffRole;
}
public Long getPrimaryStoreId() {
return primaryStoreId;
}
public void setPrimaryStoreId(Long primaryStoreId) {
this.primaryStoreId = primaryStoreId;
}
public Integer getLoyaltyPoints() {
return loyaltyPoints;
}
public void setLoyaltyPoints(Integer loyaltyPoints) {
this.loyaltyPoints = loyaltyPoints;
}
public Boolean getActive() {
return active;
}
@@ -106,26 +139,17 @@ public class UserResponse {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserResponse that = (UserResponse) o;
return Objects.equals(id, that.id) && Objects.equals(username, that.username) && Objects.equals(fullName, that.fullName) && Objects.equals(email, that.email) && Objects.equals(phone, that.phone) && Objects.equals(role, that.role) && Objects.equals(active, that.active) && Objects.equals(createdAt, that.createdAt) && Objects.equals(updatedAt, that.updatedAt);
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id, username, fullName, email, phone, role, active, createdAt, updatedAt);
return Objects.hash(id);
}
@Override
public String toString() {
return "UserResponse{" +
"id=" + id +
", username='" + username + '\'' +
", fullName='" + fullName + '\'' +
", email='" + email + '\'' +
", phone='" + phone + '\'' +
", role='" + role + '\'' +
", active=" + active +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
return "UserResponse{id=" + id + ", username='" + username + "', firstName='" + firstName +
"', lastName='" + lastName + "', role='" + role + "', active=" + active + '}';
}
}

View File

@@ -14,8 +14,12 @@ public class ActivityLog {
private Long logId;
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
@JoinColumn(name = "userId", nullable = false)
private User user;
@ManyToOne
@JoinColumn(name = "storeId")
private StoreLocation store;
@Column(nullable = false, columnDefinition = "TEXT")
private String activity;
@@ -26,9 +30,9 @@ public class ActivityLog {
public ActivityLog() {
}
public ActivityLog(Long logId, Employee employee, String activity, LocalDateTime logTimestamp) {
public ActivityLog(Long logId, User user, String activity, LocalDateTime logTimestamp) {
this.logId = logId;
this.employee = employee;
this.user = user;
this.activity = activity;
this.logTimestamp = logTimestamp;
}
@@ -41,12 +45,20 @@ public class ActivityLog {
this.logId = logId;
}
public Employee getEmployee() {
return employee;
public User getUser() {
return user;
}
public void setEmployee(Employee employee) {
this.employee = employee;
public void setUser(User user) {
this.user = user;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public String getActivity() {
@@ -82,7 +94,7 @@ public class ActivityLog {
public String toString() {
return "ActivityLog{" +
"logId=" + logId +
", employee=" + employee +
", user=" + user +
", activity='" + activity + '\'' +
", logTimestamp=" + logTimestamp +
'}';

View File

@@ -4,7 +4,6 @@ import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
@@ -23,10 +22,15 @@ public class Adoption {
@ManyToOne
@JoinColumn(name = "customerId", nullable = false)
private Customer customer;
private User customer;
@ManyToOne
private Employee employee;
@JoinColumn(name = "employeeId", nullable = false)
private User employee;
@ManyToOne
@JoinColumn(name = "sourceStoreId")
private StoreLocation sourceStore;
@Column(nullable = false)
private LocalDate adoptionDate;
@@ -45,16 +49,6 @@ public class Adoption {
public Adoption() {
}
public Adoption(Long adoptionId, Pet pet, Customer customer, LocalDate adoptionDate, String adoptionStatus, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.adoptionId = adoptionId;
this.pet = pet;
this.customer = customer;
this.adoptionDate = adoptionDate;
this.adoptionStatus = adoptionStatus;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAdoptionId() {
return adoptionId;
}
@@ -71,22 +65,30 @@ public class Adoption {
this.pet = pet;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
public StoreLocation getSourceStore() {
return sourceStore;
}
public void setSourceStore(StoreLocation sourceStore) {
this.sourceStore = sourceStore;
}
public LocalDate getAdoptionDate() {
return adoptionDate;
}
@@ -136,15 +138,8 @@ public class Adoption {
public String toString() {
return "Adoption{" +
"adoptionId=" + adoptionId +
", pet=" + pet +
", customer=" + customer +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", adoptionDate=" + adoptionDate +
'}';
}
public void setEmployeeName(String s) {
}
}

View File

@@ -7,9 +7,7 @@ import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
@Table(name = "appointment")
@@ -21,7 +19,7 @@ public class Appointment {
@ManyToOne
@JoinColumn(name = "customerId", nullable = false)
private Customer customer;
private User customer;
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
@@ -32,8 +30,8 @@ public class Appointment {
private Service service;
@ManyToOne
@JoinColumn(name = "employeeId")
private Employee employee;
@JoinColumn(name = "employeeId", nullable = false)
private User employee;
@Column(nullable = false)
private LocalDate appointmentDate;
@@ -44,13 +42,9 @@ public class Appointment {
@Column(nullable = false, length = 20)
private String appointmentStatus;
@ManyToMany
@JoinTable(
name = "appointmentPet",
joinColumns = @JoinColumn(name = "appointmentId"),
inverseJoinColumns = @JoinColumn(name = "petId")
)
private Set<Pet> pets = new HashSet<>();
@ManyToOne
@JoinColumn(name = "petId")
private Pet pet;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
@@ -63,19 +57,6 @@ public class Appointment {
public Appointment() {
}
public Appointment(Long appointmentId, Customer customer, StoreLocation store, Service service, LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus, Set<Pet> pets, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.appointmentId = appointmentId;
this.customer = customer;
this.store = store;
this.service = service;
this.appointmentDate = appointmentDate;
this.appointmentTime = appointmentTime;
this.appointmentStatus = appointmentStatus;
this.pets = pets;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAppointmentId() {
return appointmentId;
}
@@ -84,11 +65,11 @@ public class Appointment {
this.appointmentId = appointmentId;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
@@ -108,11 +89,11 @@ public class Appointment {
this.service = service;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
@@ -140,12 +121,12 @@ public class Appointment {
this.appointmentStatus = appointmentStatus;
}
public Set<Pet> getPets() {
return pets;
public Pet getPet() {
return pet;
}
public void setPets(Set<Pet> pets) {
this.pets = pets;
public void setPet(Pet pet) {
this.pet = pet;
}
public LocalDateTime getCreatedAt() {
@@ -184,10 +165,11 @@ public class Appointment {
", customer=" + customer +
", store=" + store +
", service=" + service +
", employee=" + employee +
", appointmentDate=" + appointmentDate +
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", pets=" + pets +
", pet=" + pet +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';

View File

@@ -0,0 +1,155 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "cart")
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long cartId;
@ManyToOne
@JoinColumn(name = "userId", nullable = false)
private User user;
@ManyToOne
@JoinColumn(name = "storeId")
private StoreLocation store;
@ManyToOne
@JoinColumn(name = "couponId")
private Coupon coupon;
@Column(nullable = false, length = 20)
private String cartStatus = "ACTIVE";
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal subtotalAmount = BigDecimal.ZERO;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal discountAmount = BigDecimal.ZERO;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount = BigDecimal.ZERO;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Cart() {
}
public Long getCartId() {
return cartId;
}
public void setCartId(Long cartId) {
this.cartId = cartId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public Coupon getCoupon() {
return coupon;
}
public void setCoupon(Coupon coupon) {
this.coupon = coupon;
}
public String getCartStatus() {
return cartStatus;
}
public void setCartStatus(String cartStatus) {
this.cartStatus = cartStatus;
}
public BigDecimal getSubtotalAmount() {
return subtotalAmount;
}
public void setSubtotalAmount(BigDecimal subtotalAmount) {
this.subtotalAmount = subtotalAmount;
}
public BigDecimal getDiscountAmount() {
return discountAmount;
}
public void setDiscountAmount(BigDecimal discountAmount) {
this.discountAmount = discountAmount;
}
public BigDecimal getTotalAmount() {
return totalAmount;
}
public void setTotalAmount(BigDecimal totalAmount) {
this.totalAmount = totalAmount;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cart cart = (Cart) o;
return Objects.equals(cartId, cart.cartId);
}
@Override
public int hashCode() {
return Objects.hash(cartId);
}
@Override
public String toString() {
return "Cart{" +
"cartId=" + cartId +
", cartStatus='" + cartStatus + '\'' +
", totalAmount=" + totalAmount +
'}';
}
}

View File

@@ -0,0 +1,121 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "cart_item")
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long cartItemId;
@ManyToOne
@JoinColumn(name = "cartId", nullable = false)
private Cart cart;
@ManyToOne
@JoinColumn(name = "prodId", nullable = false)
private Product product;
@Column(nullable = false)
private Integer quantity;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal unitPrice;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public CartItem() {
}
public Long getCartItemId() {
return cartItemId;
}
public void setCartItemId(Long cartItemId) {
this.cartItemId = cartItemId;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CartItem cartItem = (CartItem) o;
return Objects.equals(cartItemId, cartItem.cartItemId);
}
@Override
public int hashCode() {
return Objects.hash(cartItemId);
}
@Override
public String toString() {
return "CartItem{" +
"cartItemId=" + cartItemId +
", quantity=" + quantity +
", unitPrice=" + unitPrice +
'}';
}
}

View File

@@ -0,0 +1,162 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "coupon")
public class Coupon {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long couponId;
@Column(nullable = false, length = 50, unique = true)
private String couponCode;
@Column(nullable = false, length = 20)
private String discountType;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal discountValue;
@Column(precision = 10, scale = 2)
private BigDecimal minOrderAmount;
@Column(nullable = false)
private Boolean active = true;
private LocalDateTime startsAt;
private LocalDateTime endsAt;
private Integer usageLimit;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Coupon() {
}
public Long getCouponId() {
return couponId;
}
public void setCouponId(Long couponId) {
this.couponId = couponId;
}
public String getCouponCode() {
return couponCode;
}
public void setCouponCode(String couponCode) {
this.couponCode = couponCode;
}
public String getDiscountType() {
return discountType;
}
public void setDiscountType(String discountType) {
this.discountType = discountType;
}
public BigDecimal getDiscountValue() {
return discountValue;
}
public void setDiscountValue(BigDecimal discountValue) {
this.discountValue = discountValue;
}
public BigDecimal getMinOrderAmount() {
return minOrderAmount;
}
public void setMinOrderAmount(BigDecimal minOrderAmount) {
this.minOrderAmount = minOrderAmount;
}
public Boolean getActive() {
return active;
}
public void setActive(Boolean active) {
this.active = active;
}
public LocalDateTime getStartsAt() {
return startsAt;
}
public void setStartsAt(LocalDateTime startsAt) {
this.startsAt = startsAt;
}
public LocalDateTime getEndsAt() {
return endsAt;
}
public void setEndsAt(LocalDateTime endsAt) {
this.endsAt = endsAt;
}
public Integer getUsageLimit() {
return usageLimit;
}
public void setUsageLimit(Integer usageLimit) {
this.usageLimit = usageLimit;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Coupon coupon = (Coupon) o;
return Objects.equals(couponId, coupon.couponId);
}
@Override
public int hashCode() {
return Objects.hash(couponId);
}
@Override
public String toString() {
return "Coupon{" +
"couponId=" + couponId +
", couponCode='" + couponCode + '\'' +
", discountType='" + discountType + '\'' +
", discountValue=" + discountValue +
", active=" + active +
'}';
}
}

View File

@@ -1,132 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long customerId;
@Column(name = "user_id")
private Long userId;
@Column(nullable = false, length = 50)
private String firstName;
@Column(nullable = false, length = 50)
private String lastName;
@Column(nullable = false, length = 100)
private String email;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Customer() {
}
public Customer(Long customerId, Long userId, String firstName, String lastName, String email, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerId = customerId;
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(customerId, customer.customerId);
}
@Override
public int hashCode() {
return Objects.hash(customerId);
}
@Override
public String toString() {
return "Customer{" +
"customerId=" + customerId +
", userId=" + userId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,158 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long employeeId;
@Column(name = "user_id")
private Long userId;
@Column(nullable = false, length = 50)
private String firstName;
@Column(nullable = false, length = 50)
private String lastName;
@Column(nullable = false, length = 100)
private String email;
@Column(nullable = false, length = 50)
private String role;
@Column(nullable = false)
private Boolean isActive = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public Employee() {
}
public Employee(Long employeeId, Long userId, String firstName, String lastName, String email, String role, Boolean isActive, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.employeeId = employeeId;
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.role = role;
this.isActive = isActive;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Boolean getIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(employeeId, employee.employeeId);
}
@Override
public int hashCode() {
return Objects.hash(employeeId);
}
@Override
public String toString() {
return "Employee{" +
"employeeId=" + employeeId +
", userId=" + userId +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", role='" + role + '\'' +
", isActive=" + isActive +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,117 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "employeeStore")
@IdClass(EmployeeStore.EmployeeStoreId.class)
public class EmployeeStore {
@Id
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
@Id
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
private StoreLocation store;
public EmployeeStore() {
}
public EmployeeStore(Employee employee, StoreLocation store) {
this.employee = employee;
this.store = store;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeStore that = (EmployeeStore) o;
return Objects.equals(employee, that.employee) && Objects.equals(store, that.store);
}
@Override
public int hashCode() {
return Objects.hash(employee, store);
}
@Override
public String toString() {
return "EmployeeStore{" +
"employee=" + employee +
", store=" + store +
'}';
}
public static class EmployeeStoreId implements Serializable {
private Long employee;
private Long store;
public EmployeeStoreId() {
}
public EmployeeStoreId(Long employee, Long store) {
this.employee = employee;
this.store = store;
}
public Long getEmployee() {
return employee;
}
public void setEmployee(Long employee) {
this.employee = employee;
}
public Long getStore() {
return store;
}
public void setStore(Long store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeStoreId that = (EmployeeStoreId) o;
return Objects.equals(employee, that.employee) && Objects.equals(store, that.store);
}
@Override
public int hashCode() {
return Objects.hash(employee, store);
}
@Override
public String toString() {
return "EmployeeStoreId{" +
"employee=" + employee +
", store=" + store +
'}';
}
}
}

View File

@@ -19,6 +19,10 @@ public class Inventory {
@JoinColumn(name = "prodId", nullable = false)
private Product product;
@ManyToOne
@JoinColumn(name = "storeId")
private StoreLocation store;
@Column(nullable = false)
private Integer quantity = 0;
@@ -57,6 +61,14 @@ public class Inventory {
this.product = product;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public Integer getQuantity() {
return quantity;
}
@@ -99,6 +111,7 @@ public class Inventory {
return "Inventory{" +
"inventoryId=" + inventoryId +
", product=" + product +
", store=" + store +
", quantity=" + quantity +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +

View File

@@ -19,9 +19,20 @@ public class Message {
@Column(nullable = false)
private Long senderId;
@Column(nullable = false, columnDefinition = "TEXT")
@Column(columnDefinition = "TEXT")
private String content;
@Column(length = 255)
private String attachmentUrl;
@Column(length = 255)
private String attachmentName;
@Column(length = 100)
private String attachmentMimeType;
private Long attachmentSizeBytes;
@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime timestamp;
@@ -88,4 +99,36 @@ public class Message {
public void setIsRead(Boolean isRead) {
this.isRead = isRead;
}
public String getAttachmentUrl() {
return attachmentUrl;
}
public void setAttachmentUrl(String attachmentUrl) {
this.attachmentUrl = attachmentUrl;
}
public String getAttachmentName() {
return attachmentName;
}
public void setAttachmentName(String attachmentName) {
this.attachmentName = attachmentName;
}
public String getAttachmentMimeType() {
return attachmentMimeType;
}
public void setAttachmentMimeType(String attachmentMimeType) {
this.attachmentMimeType = attachmentMimeType;
}
public Long getAttachmentSizeBytes() {
return attachmentSizeBytes;
}
public void setAttachmentSizeBytes(Long attachmentSizeBytes) {
this.attachmentSizeBytes = attachmentSizeBytes;
}
}

View File

@@ -23,21 +23,29 @@ public class Pet {
@Column(nullable = false, length = 50)
private String petSpecies;
@Column(nullable = false, length = 50)
@Column(length = 50)
private String petBreed;
@Column(nullable = false)
@Column
private Integer petAge;
@Column(nullable = false, length = 20)
private String petStatus;
@Column(nullable = false, precision = 10, scale = 2)
@Column(precision = 10, scale = 2)
private BigDecimal petPrice;
@Column(length = 255)
private String imageUrl;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ownerUserId")
private User owner;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storeId")
private StoreLocation store;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@@ -49,19 +57,6 @@ public class Pet {
public Pet() {
}
public Pet(Long id, String petName, String petSpecies, String petBreed, Integer petAge, String petStatus, BigDecimal petPrice, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.petName = petName;
this.petSpecies = petSpecies;
this.petBreed = petBreed;
this.petAge = petAge;
this.petStatus = petStatus;
this.petPrice = petPrice;
this.imageUrl = imageUrl;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getPetId() {
return id;
}
@@ -126,6 +121,22 @@ public class Pet {
this.imageUrl = imageUrl;
}
public User getOwner() {
return owner;
}
public void setOwner(User owner) {
this.owner = owner;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
@@ -161,13 +172,7 @@ public class Pet {
"id=" + id +
", petName='" + petName + '\'' +
", petSpecies='" + petSpecies + '\'' +
", petBreed='" + petBreed + '\'' +
", petAge=" + petAge +
", petStatus='" + petStatus + '\'' +
", petPrice=" + petPrice +
", imageUrl='" + imageUrl + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -4,11 +4,8 @@ import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
@@ -23,6 +20,10 @@ public class PurchaseOrder {
@JoinColumn(name = "supId", nullable = false)
private Supplier supplier;
@ManyToOne
@JoinColumn(name = "storeId")
private StoreLocation store;
@Column(nullable = false)
private LocalDate orderDate;
@@ -65,6 +66,14 @@ public class PurchaseOrder {
this.supplier = supplier;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
public LocalDate getOrderDate() {
return orderDate;
}

View File

@@ -6,6 +6,8 @@ import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
@@ -32,9 +34,30 @@ public class Refund {
@Column(nullable = false, length = 20, columnDefinition = "VARCHAR(20)")
private RefundStatus status;
@OneToMany(mappedBy = "refund", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RefundItem> items = new ArrayList<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
// ...
public List<RefundItem> getItems() {
return items;
}
public void setItems(List<RefundItem> items) {
this.items = items;
}
public void addItem(RefundItem item) {
items.add(item);
item.setRefund(this);
}
public void removeItem(RefundItem item) {
items.remove(item);
item.setRefund(null);
}
@UpdateTimestamp
@Column(name = "updated_at")

View File

@@ -0,0 +1,112 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;
@Entity
@Table(name = "refund_item")
public class RefundItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "refund_id", nullable = false)
private Refund refund;
@ManyToOne
@JoinColumn(name = "prod_id", nullable = false)
private Product product;
@Column(nullable = false)
private Integer quantity;
@Column(name = "unit_price", nullable = false, precision = 10, scale = 2)
private BigDecimal unitPrice;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public RefundItem() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Refund getRefund() {
return refund;
}
public void setRefund(Refund refund) {
this.refund = refund;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RefundItem that = (RefundItem) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@@ -23,7 +23,7 @@ public class Sale {
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@ManyToOne
@JoinColumn(name = "storeId", nullable = false)
@@ -31,7 +31,7 @@ public class Sale {
@ManyToOne
@JoinColumn(name = "customerId")
private Customer customer;
private User customer;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@@ -46,6 +46,29 @@ public class Sale {
@JoinColumn(name = "originalSaleId")
private Sale originalSale;
@Column(nullable = false, length = 20)
private String channel = "IN_STORE";
@ManyToOne
@JoinColumn(name = "cartId")
private Cart cart;
@ManyToOne
@JoinColumn(name = "couponId")
private Coupon coupon;
@Column(precision = 10, scale = 2)
private BigDecimal subtotalAmount;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal couponDiscountAmount = BigDecimal.ZERO;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal employeeDiscountAmount = BigDecimal.ZERO;
@Column(nullable = false)
private Integer pointsEarned = 0;
@OneToMany(mappedBy = "sale", cascade = CascadeType.ALL)
private List<SaleItem> items = new ArrayList<>();
@@ -60,21 +83,6 @@ public class Sale {
public Sale() {
}
public Sale(Long saleId, LocalDateTime saleDate, Employee employee, StoreLocation store, Customer customer, BigDecimal totalAmount, String paymentMethod, Boolean isRefund, Sale originalSale, List<SaleItem> items, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.saleId = saleId;
this.saleDate = saleDate;
this.employee = employee;
this.store = store;
this.customer = customer;
this.totalAmount = totalAmount;
this.paymentMethod = paymentMethod;
this.isRefund = isRefund;
this.originalSale = originalSale;
this.items = items;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getSaleId() {
return saleId;
}
@@ -91,11 +99,11 @@ public class Sale {
this.saleDate = saleDate;
}
public Employee getEmployee() {
public User getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
public void setEmployee(User employee) {
this.employee = employee;
}
@@ -107,11 +115,11 @@ public class Sale {
this.store = store;
}
public Customer getCustomer() {
public User getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
public void setCustomer(User customer) {
this.customer = customer;
}
@@ -147,6 +155,62 @@ public class Sale {
this.originalSale = originalSale;
}
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public Coupon getCoupon() {
return coupon;
}
public void setCoupon(Coupon coupon) {
this.coupon = coupon;
}
public BigDecimal getSubtotalAmount() {
return subtotalAmount;
}
public void setSubtotalAmount(BigDecimal subtotalAmount) {
this.subtotalAmount = subtotalAmount;
}
public BigDecimal getCouponDiscountAmount() {
return couponDiscountAmount;
}
public void setCouponDiscountAmount(BigDecimal couponDiscountAmount) {
this.couponDiscountAmount = couponDiscountAmount;
}
public BigDecimal getEmployeeDiscountAmount() {
return employeeDiscountAmount;
}
public void setEmployeeDiscountAmount(BigDecimal employeeDiscountAmount) {
this.employeeDiscountAmount = employeeDiscountAmount;
}
public Integer getPointsEarned() {
return pointsEarned;
}
public void setPointsEarned(Integer pointsEarned) {
this.pointsEarned = pointsEarned;
}
public List<SaleItem> getItems() {
return items;
}
@@ -189,16 +253,9 @@ public class Sale {
return "Sale{" +
"saleId=" + saleId +
", saleDate=" + saleDate +
", employee=" + employee +
", store=" + store +
", customer=" + customer +
", totalAmount=" + totalAmount +
", paymentMethod='" + paymentMethod + '\'' +
", isRefund=" + isRefund +
", originalSale=" + originalSale +
", items=" + items +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -6,7 +6,9 @@ import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
@Table(name = "service")
@@ -28,6 +30,11 @@ public class Service {
@Column(nullable = false)
private Integer serviceDuration;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "service_species", joinColumns = @JoinColumn(name = "serviceId"))
@Column(name = "species", length = 50)
private Set<String> species = new HashSet<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@@ -89,6 +96,14 @@ public class Service {
this.serviceDuration = serviceDuration;
}
public Set<String> getSpecies() {
return species;
}
public void setSpecies(Set<String> species) {
this.species = species;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -1,5 +1,6 @@
package com.petshop.backend.entity;
import com.petshop.backend.util.PhoneUtils;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
@@ -27,6 +28,9 @@ public class StoreLocation {
@Column(nullable = false, length = 100)
private String email;
@Column(length = 500)
private String imageUrl;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@@ -77,7 +81,7 @@ public class StoreLocation {
}
public void setPhone(String phone) {
this.phone = phone;
this.phone = PhoneUtils.normalize(phone);
}
public String getEmail() {
@@ -88,6 +92,14 @@ public class StoreLocation {
this.email = email;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}

View File

@@ -1,5 +1,6 @@
package com.petshop.backend.entity;
import com.petshop.backend.util.PhoneUtils;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
@@ -97,7 +98,7 @@ public class Supplier {
}
public void setSupPhone(String supPhone) {
this.supPhone = supPhone;
this.supPhone = PhoneUtils.normalize(supPhone);
}
public LocalDateTime getCreatedAt() {

View File

@@ -1,5 +1,6 @@
package com.petshop.backend.entity;
import com.petshop.backend.util.PhoneUtils;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
@@ -15,15 +16,21 @@ public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
@Column(unique = true, length = 50)
private String username;
@Column(nullable = false)
@Column
private String password;
@Column(unique = true, length = 100)
private String email;
@Column(nullable = false, length = 50)
private String firstName;
@Column(nullable = false, length = 50)
private String lastName;
@Column(length = 100)
private String fullName;
@@ -37,6 +44,16 @@ public class User {
@Column(nullable = false, length = 20, columnDefinition = "VARCHAR(20)")
private Role role;
@Column(length = 50)
private String staffRole;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "primaryStoreId")
private StoreLocation primaryStore;
@Column(nullable = false)
private Integer loyaltyPoints = 0;
@Column(nullable = false)
private Boolean active = true;
@@ -58,21 +75,6 @@ public class User {
public User() {
}
public User(Long id, String username, String password, String email, String fullName, String phone, String avatarUrl, Role role, Boolean active, Integer tokenVersion, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.fullName = fullName;
this.phone = phone;
this.avatarUrl = avatarUrl;
this.role = role;
this.active = active;
this.tokenVersion = tokenVersion;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getId() {
return id;
}
@@ -105,6 +107,22 @@ public class User {
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFullName() {
return fullName;
}
@@ -118,7 +136,7 @@ public class User {
}
public void setPhone(String phone) {
this.phone = phone;
this.phone = PhoneUtils.normalize(phone);
}
public String getAvatarUrl() {
@@ -137,6 +155,30 @@ public class User {
this.role = role;
}
public String getStaffRole() {
return staffRole;
}
public void setStaffRole(String staffRole) {
this.staffRole = staffRole;
}
public StoreLocation getPrimaryStore() {
return primaryStore;
}
public void setPrimaryStore(StoreLocation primaryStore) {
this.primaryStore = primaryStore;
}
public Integer getLoyaltyPoints() {
return loyaltyPoints;
}
public void setLoyaltyPoints(Integer loyaltyPoints) {
this.loyaltyPoints = loyaltyPoints;
}
public Boolean getActive() {
return active;
}
@@ -187,16 +229,12 @@ public class User {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", fullName='" + fullName + '\'' +
", phone='" + phone + '\'' +
", avatarUrl='" + avatarUrl + '\'' +
", role=" + role +
", active=" + active +
", tokenVersion=" + tokenVersion +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}

View File

@@ -1,6 +1,7 @@
package com.petshop.backend.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
@@ -13,7 +14,10 @@ import java.time.LocalDateTime;
@Component
public class ApiErrorResponder {
private final ObjectMapper objectMapper = JsonMapper.builder().findAndAddModules().build();
private final ObjectMapper objectMapper = JsonMapper.builder()
.findAndAddModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
public void write(HttpServletResponse response, HttpStatus status, String message, String details, String path) throws IOException {
response.setStatus(status.value());

View File

@@ -1,15 +1,19 @@
package com.petshop.backend.exception;
import com.petshop.backend.service.PetService;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.core.PropertyReferenceException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.time.LocalDateTime;
import java.util.HashMap;
@@ -78,6 +82,26 @@ public class GlobalExceptionHandler {
return buildErrorResponse(HttpStatus.valueOf(ex.getStatusCode().value()), message, ex, request);
}
@ExceptionHandler(NoResourceFoundException.class)
public ResponseEntity<ApiErrorResponse> handleNoResourceFound(NoResourceFoundException ex, HttpServletRequest request) {
return buildErrorResponse(HttpStatus.NOT_FOUND, "Route not found", ex, request);
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ApiErrorResponse> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
return buildErrorResponse(HttpStatus.METHOD_NOT_ALLOWED, ex.getMessage(), ex, request);
}
@ExceptionHandler(PropertyReferenceException.class)
public ResponseEntity<ApiErrorResponse> handleBadSortProperty(PropertyReferenceException ex, HttpServletRequest request) {
return buildErrorResponse(HttpStatus.BAD_REQUEST, "Invalid sort field: " + ex.getPropertyName(), ex, request);
}
@ExceptionHandler(PetService.ForbiddenImageAccessException.class)
public ResponseEntity<ApiErrorResponse> handleForbiddenImageAccess(PetService.ForbiddenImageAccessException ex, HttpServletRequest request) {
return buildErrorResponse(HttpStatus.FORBIDDEN, "Access to this pet image is not allowed", ex, request);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiErrorResponse> handleGenericException(Exception ex, HttpServletRequest request) {
String message = ex.getMessage() == null || ex.getMessage().isBlank()

View File

@@ -8,24 +8,33 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDate;
import java.util.Optional;
@Repository
public interface AdoptionRepository extends JpaRepository<Adoption, Long> {
@Query("SELECT a FROM Adoption a WHERE " +
"(:q IS NULL OR (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Adoption> searchAdoptions(@Param("q") String query, Pageable pageable);
Page<Adoption> findByCustomerCustomerId(Long customerId, Pageable pageable);
@Query("SELECT a FROM Adoption a WHERE a.customer.customerId = :customerId AND (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Adoption> searchAdoptionsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable);
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))" +
")) AND " +
"(:customerId IS NULL OR a.customer.id = :customerId) AND " +
"(:status IS NULL OR LOWER(a.adoptionStatus) = LOWER(:status)) AND " +
"(:storeId IS NULL OR a.sourceStore.storeId = :storeId) AND " +
"(:date IS NULL OR a.adoptionDate = :date)")
Page<Adoption> searchAdoptions(
@Param("q") String query,
@Param("customerId") Long customerId,
@Param("status") String status,
@Param("storeId") Long storeId,
@Param("date") LocalDate date,
Pageable pageable);
Optional<Adoption> findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(Long petId, String adoptionStatus);
boolean existsByPet_IdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(Long petId, String adoptionStatus, Long adoptionId);
boolean existsByPet_IdAndAdoptionStatusIgnoreCase(Long petId, String adoptionStatus);
}

View File

@@ -18,22 +18,33 @@ public interface AppointmentRepository extends JpaRepository<Appointment, Long>
@Query("SELECT a FROM Appointment a WHERE a.appointmentDate = :date AND a.appointmentTime = :time")
List<Appointment> findByDateAndTime(@Param("date") LocalDate date, @Param("time") LocalTime time);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.store.storeId = :storeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) <> 'cancelled'")
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.store.storeId = :storeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByStoreAndDate(@Param("storeId") Long storeId, @Param("date") LocalDate date);
@Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE " +
@Query("SELECT a FROM Appointment a LEFT JOIN a.pet p WHERE " +
"(:q IS NULL OR (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Appointment> searchAppointments(@Param("q") String query, Pageable pageable);
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%'))" +
")) AND " +
"(:customerId IS NULL OR a.customer.id = :customerId) AND " +
"(:employeeId IS NULL OR a.employee.id = :employeeId) AND " +
"(:storeId IS NULL OR a.store.storeId = :storeId) AND " +
"(:status IS NULL OR LOWER(a.appointmentStatus) = LOWER(:status)) AND " +
"(:date IS NULL OR a.appointmentDate = :date)")
Page<Appointment> searchAppointments(
@Param("q") String query,
@Param("customerId") Long customerId,
@Param("employeeId") Long employeeId,
@Param("storeId") Long storeId,
@Param("status") String status,
@Param("date") LocalDate date,
Pageable pageable);
Page<Appointment> findByCustomerCustomerId(Long customerId, Pageable pageable);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id = :employeeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date);
@Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE a.customer.customerId = :customerId AND (" +
"LOWER(a.customer.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.customer.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(a.service.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Appointment> searchAppointmentsByCustomer(@Param("customerId") Long customerId, @Param("q") String query, Pageable pageable);
@Query("SELECT a FROM Appointment a JOIN FETCH a.service WHERE a.employee.id IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
}

View File

@@ -0,0 +1,13 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
List<CartItem> findByCartCartId(Long cartId);
}

View File

@@ -0,0 +1,16 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Cart;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface CartRepository extends JpaRepository<Cart, Long> {
List<Cart> findByUserId(Long userId);
Optional<Cart> findByUserIdAndCartStatus(Long userId, String cartStatus);
}

View File

@@ -16,7 +16,7 @@ public interface CategoryRepository extends JpaRepository<Category, Long> {
Optional<Category> findByCategoryName(String categoryName);
@Query("SELECT c FROM Category c WHERE " +
"LOWER(c.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.categoryType) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Category> searchCategories(@Param("q") String query, Pageable pageable);
"(:q IS NULL OR LOWER(c.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(c.categoryType) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:type IS NULL OR LOWER(c.categoryType) = LOWER(:type))")
Page<Category> searchCategories(@Param("q") String query, @Param("type") String type, Pageable pageable);
}

View File

@@ -0,0 +1,13 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Coupon;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface CouponRepository extends JpaRepository<Coupon, Long> {
Optional<Coupon> findByCouponCode(String couponCode);
}

View File

@@ -1,26 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Customer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
Optional<Customer> findByUserId(Long userId);
List<Customer> findAllByEmail(String email);
@Query("SELECT c FROM Customer c WHERE " +
"LOWER(c.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.email) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"EXISTS (SELECT u FROM User u WHERE u.id = c.userId AND LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%')))")
Page<Customer> searchCustomers(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,28 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Employee;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Optional<Employee> findByUserId(Long userId);
List<Employee> findAllByEmail(String email);
@Query("SELECT e FROM Employee e WHERE " +
"LOWER(e.firstName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.lastName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.email) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(e.role) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"EXISTS (SELECT u FROM User u WHERE u.id = e.userId AND (" +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))))")
Page<Employee> searchEmployees(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,12 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.EmployeeStore;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface EmployeeStoreRepository extends JpaRepository<EmployeeStore, EmployeeStore.EmployeeStoreId> {
Optional<EmployeeStore> findByEmployeeEmployeeId(Long employeeId);
}

View File

@@ -16,8 +16,14 @@ public interface InventoryRepository extends JpaRepository<Inventory, Long> {
@Query("SELECT i FROM Inventory i WHERE i.product.prodId = :productId")
Optional<Inventory> findByProductId(@Param("productId") Long productId);
@Query("SELECT i FROM Inventory i WHERE " +
@Query("SELECT i FROM Inventory i WHERE i.product.prodId = :productId AND i.store.storeId = :storeId")
Optional<Inventory> findByProductIdAndStoreId(@Param("productId") Long productId, @Param("storeId") Long storeId);
@Query("SELECT i FROM Inventory i LEFT JOIN i.store s WHERE " +
"(:q IS NULL OR (" +
"LOWER(i.product.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(i.product.category.categoryName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Inventory> searchInventory(@Param("q") String query, Pageable pageable);
"LOWER(i.product.category.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(s.storeName) LIKE LOWER(CONCAT('%', :q, '%')))) AND " +
"(:storeId IS NULL OR i.store.storeId = :storeId)")
Page<Inventory> searchInventory(@Param("q") String query, @Param("storeId") Long storeId, Pageable pageable);
}

View File

@@ -8,12 +8,33 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface PetRepository extends JpaRepository<Pet, Long> {
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
List<Pet> findAllByOwner_IdOrderByPetNameAsc(Long ownerId);
Optional<Pet> findByIdAndOwner_Id(Long id, Long ownerId);
@Query("SELECT p FROM Pet p WHERE " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Pet> searchPets(@Param("q") String query, Pageable pageable);
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)")
Page<Pet> searchPets(@Param("q") String query, @Param("species") String species, @Param("status") String status, @Param("storeId") Long storeId, Pageable pageable);
@Query("SELECT p FROM Pet p WHERE LOWER(p.petStatus) = 'available' AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:storeId IS NULL OR p.store.storeId = :storeId)")
Page<Pet> searchPublicPets(@Param("q") String query, @Param("species") String species, @Param("storeId") Long storeId, Pageable pageable);
@Query("SELECT DISTINCT p FROM Pet p LEFT JOIN Adoption a ON a.pet = p AND LOWER(a.adoptionStatus) = 'completed' WHERE " +
"(LOWER(p.petStatus) = 'available' OR a.customer.id = :userId OR (LOWER(p.petStatus) = 'owned' AND p.owner.id = :userId)) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:species IS NULL OR LOWER(p.petSpecies) = LOWER(:species)) AND " +
"(:status IS NULL OR LOWER(p.petStatus) = LOWER(:status))")
Page<Pet> searchCustomerVisiblePets(@Param("userId") Long userId, @Param("q") String query, @Param("species") String species, @Param("status") String status, Pageable pageable);
}

View File

@@ -12,7 +12,7 @@ import org.springframework.stereotype.Repository;
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("SELECT p FROM Product p WHERE " +
"LOWER(p.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.prodDesc) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Product> searchProducts(@Param("q") String query, Pageable pageable);
"(:q IS NULL OR LOWER(p.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.prodDesc, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:categoryId IS NULL OR p.category.categoryId = :categoryId)")
Page<Product> searchProducts(@Param("q") String query, @Param("categoryId") Long categoryId, Pageable pageable);
}

View File

@@ -12,7 +12,13 @@ import org.springframework.stereotype.Repository;
public interface ProductSupplierRepository extends JpaRepository<ProductSupplier, ProductSupplier.ProductSupplierId> {
@Query("SELECT ps FROM ProductSupplier ps WHERE " +
"LOWER(ps.product.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(ps.supplier.supCompany) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<ProductSupplier> searchProductSuppliers(@Param("q") String query, Pageable pageable);
"(:q IS NULL OR (LOWER(ps.product.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(ps.supplier.supCompany) LIKE LOWER(CONCAT('%', :q, '%')))) AND " +
"(:productId IS NULL OR ps.product.prodId = :productId) AND " +
"(:supplierId IS NULL OR ps.supplier.supId = :supplierId)")
Page<ProductSupplier> searchProductSuppliers(
@Param("q") String query,
@Param("productId") Long productId,
@Param("supplierId") Long supplierId,
Pageable pageable);
}

View File

@@ -12,6 +12,7 @@ import org.springframework.stereotype.Repository;
public interface PurchaseOrderRepository extends JpaRepository<PurchaseOrder, Long> {
@Query("SELECT po FROM PurchaseOrder po WHERE " +
"LOWER(po.supplier.supCompany) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<PurchaseOrder> searchPurchaseOrders(@Param("q") String query, Pageable pageable);
"(:q IS NULL OR LOWER(po.supplier.supCompany) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(:storeId IS NULL OR po.store.storeId = :storeId)")
Page<PurchaseOrder> searchPurchaseOrders(@Param("q") String query, @Param("storeId") Long storeId, Pageable pageable);
}

View File

@@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
@@ -17,16 +18,24 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByPhone(String phone);
boolean existsByUsername(String username);
Page<User> findByRole(User.Role role, Pageable pageable);
List<User> findByRoleAndActiveTrue(User.Role role);
List<User> findByPrimaryStoreStoreIdAndRoleAndActiveTrue(Long storeId, User.Role role);
Optional<User> findFirstByPrimaryStoreStoreIdAndRoleAndActiveTrueOrderByIdAsc(Long storeId, User.Role role);
Optional<User> findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role role);
@Query("SELECT u FROM User u WHERE " +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.username, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.firstName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<User> searchUsers(@Param("q") String query, Pageable pageable);
@Query("SELECT u FROM User u WHERE u.role = :role AND (" +
"LOWER(u.username) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.username, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.firstName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.lastName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.fullName, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.email, '')) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(COALESCE(u.phone, '')) LIKE LOWER(CONCAT('%', :q, '%')))")

View File

@@ -17,6 +17,11 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration
@EnableWebSecurity
@@ -28,12 +33,10 @@ public class SecurityConfig {
private final RestAuthenticationEntryPoint restAuthenticationEntryPoint;
private final RestAccessDeniedHandler restAccessDeniedHandler;
public SecurityConfig(
JwtAuthenticationFilter jwtAuthFilter,
public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter,
UserDetailsService userDetailsService,
RestAuthenticationEntryPoint restAuthenticationEntryPoint,
RestAccessDeniedHandler restAccessDeniedHandler
) {
RestAccessDeniedHandler restAccessDeniedHandler) {
this.jwtAuthFilter = jwtAuthFilter;
this.userDetailsService = userDetailsService;
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
@@ -42,7 +45,7 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
http.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll()
@@ -56,8 +59,7 @@ public class SecurityConfig {
.requestMatchers(HttpMethod.GET, "/api/v1/appointments/availability").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint(restAuthenticationEntryPoint)
.exceptionHandling(ex -> ex.authenticationEntryPoint(restAuthenticationEntryPoint)
.accessDeniedHandler(restAccessDeniedHandler)
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
@@ -70,16 +72,32 @@ public class SecurityConfig {
private DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return new ProviderManager(daoAuthenticationProvider());
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOriginPatterns(List.of("http://localhost:*", "http://127.0.0.1:*"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -4,52 +4,54 @@ import com.petshop.backend.dto.adoption.AdoptionRequest;
import com.petshop.backend.dto.adoption.AdoptionResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Adoption;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AdoptionRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.petshop.backend.repository.EmployeeRepository;
import java.time.LocalDate;
@Service
public class AdoptionService {
private static final String ADOPTION_STATUS_PENDING = "Pending";
private static final String ADOPTION_STATUS_COMPLETED = "Completed";
private static final String ADOPTION_STATUS_CANCELLED = "Cancelled";
private static final String PET_STATUS_AVAILABLE = "Available";
private static final String PET_STATUS_ADOPTED = "Adopted";
private final AdoptionRepository adoptionRepository;
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final EmployeeRepository employeeRepository;
private Adoption response;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, CustomerRepository customerRepository, EmployeeRepository employeeRepository) {
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, UserRepository userRepository, StoreRepository storeRepository) {
this.adoptionRepository = adoptionRepository;
this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.employeeRepository = employeeRepository;
this.userRepository = userRepository;
this.storeRepository = storeRepository;
}
public Page<AdoptionResponse> getAllAdoptions(String query, Pageable pageable, Long customerId) {
Page<Adoption> adoptions;
public Page<AdoptionResponse> getAllAdoptions(String query, Long customerId, String status, Long storeId, LocalDate date, Pageable pageable) {
String normalizedQuery = normalizeFilter(query);
String normalizedStatus = normalizeFilter(status);
if (customerId != null) {
if (query != null && !query.trim().isEmpty()) {
adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable);
} else {
adoptions = adoptionRepository.findByCustomerCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
adoptions = adoptionRepository.searchAdoptions(query, pageable);
} else {
adoptions = adoptionRepository.findAll(pageable);
}
}
Page<Adoption> adoptions = adoptionRepository.searchAdoptions(
normalizedQuery,
customerId,
normalizedStatus,
storeId,
date,
pageable
);
return adoptions.map(this::mapToResponse);
}
@@ -58,7 +60,7 @@ public class AdoptionService {
Adoption adoption = adoptionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id));
if (customerId != null && !adoption.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !adoption.getCustomer().getId().equals(customerId)) {
throw new ResourceNotFoundException("You can only view your own adoptions");
}
@@ -70,17 +72,26 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
User employee = resolveAdoptionEmployee(request.getEmployeeId());
StoreLocation sourceStore = request.getSourceStoreId() != null
? storeRepository.findById(request.getSourceStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getSourceStoreId()))
: null;
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, null);
Adoption adoption = new Adoption();
adoption.setPet(pet);
adoption.setCustomer(customer);
adoption.setEmployee(employee);
adoption.setSourceStore(sourceStore);
adoption.setAdoptionDate(request.getAdoptionDate());
adoption.setAdoptionStatus(request.getAdoptionStatus());
adoption.setAdoptionStatus(adoptionStatus);
adoption = adoptionRepository.save(adoption);
syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId());
return mapToResponse(adoption);
}
@@ -92,20 +103,25 @@ public class AdoptionService {
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
User employee = resolveAdoptionEmployee(request.getEmployeeId());
StoreLocation sourceStore = request.getSourceStoreId() != null
? storeRepository.findById(request.getSourceStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getSourceStoreId()))
: null;
String adoptionStatus = normalizeAdoptionStatus(request.getAdoptionStatus());
validatePetAvailability(pet, adoption.getAdoptionId());
adoption.setPet(pet);
adoption.setCustomer(customer);
adoption.setEmployee(employee);
adoption.setSourceStore(sourceStore);
adoption.setAdoptionDate(request.getAdoptionDate());
adoption.setAdoptionStatus(request.getAdoptionStatus());
if (request.getEmployeeId() != null) {
Employee employee = employeeRepository.findById(request.getEmployeeId())
.orElseThrow(() -> new ResourceNotFoundException(
"Employee not found with id: " + request.getEmployeeId()));
adoption.setEmployee(employee);
}
adoption.setAdoptionStatus(adoptionStatus);
adoption = adoptionRepository.save(adoption);
syncPetStatus(pet, adoptionStatus, adoption.getAdoptionId());
return mapToResponse(adoption);
}
@@ -122,28 +138,90 @@ public class AdoptionService {
adoptionRepository.deleteAllById(request.getIds());
}
private AdoptionResponse mapToResponse(Adoption adoption) {
AdoptionResponse response = new AdoptionResponse(
adoption.getAdoptionId(),
adoption.getPet().getPetId(),
adoption.getPet().getPetName(),
adoption.getCustomer().getCustomerId(),
adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(),
adoption.getAdoptionDate(),
adoption.getAdoptionStatus(),
adoption.getPet().getPetPrice(),
adoption.getCreatedAt(),
adoption.getUpdatedAt()
);
// Add employee name
if (adoption.getEmployee() != null) {
response.setEmployeeId(adoption.getEmployee().getEmployeeId());
response.setEmployeeName(
adoption.getEmployee().getFirstName() + " " +
adoption.getEmployee().getLastName());
} else {
response.setEmployeeName("Unassigned");
private String normalizeFilter(String value) {
if (value == null) {
return null;
}
return response;
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
private AdoptionResponse mapToResponse(Adoption adoption) {
StoreLocation sourceStore = adoption.getSourceStore();
return new AdoptionResponse(
adoption.getAdoptionId(),
adoption.getPet().getPetId(),
adoption.getPet().getPetName(),
adoption.getCustomer().getId(),
adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(),
adoption.getEmployee().getId(),
adoption.getEmployee().getFirstName() + " " + adoption.getEmployee().getLastName(),
sourceStore != null ? sourceStore.getStoreId() : null,
sourceStore != null ? sourceStore.getStoreName() : null,
adoption.getAdoptionDate(),
adoption.getAdoptionStatus(),
adoption.getPet().getPetPrice(),
adoption.getCreatedAt(),
adoption.getUpdatedAt()
);
}
private User resolveAdoptionEmployee(Long requestedEmployeeId) {
if (requestedEmployeeId != null) {
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
if (!isAssignableUser(employee)) {
throw new IllegalArgumentException("Selected employee is not assignable for adoption work");
}
return employee;
}
return userRepository.findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role.STAFF)
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is available for adoption assignment"));
}
private boolean isAssignableUser(User user) {
return user.getRole() == User.Role.STAFF && Boolean.TRUE.equals(user.getActive());
}
private String normalizeAdoptionStatus(String adoptionStatus) {
if (adoptionStatus == null) {
throw new IllegalArgumentException("Adoption status is required");
}
String trimmedStatus = adoptionStatus.trim();
if (ADOPTION_STATUS_PENDING.equalsIgnoreCase(trimmedStatus)) {
return ADOPTION_STATUS_PENDING;
}
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(trimmedStatus)) {
return ADOPTION_STATUS_COMPLETED;
}
if (ADOPTION_STATUS_CANCELLED.equalsIgnoreCase(trimmedStatus)) {
return ADOPTION_STATUS_CANCELLED;
}
throw new IllegalArgumentException("Adoption status must be Pending, Completed, or Cancelled");
}
private void validatePetAvailability(Pet pet, Long adoptionId) {
boolean adoptedElsewhere = adoptionId == null
? adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCase(pet.getPetId(), ADOPTION_STATUS_COMPLETED)
: adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId);
if (adoptedElsewhere) {
throw new IllegalArgumentException("Selected pet has already been adopted");
}
if (!PET_STATUS_AVAILABLE.equalsIgnoreCase(pet.getPetStatus()) && adoptionId == null) {
throw new IllegalArgumentException("Selected pet is not available for adoption");
}
}
private void syncPetStatus(Pet pet, String adoptionStatus, Long adoptionId) {
boolean completedElsewhere = adoptionId != null
&& adoptionRepository.existsByPet_IdAndAdoptionStatusIgnoreCaseAndAdoptionIdNot(pet.getPetId(), ADOPTION_STATUS_COMPLETED, adoptionId);
if (ADOPTION_STATUS_COMPLETED.equalsIgnoreCase(adoptionStatus) || completedElsewhere) {
pet.setPetStatus(PET_STATUS_ADOPTED);
} else {
pet.setPetStatus(PET_STATUS_AVAILABLE);
}
petRepository.save(pet);
}
}

View File

@@ -4,6 +4,7 @@ import com.petshop.backend.dto.analytics.DashboardResponse;
import com.petshop.backend.entity.Inventory;
import com.petshop.backend.entity.Product;
import com.petshop.backend.entity.Sale;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.InventoryRepository;
import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.SaleRepository;
@@ -32,19 +33,22 @@ public class AnalyticsService {
}
@Transactional(readOnly = true)
public DashboardResponse getDashboardData(int days, int top) {
public DashboardResponse getDashboardData(int days, int top, User user) {
LocalDateTime startDate = LocalDateTime.now().minusDays(days);
List<Sale> sales = saleRepository.findAll().stream()
.filter(sale -> sale.getSaleDate().isAfter(startDate))
.filter(sale -> includeSaleForUser(sale, user))
.collect(Collectors.toList());
DashboardResponse.SalesSummary salesSummary = calculateSalesSummary(sales);
DashboardResponse.InventorySummary inventorySummary = calculateInventorySummary();
DashboardResponse.InventorySummary inventorySummary = user.getRole() == User.Role.ADMIN ? calculateInventorySummary() : null;
List<DashboardResponse.TopProduct> topProducts = calculateTopProducts(sales, top);
List<DashboardResponse.DailySales> dailySales = calculateDailySales(sales, days);
List<DashboardResponse.PaymentMethodData> paymentMethods = calculatePaymentMethods(sales);
List<DashboardResponse.EmployeePerformanceData> employeePerformance = calculateEmployeePerformance(sales, user);
return new DashboardResponse(salesSummary, inventorySummary, topProducts, dailySales);
return new DashboardResponse(salesSummary, inventorySummary, topProducts, dailySales, paymentMethods, employeePerformance);
}
private DashboardResponse.SalesSummary calculateSalesSummary(List<Sale> sales) {
@@ -66,7 +70,13 @@ public class AnalyticsService {
.filter(Sale::getIsRefund)
.count();
return new DashboardResponse.SalesSummary(totalRevenue, totalSales, totalRefunds, totalRefundCount);
Long totalItemsSold = sales.stream()
.filter(sale -> !sale.getIsRefund())
.flatMap(sale -> sale.getItems().stream())
.mapToLong(item -> item.getQuantity())
.sum();
return new DashboardResponse.SalesSummary(totalRevenue, totalSales, totalRefunds, totalRefundCount, totalItemsSold);
}
private DashboardResponse.InventorySummary calculateInventorySummary() {
@@ -93,6 +103,9 @@ public class AnalyticsService {
Map<Long, DashboardResponse.TopProduct> productSalesMap = new HashMap<>();
for (Sale sale : sales) {
if (sale.getIsRefund()) {
continue;
}
for (var item : sale.getItems()) {
Long productId = item.getProduct().getProdId();
String productName = item.getProduct().getProdName();
@@ -128,6 +141,9 @@ public class AnalyticsService {
}
for (Sale sale : sales) {
if (sale.getIsRefund()) {
continue;
}
LocalDate saleDate = sale.getSaleDate().toLocalDate();
if (dailySalesMap.containsKey(saleDate)) {
DashboardResponse.DailySales dailySale = dailySalesMap.get(saleDate);
@@ -138,4 +154,47 @@ public class AnalyticsService {
return new ArrayList<>(dailySalesMap.values());
}
private List<DashboardResponse.PaymentMethodData> calculatePaymentMethods(List<Sale> sales) {
return sales.stream()
.filter(sale -> !sale.getIsRefund())
.collect(Collectors.groupingBy(
sale -> sale.getPaymentMethod() == null ? "Unknown" : sale.getPaymentMethod(),
TreeMap::new,
Collectors.counting()))
.entrySet().stream()
.map(entry -> new DashboardResponse.PaymentMethodData(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
private List<DashboardResponse.EmployeePerformanceData> calculateEmployeePerformance(List<Sale> sales, User user) {
Map<String, BigDecimal> employeeRevenue = new TreeMap<>();
for (Sale sale : sales) {
if (sale.getIsRefund()) {
continue;
}
String employeeName = sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName();
employeeRevenue.merge(employeeName, sale.getTotalAmount(), BigDecimal::add);
}
if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) {
String employeeName = user.getFirstName() + " " + user.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
return employeeRevenue.entrySet().stream()
.map(entry -> new DashboardResponse.EmployeePerformanceData(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
private boolean includeSaleForUser(Sale sale, User user) {
if (user.getRole() == User.Role.ADMIN) {
return true;
}
if (user.getRole() == User.Role.STAFF) {
return sale.getEmployee() != null && sale.getEmployee().getId() != null && sale.getEmployee().getId().equals(user.getId());
}
return false;
}
}

View File

@@ -4,17 +4,11 @@ import com.petshop.backend.dto.appointment.AppointmentRequest;
import com.petshop.backend.dto.appointment.AppointmentResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Appointment;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.Pet;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AppointmentRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.ServiceRepository;
import com.petshop.backend.repository.StoreRepository;
@@ -30,51 +24,48 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class AppointmentService {
private final AppointmentRepository appointmentRepository;
private final CustomerRepository customerRepository;
private final ServiceRepository serviceRepository;
private final PetRepository petRepository;
private final StoreRepository storeRepository;
private final UserRepository userRepository;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
public AppointmentService(AppointmentRepository appointmentRepository, CustomerRepository customerRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository) {
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository) {
this.appointmentRepository = appointmentRepository;
this.customerRepository = customerRepository;
this.serviceRepository = serviceRepository;
this.petRepository = petRepository;
this.storeRepository = storeRepository;
this.userRepository = userRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
}
@Transactional(readOnly = true)
public Page<AppointmentResponse> getAllAppointments(String query, Pageable pageable, Long customerId) {
Page<Appointment> appointments;
public Page<AppointmentResponse> getAllAppointments(
String query,
Long customerId,
Long employeeId,
Long storeId,
String status,
LocalDate date,
Pageable pageable) {
if (customerId != null) {
if (query != null && !query.trim().isEmpty()) {
appointments = appointmentRepository.searchAppointmentsByCustomer(customerId, query, pageable);
} else {
appointments = appointmentRepository.findByCustomerCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
appointments = appointmentRepository.searchAppointments(query, pageable);
} else {
appointments = appointmentRepository.findAll(pageable);
}
}
String normalizedQuery = normalizeFilter(query);
String normalizedStatus = normalizeFilter(status);
Page<Appointment> appointments = appointmentRepository.searchAppointments(
normalizedQuery,
customerId,
employeeId,
storeId,
normalizedStatus,
date,
pageable
);
return appointments.map(this::mapToResponse);
}
@@ -84,7 +75,7 @@ public class AppointmentService {
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
if (customerId != null && !appointment.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !appointment.getCustomer().getId().equals(customerId)) {
throw new ResourceNotFoundException("You can only view your own appointments");
}
@@ -95,15 +86,11 @@ public class AppointmentService {
public AppointmentResponse createAppointment(AppointmentRequest request) {
validateAppointmentRequest(request);
Customer customer = customerRepository.findById(request.getCustomerId())
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
Employee employee;
if (request.getEmployeeId() != null) {
employee = employeeRepository.findById(request.getEmployeeId())
.orElseThrow(() -> new ResourceNotFoundException(
"Employee not found with id: " + request.getEmployeeId()));
} else {
employee = AuthenticationHelper.getAuthenticatedEmployee(
userRepository, employeeRepository);
}
@@ -114,10 +101,11 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
validateStoreAccess(store.getStoreId());
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
Pet pet = request.getPetId() != null ? fetchPet(request.getPetId()) : null;
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Set<Pet> pets = fetchPets(request.getPetIds());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
Appointment appointment = new Appointment();
appointment.setCustomer(customer);
@@ -127,7 +115,8 @@ public class AppointmentService {
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setPet(pet);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
return mapToResponse(appointment);
@@ -137,10 +126,12 @@ public class AppointmentService {
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) {
validateAppointmentRequest(request);
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -149,10 +140,11 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
validateStoreAccess(store.getStoreId());
validateAvailability(store, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
Pet pet = request.getPetId() != null ? fetchPet(request.getPetId()) : null;
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Set<Pet> pets = fetchPets(request.getPetIds());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
appointment.setCustomer(customer);
appointment.setStore(store);
@@ -160,7 +152,8 @@ public class AppointmentService {
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setPet(pet);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
return mapToResponse(appointment);
@@ -180,7 +173,6 @@ public class AppointmentService {
}
@Transactional(readOnly = true)
public List<String> checkAvailability(Long storeId, Long serviceId, LocalDate date) {
storeRepository.findById(storeId)
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + storeId));
@@ -188,16 +180,17 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId)
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId));
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
//--------------------------------------------------------------------------
// CHANGED: filter by serviceId too
List<Appointment> existingAppointments = appointmentRepository
.findByStoreAndDate(storeId, date)
.stream()
.filter(a -> a.getService().getServiceId().equals(serviceId))
.collect(Collectors.toList());
// -------------------------------------------------------
if (assignableUsers.isEmpty()) {
return List.of();
}
List<Long> employeeIds = assignableUsers.stream().map(User::getId).collect(Collectors.toList());
List<Appointment> allAppointments = appointmentRepository.findByEmployeeIdInAndAppointmentDate(employeeIds, date);
java.util.Map<Long, List<Appointment>> appointmentsByEmployee = allAppointments.stream()
.collect(Collectors.groupingBy(a -> a.getEmployee().getId()));
List<String> availableSlots = new ArrayList<>();
LocalTime startTime = LocalTime.of(9, 0);
@@ -206,7 +199,13 @@ public class AppointmentService {
LocalTime currentTime = startTime;
while (!currentTime.isAfter(latestStart)) {
if (isSlotAvailable(existingAppointments, service, currentTime, null)) {
final LocalTime slotTime = currentTime;
boolean anyEmployeeAvailable = assignableUsers.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getId(), List.of());
return isSlotAvailable(empAppointments, service, slotTime, null);
});
if (anyEmployeeAvailable) {
availableSlots.add(currentTime.toString());
}
currentTime = currentTime.plusMinutes(30);
@@ -215,6 +214,14 @@ public class AppointmentService {
return availableSlots;
}
private String normalizeFilter(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
private void validateAppointmentRequest(AppointmentRequest request) {
if ("Booked".equalsIgnoreCase(request.getAppointmentStatus())) {
LocalDateTime appointmentDateTime = LocalDateTime.of(request.getAppointmentDate(), request.getAppointmentTime());
@@ -224,64 +231,63 @@ public class AppointmentService {
}
}
private Set<Pet> fetchPets(List<Long> petIds) {
Set<Pet> pets = new HashSet<>();
for (Long petId : petIds) {
Pet pet = petRepository.findById(petId)
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + petId));
pets.add(pet);
}
return pets;
private Pet fetchPet(Long petId) {
return petRepository.findById(petId)
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + petId));
}
private AppointmentResponse mapToResponse(Appointment appointment) {
List<String> petNames = appointment.getPets().stream()
.map(Pet::getPetName)
.collect(Collectors.toList());
Pet pet = appointment.getPet();
List<Long> petIds = appointment.getPets().stream()
.map(Pet::getPetId)
.collect(Collectors.toList());
AppointmentResponse response = new AppointmentResponse();
response.setAppointmentId(appointment.getAppointmentId());
response.setCustomerId(appointment.getCustomer().getId());
response.setCustomerName(appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName());
response.setStoreId(appointment.getStore().getStoreId());
response.setStoreName(appointment.getStore().getStoreName());
response.setServiceId(appointment.getService().getServiceId());
response.setServiceName(appointment.getService().getServiceName());
response.setAppointmentDate(appointment.getAppointmentDate());
response.setAppointmentTime(appointment.getAppointmentTime());
response.setAppointmentStatus(appointment.getAppointmentStatus());
response.setEmployeeId(appointment.getEmployee().getId());
response.setEmployeeName(appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName());
response.setPetName(pet != null ? pet.getPetName() : null);
response.setPetId(pet != null ? pet.getPetId() : null);
response.setCreatedAt(appointment.getCreatedAt());
response.setUpdatedAt(appointment.getUpdatedAt());
return new AppointmentResponse(
appointment.getAppointmentId(),
appointment.getCustomer().getCustomerId(),
appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName(),
appointment.getStore().getStoreId(),
appointment.getStore().getStoreName(),
appointment.getService().getServiceId(),
appointment.getService().getServiceName(),
appointment.getAppointmentDate(),
appointment.getAppointmentTime(),
appointment.getAppointmentStatus(),
petNames,
petIds,
appointment.getCreatedAt(),
appointment.getUpdatedAt(),
appointment.getEmployee() != null ? appointment.getEmployee().getEmployeeId() : null,
appointment.getEmployee() != null ?
appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName()
: "Unassigned"
return response;
);
}
//------------------------------------
private void validateAvailability(StoreLocation store, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
// Filter by same service only - different services can run at same time
private User resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
if (requestedEmployeeId != null) {
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
boolean assignedToStore = assignableUsers.stream()
.anyMatch(u -> u.getId().equals(requestedEmployeeId));
if (!assignedToStore) {
throw new IllegalArgumentException("Selected employee is not assignable for the selected store");
}
return employee;
}
return assignableUsers.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is assigned to the selected store"));
}
private void validateAvailability(User employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
List<Appointment> existingAppointments = appointmentRepository
.findByStoreAndDate(store.getStoreId(), date)
.stream()
.filter(a -> a.getService().getServiceId().equals(service.getServiceId()))
.collect(Collectors.toList());
.findByEmployeeIdAndAppointmentDate(employee.getId(), date);
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
throw new IllegalArgumentException("Appointment time is not available for the selected store and service");
throw new IllegalArgumentException("The selected employee is already booked for this time slot");
}
}
//------------------------------------------------
private boolean isSlotAvailable(List<Appointment> existingAppointments, com.petshop.backend.entity.Service requestedService, LocalTime requestedStart, Long appointmentIdToIgnore) {
LocalTime requestedEnd = requestedStart.plusMinutes(requestedService.getServiceDuration());
for (Appointment existingAppointment : existingAppointments) {
@@ -297,17 +303,12 @@ public class AppointmentService {
return true;
}
private void validateStoreAccess(Long requestedStoreId) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
private void validateStoreAccess(Long requestedStoreId, User user) {
if (user.getRole() != User.Role.STAFF) {
return;
}
Employee employee = AuthenticationHelper.getAuthenticatedEmployee(userRepository, employeeRepository);
EmployeeStore employeeStore = employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId())
.orElseThrow(() -> new AccessDeniedException("Authenticated staff member is not assigned to a store"));
if (!employeeStore.getStore().getStoreId().equals(requestedStoreId)) {
if (user.getPrimaryStore() == null || !user.getPrimaryStore().getStoreId().equals(requestedStoreId)) {
throw new AccessDeniedException("Staff can only manage appointments for their assigned store");
}
}

View File

@@ -48,7 +48,10 @@ public class AvatarStorageService {
if (user.getAvatarUrl() == null || user.getAvatarUrl().isBlank()) {
return;
}
Files.deleteIfExists(resolveStoredAvatarPath(user.getAvatarUrl()));
try {
Files.deleteIfExists(resolveStoredAvatarPath(user.getAvatarUrl()));
} catch (IllegalArgumentException ignored) {
}
}
public String toOwnerAvatarUrl(User user) {

View File

@@ -20,14 +20,9 @@ public class CategoryService {
this.categoryRepository = categoryRepository;
}
public Page<CategoryResponse> getAllCategories(String query, Pageable pageable) {
Page<Category> categories;
if (query != null && !query.trim().isEmpty()) {
categories = categoryRepository.searchCategories(query, pageable);
} else {
categories = categoryRepository.findAll(pageable);
}
return categories.map(this::mapToResponse);
public Page<CategoryResponse> getAllCategories(String query, String type, Pageable pageable) {
return categoryRepository.searchCategories(normalizeFilter(query), normalizeFilter(type), pageable)
.map(this::mapToResponse);
}
public CategoryResponse getCategoryById(Long id) {
@@ -80,4 +75,12 @@ public class CategoryService {
category.getUpdatedAt()
);
}
private String normalizeFilter(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
}

View File

@@ -3,11 +3,9 @@ package com.petshop.backend.service;
import com.petshop.backend.dto.chat.ConversationResponse;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.messaging.simp.SimpMessagingTemplate;
@@ -21,14 +19,12 @@ public class ChatRealtimeService {
private final SimpMessagingTemplate messagingTemplate;
private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
public ChatRealtimeService(SimpMessagingTemplate messagingTemplate, ConversationRepository conversationRepository, MessageRepository messageRepository, CustomerRepository customerRepository, UserRepository userRepository) {
public ChatRealtimeService(SimpMessagingTemplate messagingTemplate, ConversationRepository conversationRepository, MessageRepository messageRepository, UserRepository userRepository) {
this.messagingTemplate = messagingTemplate;
this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
}
@@ -54,13 +50,11 @@ public class ChatRealtimeService {
}
private void sendConversationToCustomerQueue(ConversationResponse conversation) {
Customer customer = customerRepository.findById(conversation.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found"));
if (customer.getUserId() == null) {
User customerUser = userRepository.findById(conversation.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
if (customerUser.getUsername() == null) {
return;
}
User customerUser = userRepository.findById(customer.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
messagingTemplate.convertAndSendToUser(customerUser.getUsername(), "/queue/chat/conversations", conversation);
}

View File

@@ -4,13 +4,12 @@ import com.petshop.backend.dto.chat.ConversationRequest;
import com.petshop.backend.dto.chat.ConversationResponse;
import com.petshop.backend.dto.chat.MessageRequest;
import com.petshop.backend.dto.chat.MessageResponse;
import com.petshop.backend.dto.chat.UpdateConversationRequest;
import com.petshop.backend.entity.Conversation;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Message;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.ConversationRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.MessageRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.security.access.AccessDeniedException;
@@ -27,16 +26,13 @@ public class ChatService {
private final ConversationRepository conversationRepository;
private final MessageRepository messageRepository;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public ChatService(ConversationRepository conversationRepository,
MessageRepository messageRepository,
UserRepository userRepository,
CustomerRepository customerRepository) {
UserRepository userRepository) {
this.conversationRepository = conversationRepository;
this.messageRepository = messageRepository;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@Transactional
@@ -48,11 +44,8 @@ public class ChatService {
throw new AccessDeniedException("Only customers can start new conversations");
}
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
Conversation conversation = new Conversation();
conversation.setCustomerId(customer.getCustomerId());
conversation.setCustomerId(userId);
conversation.setStatus(Conversation.ConversationStatus.OPEN);
conversation.setMode(Conversation.ConversationMode.AUTOMATED);
conversation = conversationRepository.save(conversation);
@@ -71,9 +64,7 @@ public class ChatService {
List<Conversation> conversations;
if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
conversations = conversationRepository.findByCustomerId(customer.getCustomerId());
conversations = conversationRepository.findByCustomerId(userId);
} else if (role == User.Role.STAFF) {
List<Conversation> assignedToMe = conversationRepository.findByStaffId(userId);
List<Conversation> unassigned = conversationRepository.findByStaffIdIsNull();
@@ -86,7 +77,8 @@ public class ChatService {
return conversations.stream()
.map(conv -> {
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conv.getId());
String lastMessage = messages.isEmpty() ? "" : messages.get(messages.size() - 1).getContent();
Message last = messages.isEmpty() ? null : messages.get(messages.size() - 1);
String lastMessage = last != null && last.getContent() != null ? last.getContent() : "";
return ConversationResponse.fromEntity(conv, lastMessage);
})
.collect(Collectors.toList());
@@ -106,7 +98,8 @@ public class ChatService {
}
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
String lastMessage = messages.isEmpty() ? "" : messages.get(messages.size() - 1).getContent();
Message last = messages.isEmpty() ? null : messages.get(messages.size() - 1);
String lastMessage = last != null && last.getContent() != null ? last.getContent() : "";
return ConversationResponse.fromEntity(conversation, lastMessage);
}
@@ -116,6 +109,10 @@ public class ChatService {
Conversation conversation = conversationRepository.findById(conversationId)
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
if (conversation.getStatus() == Conversation.ConversationStatus.CLOSED) {
throw new AccessDeniedException("Conversation is closed");
}
if (!hasConversationAccess(conversation, userId, role)) {
if (role == User.Role.CUSTOMER) {
throw new AccessDeniedException("You can only send messages to your own conversations");
@@ -129,6 +126,10 @@ public class ChatService {
message.setConversationId(conversationId);
message.setSenderId(userId);
message.setContent(request.getContent());
message.setAttachmentUrl(request.getAttachmentUrl());
message.setAttachmentName(request.getAttachmentName());
message.setAttachmentMimeType(request.getAttachmentMimeType());
message.setAttachmentSizeBytes(request.getAttachmentSizeBytes());
message.setIsRead(false);
message = messageRepository.save(message);
@@ -149,6 +150,10 @@ public class ChatService {
Conversation conversation = conversationRepository.findById(conversationId)
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
if (conversation.getStatus() == Conversation.ConversationStatus.CLOSED) {
throw new AccessDeniedException("Conversation is closed");
}
if (role != User.Role.CUSTOMER || !hasConversationAccess(conversation, userId, role)) {
throw new AccessDeniedException("You can only request human takeover for your own conversations");
}
@@ -159,7 +164,31 @@ public class ChatService {
conversationRepository.save(conversation);
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
String lastMessage = messages.isEmpty() ? "" : messages.get(messages.size() - 1).getContent();
Message last = messages.isEmpty() ? null : messages.get(messages.size() - 1);
String lastMessage = last != null && last.getContent() != null ? last.getContent() : "";
return ConversationResponse.fromEntity(conversation, lastMessage);
}
@Transactional
public ConversationResponse updateConversation(Long conversationId, Long userId, User.Role role, UpdateConversationRequest request) {
Conversation conversation = conversationRepository.findById(conversationId)
.orElseThrow(() -> new ResourceNotFoundException("Conversation not found"));
if (!hasConversationAccess(conversation, userId, role)) {
if (role == User.Role.CUSTOMER) {
throw new AccessDeniedException("You can only close your own conversations");
}
if (role == User.Role.STAFF) {
throw new AccessDeniedException("You can only close conversations assigned to you or unassigned conversations");
}
}
conversation.setStatus(Conversation.ConversationStatus.valueOf(request.getStatus()));
conversation = conversationRepository.save(conversation);
List<Message> messages = messageRepository.findByConversationIdOrderByTimestampAsc(conversationId);
Message last = messages.isEmpty() ? null : messages.get(messages.size() - 1);
String lastMessage = last != null && last.getContent() != null ? last.getContent() : "";
return ConversationResponse.fromEntity(conversation, lastMessage);
}
@@ -194,9 +223,7 @@ public class ChatService {
}
if (role == User.Role.CUSTOMER) {
Customer customer = customerRepository.findByUserId(userId)
.orElseThrow(() -> new ResourceNotFoundException("Customer record not found for user"));
return conversation.getCustomerId().equals(customer.getCustomerId());
return conversation.getCustomerId().equals(userId);
}
if (role == User.Role.STAFF) {

View File

@@ -1,102 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest;
import com.petshop.backend.dto.customer.CustomerResponse;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
public CustomerService(CustomerRepository customerRepository, UserRepository userRepository) {
this.customerRepository = customerRepository;
this.userRepository = userRepository;
}
public Page<CustomerResponse> getAllCustomers(String query, Pageable pageable) {
Page<Customer> customers;
if (query != null && !query.trim().isEmpty()) {
customers = customerRepository.searchCustomers(query, pageable);
} else {
customers = customerRepository.findAll(pageable);
}
return customers.map(this::mapToResponse);
}
public CustomerResponse getCustomerById(Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
return mapToResponse(customer);
}
@Transactional
public CustomerResponse createCustomer(CustomerRequest request) {
Customer customer = new Customer();
customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail());
customer = customerRepository.save(customer);
syncLinkedUser(customer);
return mapToResponse(customer);
}
@Transactional
public CustomerResponse updateCustomer(Long id, CustomerRequest request) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail());
customer = customerRepository.save(customer);
syncLinkedUser(customer);
return mapToResponse(customer);
}
@Transactional
public void deleteCustomer(Long id) {
if (!customerRepository.existsById(id)) {
throw new ResourceNotFoundException("Customer not found with id: " + id);
}
customerRepository.deleteById(id);
}
@Transactional
public void bulkDeleteCustomers(BulkDeleteRequest request) {
customerRepository.deleteAllById(request.getIds());
}
private CustomerResponse mapToResponse(Customer customer) {
return new CustomerResponse(
customer.getCustomerId(),
customer.getFirstName(),
customer.getLastName(),
customer.getEmail(),
customer.getCreatedAt(),
customer.getUpdatedAt()
);
}
private void syncLinkedUser(Customer customer) {
if (customer.getUserId() == null) {
return;
}
userRepository.findById(customer.getUserId()).ifPresent(user -> {
user.setEmail(customer.getEmail());
user.setFullName((customer.getFirstName() + " " + customer.getLastName()).trim());
userRepository.save(user);
});
}
}

View File

@@ -1,183 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.employee.EmployeeRequest;
import com.petshop.backend.dto.employee.EmployeeResponse;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import static org.springframework.http.HttpStatus.CONFLICT;
@Service
public class EmployeeService {
private final EmployeeRepository employeeRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
public EmployeeService(EmployeeRepository employeeRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService) {
this.employeeRepository = employeeRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
}
public Page<EmployeeResponse> getAllEmployees(String query, Pageable pageable) {
Page<Employee> employees;
if (query != null && !query.trim().isEmpty()) {
employees = employeeRepository.searchEmployees(query, pageable);
} else {
employees = employeeRepository.findAll(pageable);
}
return employees.map(this::mapToResponse);
}
public EmployeeResponse getEmployeeById(Long id) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
return mapToResponse(employee);
}
@Transactional
public EmployeeResponse createEmployee(EmployeeRequest request) {
validateRole(request.getRole());
if (request.getPassword() == null || request.getPassword().trim().length() < 6) {
throw new IllegalArgumentException("Password must be at least 6 characters");
}
if (userRepository.findByUsername(request.getUsername()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Username already exists");
}
if (request.getEmail() != null && userRepository.findByEmail(request.getEmail()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
String phone = trimToNull(request.getPhone());
if (phone != null && userRepository.findByPhone(phone).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Phone already exists");
}
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setFullName(fullName(request));
user.setEmail(request.getEmail());
user.setPhone(phone);
user.setRole(request.getRole());
user.setActive(request.getActive() != null ? request.getActive() : true);
user = userRepository.save(user);
Employee employee = userBusinessLinkageService.ensureLinkedEmployee(user);
return mapToResponse(employee, user);
}
@Transactional
public EmployeeResponse updateEmployee(Long id, EmployeeRequest request) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
User user = requireLinkedUser(employee);
validateRole(request.getRole());
if (!user.getUsername().equals(request.getUsername()) && userRepository.findByUsername(request.getUsername()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Username already exists");
}
if (!java.util.Objects.equals(user.getEmail(), request.getEmail()) && request.getEmail() != null && userRepository.findByEmail(request.getEmail()).isPresent()) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
String phone = trimToNull(request.getPhone());
Long currentUserId = user.getId();
if (!java.util.Objects.equals(user.getPhone(), phone)) {
userRepository.findByPhone(phone)
.filter(existing -> !existing.getId().equals(currentUserId))
.ifPresent(existing -> { throw new ResponseStatusException(CONFLICT, "Phone already exists"); });
}
user.setUsername(request.getUsername());
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setTokenVersion(user.getTokenVersion() + 1);
}
user.setEmail(request.getEmail());
user.setPhone(phone);
user.setFullName(fullName(request));
user.setRole(request.getRole());
user.setActive(request.getActive() != null ? request.getActive() : true);
user = userRepository.save(user);
employee = userBusinessLinkageService.ensureLinkedEmployee(user);
return mapToResponse(employee, user);
}
@Transactional
public void deleteEmployee(Long id) {
Employee employee = employeeRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + id));
if (employee.getUserId() != null && userRepository.existsById(employee.getUserId())) {
userRepository.deleteById(employee.getUserId());
return;
}
employeeRepository.deleteById(id);
}
private EmployeeResponse mapToResponse(Employee employee) {
User user = employee.getUserId() == null ? null : userRepository.findById(employee.getUserId()).orElse(null);
return mapToResponse(employee, user);
}
private EmployeeResponse mapToResponse(Employee employee, User user) {
EmployeeResponse response = new EmployeeResponse();
response.setEmployeeId(employee.getEmployeeId());
response.setUserId(user != null ? user.getId() : employee.getUserId());
response.setUsername(user != null ? user.getUsername() : null);
response.setFirstName(employee.getFirstName());
response.setLastName(employee.getLastName());
response.setFullName(user != null ? user.getFullName() : fullName(employee));
response.setEmail(user != null ? user.getEmail() : employee.getEmail());
response.setPhone(user != null ? user.getPhone() : null);
response.setRole(user != null ? user.getRole().name() : normalizeRole(employee.getRole()));
response.setActive(user != null ? user.getActive() : employee.getIsActive());
response.setCreatedAt(employee.getCreatedAt());
response.setUpdatedAt(employee.getUpdatedAt());
return response;
}
private User requireLinkedUser(Employee employee) {
if (employee.getUserId() == null) {
throw new ResourceNotFoundException("Employee user account not found");
}
return userRepository.findById(employee.getUserId())
.orElseThrow(() -> new ResourceNotFoundException("Employee user account not found"));
}
private void validateRole(User.Role role) {
if (role != User.Role.STAFF && role != User.Role.ADMIN) {
throw new IllegalArgumentException("Employee role must be STAFF or ADMIN");
}
}
private String fullName(EmployeeRequest request) {
return (request.getFirstName().trim() + " " + request.getLastName().trim()).trim();
}
private String fullName(Employee employee) {
return (employee.getFirstName().trim() + " " + employee.getLastName().trim()).trim();
}
private String normalizeRole(String role) {
return role == null ? null : role.trim().toUpperCase(java.util.Locale.ROOT);
}
private String trimToNull(String value) {
if (value == null) {
return null;
}
String trimmed = value.trim();
return trimmed.isEmpty() ? null : trimmed;
}
}

Some files were not shown because too many files have changed in this diff Show More