AttachmentsToChat #145

Merged
RecentRunner merged 35 commits from AttachmentsToChat into main 2026-04-07 06:53:07 -06:00
117 changed files with 6128 additions and 4763 deletions
Showing only changes of commit 2b097cf4a9 - Show all commits

View File

@@ -0,0 +1,25 @@
services:
db-target:
image: mysql:8.0
container_name: petshop-db-target
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: Petstoredb_target
MYSQL_USER: petshop
MYSQL_PASSWORD: petshop
ports:
- "3307:3306"
volumes:
- db_target_data:/var/lib/mysql
- ./src/main/resources/dev/final-target/final_target_schema.sql:/docker-entrypoint-initdb.d/01_final_target_schema.sql:ro
- ./src/main/resources/dev/final-target/final_target_seed.sql:/docker-entrypoint-initdb.d/02_final_target_seed.sql:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-proot"]
interval: 10s
timeout: 5s
retries: 30
start_period: 40s
volumes:
db_target_data:

View File

@@ -197,6 +197,57 @@
</arguments>
</configuration>
</execution>
<execution>
<id>docker-up-target-db</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>docker-compose.target-db.yml</argument>
<argument>up</argument>
<argument>-d</argument>
<argument>--wait</argument>
<argument>db-target</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>docker-down-target-db</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>docker-compose.target-db.yml</argument>
<argument>down</argument>
<argument>-v</argument>
<argument>--remove-orphans</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>docker-logs-target-db</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>docker-compose.target-db.yml</argument>
<argument>logs</argument>
<argument>db-target</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

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

