diff --git a/backend/src/main/java/com/petshop/backend/service/CustomerService.java b/backend/src/main/java/com/petshop/backend/service/CustomerService.java index 33c731d1..040be22a 100644 --- a/backend/src/main/java/com/petshop/backend/service/CustomerService.java +++ b/backend/src/main/java/com/petshop/backend/service/CustomerService.java @@ -4,34 +4,23 @@ 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) { + public CustomerService(CustomerRepository customerRepository, UserRepository userRepository) { this.customerRepository = customerRepository; this.userRepository = userRepository; - this.passwordEncoder = passwordEncoder; - this.userBusinessLinkageService = userBusinessLinkageService; } public Page getAllCustomers(String query, Pageable pageable) { @@ -52,19 +41,14 @@ public class CustomerService { @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); + syncLinkedUser(customer); + return mapToResponse(customer); } @Transactional @@ -72,8 +56,6 @@ public class CustomerService { 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()); @@ -85,14 +67,9 @@ public class CustomerService { @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; + if (!customerRepository.existsById(id)) { + throw new ResourceNotFoundException("Customer not found with id: " + id); } - customerRepository.deleteById(id); } @@ -122,37 +99,4 @@ public class CustomerService { 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"); - } - }); - } } diff --git a/backend/src/main/java/com/petshop/backend/service/SaleService.java b/backend/src/main/java/com/petshop/backend/service/SaleService.java index b8d5861e..b426dc38 100644 --- a/backend/src/main/java/com/petshop/backend/service/SaleService.java +++ b/backend/src/main/java/com/petshop/backend/service/SaleService.java @@ -78,7 +78,7 @@ public class SaleService { sale.setSaleDate(LocalDateTime.now()); sale.setEmployee(employee); sale.setStore(store); - sale.setPaymentMethod(normalizePaymentMethod(request.getPaymentMethod())); + sale.setPaymentMethod(request.getPaymentMethod()); sale.setIsRefund(request.getIsRefund() != null ? request.getIsRefund() : false); if (request.getCustomerId() != null) { @@ -215,22 +215,4 @@ public class SaleService { return response; } - - String normalizePaymentMethod(String paymentMethod) { - if (paymentMethod == null) { - return null; - } - - String normalized = paymentMethod.trim(); - if (normalized.equalsIgnoreCase("Debit")) { - return "Card"; - } - if (normalized.equalsIgnoreCase("Cash")) { - return "Cash"; - } - if (normalized.equalsIgnoreCase("Card")) { - return "Card"; - } - return normalized; - } } diff --git a/backend/src/main/resources/db/migration/V10__remove_debit_payment_method.sql b/backend/src/main/resources/db/migration/V10__remove_debit_payment_method.sql deleted file mode 100644 index 874b0205..00000000 --- a/backend/src/main/resources/db/migration/V10__remove_debit_payment_method.sql +++ /dev/null @@ -1,3 +0,0 @@ -UPDATE sale -SET paymentMethod = 'Card' -WHERE LOWER(paymentMethod) = 'debit'; diff --git a/backend/src/main/resources/db/migration/V2__seed_data.sql b/backend/src/main/resources/db/migration/V2__seed_data.sql index d7308ad4..5e8d3fb6 100644 --- a/backend/src/main/resources/db/migration/V2__seed_data.sql +++ b/backend/src/main/resources/db/migration/V2__seed_data.sql @@ -128,7 +128,7 @@ VALUES ('2026-01-05 09:15:00', 125.00, 'Card', 1, 1, 1), ('2026-01-08 11:30:00', 200.00, 'Card', 2, 1, 2), ('2026-01-12 14:20:00', 60.00, 'Cash', 3, 2, 3), -('2026-01-15 10:45:00', 150.00, 'Card', 1, 1, 1), +('2026-01-15 10:45:00', 150.00, 'Debit', 1, 1, 1), ('2026-01-18 16:30:00', 80.00, 'Card', 4, 3, 2), ('2026-01-22 13:15:00', 95.00, 'Cash', 2, 2, NULL), ('2026-01-25 15:40:00', 240.00, 'Card', 5, 4, 4), @@ -136,12 +136,12 @@ VALUES ('2026-02-01 09:00:00', 175.00, 'Card', 3, 3, 1), ('2026-02-03 11:20:00', 120.00, 'Card', 2, 1, 3), ('2026-02-05 14:50:00', 45.00, 'Cash', 4, 2, NULL), -('2026-02-08 16:15:00', 160.00, 'Card', 1, 1, 2), +('2026-02-08 16:15:00', 160.00, 'Debit', 1, 1, 2), ('2026-02-10 10:25:00', 100.00, 'Card', 5, 4, NULL), ('2026-02-12 13:45:00', 50.00, 'Cash', 2, 2, 1), ('2026-02-15 15:30:00', 85.00, 'Card', 3, 3, NULL), ('2026-02-18 11:10:00', 200.00, 'Card', 1, 1, 4), -('2026-02-20 14:35:00', 155.00, 'Card', 4, 3, NULL), +('2026-02-20 14:35:00', 155.00, 'Debit', 4, 3, NULL), ('2026-02-22 16:50:00', 75.00, 'Cash', 2, 1, 2), ('2026-02-24 10:15:00', 140.00, 'Card', 5, 4, NULL), (NOW(), 95.00, 'Card', 1, 1, 1); diff --git a/backend/src/main/resources/db/migration/V9__backfill_user_accounts.sql b/backend/src/main/resources/db/migration/V9__backfill_user_accounts.sql deleted file mode 100644 index 4af69669..00000000 --- a/backend/src/main/resources/db/migration/V9__backfill_user_accounts.sql +++ /dev/null @@ -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; diff --git a/backend/src/test/java/com/petshop/backend/service/CustomerServiceTest.java b/backend/src/test/java/com/petshop/backend/service/CustomerServiceTest.java deleted file mode 100644 index dde07768..00000000 --- a/backend/src/test/java/com/petshop/backend/service/CustomerServiceTest.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.petshop.backend.service; - -import com.petshop.backend.dto.customer.CustomerRequest; -import com.petshop.backend.entity.Customer; -import com.petshop.backend.entity.User; -import com.petshop.backend.repository.CustomerRepository; -import com.petshop.backend.repository.UserRepository; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.web.server.ResponseStatusException; - -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class CustomerServiceTest { - - @Mock - private CustomerRepository customerRepository; - - @Mock - private UserRepository userRepository; - - @Mock - private PasswordEncoder passwordEncoder; - - @Mock - private UserBusinessLinkageService userBusinessLinkageService; - - @InjectMocks - private CustomerService customerService; - - @Test - void createCustomerCreatesLinkedUser() { - CustomerRequest request = new CustomerRequest(); - request.setFirstName("Pat"); - request.setLastName("Owner"); - request.setEmail("pat@example.com"); - - Customer savedCustomer = new Customer(); - savedCustomer.setCustomerId(7L); - savedCustomer.setFirstName("Pat"); - savedCustomer.setLastName("Owner"); - savedCustomer.setEmail("pat@example.com"); - - when(customerRepository.save(any(Customer.class))).thenReturn(savedCustomer); - when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.empty()); - when(passwordEncoder.encode(any())).thenReturn("hashed-temp-password"); - when(userRepository.save(any(User.class))).thenAnswer(invocation -> { - User user = invocation.getArgument(0); - user.setId(11L); - return user; - }); - when(userBusinessLinkageService.ensureLinkedCustomer(any(User.class))).thenAnswer(invocation -> { - User user = invocation.getArgument(0); - savedCustomer.setUserId(user.getId()); - return savedCustomer; - }); - - var response = customerService.createCustomer(request); - - assertNotNull(response); - assertEquals("Pat", response.getFirstName()); - assertEquals("Owner", response.getLastName()); - assertEquals("pat@example.com", response.getEmail()); - - ArgumentCaptor userCaptor = ArgumentCaptor.forClass(User.class); - verify(userRepository).save(userCaptor.capture()); - User createdUser = userCaptor.getValue(); - assertEquals("customer_7", createdUser.getUsername()); - assertEquals("hashed-temp-password", createdUser.getPassword()); - assertEquals("pat@example.com", createdUser.getEmail()); - assertEquals("Pat Owner", createdUser.getFullName()); - assertEquals("200-000-0007", createdUser.getPhone()); - assertEquals(false, createdUser.getActive()); - } - - @Test - void createCustomerRejectsExistingNonCustomerEmail() { - CustomerRequest request = new CustomerRequest(); - request.setFirstName("Pat"); - request.setLastName("Owner"); - request.setEmail("pat@example.com"); - - User existing = new User(); - existing.setId(22L); - existing.setUsername("staff1"); - existing.setEmail("pat@example.com"); - existing.setRole(User.Role.STAFF); - - when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing)); - - assertThrows(ResponseStatusException.class, () -> customerService.createCustomer(request)); - } - - @Test - void createCustomerRejectsExistingCustomerEmail() { - CustomerRequest request = new CustomerRequest(); - request.setFirstName("Pat"); - request.setLastName("Owner"); - request.setEmail("pat@example.com"); - - User existing = new User(); - existing.setId(22L); - existing.setUsername("customer1"); - existing.setEmail("pat@example.com"); - existing.setRole(User.Role.CUSTOMER); - - when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing)); - - assertThrows(ResponseStatusException.class, () -> customerService.createCustomer(request)); - } - - @Test - void updateCustomerRejectsExistingEmailFromOtherUser() { - Customer customer = new Customer(); - customer.setCustomerId(7L); - customer.setUserId(11L); - customer.setFirstName("Pat"); - customer.setLastName("Owner"); - customer.setEmail("old@example.com"); - - CustomerRequest request = new CustomerRequest(); - request.setFirstName("Pat"); - request.setLastName("Owner"); - request.setEmail("pat@example.com"); - - User existing = new User(); - existing.setId(22L); - existing.setUsername("customer2"); - existing.setEmail("pat@example.com"); - existing.setRole(User.Role.CUSTOMER); - - when(customerRepository.findById(7L)).thenReturn(Optional.of(customer)); - when(userRepository.findByEmail("pat@example.com")).thenReturn(Optional.of(existing)); - - assertThrows(ResponseStatusException.class, () -> customerService.updateCustomer(7L, request)); - } - - @Test - void deleteCustomerDeletesLinkedUser() { - Customer customer = new Customer(); - customer.setCustomerId(7L); - customer.setUserId(11L); - - when(customerRepository.findById(7L)).thenReturn(Optional.of(customer)); - when(userRepository.existsById(11L)).thenReturn(true); - - customerService.deleteCustomer(7L); - - verify(userRepository).deleteById(11L); - verify(customerRepository, never()).deleteById(7L); - } - - @Test - void deleteCustomerDeletesCustomerWhenNoLinkedUserExists() { - Customer customer = new Customer(); - customer.setCustomerId(7L); - customer.setUserId(11L); - - when(customerRepository.findById(7L)).thenReturn(Optional.of(customer)); - when(userRepository.existsById(11L)).thenReturn(false); - - customerService.deleteCustomer(7L); - - verify(customerRepository).deleteById(7L); - } -} diff --git a/backend/src/test/java/com/petshop/backend/service/SaleServiceTest.java b/backend/src/test/java/com/petshop/backend/service/SaleServiceTest.java deleted file mode 100644 index b0ffeded..00000000 --- a/backend/src/test/java/com/petshop/backend/service/SaleServiceTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.petshop.backend.service; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class SaleServiceTest { - - @Test - void normalizePaymentMethodMapsDebitToCard() { - SaleService saleService = new SaleService(null, null, null, null, null, null, null, null); - - assertEquals("Card", saleService.normalizePaymentMethod("Debit")); - assertEquals("Card", saleService.normalizePaymentMethod("debit")); - assertEquals("Cash", saleService.normalizePaymentMethod("Cash")); - } -}