@@ -1,34 +0,0 @@
package com.petshop.backend.config;
import com.petshop.backend.repository.CustomerPetRepository;
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 LocalAppointmentCustomerSeedInitializer implements CommandLineRunner {
private final DataSource dataSource;
private final CustomerPetRepository customerPetRepository;
public LocalAppointmentCustomerSeedInitializer(DataSource dataSource, CustomerPetRepository customerPetRepository) {
this.dataSource = dataSource;
this.customerPetRepository = customerPetRepository;
}
@Override
public void run(String... args) {
if (customerPetRepository.count() > 0) {
return;
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(false, false, "UTF-8",
new ClassPathResource("dev/seed_demo_customer_pets.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;
@@ -24,12 +23,10 @@ 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
@@ -45,8 +42,8 @@ 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.getAllAdoptions(q, pageable, customerId));
@@ -63,8 +60,8 @@ 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));

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,12 +26,10 @@ 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
@@ -48,8 +45,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.getAllAppointments(q, pageable, customerId));
@@ -66,8 +63,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 +80,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,11 @@ 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.CustomerRepository;
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 jakarta.validation.Valid;
import org.springframework.core.io.Resource;
@@ -29,6 +25,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;
@@ -44,22 +41,14 @@ 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;
private final CustomerRepository customerRepository;
public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtUtil jwtUtil, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository, AvatarStorageService avatarStorageService, CustomerRepository customerRepository) {
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;
this.customerRepository = customerRepository;
}
@PostMapping("/register")
@@ -94,9 +83,6 @@ public class AuthController {
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(
@@ -145,25 +131,11 @@ public class AuthController {
}
}
@Transactional(readOnly = true)
@GetMapping("/me")
public ResponseEntity<UserInfoResponse> getCurrentUser() {
User user = getAuthenticatedUser();
EmployeeStore employeeStore = resolveEmployeeStore(user);
Long customerId = resolveCustomerId(user);
return ResponseEntity.ok(new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
customerId,
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
return ResponseEntity.ok(toUserInfoResponse(user));
}
@PutMapping("/me")
@@ -218,39 +190,24 @@ public class AuthController {
}
User updatedUser = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(updatedUser);
return ResponseEntity.ok(toUserInfoResponse(updatedUser));
}
EmployeeStore employeeStore = resolveEmployeeStore(updatedUser);
Long customerId = resolveCustomerId(updatedUser);
return ResponseEntity.ok(new UserInfoResponse(
updatedUser.getId(),
updatedUser.getUsername(),
updatedUser.getEmail(),
updatedUser.getFullName(),
updatedUser.getPhone(),
avatarStorageService.toOwnerAvatarUrl(updatedUser),
updatedUser.getRole().name(),
private UserInfoResponse toUserInfoResponse(User user) {
StoreLocation primaryStore = user.getPrimaryStore();
Long customerId = user.getRole() == User.Role.CUSTOMER ? user.getId() : null;
return new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getEmail(),
user.getFullName(),
user.getPhone(),
avatarStorageService.toOwnerAvatarUrl(user),
user.getRole().name(),
customerId,
employeeStore != null ? employeeStore.getStore().getStoreId() : null,
employeeStore != null ? employeeStore.getStore().getStoreName() : null
));
}
private EmployeeStore resolveEmployeeStore(User user) {
if (user.getRole() == User.Role.CUSTOMER) {
return null;
}
return employeeRepository.findByUserId(user.getId())
.flatMap(employee -> employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId()))
.orElse(null);
}
private Long resolveCustomerId(User user) {
return customerRepository.findByUserId(user.getId())
.map(c -> c.getCustomerId())
.orElse(null);
primaryStore != null ? primaryStore.getStoreId() : null,
primaryStore != null ? primaryStore.getStoreName() : null
);
}
private String trimToNull(String value) {

View File

@@ -6,7 +6,6 @@ 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;
@@ -27,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() {

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,118 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.customerpet.CustomerPetRequest;
import com.petshop.backend.dto.customerpet.CustomerPetResponse;
import com.petshop.backend.service.CatalogImageStorageService;
import com.petshop.backend.service.CustomerPetService;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.repository.CustomerPetRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.util.AuthenticationHelper;
import jakarta.validation.Valid;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/my-pets")
@PreAuthorize("hasRole('CUSTOMER')")
public class CustomerPetController {
private final CustomerPetService customerPetService;
private final CustomerPetRepository customerPetRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final CatalogImageStorageService catalogImageStorageService;
public CustomerPetController(CustomerPetService customerPetService,
CustomerPetRepository customerPetRepository,
CustomerRepository customerRepository,
UserRepository userRepository,
CatalogImageStorageService catalogImageStorageService) {
this.customerPetService = customerPetService;
this.customerPetRepository = customerPetRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@GetMapping
public ResponseEntity<List<CustomerPetResponse>> getMyPets() {
return ResponseEntity.ok(customerPetService.getMyPets());
}
@PostMapping
public ResponseEntity<CustomerPetResponse> createPet(@Valid @RequestBody CustomerPetRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerPetService.createPet(request));
}
@PutMapping("/{id}")
public ResponseEntity<CustomerPetResponse> updatePet(@PathVariable Long id, @Valid @RequestBody CustomerPetRequest request) {
return ResponseEntity.ok(customerPetService.updatePet(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePet(@PathVariable Long id) {
customerPetService.deletePet(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/image")
public ResponseEntity<?> uploadImage(@PathVariable Long id, @RequestParam("image") MultipartFile image) {
try {
return ResponseEntity.ok(customerPetService.uploadImage(id, image));
}
catch (IllegalArgumentException ex) {
Map<String, String> error = new HashMap<>();
error.put("message", ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
catch (IOException ex) {
Map<String, String> error = new HashMap<>();
error.put("message", "Failed to upload image: " + ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
@GetMapping("/{id}/image")
public ResponseEntity<Resource> getImage(@PathVariable Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElse(null);
if (pet == null || pet.getImageUrl() == null || pet.getImageUrl().isBlank()) {
return ResponseEntity.notFound().build();
}
Resource resource = catalogImageStorageService.loadPetImage(pet.getImageUrl());
MediaType mediaType = catalogImageStorageService.resolveMediaType(resource);
return ResponseEntity.ok().contentType(mediaType).body(resource);
}
@DeleteMapping("/{id}/image")
public ResponseEntity<CustomerPetResponse> deleteImage(@PathVariable Long id) {
return ResponseEntity.ok(customerPetService.deleteImage(id));
}
}

View File

@@ -1,8 +1,6 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.DropdownOption;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.*;
import org.springframework.http.ResponseEntity;
@@ -20,31 +18,24 @@ import java.util.stream.Collectors;
public class DropdownController {
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final CustomerPetRepository customerPetRepository;
private final ServiceRepository serviceRepository;
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
private final StoreRepository storeRepository;
private final SupplierRepository supplierRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository;
public DropdownController(PetRepository petRepository, CustomerRepository customerRepository,
CustomerPetRepository customerPetRepository,
public DropdownController(PetRepository petRepository,
ServiceRepository serviceRepository, ProductRepository productRepository,
CategoryRepository categoryRepository, StoreRepository storeRepository,
SupplierRepository supplierRepository, EmployeeStoreRepository employeeStoreRepository,
SupplierRepository supplierRepository,
UserRepository userRepository) {
this.petRepository = petRepository;
this.customerRepository = customerRepository;
this.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository;
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
this.storeRepository = storeRepository;
this.supplierRepository = supplierRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository;
}
@@ -71,8 +62,8 @@ public class DropdownController {
@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())
);
}
@@ -81,18 +72,8 @@ public class DropdownController {
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getAppointmentCustomers() {
return ResponseEntity.ok(
customerRepository.findAllWithPets().stream()
.map(c -> new DropdownOption(c.getCustomerId(), c.getFirstName() + " " + c.getLastName()))
.collect(Collectors.toList())
);
}
@GetMapping("/customers/{customerId}/pets")
@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getCustomerPets(@PathVariable Long customerId) {
return ResponseEntity.ok(
customerPetRepository.findByCustomerCustomerIdOrderByPetNameAsc(customerId).stream()
.map(this::toCustomerPetOption)
userRepository.findByRoleAndActiveTrue(User.Role.CUSTOMER).stream()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@@ -159,17 +140,15 @@ public class DropdownController {
@GetMapping({"/stores/{storeId}/employees", "/employees"})
@PreAuthorize("hasAnyRole('CUSTOMER', 'STAFF', 'ADMIN')")
public ResponseEntity<List<DropdownOption>> getStoreEmployees(@PathVariable(required = false) Long storeId) {
List<EmployeeStore> employees;
List<User> employees;
if (storeId == null || storeId == 0) {
employees = employeeStoreRepository.findActiveAllOrderByEmployeeEmployeeIdAsc();
employees = userRepository.findByRoleAndActiveTrue(User.Role.STAFF);
} else {
employees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId);
employees = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
}
return ResponseEntity.ok(
employees.stream()
.filter(this::isAssignableEmployee)
.map(this::toEmployeeOption)
.distinct()
.map(u -> new DropdownOption(u.getId(), u.getFirstName() + " " + u.getLastName()))
.collect(Collectors.toList())
);
}
@@ -183,26 +162,4 @@ public class DropdownController {
.collect(Collectors.toList())
);
}
private DropdownOption toCustomerPetOption(CustomerPet pet) {
String species = pet.getSpecies() == null || pet.getSpecies().isBlank() ? "Pet" : pet.getSpecies();
String breed = pet.getBreed() == null || pet.getBreed().isBlank() ? "" : " · " + pet.getBreed();
return new DropdownOption(pet.getCustomerPetId(), pet.getPetName() + " (" + species + breed + ")");
}
private DropdownOption toEmployeeOption(EmployeeStore employeeStore) {
var employee = employeeStore.getEmployee();
return new DropdownOption(employee.getEmployeeId(), employee.getFirstName() + " " + employee.getLastName());
}
private boolean isAssignableEmployee(EmployeeStore employeeStore) {
Long userId = employeeStore.getEmployee().getUserId();
if (userId == null) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
}
}

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

@@ -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;
@@ -26,12 +25,10 @@ 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
@@ -46,8 +43,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();
}
RefundResponse refund = refundService.createRefund(request, customerId);
@@ -70,8 +67,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);
@@ -90,8 +87,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();
}
RefundResponse refund = refundService.getRefundById(id, customerId);

View File

@@ -20,6 +20,8 @@ public class AdoptionRequest {
private Long employeeId;
private Long sourceStoreId;
public Long getPetId() {
return petId;
}
@@ -60,6 +62,14 @@ public class AdoptionRequest {
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;
@@ -69,12 +79,13 @@ public class AdoptionRequest {
Objects.equals(customerId, that.customerId) &&
Objects.equals(adoptionDate, that.adoptionDate) &&
Objects.equals(adoptionStatus, that.adoptionStatus) &&
Objects.equals(employeeId, that.employeeId);
Objects.equals(employeeId, that.employeeId) &&
Objects.equals(sourceStoreId, that.sourceStoreId);
}
@Override
public int hashCode() {
return Objects.hash(petId, customerId, adoptionDate, adoptionStatus, employeeId);
return Objects.hash(petId, customerId, adoptionDate, adoptionStatus, employeeId, sourceStoreId);
}
@Override
@@ -85,6 +96,7 @@ public class AdoptionRequest {
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", employeeId=" + employeeId +
", sourceStoreId=" + sourceStoreId +
'}';
}
}

View File

@@ -13,6 +13,8 @@ public class AdoptionResponse {
private String customerName;
private Long employeeId;
private String employeeName;
private Long sourceStoreId;
private String sourceStoreName;
private LocalDate adoptionDate;
private String adoptionStatus;
private BigDecimal adoptionFee;
@@ -22,7 +24,7 @@ public class AdoptionResponse {
public AdoptionResponse() {
}
public AdoptionResponse(Long adoptionId, Long petId, String petName, Long customerId, String customerName, Long employeeId, String employeeName, 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;
@@ -30,6 +32,8 @@ public class AdoptionResponse {
this.customerName = customerName;
this.employeeId = employeeId;
this.employeeName = employeeName;
this.sourceStoreId = sourceStoreId;
this.sourceStoreName = sourceStoreName;
this.adoptionDate = adoptionDate;
this.adoptionStatus = adoptionStatus;
this.adoptionFee = adoptionFee;
@@ -93,6 +97,22 @@ public class AdoptionResponse {
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;
}
@@ -138,12 +158,12 @@ public class AdoptionResponse {
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);
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(sourceStoreId, that.sourceStoreId) && Objects.equals(sourceStoreName, that.sourceStoreName) && 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);
}
@Override
public int hashCode() {
return Objects.hash(adoptionId, petId, petName, customerId, customerName, adoptionDate, adoptionStatus, adoptionFee, createdAt, updatedAt);
return Objects.hash(adoptionId, petId, petName, customerId, customerName, sourceStoreId, sourceStoreName, adoptionDate, adoptionStatus, adoptionFee, createdAt, updatedAt);
}
@Override
@@ -154,6 +174,8 @@ public class AdoptionResponse {
", petName='" + petName + '\'' +
", customerId=" + customerId +
", customerName='" + customerName + '\'' +
", sourceStoreId=" + sourceStoreId +
", sourceStoreName='" + sourceStoreName + '\'' +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", adoptionFee=" + adoptionFee +

View File

@@ -3,7 +3,6 @@ package com.petshop.backend.dto.appointment;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
import java.util.Objects;
public class AppointmentRequest {
@@ -25,9 +24,7 @@ public class AppointmentRequest {
@NotNull(message = "Appointment status is required")
private String appointmentStatus;
private List<Long> petIds;
private List<Long> customerPetIds;
private Long petId;
private Long employeeId;
@@ -79,20 +76,12 @@ 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 List<Long> getCustomerPetIds() {
return customerPetIds;
}
public void setCustomerPetIds(List<Long> customerPetIds) {
this.customerPetIds = customerPetIds;
public void setPetId(Long petId) {
this.petId = petId;
}
public Long getEmployeeId() {
@@ -114,14 +103,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(customerPetIds, that.customerPetIds) &&
Objects.equals(petId, that.petId) &&
Objects.equals(employeeId, that.employeeId);
}
@Override
public int hashCode() {
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petIds, customerPetIds, employeeId);
return Objects.hash(customerId, storeId, serviceId, appointmentDate, appointmentTime, appointmentStatus, petId, employeeId);
}
@Override
@@ -133,8 +121,7 @@ public class AppointmentRequest {
", appointmentDate=" + appointmentDate +
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", petIds=" + petIds +
", customerPetIds=" + customerPetIds +
", petId=" + petId +
", employeeId=" + employeeId +
'}';
}

View File

@@ -3,7 +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 {
@@ -19,17 +18,15 @@ public class AppointmentResponse {
private String appointmentStatus;
private Long employeeId;
private String employeeName;
private List<String> petNames;
private List<Long> petIds;
private List<String> customerPetNames;
private List<Long> customerPetIds;
private String petName;
private Long petId;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public 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) {
public AppointmentResponse(Long appointmentId, Long customerId, String customerName, Long storeId, String storeName, Long serviceId, String serviceName, LocalDate appointmentDate, LocalTime appointmentTime, String appointmentStatus, String petName, Long petId, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.appointmentId = appointmentId;
this.customerId = customerId;
this.customerName = customerName;
@@ -40,8 +37,8 @@ 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;
}
@@ -142,38 +139,20 @@ public class AppointmentResponse {
this.employeeName = employeeName;
}
public List<String> getPetNames() {
return petNames;
public String getPetName() {
return petName;
}
public void setPetNames(List<String> petNames) {
this.petNames = petNames;
public void setPetName(String petName) {
this.petName = petName;
}
public List<Long> getPetIds() {
return petIds;
public Long getPetId() {
return petId;
}
public void setPetIds(List<Long> petIds) {
this.petIds = petIds;
}
public List<String> getCustomerPetNames() {
return customerPetNames;
}
public void setCustomerPetNames(List<String> customerPetNames) {
this.customerPetNames = customerPetNames;
}
public List<Long> getCustomerPetIds() {
return customerPetIds;
}
public void setCustomerPetIds(List<Long> customerPetIds) {
this.customerPetIds = customerPetIds;
public void setPetId(Long petId) {
this.petId = petId;
}
public LocalDateTime getCreatedAt() {
@@ -197,12 +176,12 @@ public class AppointmentResponse {
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);
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(petName, that.petName) && Objects.equals(petId, that.petId) && 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);
return Objects.hash(appointmentId, customerId, customerName, storeId, storeName, serviceId, serviceName, appointmentDate, appointmentTime, appointmentStatus, petName, petId, createdAt, updatedAt);
}
@Override
@@ -218,8 +197,8 @@ public class AppointmentResponse {
", appointmentDate=" + appointmentDate +
", appointmentTime=" + appointmentTime +
", appointmentStatus='" + appointmentStatus + '\'' +
", petNames=" + petNames +
", petIds=" + petIds +
", petName='" + petName + '\'' +
", petId=" + petId +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';

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

@@ -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,66 +0,0 @@
package com.petshop.backend.dto.customerpet;
import jakarta.validation.constraints.NotBlank;
import java.util.Objects;
public class CustomerPetRequest {
@NotBlank(message = "Pet name is required")
private String petName;
@NotBlank(message = "Species is required")
private String species;
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;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CustomerPetRequest that = (CustomerPetRequest) o;
return Objects.equals(petName, that.petName) && Objects.equals(species, that.species) && Objects.equals(breed, that.breed);
}
@Override
public int hashCode() {
return Objects.hash(petName, species, breed);
}
}

View File

@@ -1,123 +0,0 @@
package com.petshop.backend.dto.customerpet;
import java.time.LocalDateTime;
import java.util.Objects;
public class CustomerPetResponse {
private Long customerPetId;
private Long customerId;
private String petName;
private String species;
private String breed;
private String imageUrl;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public CustomerPetResponse() {
}
public CustomerPetResponse(Long customerPetId, Long customerId, String petName, String species, String breed, String imageUrl, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.customerPetId = customerPetId;
this.customerId = customerId;
this.petName = petName;
this.species = species;
this.breed = breed;
this.imageUrl = imageUrl;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getCustomerPetId() {
return customerPetId;
}
public void setCustomerPetId(Long customerPetId) {
this.customerPetId = customerPetId;
}
public Long getCustomerId() {
return customerId;
}
public void setCustomerId(Long customerId) {
this.customerId = customerId;
}
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;
}
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;
}
CustomerPetResponse that = (CustomerPetResponse) o;
return Objects.equals(customerPetId, that.customerPetId);
}
@Override
public int hashCode() {
return Objects.hash(customerPetId);
}
}

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

@@ -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

@@ -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,11 +22,15 @@ public class Adoption {
@ManyToOne
@JoinColumn(name = "customerId", nullable = false)
private Customer customer;
private User customer;
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@ManyToOne
@JoinColumn(name = "sourceStoreId")
private StoreLocation sourceStore;
@Column(nullable = false)
private LocalDate adoptionDate;
@@ -46,17 +49,6 @@ public class Adoption {
public Adoption() {
}
public Adoption(Long adoptionId, Pet pet, Customer customer, Employee employee, LocalDate adoptionDate, String adoptionStatus, LocalDateTime createdAt, LocalDateTime updatedAt) {
this.adoptionId = adoptionId;
this.pet = pet;
this.customer = customer;
this.employee = employee;
this.adoptionDate = adoptionDate;
this.adoptionStatus = adoptionStatus;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAdoptionId() {
return adoptionId;
}
@@ -73,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;
}
@@ -138,13 +138,8 @@ public class Adoption {
public String toString() {
return "Adoption{" +
"adoptionId=" + adoptionId +
", pet=" + pet +
", customer=" + customer +
", employee=" + employee +
", adoptionDate=" + adoptionDate +
", adoptionStatus='" + adoptionStatus + '\'' +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
", adoptionDate=" + adoptionDate +
'}';
}
}

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)
@@ -33,7 +31,7 @@ public class Appointment {
@ManyToOne
@JoinColumn(name = "employeeId", nullable = false)
private Employee employee;
private User employee;
@Column(nullable = false)
private LocalDate appointmentDate;
@@ -44,21 +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<>();
@ManyToMany
@JoinTable(
name = "appointment_customer_pet",
joinColumns = @JoinColumn(name = "appointment_id"),
inverseJoinColumns = @JoinColumn(name = "customer_pet_id")
)
private Set<CustomerPet> customerPets = new HashSet<>();
@ManyToOne
@JoinColumn(name = "petId")
private Pet pet;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
@@ -71,20 +57,6 @@ public class Appointment {
public Appointment() {
}
public Appointment(Long appointmentId, Customer customer, StoreLocation store, Service service, Employee employee, 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.employee = employee;
this.appointmentDate = appointmentDate;
this.appointmentTime = appointmentTime;
this.appointmentStatus = appointmentStatus;
this.pets = pets;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
public Long getAppointmentId() {
return appointmentId;
}
@@ -93,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;
}
@@ -117,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;
}
@@ -149,21 +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 Set<CustomerPet> getCustomerPets() {
return customerPets;
}
public void setCustomerPets(Set<CustomerPet> customerPets) {
this.customerPets = customerPets;
public void setPet(Pet pet) {
this.pet = pet;
}
public LocalDateTime getCreatedAt() {
@@ -206,7 +169,7 @@ public class Appointment {
", 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,137 +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_pet")
public class CustomerPet {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_pet_id")
private Long customerPetId;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@Column(name = "pet_name", nullable = false, length = 50)
private String petName;
@Column(nullable = false, length = 50)
private String species;
@Column(length = 50)
private String breed;
@Column(name = "image_url", length = 255)
private String imageUrl;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public CustomerPet() {
}
public Long getCustomerPetId() {
return customerPetId;
}
public void setCustomerPetId(Long customerPetId) {
this.customerPetId = customerPetId;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
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;
}
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;
}
CustomerPet that = (CustomerPet) o;
return Objects.equals(customerPetId, that.customerPetId);
}
@Override
public int hashCode() {
return Objects.hash(customerPetId);
}
}

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,24 +23,24 @@ 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 = "customerId")
private Customer customer;
@JoinColumn(name = "ownerUserId")
private User owner;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storeId")
@@ -57,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;
}
@@ -134,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;
}
@@ -150,22 +153,6 @@ public class Pet {
this.updatedAt = updatedAt;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public StoreLocation getStore() {
return store;
}
public void setStore(StoreLocation store) {
this.store = store;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -185,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

@@ -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

@@ -28,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;
@@ -89,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

@@ -16,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;
@@ -38,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;
@@ -59,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;
}
@@ -106,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;
}
@@ -138,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;
}
@@ -188,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

@@ -19,9 +19,9 @@ public interface AdoptionRepository extends JpaRepository<Adoption, Long> {
"LOWER(a.pet.petName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Adoption> searchAdoptions(@Param("q") String query, Pageable pageable);
Page<Adoption> findByCustomerCustomerId(Long customerId, Pageable pageable);
Page<Adoption> findByCustomerId(Long customerId, Pageable pageable);
@Query("SELECT a FROM Adoption a WHERE a.customer.customerId = :customerId AND (" +
@Query("SELECT a FROM Adoption a WHERE a.customer.id = :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, '%')))")

View File

@@ -21,25 +21,25 @@ public interface AppointmentRepository extends JpaRepository<Appointment, Long>
@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 " +
"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);
Page<Appointment> findByCustomerCustomerId(Long customerId, Pageable pageable);
Page<Appointment> findByCustomerId(Long customerId, Pageable pageable);
@Query("SELECT DISTINCT a FROM Appointment a LEFT JOIN a.pets p WHERE a.customer.customerId = :customerId AND (" +
@Query("SELECT a FROM Appointment a LEFT JOIN a.pet p WHERE a.customer.id = :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.employeeId = :employeeId AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeEmployeeIdAndAppointmentDate(@Param("employeeId") Long employeeId, @Param("date") LocalDate date);
@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 a FROM Appointment a JOIN FETCH a.service WHERE a.employee.employeeId IN :employeeIds AND a.appointmentDate = :date AND LOWER(a.appointmentStatus) NOT IN ('cancelled', 'missed')")
List<Appointment> findByEmployeeEmployeeIdInAndAppointmentDate(@Param("employeeIds") List<Long> employeeIds, @Param("date") LocalDate date);
@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

@@ -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,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.CustomerPet;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface CustomerPetRepository extends JpaRepository<CustomerPet, Long> {
List<CustomerPet> findByCustomerCustomerIdOrderByCreatedAtDesc(Long customerId);
List<CustomerPet> findByCustomerCustomerIdOrderByPetNameAsc(Long customerId);
Optional<CustomerPet> findByCustomerPetIdAndCustomerCustomerId(Long customerPetId, Long customerId);
}

View File

@@ -1,29 +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 DISTINCT c FROM Customer c WHERE EXISTS (SELECT cp FROM CustomerPet cp WHERE cp.customer = c) ORDER BY c.firstName ASC, c.lastName ASC")
List<Customer> findAllWithPets();
@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,30 +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);
Optional<Employee> findFirstByIsActiveTrueOrderByEmployeeIdAsc();
List<Employee> findAllByIsActiveTrueOrderByEmployeeIdAsc();
@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,21 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.EmployeeStore;
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 EmployeeStoreRepository extends JpaRepository<EmployeeStore, EmployeeStore.EmployeeStoreId> {
Optional<EmployeeStore> findByEmployeeEmployeeId(Long employeeId);
@Query("SELECT es FROM EmployeeStore es WHERE es.store.storeId = :storeId AND es.employee.isActive = true ORDER BY es.employee.employeeId ASC")
List<EmployeeStore> findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(@Param("storeId") Long storeId);
@Query("SELECT es FROM EmployeeStore es WHERE es.employee.isActive = true ORDER BY es.employee.employeeId ASC")
List<EmployeeStore> findActiveAllOrderByEmployeeEmployeeIdAsc();
}

View File

@@ -16,8 +16,12 @@ 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 " +
"LOWER(i.product.prodName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(i.product.category.categoryName) LIKE LOWER(CONCAT('%', :q, '%'))")
"LOWER(i.product.category.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(s.storeName) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Inventory> searchInventory(@Param("q") String query, Pageable pageable);
}

View File

@@ -16,21 +16,21 @@ public interface PetRepository extends JpaRepository<Pet, Long> {
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
@Query("SELECT p FROM Pet p WHERE " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) 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)) 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(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) 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.userId = :userId OR (LOWER(p.petStatus) = 'owned' AND p.customer.userId = :userId)) AND " +
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
"(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

@@ -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

@@ -4,15 +4,13 @@ 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.EmployeeRepository;
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;
@@ -30,16 +28,14 @@ public class AdoptionService {
private final AdoptionRepository adoptionRepository;
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final EmployeeRepository employeeRepository;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
public AdoptionService(AdoptionRepository adoptionRepository, PetRepository petRepository, CustomerRepository customerRepository, EmployeeRepository employeeRepository, UserRepository userRepository) {
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) {
@@ -49,7 +45,7 @@ public class AdoptionService {
if (query != null && !query.trim().isEmpty()) {
adoptions = adoptionRepository.searchAdoptionsByCustomer(customerId, query, pageable);
} else {
adoptions = adoptionRepository.findByCustomerCustomerId(customerId, pageable);
adoptions = adoptionRepository.findByCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
@@ -66,7 +62,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");
}
@@ -78,9 +74,13 @@ 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()));
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
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);
@@ -88,6 +88,7 @@ public class AdoptionService {
adoption.setPet(pet);
adoption.setCustomer(customer);
adoption.setEmployee(employee);
adoption.setSourceStore(sourceStore);
adoption.setAdoptionDate(request.getAdoptionDate());
adoption.setAdoptionStatus(adoptionStatus);
@@ -104,15 +105,20 @@ 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()));
Employee employee = resolveAdoptionEmployee(request.getEmployeeId());
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(adoptionStatus);
@@ -135,14 +141,17 @@ public class AdoptionService {
}
private AdoptionResponse mapToResponse(Adoption adoption) {
StoreLocation sourceStore = adoption.getSourceStore();
return new AdoptionResponse(
adoption.getAdoptionId(),
adoption.getPet().getPetId(),
adoption.getPet().getPetName(),
adoption.getCustomer().getCustomerId(),
adoption.getCustomer().getId(),
adoption.getCustomer().getFirstName() + " " + adoption.getCustomer().getLastName(),
adoption.getEmployee().getEmployeeId(),
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(),
@@ -151,31 +160,22 @@ public class AdoptionService {
);
}
private Employee resolveAdoptionEmployee(Long requestedEmployeeId) {
private User resolveAdoptionEmployee(Long requestedEmployeeId) {
if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId)
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
if (!isAssignableEmployee(employee)) {
if (!isAssignableUser(employee)) {
throw new IllegalArgumentException("Selected employee is not assignable for adoption work");
}
return employee;
}
return employeeRepository.findAllByIsActiveTrueOrderByEmployeeIdAsc().stream()
.filter(this::isAssignableEmployee)
.findFirst()
return userRepository.findFirstByRoleAndActiveTrueOrderByIdAsc(User.Role.STAFF)
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is available for adoption assignment"));
}
private boolean isAssignableEmployee(Employee employee) {
Long userId = employee.getUserId();
if (userId == null || !Boolean.TRUE.equals(employee.getIsActive())) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
private boolean isAssignableUser(User user) {
return user.getRole() == User.Role.STAFF && Boolean.TRUE.equals(user.getActive());
}
private String normalizeAdoptionStatus(String adoptionStatus) {

View File

@@ -1,12 +1,10 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.analytics.DashboardResponse;
import com.petshop.backend.entity.Employee;
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.EmployeeRepository;
import com.petshop.backend.repository.InventoryRepository;
import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.SaleRepository;
@@ -26,14 +24,12 @@ public class AnalyticsService {
private final SaleRepository saleRepository;
private final InventoryRepository inventoryRepository;
private final ProductRepository productRepository;
private final EmployeeRepository employeeRepository;
public AnalyticsService(SaleRepository saleRepository,
InventoryRepository inventoryRepository, ProductRepository productRepository, EmployeeRepository employeeRepository) {
InventoryRepository inventoryRepository, ProductRepository productRepository) {
this.saleRepository = saleRepository;
this.inventoryRepository = inventoryRepository;
this.productRepository = productRepository;
this.employeeRepository = employeeRepository;
}
@Transactional(readOnly = true)
@@ -183,11 +179,8 @@ public class AnalyticsService {
}
if (user.getRole() == User.Role.STAFF && employeeRevenue.isEmpty()) {
Employee employee = employeeRepository.findByUserId(user.getId()).orElse(null);
if (employee != null) {
String employeeName = employee.getFirstName() + " " + employee.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
String employeeName = user.getFirstName() + " " + user.getLastName();
employeeRevenue.put(employeeName, BigDecimal.ZERO);
}
return employeeRevenue.entrySet().stream()
@@ -200,7 +193,7 @@ public class AnalyticsService {
return true;
}
if (user.getRole() == User.Role.STAFF) {
return sale.getEmployee() != null && sale.getEmployee().getUserId() != null && sale.getEmployee().getUserId().equals(user.getId());
return sale.getEmployee() != null && sale.getEmployee().getId() != null && sale.getEmployee().getId().equals(user.getId());
}
return false;
}

View File

@@ -4,19 +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.CustomerPet;
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.CustomerPetRepository;
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;
@@ -32,34 +24,24 @@ 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 CustomerPetRepository customerPetRepository;
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, CustomerPetRepository customerPetRepository, 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.customerPetRepository = customerPetRepository;
this.serviceRepository = serviceRepository;
this.petRepository = petRepository;
this.storeRepository = storeRepository;
this.userRepository = userRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
}
@Transactional(readOnly = true)
@@ -70,7 +52,7 @@ public class AppointmentService {
if (query != null && !query.trim().isEmpty()) {
appointments = appointmentRepository.searchAppointmentsByCustomer(customerId, query, pageable);
} else {
appointments = appointmentRepository.findByCustomerCustomerId(customerId, pageable);
appointments = appointmentRepository.findByCustomerId(customerId, pageable);
}
} else {
if (query != null && !query.trim().isEmpty()) {
@@ -88,7 +70,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");
}
@@ -101,7 +83,7 @@ public class AppointmentService {
User authenticatedUser = AuthenticationHelper.getAuthenticatedUser(userRepository);
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())
@@ -110,16 +92,8 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
boolean hasCustomerPetIds = request.getCustomerPetIds() != null && !request.getCustomerPetIds().isEmpty();
if (!hasPetIds && !hasCustomerPetIds) {
throw new IllegalArgumentException("Please specify at least one pet.");
}
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Pet pet = request.getPetId() != null ? fetchPet(request.getPetId()) : null;
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null);
@@ -131,8 +105,7 @@ public class AppointmentService {
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setPet(pet);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
@@ -148,7 +121,7 @@ public class AppointmentService {
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())
@@ -157,16 +130,8 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
boolean hasPetIds = request.getPetIds() != null && !request.getPetIds().isEmpty();
boolean hasCustomerPetIds = request.getCustomerPetIds() != null && !request.getCustomerPetIds().isEmpty();
if (!hasPetIds && !hasCustomerPetIds) {
throw new IllegalArgumentException("Please specify at least one pet.");
}
Set<Pet> pets = hasPetIds ? fetchPets(request.getPetIds()) : new HashSet<>();
Set<CustomerPet> customerPets = hasCustomerPetIds ? fetchCustomerPets(request.getCustomerPetIds(), customer.getCustomerId()) : new HashSet<>();
Employee employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
Pet pet = request.getPetId() != null ? fetchPet(request.getPetId()) : null;
User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId());
validateStoreAccess(store.getStoreId(), authenticatedUser);
validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id);
@@ -177,8 +142,7 @@ public class AppointmentService {
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setAppointmentStatus(request.getAppointmentStatus());
appointment.setPets(pets);
appointment.setCustomerPets(customerPets);
appointment.setPet(pet);
appointment.setEmployee(employee);
appointment = appointmentRepository.save(appointment);
@@ -206,20 +170,17 @@ public class AppointmentService {
com.petshop.backend.entity.Service service = serviceRepository.findById(serviceId)
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + serviceId));
List<Employee> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream()
.filter(es -> isAssignableEmployee(es.getEmployee()))
.map(EmployeeStore::getEmployee)
.collect(Collectors.toList());
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
if (assignableEmployees.isEmpty()) {
if (assignableUsers.isEmpty()) {
return List.of();
}
List<Long> employeeIds = assignableEmployees.stream().map(Employee::getEmployeeId).collect(Collectors.toList());
List<Appointment> allAppointments = appointmentRepository.findByEmployeeEmployeeIdInAndAppointmentDate(employeeIds, date);
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().getEmployeeId()));
.collect(Collectors.groupingBy(a -> a.getEmployee().getId()));
List<String> availableSlots = new ArrayList<>();
LocalTime startTime = LocalTime.of(9, 0);
@@ -229,8 +190,8 @@ public class AppointmentService {
LocalTime currentTime = startTime;
while (!currentTime.isAfter(latestStart)) {
final LocalTime slotTime = currentTime;
boolean anyEmployeeAvailable = assignableEmployees.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getEmployeeId(), List.of());
boolean anyEmployeeAvailable = assignableUsers.stream().anyMatch(emp -> {
List<Appointment> empAppointments = appointmentsByEmployee.getOrDefault(emp.getId(), List.of());
return isSlotAvailable(empAppointments, service, slotTime, null);
});
@@ -252,50 +213,17 @@ 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 Set<CustomerPet> fetchCustomerPets(List<Long> customerPetIds, Long customerId) {
Set<CustomerPet> customerPets = new HashSet<>();
for (Long customerPetId : customerPetIds) {
CustomerPet customerPet = customerPetRepository.findById(customerPetId)
.orElseThrow(() -> new ResourceNotFoundException("Customer pet not found with id: " + customerPetId));
if (!customerPet.getCustomer().getCustomerId().equals(customerId)) {
throw new IllegalArgumentException("Selected pet does not belong to the selected customer");
}
customerPets.add(customerPet);
}
return customerPets;
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());
List<Long> petIds = appointment.getPets().stream()
.map(Pet::getPetId)
.collect(Collectors.toList());
List<String> customerPetNames = appointment.getCustomerPets().stream()
.map(CustomerPet::getPetName)
.collect(Collectors.toList());
List<Long> customerPetIds = appointment.getCustomerPets().stream()
.map(CustomerPet::getCustomerPetId)
.collect(Collectors.toList());
Pet pet = appointment.getPet();
AppointmentResponse response = new AppointmentResponse();
response.setAppointmentId(appointment.getAppointmentId());
response.setCustomerId(appointment.getCustomer().getCustomerId());
response.setCustomerId(appointment.getCustomer().getId());
response.setCustomerName(appointment.getCustomer().getFirstName() + " " + appointment.getCustomer().getLastName());
response.setStoreId(appointment.getStore().getStoreId());
response.setStoreName(appointment.getStore().getStoreName());
@@ -304,54 +232,38 @@ public class AppointmentService {
response.setAppointmentDate(appointment.getAppointmentDate());
response.setAppointmentTime(appointment.getAppointmentTime());
response.setAppointmentStatus(appointment.getAppointmentStatus());
response.setEmployeeId(appointment.getEmployee().getEmployeeId());
response.setEmployeeId(appointment.getEmployee().getId());
response.setEmployeeName(appointment.getEmployee().getFirstName() + " " + appointment.getEmployee().getLastName());
response.setPetNames(petNames);
response.setPetIds(petIds);
response.setCustomerPetNames(customerPetNames);
response.setCustomerPetIds(customerPetIds);
response.setPetName(pet != null ? pet.getPetName() : null);
response.setPetId(pet != null ? pet.getPetId() : null);
response.setCreatedAt(appointment.getCreatedAt());
response.setUpdatedAt(appointment.getUpdatedAt());
return response;
}
private Employee resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<EmployeeStore> assignableEmployees = employeeStoreRepository.findActiveByStoreStoreIdOrderByEmployeeEmployeeIdAsc(storeId).stream()
.filter(es -> isAssignableEmployee(es.getEmployee()))
.collect(Collectors.toList());
private User resolveAppointmentEmployee(Long requestedEmployeeId, Long storeId) {
List<User> assignableUsers = userRepository.findByPrimaryStoreStoreIdAndRoleAndActiveTrue(storeId, User.Role.STAFF);
if (requestedEmployeeId != null) {
Employee employee = employeeRepository.findById(requestedEmployeeId)
User employee = userRepository.findById(requestedEmployeeId)
.orElseThrow(() -> new ResourceNotFoundException("Employee not found with id: " + requestedEmployeeId));
boolean assignedToStore = assignableEmployees.stream()
.anyMatch(es -> es.getEmployee().getEmployeeId().equals(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 assignableEmployees.stream()
.map(EmployeeStore::getEmployee)
return assignableUsers.stream()
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No assignable staff member is assigned to the selected store"));
}
private boolean isAssignableEmployee(Employee employee) {
Long userId = employee.getUserId();
if (userId == null || !Boolean.TRUE.equals(employee.getIsActive())) {
return false;
}
return userRepository.findById(userId)
.filter(user -> user.getRole() == User.Role.STAFF)
.filter(user -> Boolean.TRUE.equals(user.getActive()))
.isPresent();
}
private void validateAvailability(Employee employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
private void validateAvailability(User employee, com.petshop.backend.entity.Service service, LocalDate date, LocalTime time, Long appointmentIdToIgnore) {
List<Appointment> existingAppointments = appointmentRepository
.findByEmployeeEmployeeIdAndAppointmentDate(employee.getEmployeeId(), date);
.findByEmployeeIdAndAppointmentDate(employee.getId(), date);
if (!isSlotAvailable(existingAppointments, service, time, appointmentIdToIgnore)) {
throw new IllegalArgumentException("The selected employee is already booked for this time slot");
}
@@ -377,11 +289,7 @@ public class AppointmentService {
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

@@ -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

@@ -6,12 +6,10 @@ 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;
@@ -28,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
@@ -49,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);
@@ -72,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();
@@ -87,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());
@@ -107,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);
}
@@ -134,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);
@@ -168,7 +164,8 @@ 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);
}
@@ -190,7 +187,8 @@ public class ChatService {
conversation = 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);
}
@@ -225,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,163 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.customerpet.CustomerPetRequest;
import com.petshop.backend.dto.customerpet.CustomerPetResponse;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.CustomerPet;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CustomerPetRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.util.AuthenticationHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@Service
public class CustomerPetService {
private final CustomerPetRepository customerPetRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final CatalogImageStorageService catalogImageStorageService;
public CustomerPetService(CustomerPetRepository customerPetRepository,
CustomerRepository customerRepository,
UserRepository userRepository,
CatalogImageStorageService catalogImageStorageService) {
this.customerPetRepository = customerPetRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@Transactional(readOnly = true)
public List<CustomerPetResponse> getMyPets() {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
return customerPetRepository.findByCustomerCustomerIdOrderByCreatedAtDesc(customer.getCustomerId())
.stream()
.map(this::mapToResponse)
.collect(Collectors.toList());
}
@Transactional
public CustomerPetResponse createPet(CustomerPetRequest request) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = new CustomerPet();
pet.setCustomer(customer);
pet.setPetName(request.getPetName());
pet.setSpecies(request.getSpecies());
pet.setBreed(request.getBreed());
pet = customerPetRepository.save(pet);
return mapToResponse(pet);
}
@Transactional
public CustomerPetResponse updatePet(Long id, CustomerPetRequest request) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
pet.setPetName(request.getPetName());
pet.setSpecies(request.getSpecies());
pet.setBreed(request.getBreed());
pet = customerPetRepository.save(pet);
return mapToResponse(pet);
}
@Transactional
public void deletePet(Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
customerPetRepository.delete(pet);
}
@Transactional
public CustomerPetResponse uploadImage(Long id, MultipartFile file) throws IOException {
validateImageFile(file);
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
pet.setImageUrl(catalogImageStorageService.storePetImage(file));
return mapToResponse(customerPetRepository.save(pet));
}
@Transactional
public CustomerPetResponse deleteImage(Long id) {
Customer customer = AuthenticationHelper.getAuthenticatedCustomer(userRepository, customerRepository);
CustomerPet pet = customerPetRepository.findByCustomerPetIdAndCustomerCustomerId(id, customer.getCustomerId()).orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id));
deleteStoredImageIfPresent(pet.getImageUrl());
pet.setImageUrl(null);
return mapToResponse(customerPetRepository.save(pet));
}
private CustomerPetResponse mapToResponse(CustomerPet pet) {
return new CustomerPetResponse(
pet.getCustomerPetId(),
pet.getCustomer().getCustomerId(),
pet.getPetName(),
pet.getSpecies(),
pet.getBreed(),
pet.getImageUrl() != null && !pet.getImageUrl().isBlank()
? "/api/v1/my-pets/" + pet.getCustomerPetId() + "/image"
: null,
pet.getCreatedAt(),
pet.getUpdatedAt()
);
}
private void validateImageFile(MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("Please select an image to upload");
}
if (file.getSize() > 5 * 1024 * 1024) {
throw new IllegalArgumentException("Image file size must be less than 5MB");
}
String contentType = file.getContentType();
if (contentType == null) {
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
}
String normalized = contentType.toLowerCase(Locale.ROOT);
if (!normalized.equals("image/jpeg") && !normalized.equals("image/png") && !normalized.equals("image/gif")) {
throw new IllegalArgumentException("Only JPG, PNG, and GIF images are allowed");
}
}
private void deleteStoredImageIfPresent(String storedImagePath) {
if (storedImagePath == null || storedImagePath.isBlank()) {
return;
}
try {
catalogImageStorageService.deletePetImage(storedImagePath);
}
catch (IOException ignored) {
}
}
}

View File

@@ -1,158 +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.entity.User;
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.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 CustomerService {
private static final String TEMP_PASSWORD = "TempPass123!";
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
public CustomerService(CustomerRepository customerRepository, UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService) {
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
}
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) {
ensureEmailAvailable(request.getEmail(), null);
Customer customer = new Customer();
customer.setFirstName(request.getFirstName());
customer.setLastName(request.getLastName());
customer.setEmail(request.getEmail());
customer = customerRepository.save(customer);
User user = createLinkedUser(customer);
Customer linkedCustomer = userBusinessLinkageService.ensureLinkedCustomer(user);
syncLinkedUser(linkedCustomer);
return mapToResponse(linkedCustomer);
}
@Transactional
public CustomerResponse updateCustomer(Long id, CustomerRequest request) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
ensureEmailAvailable(request.getEmail(), customer.getUserId());
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) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
if (customer.getUserId() != null && userRepository.existsById(customer.getUserId())) {
userRepository.deleteById(customer.getUserId());
return;
}
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);
});
}
private User createLinkedUser(Customer customer) {
User user = new User();
user.setUsername(generateUsername(customer));
user.setPassword(passwordEncoder.encode(TEMP_PASSWORD));
user.setEmail(customer.getEmail());
user.setFullName((customer.getFirstName() + " " + customer.getLastName()).trim());
user.setPhone(generatePhone(customer));
user.setRole(User.Role.CUSTOMER);
user.setActive(false);
user.setTokenVersion(0);
return userRepository.save(user);
}
private String generateUsername(Customer customer) {
return "customer_" + customer.getCustomerId();
}
private String generatePhone(Customer customer) {
return String.format("200-000-%04d", customer.getCustomerId());
}
private void ensureEmailAvailable(String email, Long currentUserId) {
if (email == null || email.isBlank()) {
return;
}
userRepository.findByEmail(email).ifPresent(existing -> {
if (currentUserId == null || !existing.getId().equals(currentUserId)) {
throw new ResponseStatusException(CONFLICT, "Email already exists");
}
});
}
}

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;
}
}

View File

@@ -5,9 +5,11 @@ import com.petshop.backend.dto.inventory.InventoryRequest;
import com.petshop.backend.dto.inventory.InventoryResponse;
import com.petshop.backend.entity.Inventory;
import com.petshop.backend.entity.Product;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.InventoryRepository;
import com.petshop.backend.repository.ProductRepository;
import com.petshop.backend.repository.StoreRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@@ -18,10 +20,12 @@ public class InventoryService {
private final InventoryRepository inventoryRepository;
private final ProductRepository productRepository;
private final StoreRepository storeRepository;
public InventoryService(InventoryRepository inventoryRepository, ProductRepository productRepository) {
public InventoryService(InventoryRepository inventoryRepository, ProductRepository productRepository, StoreRepository storeRepository) {
this.inventoryRepository = inventoryRepository;
this.productRepository = productRepository;
this.storeRepository = storeRepository;
}
public Page<InventoryResponse> getAllInventory(String query, Pageable pageable) {
@@ -45,8 +49,14 @@ public class InventoryService {
Product product = productRepository.findById(request.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + request.getProdId()));
StoreLocation store = request.getStoreId() != null
? storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()))
: null;
Inventory inventory = new Inventory();
inventory.setProduct(product);
inventory.setStore(store);
inventory.setQuantity(request.getQuantity());
inventory = inventoryRepository.save(inventory);
@@ -61,7 +71,13 @@ public class InventoryService {
Product product = productRepository.findById(request.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + request.getProdId()));
StoreLocation store = request.getStoreId() != null
? storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()))
: null;
inventory.setProduct(product);
inventory.setStore(store);
inventory.setQuantity(request.getQuantity());
inventory = inventoryRepository.save(inventory);
@@ -82,11 +98,14 @@ public class InventoryService {
}
private InventoryResponse mapToResponse(Inventory inventory) {
StoreLocation store = inventory.getStore();
return new InventoryResponse(
inventory.getInventoryId(),
inventory.getProduct().getProdId(),
inventory.getProduct().getProdName(),
inventory.getProduct().getCategory().getCategoryName(),
store != null ? store.getStoreId() : null,
store != null ? store.getStoreName() : null,
inventory.getQuantity(),
inventory.getCreatedAt(),
inventory.getUpdatedAt()

View File

@@ -4,16 +4,15 @@ import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.pet.PetRequest;
import com.petshop.backend.dto.pet.PetResponse;
import com.petshop.backend.entity.Adoption;
import com.petshop.backend.entity.Customer;
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.security.AppPrincipal;
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.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.data.domain.Page;
@@ -33,14 +32,14 @@ public class PetService {
private final PetRepository petRepository;
private final AdoptionRepository adoptionRepository;
private final CustomerRepository customerRepository;
private final UserRepository userRepository;
private final StoreRepository storeRepository;
private final CatalogImageStorageService catalogImageStorageService;
public PetService(PetRepository petRepository, AdoptionRepository adoptionRepository, CustomerRepository customerRepository, StoreRepository storeRepository, CatalogImageStorageService catalogImageStorageService) {
public PetService(PetRepository petRepository, AdoptionRepository adoptionRepository, UserRepository userRepository, StoreRepository storeRepository, CatalogImageStorageService catalogImageStorageService) {
this.petRepository = petRepository;
this.adoptionRepository = adoptionRepository;
this.customerRepository = customerRepository;
this.userRepository = userRepository;
this.storeRepository = storeRepository;
this.catalogImageStorageService = catalogImageStorageService;
}
@@ -188,7 +187,7 @@ public class PetService {
}
return adoptionRepository.findFirstByPet_IdAndAdoptionStatusOrderByAdoptionDateDesc(pet.getPetId(), "Completed")
.map(Adoption::getCustomer)
.map(customer -> userId.equals(customer.getUserId()))
.map(customer -> userId.equals(customer.getId()))
.orElse(false);
}
@@ -257,7 +256,7 @@ public class PetService {
}
private PetResponse mapToResponse(Pet pet) {
Customer customer = pet.getCustomer();
User owner = pet.getOwner();
StoreLocation store = pet.getStore();
return new PetResponse(
pet.getPetId(),
@@ -270,8 +269,8 @@ public class PetService {
pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null,
pet.getCreatedAt(),
pet.getUpdatedAt(),
customer != null ? customer.getCustomerId() : null,
customer != null ? customer.getFirstName() + " " + customer.getLastName() : null,
owner != null ? owner.getId() : null,
owner != null ? owner.getFirstName() + " " + owner.getLastName() : null,
store != null ? store.getStoreId() : null,
store != null ? store.getStoreName() : null
);
@@ -280,11 +279,11 @@ public class PetService {
private void applyOwnerAndStore(Pet pet, PetRequest request) {
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User owner = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer);
pet.setOwner(owner);
} else {
pet.setCustomer(null);
pet.setOwner(null);
}
pet.setStore(null);
} else if ("available".equalsIgnoreCase(request.getPetStatus()) || "unadopted".equalsIgnoreCase(request.getPetStatus())) {
@@ -295,14 +294,14 @@ public class PetService {
} else {
pet.setStore(null);
}
pet.setCustomer(null);
pet.setOwner(null);
} else {
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User owner = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
pet.setCustomer(customer);
pet.setOwner(owner);
} else {
pet.setCustomer(null);
pet.setOwner(null);
}
if (request.getStoreId() != null) {
StoreLocation store = storeRepository.findById(request.getStoreId())
@@ -318,8 +317,8 @@ public class PetService {
if (!"owned".equalsIgnoreCase(normalizeStatus(pet.getPetStatus()))) {
return false;
}
Customer customer = pet.getCustomer();
return customer != null && userId.equals(customer.getUserId());
User owner = pet.getOwner();
return owner != null && userId.equals(owner.getId());
}
public record ImagePayload(Resource resource, MediaType mediaType) {

View File

@@ -2,6 +2,7 @@ package com.petshop.backend.service;
import com.petshop.backend.dto.purchaseorder.PurchaseOrderResponse;
import com.petshop.backend.entity.PurchaseOrder;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.PurchaseOrderRepository;
import org.springframework.data.domain.Page;
@@ -34,10 +35,13 @@ public class PurchaseOrderService {
}
private PurchaseOrderResponse mapToResponse(PurchaseOrder purchaseOrder) {
StoreLocation store = purchaseOrder.getStore();
return new PurchaseOrderResponse(
purchaseOrder.getPurchaseOrderId(),
purchaseOrder.getSupplier().getSupId(),
purchaseOrder.getSupplier().getSupCompany(),
store != null ? store.getStoreId() : null,
store != null ? store.getStoreName() : null,
purchaseOrder.getOrderDate(),
purchaseOrder.getStatus(),
purchaseOrder.getCreatedAt(),

View File

@@ -46,13 +46,13 @@ public class RefundService {
throw new RuntimeException("Sale has no associated customer");
}
if (customerId != null && !sale.getCustomer().getCustomerId().equals(customerId)) {
if (customerId != null && !sale.getCustomer().getId().equals(customerId)) {
throw new RuntimeException("You can only create refunds for your own purchases");
}
Refund refund = new Refund();
refund.setSaleId(sale.getSaleId());
refund.setCustomerId(sale.getCustomer().getCustomerId());
refund.setCustomerId(sale.getCustomer().getId());
refund.setReason(request.getReason());
refund.setStatus(Refund.RefundStatus.PENDING);
@@ -81,6 +81,7 @@ public class RefundService {
return toResponse(savedRefund);
}
@Transactional(readOnly = true)
public RefundResponse getRefundById(Long id, Long customerId) {
Refund refund = refundRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Refund not found"));
@@ -92,6 +93,7 @@ public class RefundService {
return toResponse(refund);
}
@Transactional(readOnly = true)
public List<RefundResponse> getAllRefunds(Long customerId) {
List<Refund> refunds;

View File

@@ -24,20 +24,18 @@ public class SaleService {
private final ProductRepository productRepository;
private final StoreRepository storeRepository;
private final InventoryRepository inventoryRepository;
private final EmployeeRepository employeeRepository;
private final EmployeeStoreRepository employeeStoreRepository;
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
private final CouponRepository couponRepository;
private final CartRepository cartRepository;
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, EmployeeRepository employeeRepository, EmployeeStoreRepository employeeStoreRepository, UserRepository userRepository, CustomerRepository customerRepository) {
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CartRepository cartRepository) {
this.saleRepository = saleRepository;
this.productRepository = productRepository;
this.storeRepository = storeRepository;
this.inventoryRepository = inventoryRepository;
this.employeeRepository = employeeRepository;
this.employeeStoreRepository = employeeStoreRepository;
this.userRepository = userRepository;
this.customerRepository = customerRepository;
this.couponRepository = couponRepository;
this.cartRepository = cartRepository;
}
@Transactional(readOnly = true)
@@ -60,18 +58,16 @@ public class SaleService {
@Transactional
public SaleResponse createSale(SaleRequest request) {
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
Employee employee = AuthenticationHelper.getAuthenticatedEmployee(userRepository, employeeRepository);
Long employeeStoreId = employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId())
.orElseThrow(() -> new BusinessException("Authenticated staff member is not assigned to a store"))
.getStore()
.getStoreId();
User employee = AuthenticationHelper.getAuthenticatedUser(userRepository);
StoreLocation store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
if (user.getRole() == User.Role.STAFF && !employeeStoreId.equals(store.getStoreId())) {
throw new BusinessException("Staff can only create sales for their assigned store");
if (employee.getRole() == User.Role.STAFF) {
Long assignedStoreId = employee.getPrimaryStore() != null ? employee.getPrimaryStore().getStoreId() : null;
if (!store.getStoreId().equals(assignedStoreId)) {
throw new BusinessException("Staff can only create sales for their assigned store");
}
}
Sale sale = new Sale();
@@ -80,9 +76,22 @@ public class SaleService {
sale.setStore(store);
sale.setPaymentMethod(normalizePaymentMethod(request.getPaymentMethod()));
sale.setIsRefund(request.getIsRefund() != null ? request.getIsRefund() : false);
sale.setChannel(request.getChannel() != null ? request.getChannel() : "IN_STORE");
if (request.getCouponId() != null) {
Coupon coupon = couponRepository.findById(request.getCouponId())
.orElseThrow(() -> new ResourceNotFoundException("Coupon not found with id: " + request.getCouponId()));
sale.setCoupon(coupon);
}
if (request.getCartId() != null) {
Cart cart = cartRepository.findById(request.getCartId())
.orElseThrow(() -> new ResourceNotFoundException("Cart not found with id: " + request.getCartId()));
sale.setCart(cart);
}
if (request.getCustomerId() != null) {
Customer customer = customerRepository.findById(request.getCustomerId())
User customer = userRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
sale.setCustomer(customer);
}
@@ -125,8 +134,8 @@ public class SaleService {
" for product: " + product.getProdName());
}
Inventory inventory = inventoryRepository.findByProductId(itemRequest.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found for product " + itemRequest.getProdId()));
Inventory inventory = inventoryRepository.findByProductIdAndStoreId(itemRequest.getProdId(), store.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found for product " + itemRequest.getProdId() + " at store " + store.getStoreId()));
inventory.setQuantity(inventory.getQuantity() + itemRequest.getQuantity());
inventoryRepository.save(inventory);
@@ -149,8 +158,8 @@ public class SaleService {
Product product = productRepository.findById(itemRequest.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + itemRequest.getProdId()));
Inventory inventory = inventoryRepository.findByProductId(itemRequest.getProdId())
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found for product " + itemRequest.getProdId()));
Inventory inventory = inventoryRepository.findByProductIdAndStoreId(itemRequest.getProdId(), store.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found for product " + itemRequest.getProdId() + " at store " + store.getStoreId()));
if (inventory.getQuantity() < itemRequest.getQuantity()) {
throw new BusinessException("Insufficient stock for product: " + product.getProdName() +
@@ -185,7 +194,7 @@ public class SaleService {
SaleResponse response = new SaleResponse();
response.setSaleId(sale.getSaleId());
response.setSaleDate(sale.getSaleDate());
response.setEmployeeId(sale.getEmployee().getEmployeeId());
response.setEmployeeId(sale.getEmployee().getId());
response.setEmployeeName(sale.getEmployee().getFirstName() + " " + sale.getEmployee().getLastName());
if (sale.getStore() != null) {
@@ -194,6 +203,17 @@ public class SaleService {
}
response.setTotalAmount(sale.getTotalAmount());
response.setSubtotalAmount(sale.getSubtotalAmount());
response.setCouponDiscountAmount(sale.getCouponDiscountAmount());
response.setEmployeeDiscountAmount(sale.getEmployeeDiscountAmount());
response.setPointsEarned(sale.getPointsEarned());
response.setChannel(sale.getChannel());
if (sale.getCoupon() != null) {
response.setCouponId(sale.getCoupon().getCouponId());
}
if (sale.getCart() != null) {
response.setCartId(sale.getCart().getCartId());
}
response.setPaymentMethod(sale.getPaymentMethod());
response.setIsRefund(sale.getIsRefund());
if (sale.getOriginalSale() != null) {

View File

@@ -19,6 +19,7 @@ public class ServiceService {
this.serviceRepository = serviceRepository;
}
@Transactional(readOnly = true)
public Page<ServiceResponse> getAllServices(String query, Pageable pageable) {
Page<com.petshop.backend.entity.Service> services;
if (query != null && !query.trim().isEmpty()) {
@@ -29,6 +30,7 @@ public class ServiceService {
return services.map(this::mapToResponse);
}
@Transactional(readOnly = true)
public ServiceResponse getServiceById(Long id) {
com.petshop.backend.entity.Service service = serviceRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + id));
@@ -42,6 +44,9 @@ public class ServiceService {
service.setServiceDesc(request.getServiceDesc());
service.setServicePrice(request.getServicePrice());
service.setServiceDuration(request.getServiceDuration());
if (request.getSpecies() != null) {
service.setSpecies(request.getSpecies());
}
service = serviceRepository.save(service);
return mapToResponse(service);
@@ -56,6 +61,9 @@ public class ServiceService {
service.setServiceDesc(request.getServiceDesc());
service.setServicePrice(request.getServicePrice());
service.setServiceDuration(request.getServiceDuration());
if (request.getSpecies() != null) {
service.setSpecies(request.getSpecies());
}
service = serviceRepository.save(service);
return mapToResponse(service);
@@ -81,6 +89,7 @@ public class ServiceService {
service.getServiceDesc(),
service.getServicePrice(),
service.getServiceDuration(),
service.getSpecies(),
service.getCreatedAt(),
service.getUpdatedAt()
);

View File

@@ -1,34 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.EmployeeStore;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.EmployeeStoreRepository;
import com.petshop.backend.repository.StoreRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class StoreAssignmentService {
private final EmployeeStoreRepository employeeStoreRepository;
private final StoreRepository storeRepository;
public StoreAssignmentService(EmployeeStoreRepository employeeStoreRepository, StoreRepository storeRepository) {
this.employeeStoreRepository = employeeStoreRepository;
this.storeRepository = storeRepository;
}
@Transactional
public void assignStoreIfMissing(Employee employee, Long storeId) {
if (employeeStoreRepository.findByEmployeeEmployeeId(employee.getEmployeeId()).isPresent()) {
return;
}
StoreLocation store = storeRepository.findById(storeId)
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + storeId));
employeeStoreRepository.save(new EmployeeStore(employee, store));
}
}

View File

@@ -43,6 +43,7 @@ public class StoreService {
store.setAddress(request.getAddress());
store.setPhone(request.getPhone());
store.setEmail(request.getEmail());
store.setImageUrl(request.getImageUrl());
store = storeRepository.save(store);
return mapToResponse(store);
@@ -57,6 +58,7 @@ public class StoreService {
store.setAddress(request.getAddress());
store.setPhone(request.getPhone());
store.setEmail(request.getEmail());
store.setImageUrl(request.getImageUrl());
store = storeRepository.save(store);
return mapToResponse(store);
@@ -82,6 +84,7 @@ public class StoreService {
store.getAddress(),
store.getPhone(),
store.getEmail(),
store.getImageUrl(),
store.getCreatedAt()
);
}

View File

@@ -1,152 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class UserBusinessLinkageService {
private final EmployeeRepository employeeRepository;
private final CustomerRepository customerRepository;
@Autowired
public UserBusinessLinkageService(EmployeeRepository employeeRepository, CustomerRepository customerRepository) {
this.employeeRepository = employeeRepository;
this.customerRepository = customerRepository;
}
@Transactional
public Employee ensureLinkedEmployee(User user) {
if (user.getId() != null) {
var existing = employeeRepository.findByUserId(user.getId());
if (existing.isPresent()) {
return syncEmployee(existing.get(), user);
}
}
List<Employee> emailMatches = employeeRepository.findAllByEmail(user.getEmail());
if (emailMatches.size() == 1) {
Employee employee = emailMatches.get(0);
if (employee.getUserId() == null) {
employee.setUserId(user.getId());
return syncEmployee(employee, user);
}
}
Employee newEmployee = new Employee();
newEmployee.setUserId(user.getId());
newEmployee.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
newEmployee.setFirstName(nameParts[0]);
newEmployee.setLastName(nameParts[1]);
newEmployee.setIsActive(true);
if (user.getRole() == User.Role.ADMIN) {
newEmployee.setRole("Manager");
} else if (user.getRole() == User.Role.STAFF) {
newEmployee.setRole("Staff");
} else {
newEmployee.setRole("Staff");
}
return syncEmployee(newEmployee, user);
}
@Transactional
public Customer ensureLinkedCustomer(User user) {
if (user.getId() != null) {
var existing = customerRepository.findByUserId(user.getId());
if (existing.isPresent()) {
return syncCustomer(existing.get(), user);
}
}
List<Customer> emailMatches = customerRepository.findAllByEmail(user.getEmail());
if (emailMatches.size() == 1) {
Customer customer = emailMatches.get(0);
if (customer.getUserId() == null) {
customer.setUserId(user.getId());
return syncCustomer(customer, user);
}
}
Customer newCustomer = new Customer();
newCustomer.setUserId(user.getId());
newCustomer.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
newCustomer.setFirstName(nameParts[0]);
newCustomer.setLastName(nameParts[1]);
return syncCustomer(newCustomer, user);
}
@Transactional
public void syncLinkedRecords(User user) {
if (user.getRole() == User.Role.CUSTOMER) {
ensureLinkedCustomer(user);
return;
}
ensureLinkedEmployee(user);
}
private Employee syncEmployee(Employee employee, User user) {
employee.setUserId(user.getId());
employee.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
employee.setFirstName(nameParts[0]);
employee.setLastName(nameParts[1]);
if (user.getRole() == User.Role.ADMIN) {
employee.setRole("Manager");
} else {
employee.setRole("Staff");
}
return employeeRepository.save(employee);
}
private Customer syncCustomer(Customer customer, User user) {
customer.setUserId(user.getId());
customer.setEmail(user.getEmail());
String[] nameParts = splitFullName(user.getFullName());
customer.setFirstName(nameParts[0]);
customer.setLastName(nameParts[1]);
return customerRepository.save(customer);
}
private String[] splitFullName(String fullName) {
if (fullName == null || fullName.trim().isEmpty()) {
return new String[]{"System", "User"};
}
String trimmed = fullName.trim();
int spaceIndex = trimmed.indexOf(' ');
if (spaceIndex == -1) {
return new String[]{trimmed, "User"};
}
String firstName = trimmed.substring(0, spaceIndex).trim();
String lastName = trimmed.substring(spaceIndex + 1).trim();
if (firstName.isEmpty()) {
firstName = "System";
}
if (lastName.isEmpty()) {
lastName = "User";
}
return new String[]{firstName, lastName};
}
}

View File

@@ -3,8 +3,10 @@ package com.petshop.backend.service;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.user.UserRequest;
import com.petshop.backend.dto.user.UserResponse;
import com.petshop.backend.entity.StoreLocation;
import com.petshop.backend.entity.User;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.StoreRepository;
import com.petshop.backend.repository.UserRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -14,6 +16,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;
import java.util.Locale;
import java.util.Objects;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
@@ -23,12 +26,12 @@ public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final UserBusinessLinkageService userBusinessLinkageService;
private final StoreRepository storeRepository;
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, UserBusinessLinkageService userBusinessLinkageService) {
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, StoreRepository storeRepository) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.userBusinessLinkageService = userBusinessLinkageService;
this.storeRepository = storeRepository;
}
public Page<UserResponse> getAllUsers(String query, String role, Pageable pageable) {
@@ -56,20 +59,24 @@ public class UserService {
@Transactional
public UserResponse createUser(UserRequest request) {
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setUsername(trimToNull(request.getUsername()));
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
user.setPassword(passwordEncoder.encode(request.getPassword()));
}
user.setFirstName(request.getFirstName());
user.setLastName(request.getLastName());
user.setFullName(request.getFullName());
user.setEmail(request.getEmail());
user.setPhone(trimToNull(request.getPhone()));
user.setRole(request.getRole());
user.setStaffRole(trimToNull(request.getStaffRole()));
user.setPrimaryStore(resolveStore(request.getPrimaryStoreId()));
user.setActive(request.getActive() != null ? request.getActive() : true);
validateUniquePhone(user.getPhone(), null);
user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user);
}
@@ -79,30 +86,33 @@ public class UserService {
.orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
boolean invalidateToken =
!user.getUsername().equals(request.getUsername())
!Objects.equals(user.getUsername(), request.getUsername())
|| user.getRole() != request.getRole()
|| !user.getActive().equals(request.getActive() != null ? request.getActive() : true);
user.setUsername(request.getUsername());
user.setUsername(trimToNull(request.getUsername()));
if (request.getPassword() != null && !request.getPassword().trim().isEmpty()) {
user.setPassword(passwordEncoder.encode(request.getPassword()));
invalidateToken = true;
}
user.setFirstName(request.getFirstName());
user.setLastName(request.getLastName());
user.setFullName(request.getFullName());
user.setEmail(request.getEmail());
String phone = trimToNull(request.getPhone());
if (!java.util.Objects.equals(user.getPhone(), phone)) {
if (!Objects.equals(user.getPhone(), phone)) {
validateUniquePhone(phone, user.getId());
}
user.setPhone(phone);
user.setRole(request.getRole());
user.setStaffRole(trimToNull(request.getStaffRole()));
user.setPrimaryStore(resolveStore(request.getPrimaryStoreId()));
user.setActive(request.getActive() != null ? request.getActive() : true);
if (invalidateToken) {
user.setTokenVersion(user.getTokenVersion() + 1);
}
user = userRepository.save(user);
userBusinessLinkageService.syncLinkedRecords(user);
return mapToResponse(user);
}
@@ -123,16 +133,27 @@ public class UserService {
UserResponse response = new UserResponse();
response.setId(user.getId());
response.setUsername(user.getUsername());
response.setFirstName(user.getFirstName());
response.setLastName(user.getLastName());
response.setFullName(user.getFullName());
response.setEmail(user.getEmail());
response.setPhone(user.getPhone());
response.setRole(user.getRole().toString());
response.setStaffRole(user.getStaffRole());
response.setPrimaryStoreId(user.getPrimaryStore() != null ? user.getPrimaryStore().getStoreId() : null);
response.setLoyaltyPoints(user.getLoyaltyPoints());
response.setActive(user.getActive());
response.setCreatedAt(user.getCreatedAt());
response.setUpdatedAt(user.getUpdatedAt());
return response;
}
private StoreLocation resolveStore(Long storeId) {
if (storeId == null) return null;
return storeRepository.findById(storeId)
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + storeId));
}
private void validateUniquePhone(String phone, Long currentUserId) {
if (phone == null || phone.isBlank()) {
return;

View File

@@ -1,10 +1,6 @@
package com.petshop.backend.util;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.entity.Employee;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.EmployeeRepository;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.AppPrincipal;
import org.springframework.security.core.Authentication;
@@ -47,16 +43,4 @@ public class AuthenticationHelper {
return userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found: " + username));
}
public static Employee getAuthenticatedEmployee(UserRepository userRepository, EmployeeRepository employeeRepository) {
User user = getAuthenticatedUser(userRepository);
return employeeRepository.findByUserId(user.getId())
.orElseThrow(() -> new RuntimeException("Employee record not found for user: " + user.getUsername()));
}
public static Customer getAuthenticatedCustomer(UserRepository userRepository, CustomerRepository customerRepository) {
User user = getAuthenticatedUser(userRepository);
return customerRepository.findByUserId(user.getId())
.orElseThrow(() -> new RuntimeException("Customer record not found for user: " + user.getUsername()));
}
}

View File

@@ -32,7 +32,7 @@ spring:
flyway:
enabled: true
baseline-on-migrate: true
baseline-version: 0
baseline-version: 1
server:
port: ${SERVER_PORT:8080}

View File

@@ -1,2 +0,0 @@
INSERT INTO service (serviceName, serviceDesc, serviceDuration, servicePrice)
VALUES ('Pet Adoption', 'Schedule a visit to meet and adopt an available pet', 30, 0.00);

View File

@@ -1,7 +0,0 @@
CREATE TABLE IF NOT EXISTS appointment_customer_pet (
appointment_id BIGINT NOT NULL,
customer_pet_id BIGINT NOT NULL,
PRIMARY KEY (appointment_id, customer_pet_id),
FOREIGN KEY (appointment_id) REFERENCES appointment(appointmentId),
FOREIGN KEY (customer_pet_id) REFERENCES customer_pet(customer_pet_id)
);

View File

@@ -1,91 +0,0 @@
INSERT INTO users (username, password, email, fullName, phone, role, active, tokenVersion)
SELECT
CONCAT('customer_', c.customerId) AS username,
'$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password,
CASE
WHEN c.email IS NOT NULL
AND c.email <> ''
AND (SELECT COUNT(*) FROM customer c2 WHERE c2.email = c.email) = 1
AND NOT EXISTS (SELECT 1 FROM employee e2 WHERE e2.email = c.email)
AND NOT EXISTS (SELECT 1 FROM users u WHERE u.email = c.email)
THEN c.email
ELSE CONCAT('customer_', c.customerId, '@petshop.local')
END AS email,
CONCAT(c.firstName, ' ', c.lastName) AS fullName,
CONCAT('200-000-', LPAD(c.customerId, 4, '0')) AS phone,
'CUSTOMER' AS role,
FALSE AS active,
0 AS tokenVersion
FROM customer c
WHERE c.user_id IS NULL
AND NOT EXISTS (
SELECT 1
FROM users u
WHERE u.username = CONCAT('customer_', c.customerId)
);
INSERT INTO users (username, password, email, fullName, phone, role, active, tokenVersion)
SELECT
CONCAT('employee_', e.employeeId) AS username,
'$2a$10$mE0D/HrnCuqFeEqMy0NJwuy2jkoRYjQ7GrKcc/7QQ0r2AqnZTvyGq' AS password,
CASE
WHEN e.email IS NOT NULL
AND e.email <> ''
AND (SELECT COUNT(*) FROM employee e2 WHERE e2.email = e.email) = 1
AND NOT EXISTS (SELECT 1 FROM customer c2 WHERE c2.email = e.email)
AND NOT EXISTS (SELECT 1 FROM users u WHERE u.email = e.email)
THEN e.email
ELSE CONCAT('employee_', e.employeeId, '@petshop.local')
END AS email,
CONCAT(e.firstName, ' ', e.lastName) AS fullName,
CONCAT('300-000-', LPAD(e.employeeId, 4, '0')) AS phone,
CASE
WHEN UPPER(e.role) = 'MANAGER' THEN 'ADMIN'
ELSE 'STAFF'
END AS role,
FALSE AS active,
0 AS tokenVersion
FROM employee e
WHERE e.user_id IS NULL
AND NOT EXISTS (
SELECT 1
FROM users u
WHERE u.username = CONCAT('employee_', e.employeeId)
);
UPDATE customer c
JOIN users u ON u.username = CONCAT('customer_', c.customerId)
AND u.role = 'CUSTOMER'
SET c.user_id = u.id
WHERE c.user_id IS NULL;
UPDATE employee e
JOIN users u ON u.username = CONCAT('employee_', e.employeeId)
AND u.role IN ('STAFF', 'ADMIN')
SET e.user_id = u.id
WHERE e.user_id IS NULL;
UPDATE users
SET
fullName = CASE
WHEN fullName IS NULL OR fullName = '' THEN username
ELSE fullName
END,
email = CASE
WHEN email IS NULL OR email = '' THEN CONCAT(username, '@petshop.local')
ELSE email
END,
phone = CASE
WHEN phone IS NULL OR phone = '' THEN CONCAT('000-000-', LPAD(id, 4, '0'))
ELSE phone
END,
active = COALESCE(active, TRUE),
tokenVersion = COALESCE(tokenVersion, 0)
WHERE fullName IS NULL
OR fullName = ''
OR email IS NULL
OR email = ''
OR phone IS NULL
OR phone = ''
OR active IS NULL
OR tokenVersion IS NULL;

View File

@@ -1,3 +0,0 @@
UPDATE sale
SET paymentMethod = 'Card'
WHERE LOWER(paymentMethod) = 'debit';

View File

@@ -1,33 +0,0 @@
-- Consolidated Updates: Phone Normalization and Refund Items
-- 1. Create refund_item table
CREATE TABLE IF NOT EXISTS refund_item (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
refund_id BIGINT NOT NULL,
prod_id BIGINT NOT NULL,
quantity INT NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (refund_id) REFERENCES refund(id) ON DELETE CASCADE,
FOREIGN KEY (prod_id) REFERENCES product(prodId)
);
-- 2. Normalize existing phone numbers (MySQL Set-based)
UPDATE users
SET phone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -10, 3), ') ',
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -7, 3), '-',
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -4))
WHERE phone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';
UPDATE supplier
SET supPhone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -10, 3), ') ',
SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -7, 3), '-',
SUBSTRING(REGEXP_REPLACE(supPhone, '[^0-9]', ''), -4))
WHERE supPhone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';
UPDATE storeLocation
SET phone = CONCAT('(', SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -10, 3), ') ',
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -7, 3), '-',
SUBSTRING(REGEXP_REPLACE(phone, '[^0-9]', ''), -4))
WHERE phone REGEXP '[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9].*[0-9]';

View File

@@ -1,61 +0,0 @@
ALTER TABLE appointment
ADD COLUMN employeeId BIGINT NULL;
UPDATE appointment a
SET a.employeeId = (
SELECT es.employeeId
FROM employeeStore es
JOIN employee e ON e.employeeId = es.employeeId
JOIN users u ON u.id = e.user_id
WHERE es.storeId = a.storeId
AND e.isActive = TRUE
AND u.role = 'STAFF'
ORDER BY es.employeeId ASC
LIMIT 1
)
WHERE a.employeeId IS NULL;
UPDATE appointment a
SET a.employeeId = (
SELECT e.employeeId
FROM employee e
JOIN users u ON u.id = e.user_id
WHERE e.isActive = TRUE
AND u.role = 'STAFF'
ORDER BY e.employeeId ASC
LIMIT 1
)
WHERE a.employeeId IS NULL;
ALTER TABLE appointment
ADD CONSTRAINT fk_appointment_employee
FOREIGN KEY (employeeId) REFERENCES employee(employeeId);
CREATE INDEX idx_appointment_employeeId ON appointment(employeeId);
ALTER TABLE appointment
MODIFY employeeId BIGINT NOT NULL;
ALTER TABLE adoption
ADD COLUMN employeeId BIGINT NULL;
UPDATE adoption a
SET a.employeeId = (
SELECT e.employeeId
FROM employee e
JOIN users u ON u.id = e.user_id
WHERE e.isActive = TRUE
AND u.role = 'STAFF'
ORDER BY e.employeeId ASC
LIMIT 1
)
WHERE a.employeeId IS NULL;
ALTER TABLE adoption
ADD CONSTRAINT fk_adoption_employee
FOREIGN KEY (employeeId) REFERENCES employee(employeeId);
CREATE INDEX idx_adoption_employeeId ON adoption(employeeId);
ALTER TABLE adoption
MODIFY employeeId BIGINT NOT NULL;

View File

@@ -1,4 +0,0 @@
UPDATE users u
SET u.active = TRUE
WHERE u.role IN ('STAFF', 'ADMIN')
AND EXISTS (SELECT 1 FROM employee e WHERE e.user_id = u.id);

View File

@@ -1,22 +0,0 @@
INSERT INTO customer_pet (customer_id, pet_name, species, breed)
SELECT DISTINCT a.customerId, p.petName, p.petSpecies, p.petBreed
FROM appointmentPet ap
JOIN appointment a ON a.appointmentId = ap.appointmentId
JOIN pet p ON p.petId = ap.petId
WHERE NOT EXISTS (
SELECT 1 FROM customer_pet cp
WHERE cp.customer_id = a.customerId AND cp.pet_name = p.petName
);
INSERT INTO appointment_customer_pet (appointment_id, customer_pet_id)
SELECT ap.appointmentId, cp.customer_pet_id
FROM appointmentPet ap
JOIN appointment a ON a.appointmentId = ap.appointmentId
JOIN pet p ON p.petId = ap.petId
JOIN customer_pet cp ON cp.customer_id = a.customerId AND cp.pet_name = p.petName
WHERE NOT EXISTS (
SELECT 1 FROM appointment_customer_pet acp
WHERE acp.appointment_id = ap.appointmentId AND acp.customer_pet_id = cp.customer_pet_id
);
DELETE FROM appointmentPet;

View File

@@ -1,40 +0,0 @@
UPDATE appointment
SET appointmentStatus = 'Missed'
WHERE LOWER(appointmentStatus) = 'booked'
AND (
appointmentDate < CURRENT_DATE
OR (appointmentDate = CURRENT_DATE AND appointmentTime < CURRENT_TIME)
);
UPDATE appointment a1
JOIN (
SELECT a3.appointmentId
FROM appointment a3
INNER JOIN appointment a4
ON a4.employeeId = a3.employeeId
AND a4.appointmentDate = a3.appointmentDate
AND a4.appointmentTime = a3.appointmentTime
AND a4.appointmentId < a3.appointmentId
WHERE LOWER(a3.appointmentStatus) NOT IN ('cancelled', 'missed')
) conflicting ON conflicting.appointmentId = a1.appointmentId
SET a1.employeeId = (
SELECT es.employeeId
FROM employeeStore es
JOIN employee e ON e.employeeId = es.employeeId
JOIN users u ON u.id = e.user_id
WHERE es.storeId = a1.storeId
AND e.isActive = TRUE
AND u.role = 'STAFF'
AND NOT EXISTS (
SELECT 1 FROM (
SELECT employeeId, appointmentDate, appointmentTime, appointmentId
FROM appointment
) snap
WHERE snap.employeeId = es.employeeId
AND snap.appointmentDate = a1.appointmentDate
AND snap.appointmentTime = a1.appointmentTime
AND snap.appointmentId <> a1.appointmentId
)
ORDER BY es.employeeId ASC
LIMIT 1
);

View File

@@ -1,23 +0,0 @@
ALTER TABLE pet ADD COLUMN customerId BIGINT NULL;
ALTER TABLE pet ADD COLUMN storeId BIGINT NULL;
ALTER TABLE pet ADD CONSTRAINT fk_pet_customer
FOREIGN KEY (customerId) REFERENCES customer(customerId);
ALTER TABLE pet ADD CONSTRAINT fk_pet_store
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId);
CREATE INDEX idx_pet_customerId ON pet(customerId);
CREATE INDEX idx_pet_storeId ON pet(storeId);
UPDATE pet
SET storeId = (SELECT storeId FROM storeLocation ORDER BY storeId ASC LIMIT 1)
WHERE LOWER(petStatus) IN ('available', 'unadopted');
UPDATE pet p
JOIN (
SELECT a.petId, a.customerId
FROM adoption a
WHERE LOWER(a.adoptionStatus) = 'completed'
) latest ON latest.petId = p.petId
SET p.customerId = latest.customerId
WHERE LOWER(p.petStatus) = 'adopted';

View File

@@ -1,250 +0,0 @@
-- Create Tables
CREATE TABLE IF NOT EXISTS storeLocation (
storeId BIGINT AUTO_INCREMENT PRIMARY KEY,
storeName VARCHAR(100) NOT NULL,
address VARCHAR(255) NOT NULL,
phone VARCHAR(20) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS employee (
employeeId BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NULL,
firstName VARCHAR(50) NOT NULL,
lastName VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT NULL,
role VARCHAR(50) NOT NULL,
isActive BOOLEAN DEFAULT TRUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_employee_user_id UNIQUE (user_id)
);
CREATE TABLE IF NOT EXISTS employeeStore (
employeeId BIGINT NOT NULL,
storeId BIGINT NOT NULL,
PRIMARY KEY (employeeId, storeId),
FOREIGN KEY (employeeId) REFERENCES employee(employeeId),
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId)
);
CREATE TABLE IF NOT EXISTS customer (
customerId BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NULL,
firstName VARCHAR(50) NOT NULL,
lastName VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
phone VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_customer_user_id UNIQUE (user_id)
);
CREATE TABLE IF NOT EXISTS pet (
petId BIGINT AUTO_INCREMENT PRIMARY KEY,
petName VARCHAR(50) NOT NULL,
petSpecies VARCHAR(50) NOT NULL,
petBreed VARCHAR(50) NOT NULL,
petAge INT NOT NULL,
petStatus VARCHAR(20) NOT NULL,
petPrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS adoption (
adoptionId BIGINT AUTO_INCREMENT PRIMARY KEY,
petId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
adoptionDate DATE NOT NULL,
adoptionStatus VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (petId) REFERENCES pet(petId),
FOREIGN KEY (customerId) REFERENCES customer(customerId)
);
CREATE TABLE IF NOT EXISTS supplier (
supId BIGINT AUTO_INCREMENT PRIMARY KEY,
supCompany VARCHAR(100) NOT NULL,
supContactFirstName VARCHAR(50) NOT NULL,
supContactLastName VARCHAR(50) NOT NULL,
supEmail VARCHAR(100) NOT NULL,
supPhone VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS category (
categoryId BIGINT AUTO_INCREMENT PRIMARY KEY,
categoryName VARCHAR(100) NOT NULL,
categoryType VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS product (
prodId BIGINT AUTO_INCREMENT PRIMARY KEY,
prodName VARCHAR(100) NOT NULL,
prodPrice DECIMAL(10, 2) NOT NULL,
categoryId BIGINT NOT NULL,
prodDesc TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (categoryId) REFERENCES category(categoryId)
);
CREATE TABLE IF NOT EXISTS productSupplier (
supId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
cost DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (supId, prodId),
FOREIGN KEY (supId) REFERENCES supplier(supId),
FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS inventory (
inventoryId BIGINT AUTO_INCREMENT PRIMARY KEY,
prodId BIGINT NOT NULL,
quantity INT DEFAULT 0 NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS service (
serviceId BIGINT AUTO_INCREMENT PRIMARY KEY,
serviceName VARCHAR(100) NOT NULL,
serviceDesc TEXT,
serviceDuration INT NOT NULL,
servicePrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS appointment (
appointmentId BIGINT AUTO_INCREMENT PRIMARY KEY,
serviceId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
appointmentDate DATE NOT NULL,
appointmentTime TIME NOT NULL,
appointmentStatus VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (serviceId) REFERENCES service(serviceId),
FOREIGN KEY (customerId) REFERENCES customer(customerId)
);
CREATE TABLE IF NOT EXISTS appointmentPet (
appointmentId BIGINT NOT NULL,
petId BIGINT NOT NULL,
PRIMARY KEY (appointmentId, petId),
FOREIGN KEY (appointmentId) REFERENCES appointment(appointmentId),
FOREIGN KEY (petId) REFERENCES pet(petId)
);
CREATE TABLE IF NOT EXISTS sale (
saleId BIGINT AUTO_INCREMENT PRIMARY KEY,
saleDate DATETIME NOT NULL,
totalAmount DECIMAL(10, 2) NOT NULL,
paymentMethod VARCHAR(50) NOT NULL,
employeeId BIGINT NOT NULL,
storeId BIGINT NOT NULL,
customerId BIGINT NULL,
isRefund BOOLEAN DEFAULT FALSE NOT NULL,
originalSaleId BIGINT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (employeeId) REFERENCES employee(employeeId),
FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
FOREIGN KEY (customerId) REFERENCES customer(customerId),
FOREIGN KEY (originalSaleId) REFERENCES sale(saleId)
);
CREATE TABLE IF NOT EXISTS saleItem (
saleItemId BIGINT AUTO_INCREMENT PRIMARY KEY,
saleId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
quantity INT NOT NULL,
unitPrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (saleId) REFERENCES sale(saleId),
FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS purchaseOrder (
purchaseOrderId BIGINT AUTO_INCREMENT PRIMARY KEY,
supId BIGINT NOT NULL,
orderDate DATE NOT NULL,
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (supId) REFERENCES supplier(supId)
);
CREATE TABLE IF NOT EXISTS activityLog (
logId BIGINT AUTO_INCREMENT PRIMARY KEY,
employeeId BIGINT NOT NULL,
activity TEXT NOT NULL,
logTimestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (employeeId) REFERENCES employee(employeeId)
);
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(100) UNIQUE,
fullName VARCHAR(100),
avatarUrl VARCHAR(255),
role VARCHAR(20) NOT NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS refund (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
saleId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
reason VARCHAR(500) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (saleId) REFERENCES sale(saleId),
FOREIGN KEY (customerId) REFERENCES customer(customerId)
);
CREATE TABLE IF NOT EXISTS conversation (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
customerId BIGINT NOT NULL,
staffId BIGINT,
status VARCHAR(20) NOT NULL DEFAULT 'OPEN',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (customerId) REFERENCES customer(customerId),
FOREIGN KEY (staffId) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS message (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
conversationId BIGINT NOT NULL,
senderId BIGINT NOT NULL,
content TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
isRead BOOLEAN NOT NULL DEFAULT FALSE,
FOREIGN KEY (conversationId) REFERENCES conversation(id),
FOREIGN KEY (senderId) REFERENCES users(id)
);
-- Add foreign keys for user_id linkage
ALTER TABLE employee ADD CONSTRAINT fk_employee_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE customer ADD CONSTRAINT fk_customer_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@@ -0,0 +1,341 @@
CREATE TABLE IF NOT EXISTS storeLocation (
storeId BIGINT AUTO_INCREMENT PRIMARY KEY,
storeName VARCHAR(100) NOT NULL,
address VARCHAR(255) NOT NULL,
phone VARCHAR(20) NOT NULL,
email VARCHAR(100) NOT NULL,
imageUrl VARCHAR(255) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NULL UNIQUE,
password VARCHAR(255) NULL,
email VARCHAR(100) NULL UNIQUE,
firstName VARCHAR(50) NOT NULL,
lastName VARCHAR(50) NOT NULL,
fullName VARCHAR(100) NULL,
phone VARCHAR(20) NULL,
avatarUrl VARCHAR(255) NULL,
role VARCHAR(20) NOT NULL,
staffRole VARCHAR(50) NULL,
primaryStoreId BIGINT NULL,
loyaltyPoints INT NOT NULL DEFAULT 0,
active BOOLEAN NOT NULL DEFAULT TRUE,
tokenVersion INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_users_primary_store FOREIGN KEY (primaryStoreId) REFERENCES storeLocation(storeId) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS supplier (
supId BIGINT AUTO_INCREMENT PRIMARY KEY,
supCompany VARCHAR(100) NOT NULL,
supContactFirstName VARCHAR(50) NOT NULL,
supContactLastName VARCHAR(50) NOT NULL,
supEmail VARCHAR(100) NOT NULL,
supPhone VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS category (
categoryId BIGINT AUTO_INCREMENT PRIMARY KEY,
categoryName VARCHAR(100) NOT NULL,
categoryType VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uq_category_name_type UNIQUE (categoryName, categoryType)
);
CREATE TABLE IF NOT EXISTS service (
serviceId BIGINT AUTO_INCREMENT PRIMARY KEY,
serviceName VARCHAR(100) NOT NULL,
serviceDesc TEXT NULL,
serviceDuration INT NOT NULL,
servicePrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS service_species (
serviceId BIGINT NOT NULL,
species VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (serviceId, species),
CONSTRAINT fk_service_species_service FOREIGN KEY (serviceId) REFERENCES service(serviceId) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS product (
prodId BIGINT AUTO_INCREMENT PRIMARY KEY,
prodName VARCHAR(100) NOT NULL,
prodPrice DECIMAL(10, 2) NOT NULL,
categoryId BIGINT NOT NULL,
prodDesc TEXT NULL,
imageUrl VARCHAR(255) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_product_category FOREIGN KEY (categoryId) REFERENCES category(categoryId)
);
CREATE TABLE IF NOT EXISTS inventory (
inventoryId BIGINT AUTO_INCREMENT PRIMARY KEY,
storeId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
quantity INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uq_inventory_store_product UNIQUE (storeId, prodId),
CONSTRAINT fk_inventory_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
CONSTRAINT fk_inventory_product FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS productSupplier (
supId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
cost DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (supId, prodId),
CONSTRAINT fk_product_supplier_supplier FOREIGN KEY (supId) REFERENCES supplier(supId),
CONSTRAINT fk_product_supplier_product FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS purchaseOrder (
purchaseOrderId BIGINT AUTO_INCREMENT PRIMARY KEY,
supId BIGINT NOT NULL,
storeId BIGINT NOT NULL,
orderDate DATE NOT NULL,
status VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_purchase_order_supplier FOREIGN KEY (supId) REFERENCES supplier(supId),
CONSTRAINT fk_purchase_order_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId)
);
CREATE TABLE IF NOT EXISTS coupon (
couponId BIGINT AUTO_INCREMENT PRIMARY KEY,
couponCode VARCHAR(50) NOT NULL,
discountType VARCHAR(20) NOT NULL,
discountValue DECIMAL(10, 2) NOT NULL,
minOrderAmount DECIMAL(10, 2) NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
startsAt DATETIME NULL,
endsAt DATETIME NULL,
usageLimit INT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uq_coupon_code UNIQUE (couponCode)
);
CREATE TABLE IF NOT EXISTS pet (
petId BIGINT AUTO_INCREMENT PRIMARY KEY,
petName VARCHAR(50) NOT NULL,
petSpecies VARCHAR(50) NOT NULL,
petBreed VARCHAR(50) NULL,
petAge INT NULL,
petStatus VARCHAR(20) NOT NULL,
petPrice DECIMAL(10, 2) NULL,
imageUrl VARCHAR(255) NULL,
ownerUserId BIGINT NULL,
storeId BIGINT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_pet_owner_user FOREIGN KEY (ownerUserId) REFERENCES users(id) ON DELETE SET NULL,
CONSTRAINT fk_pet_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS appointment (
appointmentId BIGINT AUTO_INCREMENT PRIMARY KEY,
serviceId BIGINT NOT NULL,
petId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
storeId BIGINT NOT NULL,
employeeId BIGINT NOT NULL,
appointmentDate DATE NOT NULL,
appointmentTime TIME NOT NULL,
appointmentStatus VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_appointment_service FOREIGN KEY (serviceId) REFERENCES service(serviceId),
CONSTRAINT fk_appointment_pet FOREIGN KEY (petId) REFERENCES pet(petId),
CONSTRAINT fk_appointment_customer FOREIGN KEY (customerId) REFERENCES users(id),
CONSTRAINT fk_appointment_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
CONSTRAINT fk_appointment_employee FOREIGN KEY (employeeId) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS adoption (
adoptionId BIGINT AUTO_INCREMENT PRIMARY KEY,
petId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
employeeId BIGINT NOT NULL,
sourceStoreId BIGINT NOT NULL,
adoptionDate DATE NOT NULL,
adoptionStatus VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_adoption_pet FOREIGN KEY (petId) REFERENCES pet(petId),
CONSTRAINT fk_adoption_customer FOREIGN KEY (customerId) REFERENCES users(id),
CONSTRAINT fk_adoption_employee FOREIGN KEY (employeeId) REFERENCES users(id),
CONSTRAINT fk_adoption_source_store FOREIGN KEY (sourceStoreId) REFERENCES storeLocation(storeId)
);
CREATE TABLE IF NOT EXISTS cart (
cartId BIGINT AUTO_INCREMENT PRIMARY KEY,
userId BIGINT NOT NULL,
storeId BIGINT NULL,
couponId BIGINT NULL,
cartStatus VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
subtotalAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
discountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
totalAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_cart_user FOREIGN KEY (userId) REFERENCES users(id),
CONSTRAINT fk_cart_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId) ON DELETE SET NULL,
CONSTRAINT fk_cart_coupon FOREIGN KEY (couponId) REFERENCES coupon(couponId) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS cart_item (
cartItemId BIGINT AUTO_INCREMENT PRIMARY KEY,
cartId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
quantity INT NOT NULL,
unitPrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_cart_item_cart FOREIGN KEY (cartId) REFERENCES cart(cartId) ON DELETE CASCADE,
CONSTRAINT fk_cart_item_product FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS sale (
saleId BIGINT AUTO_INCREMENT PRIMARY KEY,
saleDate DATETIME NOT NULL,
totalAmount DECIMAL(10, 2) NOT NULL,
paymentMethod VARCHAR(50) NOT NULL,
employeeId BIGINT NOT NULL,
storeId BIGINT NOT NULL,
customerId BIGINT NULL,
isRefund BOOLEAN NOT NULL DEFAULT FALSE,
originalSaleId BIGINT NULL,
channel VARCHAR(20) NOT NULL DEFAULT 'IN_STORE',
cartId BIGINT NULL,
couponId BIGINT NULL,
subtotalAmount DECIMAL(10, 2) NULL,
couponDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
employeeDiscountAmount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
pointsEarned INT NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_sale_employee FOREIGN KEY (employeeId) REFERENCES users(id),
CONSTRAINT fk_sale_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId),
CONSTRAINT fk_sale_customer FOREIGN KEY (customerId) REFERENCES users(id) ON DELETE SET NULL,
CONSTRAINT fk_sale_original_sale FOREIGN KEY (originalSaleId) REFERENCES sale(saleId),
CONSTRAINT fk_sale_cart FOREIGN KEY (cartId) REFERENCES cart(cartId) ON DELETE SET NULL,
CONSTRAINT fk_sale_coupon FOREIGN KEY (couponId) REFERENCES coupon(couponId) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS saleItem (
saleItemId BIGINT AUTO_INCREMENT PRIMARY KEY,
saleId BIGINT NOT NULL,
prodId BIGINT NOT NULL,
quantity INT NOT NULL,
unitPrice DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_sale_item_sale FOREIGN KEY (saleId) REFERENCES sale(saleId) ON DELETE CASCADE,
CONSTRAINT fk_sale_item_product FOREIGN KEY (prodId) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS refund (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
saleId BIGINT NOT NULL,
customerId BIGINT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
reason VARCHAR(500) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_refund_sale FOREIGN KEY (saleId) REFERENCES sale(saleId),
CONSTRAINT fk_refund_customer FOREIGN KEY (customerId) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS refund_item (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
refund_id BIGINT NOT NULL,
prod_id BIGINT NOT NULL,
quantity INT NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_refund_item_refund FOREIGN KEY (refund_id) REFERENCES refund(id) ON DELETE CASCADE,
CONSTRAINT fk_refund_item_product FOREIGN KEY (prod_id) REFERENCES product(prodId)
);
CREATE TABLE IF NOT EXISTS conversation (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
customerId BIGINT NOT NULL,
staffId BIGINT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'OPEN',
mode VARCHAR(20) NOT NULL DEFAULT 'AUTOMATED',
humanRequestedAt TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT fk_conversation_customer FOREIGN KEY (customerId) REFERENCES users(id),
CONSTRAINT fk_conversation_staff FOREIGN KEY (staffId) REFERENCES users(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS message (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
conversationId BIGINT NOT NULL,
senderId BIGINT NOT NULL,
content TEXT NULL,
attachmentUrl VARCHAR(255) NULL,
attachmentName VARCHAR(255) NULL,
attachmentMimeType VARCHAR(100) NULL,
attachmentSizeBytes BIGINT NULL,
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
isRead BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT fk_message_conversation FOREIGN KEY (conversationId) REFERENCES conversation(id) ON DELETE CASCADE,
CONSTRAINT fk_message_sender FOREIGN KEY (senderId) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS activityLog (
logId BIGINT AUTO_INCREMENT PRIMARY KEY,
userId BIGINT NOT NULL,
storeId BIGINT NULL,
activity TEXT NOT NULL,
logTimestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_activity_log_user FOREIGN KEY (userId) REFERENCES users(id),
CONSTRAINT fk_activity_log_store FOREIGN KEY (storeId) REFERENCES storeLocation(storeId) ON DELETE SET NULL
);
CREATE INDEX idx_users_primary_store ON users(primaryStoreId);
CREATE INDEX idx_users_role ON users(role);
CREATE INDEX idx_users_name ON users(lastName, firstName);
CREATE INDEX idx_service_species_species ON service_species(species);
CREATE INDEX idx_inventory_store ON inventory(storeId);
CREATE INDEX idx_inventory_product ON inventory(prodId);
CREATE INDEX idx_purchase_order_store ON purchaseOrder(storeId);
CREATE INDEX idx_pet_owner_user ON pet(ownerUserId);
CREATE INDEX idx_pet_store ON pet(storeId);
CREATE INDEX idx_pet_species ON pet(petSpecies);
CREATE INDEX idx_pet_name ON pet(petName);
CREATE INDEX idx_appointment_store ON appointment(storeId);
CREATE INDEX idx_appointment_employee ON appointment(employeeId);
CREATE INDEX idx_appointment_customer ON appointment(customerId);
CREATE INDEX idx_appointment_pet ON appointment(petId);
CREATE INDEX idx_appointment_date_status ON appointment(appointmentDate, appointmentStatus);
CREATE INDEX idx_adoption_store ON adoption(sourceStoreId);
CREATE INDEX idx_adoption_employee ON adoption(employeeId);
CREATE INDEX idx_sale_store ON sale(storeId);
CREATE INDEX idx_sale_employee ON sale(employeeId);
CREATE INDEX idx_sale_customer ON sale(customerId);
CREATE INDEX idx_sale_date ON sale(saleDate);
CREATE INDEX idx_cart_user ON cart(userId);
CREATE INDEX idx_conversation_customer ON conversation(customerId);
CREATE INDEX idx_conversation_staff ON conversation(staffId);
CREATE INDEX idx_activity_log_store ON activityLog(storeId);

View File

@@ -1,6 +0,0 @@
INSERT INTO pet (petName, petSpecies, petBreed, petAge, petStatus, petPrice, customerId)
SELECT 'Pepper', 'Cat', 'Tabby', 3, 'Owned', 0.00, customerId FROM customer WHERE email = 'alex@gmail.com'
UNION ALL
SELECT 'Coco', 'Dog', 'Pomeranian', 2, 'Owned', 0.00, customerId FROM customer WHERE email = 'olivia@gmail.com'
UNION ALL
SELECT 'Finn', 'Dog', 'Border Collie', 5, 'Owned', 0.00, customerId FROM customer WHERE email = 'sophia@gmail.com';

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