diff --git a/petshop-api.postman_collection.json b/petshop-api.postman_collection.json index a2537681..7777a8ac 100644 --- a/petshop-api.postman_collection.json +++ b/petshop-api.postman_collection.json @@ -114,7 +114,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"username\": \"admin\",\n \"password\": \"admin\"\n}", + "raw": "{\n \"username\": \"admin\",\n \"password\": \"admin123\"\n}", "options": { "raw": { "language": "json" @@ -245,6 +245,14 @@ ] } } + }, + { + "name": "Health Check", + "request": { + "method": "GET", + "url": "{{baseUrl}}/api/v1/health", + "header": [] + } } ] }, diff --git a/src/main/java/com/petshop/backend/controller/AdoptionController.java b/src/main/java/com/petshop/backend/controller/AdoptionController.java index 30da7d5e..d200a3db 100644 --- a/src/main/java/com/petshop/backend/controller/AdoptionController.java +++ b/src/main/java/com/petshop/backend/controller/AdoptionController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/adoptions") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class AdoptionController { private final AdoptionService adoptionService; diff --git a/src/main/java/com/petshop/backend/controller/AppointmentController.java b/src/main/java/com/petshop/backend/controller/AppointmentController.java index 6c9f8fa4..b607a577 100644 --- a/src/main/java/com/petshop/backend/controller/AppointmentController.java +++ b/src/main/java/com/petshop/backend/controller/AppointmentController.java @@ -9,6 +9,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import java.time.LocalDate; @@ -16,6 +17,7 @@ import java.util.List; @RestController @RequestMapping("/api/v1/appointments") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class AppointmentController { private final AppointmentService appointmentService; diff --git a/src/main/java/com/petshop/backend/controller/AuthController.java b/src/main/java/com/petshop/backend/controller/AuthController.java index d0a6cc39..4055893e 100644 --- a/src/main/java/com/petshop/backend/controller/AuthController.java +++ b/src/main/java/com/petshop/backend/controller/AuthController.java @@ -88,6 +88,7 @@ public class AuthController { public ResponseEntity logout() { Map response = new HashMap<>(); response.put("message", "Logged out successfully"); + response.put("note", "Token remains valid until expiration. Clear token from client storage."); return ResponseEntity.ok(response); } } diff --git a/src/main/java/com/petshop/backend/controller/CategoryController.java b/src/main/java/com/petshop/backend/controller/CategoryController.java index ddd7b934..5e1b80c4 100644 --- a/src/main/java/com/petshop/backend/controller/CategoryController.java +++ b/src/main/java/com/petshop/backend/controller/CategoryController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/categories") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class CategoryController { private final CategoryService categoryService; diff --git a/src/main/java/com/petshop/backend/controller/CustomerController.java b/src/main/java/com/petshop/backend/controller/CustomerController.java index 75bed4fc..f3ab880e 100644 --- a/src/main/java/com/petshop/backend/controller/CustomerController.java +++ b/src/main/java/com/petshop/backend/controller/CustomerController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/customers") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class CustomerController { private final CustomerService customerService; diff --git a/src/main/java/com/petshop/backend/controller/HealthController.java b/src/main/java/com/petshop/backend/controller/HealthController.java new file mode 100644 index 00000000..8ee609c3 --- /dev/null +++ b/src/main/java/com/petshop/backend/controller/HealthController.java @@ -0,0 +1,18 @@ +package com.petshop.backend.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/health") +public class HealthController { + + @GetMapping + public ResponseEntity> healthCheck() { + return ResponseEntity.ok(Map.of("status", "UP")); + } +} diff --git a/src/main/java/com/petshop/backend/controller/PetController.java b/src/main/java/com/petshop/backend/controller/PetController.java index 7ae7ca64..07532b93 100644 --- a/src/main/java/com/petshop/backend/controller/PetController.java +++ b/src/main/java/com/petshop/backend/controller/PetController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/pets") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class PetController { private final PetService petService; diff --git a/src/main/java/com/petshop/backend/controller/ProductController.java b/src/main/java/com/petshop/backend/controller/ProductController.java index ada0e4dc..f281fb57 100644 --- a/src/main/java/com/petshop/backend/controller/ProductController.java +++ b/src/main/java/com/petshop/backend/controller/ProductController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/products") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class ProductController { private final ProductService productService; diff --git a/src/main/java/com/petshop/backend/controller/SaleController.java b/src/main/java/com/petshop/backend/controller/SaleController.java index aae791fe..2426bb19 100644 --- a/src/main/java/com/petshop/backend/controller/SaleController.java +++ b/src/main/java/com/petshop/backend/controller/SaleController.java @@ -8,10 +8,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/sales") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class SaleController { private final SaleService saleService; diff --git a/src/main/java/com/petshop/backend/controller/ServiceController.java b/src/main/java/com/petshop/backend/controller/ServiceController.java index 53bb5343..a7160a62 100644 --- a/src/main/java/com/petshop/backend/controller/ServiceController.java +++ b/src/main/java/com/petshop/backend/controller/ServiceController.java @@ -9,10 +9,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/services") +@PreAuthorize("hasAnyRole('STAFF', 'ADMIN')") public class ServiceController { private final ServiceService serviceService; diff --git a/src/main/java/com/petshop/backend/controller/StoreController.java b/src/main/java/com/petshop/backend/controller/StoreController.java index 4fa716be..58110d7e 100644 --- a/src/main/java/com/petshop/backend/controller/StoreController.java +++ b/src/main/java/com/petshop/backend/controller/StoreController.java @@ -1,14 +1,20 @@ package com.petshop.backend.controller; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.store.StoreRequest; import com.petshop.backend.dto.store.StoreResponse; import com.petshop.backend.service.StoreService; +import jakarta.validation.Valid; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/v1/stores") +@PreAuthorize("hasRole('ADMIN')") public class StoreController { private final StoreService storeService; @@ -23,4 +29,33 @@ public class StoreController { Pageable pageable) { return ResponseEntity.ok(storeService.getAllStores(q, pageable)); } + + @GetMapping("/{id}") + public ResponseEntity getStoreById(@PathVariable Long id) { + return ResponseEntity.ok(storeService.getStoreById(id)); + } + + @PostMapping + public ResponseEntity createStore(@Valid @RequestBody StoreRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(storeService.createStore(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateStore( + @PathVariable Long id, + @Valid @RequestBody StoreRequest request) { + return ResponseEntity.ok(storeService.updateStore(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteStore(@PathVariable Long id) { + storeService.deleteStore(id); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping + public ResponseEntity bulkDeleteStores(@Valid @RequestBody BulkDeleteRequest request) { + storeService.bulkDeleteStores(request); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/petshop/backend/dto/store/StoreRequest.java b/src/main/java/com/petshop/backend/dto/store/StoreRequest.java new file mode 100644 index 00000000..d6b5fc12 --- /dev/null +++ b/src/main/java/com/petshop/backend/dto/store/StoreRequest.java @@ -0,0 +1,78 @@ +package com.petshop.backend.dto.store; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import java.util.Objects; + +public class StoreRequest { + @NotBlank(message = "Store name is required") + private String storeName; + + @NotBlank(message = "Address is required") + private String address; + + @NotBlank(message = "Phone is required") + private String phone; + + @NotBlank(message = "Email is required") + @Email(message = "Email must be valid") + private String email; + + public String getStoreName() { + return storeName; + } + + public void setStoreName(String storeName) { + this.storeName = storeName; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + 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; + StoreRequest that = (StoreRequest) o; + return Objects.equals(storeName, that.storeName) && + Objects.equals(address, that.address) && + Objects.equals(phone, that.phone) && + Objects.equals(email, that.email); + } + + @Override + public int hashCode() { + return Objects.hash(storeName, address, phone, email); + } + + @Override + public String toString() { + return "StoreRequest{" + + "storeName='" + storeName + '\'' + + ", address='" + address + '\'' + + ", phone='" + phone + '\'' + + ", email='" + email + '\'' + + '}'; + } +} diff --git a/src/main/java/com/petshop/backend/security/SecurityConfig.java b/src/main/java/com/petshop/backend/security/SecurityConfig.java index dadf9d93..4f5ecb51 100644 --- a/src/main/java/com/petshop/backend/security/SecurityConfig.java +++ b/src/main/java/com/petshop/backend/security/SecurityConfig.java @@ -37,6 +37,7 @@ public class SecurityConfig { .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/v1/auth/login").permitAll() + .requestMatchers("/api/v1/health").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll() .requestMatchers(HttpMethod.GET, "/api/v1/dropdowns/suppliers").hasRole("ADMIN") .requestMatchers("/api/v1/inventory/**").hasRole("ADMIN") diff --git a/src/main/java/com/petshop/backend/service/StoreService.java b/src/main/java/com/petshop/backend/service/StoreService.java index 2d7564e7..5d2c9ce3 100644 --- a/src/main/java/com/petshop/backend/service/StoreService.java +++ b/src/main/java/com/petshop/backend/service/StoreService.java @@ -1,11 +1,15 @@ package com.petshop.backend.service; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.store.StoreRequest; import com.petshop.backend.dto.store.StoreResponse; import com.petshop.backend.entity.StoreLocation; +import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.repository.StoreRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service public class StoreService { @@ -26,6 +30,51 @@ public class StoreService { return stores.map(this::mapToResponse); } + public StoreResponse getStoreById(Long id) { + StoreLocation store = storeRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + id)); + return mapToResponse(store); + } + + @Transactional + public StoreResponse createStore(StoreRequest request) { + StoreLocation store = new StoreLocation(); + store.setStoreName(request.getStoreName()); + store.setAddress(request.getAddress()); + store.setPhone(request.getPhone()); + store.setEmail(request.getEmail()); + + store = storeRepository.save(store); + return mapToResponse(store); + } + + @Transactional + public StoreResponse updateStore(Long id, StoreRequest request) { + StoreLocation store = storeRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + id)); + + store.setStoreName(request.getStoreName()); + store.setAddress(request.getAddress()); + store.setPhone(request.getPhone()); + store.setEmail(request.getEmail()); + + store = storeRepository.save(store); + return mapToResponse(store); + } + + @Transactional + public void deleteStore(Long id) { + if (!storeRepository.existsById(id)) { + throw new ResourceNotFoundException("Store not found with id: " + id); + } + storeRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteStores(BulkDeleteRequest request) { + storeRepository.deleteAllById(request.getIds()); + } + private StoreResponse mapToResponse(StoreLocation store) { return new StoreResponse( store.getStoreId(), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f04fe658..2b7c9b34 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,6 +8,13 @@ spring: password: ${SPRING_DATASOURCE_PASSWORD:petshop} driver-class-name: com.mysql.cj.jdbc.Driver + sql: + init: + mode: always + schema-locations: classpath:schema.sql + data-locations: classpath:data.sql + continue-on-error: false + jpa: hibernate: ddl-auto: validate diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 00000000..b6a12775 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,205 @@ +-- Insert Sample Data + +INSERT INTO storeLocation (storeName, address, phone, email) +VALUES +('Downtown Branch', '123 Main St', '123-456-7890', 'downtown@petshop.com'), +('North Branch', '456 North Ave', '987-654-3210', 'north@petshop.com'), +('West Side Store', '789 West Blvd', '555-123-4567', 'westside@petshop.com'), +('East End Shop', '321 East Road', '555-987-6543', 'eastend@petshop.com'), +('South Mall Location', '654 South Plaza', '555-246-8135', 'southmall@petshop.com'); + +INSERT INTO employee (firstName, lastName, email, phone, role, isActive) +VALUES +('John', 'Doe', 'john@petshop.com', '111-222-3333', 'Manager', TRUE), +('Sara', 'Smith', 'sara@petshop.com', '444-555-6666', 'Staff', TRUE), +('Michael', 'Johnson', 'michael@petshop.com', '222-333-4444', 'Groomer', TRUE), +('Lisa', 'Williams', 'lisa@petshop.com', '333-444-5555', 'Staff', TRUE), +('David', 'Brown', 'david@petshop.com', '555-666-7777', 'Veterinarian', TRUE), +('Emma', 'Davis', 'emma@petshop.com', '666-777-8888', 'Manager', FALSE); + +INSERT INTO employeeStore (employeeId, storeId) +VALUES +(1, 1), +(2, 1), +(2, 2), +(3, 2), +(4, 3), +(5, 1), +(5, 4), +(6, 5); + +INSERT INTO customer (firstName, lastName, email, phone) +VALUES +('Alex', 'Brown', 'alex@gmail.com', '777-888-9999'), +('Emily', 'Clark', 'emily@gmail.com', '666-555-4444'), +('James', 'Wilson', 'james@gmail.com', '888-999-0000'), +('Olivia', 'Martinez', 'olivia@gmail.com', '999-000-1111'), +('William', 'Anderson', 'william@gmail.com', '000-111-2222'), +('Sophia', 'Taylor', 'sophia@gmail.com', '111-222-3333'); + +INSERT INTO pet (petName, petSpecies, petBreed, petAge, petStatus, petPrice) +VALUES +('Buddy', 'Dog', 'Labrador', 2, 'Available', 500.00), +('Milo', 'Cat', 'Persian', 1, 'Available', 300.00), +('Charlie', 'Dog', 'Golden Retriever', 3, 'Available', 550.00), +('Luna', 'Cat', 'Siamese', 2, 'Adopted', 350.00), +('Max', 'Dog', 'Beagle', 1, 'Available', 450.00), +('Bella', 'Cat', 'Maine Coon', 4, 'Available', 400.00); + +INSERT INTO adoption (petId, customerId, adoptionDate, adoptionStatus) +VALUES +(1, 1, '2026-01-15', 'Completed'), +(4, 3, '2026-01-20', 'Completed'), +(2, 2, '2026-01-25', 'Pending'), +(5, 4, '2026-02-01', 'Completed'), +(6, 5, '2026-02-02', 'Pending'); + +INSERT INTO supplier (supCompany, supContactFirstName, supContactLastName, supEmail, supPhone) +VALUES +('PetFood Inc', 'Robert', 'King', 'contact@petfood.com', '888-111-2222'), +('Toy World', 'Jennifer', 'Lee', 'sales@toyworld.com', '888-222-3333'), +('Pet Supplies Co', 'Kevin', 'White', 'info@petsupplies.com', '888-333-4444'), +('Animal Care Products', 'Nancy', 'Green', 'orders@animalcare.com', '888-444-5555'), +('Premium Pet Goods', 'Tom', 'Black', 'support@premiumpet.com', '888-555-6666'); + +INSERT INTO category (categoryName, categoryType) +VALUES +('Dog Food', 'Product'), +('Cat Toys', 'Product'), +('Bird Supplies', 'Product'), +('Aquarium', 'Product'), +('Small Animals', 'Product'); + +INSERT INTO product (prodName, prodPrice, categoryId, prodDesc) +VALUES +('Premium Dog Food', 50.00, 1, 'High quality dog food'), +('Cat Toy Ball', 10.00, 2, 'Colorful toy for cats'), +('Bird Cage Large', 120.00, 3, 'Spacious bird cage'), +('Fish Tank 20 Gallon', 80.00, 4, 'Complete aquarium kit'), +('Hamster Wheel', 15.00, 5, 'Exercise wheel for small pets'), +('Organic Dog Treats', 25.00, 1, 'Natural dog treats'); + +INSERT INTO productSupplier (supId, prodId, cost) +VALUES +(1, 1, 35.00), +(1, 2, 6.50), +(2, 2, 7.00), +(3, 3, 90.00), +(3, 4, 60.00), +(4, 5, 10.00), +(5, 6, 18.00), +(1, 6, 17.50); + +INSERT INTO inventory (prodId, quantity) +VALUES +(1, 100), +(2, 200), +(3, 50), +(4, 30), +(5, 150), +(6, 75); + +INSERT INTO service (serviceName, serviceDesc, serviceDuration, servicePrice) +VALUES +('Pet Grooming', 'Full grooming service', 60, 40.00), +('Nail Trimming', 'Quick nail trim', 15, 10.00), +('Bath and Brush', 'Bathing and brushing service', 45, 30.00), +('Veterinary Checkup', 'Complete health examination', 30, 75.00), +('Teeth Cleaning', 'Professional dental cleaning', 90, 100.00); + +INSERT INTO appointment (serviceId, customerId, appointmentDate, appointmentTime, appointmentStatus) +VALUES +(1, 2, '2026-02-01', '10:30:00', 'Booked'), +(2, 1, '2026-02-03', '14:00:00', 'Booked'), +(3, 3, '2026-02-05', '09:00:00', 'Completed'), +(4, 4, '2026-02-07', '11:30:00', 'Booked'), +(5, 5, '2026-02-10', '15:00:00', 'Cancelled'); + +INSERT INTO appointmentPet (appointmentId, petId) +VALUES +(1, 2), +(2, 1), +(3, 3), +(4, 5), +(5, 6); + +INSERT INTO sale (saleDate, totalAmount, paymentMethod, employeeId, storeId) +VALUES +('2026-01-05 09:15:00', 125.00, 'Card', 1, 1), +('2026-01-08 11:30:00', 200.00, 'Card', 2, 1), +('2026-01-12 14:20:00', 60.00, 'Cash', 3, 2), +('2026-01-15 10:45:00', 150.00, 'Debit', 1, 1), +('2026-01-18 16:30:00', 80.00, 'Card', 4, 3), +('2026-01-22 13:15:00', 95.00, 'Cash', 2, 2), +('2026-01-25 15:40:00', 240.00, 'Card', 5, 4), +('2026-01-28 10:30:00', 80.00, 'Cash', 1, 1), +('2026-02-01 09:00:00', 175.00, 'Card', 3, 3), +('2026-02-03 11:20:00', 120.00, 'Card', 2, 1), +('2026-02-05 14:50:00', 45.00, 'Cash', 4, 2), +('2026-02-08 16:15:00', 160.00, 'Debit', 1, 1), +('2026-02-10 10:25:00', 100.00, 'Card', 5, 4), +('2026-02-12 13:45:00', 50.00, 'Cash', 2, 2), +('2026-02-15 15:30:00', 85.00, 'Card', 3, 3), +('2026-02-18 11:10:00', 200.00, 'Card', 1, 1), +('2026-02-20 14:35:00', 155.00, 'Debit', 4, 3), +('2026-02-22 16:50:00', 75.00, 'Cash', 2, 1), +('2026-02-24 10:15:00', 140.00, 'Card', 5, 4), +(NOW(), 95.00, 'Card', 1, 1); + +INSERT INTO saleItem (saleId, prodId, quantity, unitPrice) +VALUES +(1, 1, 2, 50.00), +(1, 6, 1, 25.00), +(2, 3, 1, 120.00), +(2, 4, 1, 80.00), +(3, 2, 3, 10.00), +(3, 5, 2, 15.00), +(4, 1, 3, 50.00), +(5, 4, 1, 80.00), +(6, 2, 4, 10.00), +(6, 5, 1, 15.00), +(6, 6, 1, 25.00), +(6, 1, 1, 50.00), +(7, 3, 2, 120.00), +(8, 1, 1, 50.00), +(8, 2, 3, 10.00), +(9, 1, 3, 50.00), +(9, 6, 1, 25.00), +(10, 3, 1, 120.00), +(11, 5, 1, 15.00), +(11, 2, 3, 10.00), +(12, 4, 2, 80.00), +(13, 6, 4, 25.00), +(14, 1, 1, 50.00), +(15, 2, 2, 10.00), +(15, 5, 1, 15.00), +(15, 6, 2, 25.00), +(16, 3, 1, 120.00), +(16, 4, 1, 80.00), +(17, 4, 1, 80.00), +(17, 1, 1, 50.00), +(17, 6, 1, 25.00), +(18, 6, 2, 25.00), +(18, 2, 2, 10.00), +(18, 5, 1, 15.00), +(19, 1, 2, 50.00), +(19, 6, 2, 25.00), +(20, 2, 5, 10.00), +(20, 5, 3, 15.00); + +INSERT INTO purchaseOrder (supId, orderDate, status) +VALUES +(1, '2025-01-15', 'Delivered'), +(2, '2025-01-20', 'Pending'), +(3, '2025-02-01', 'Delivered'), +(4, '2025-02-10', 'In Transit'), +(1, '2025-02-15', 'Pending'); + +INSERT INTO activityLog (employeeId, activity) +VALUES +(1, 'Created new sale'), +(2, 'Booked appointment'), +(3, 'Completed grooming service'), +(4, 'Processed inventory order'), +(5, 'Conducted health checkup'), +(1, 'Updated customer information'); diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 00000000..35ee87ca --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,201 @@ +-- 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, + 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 +); + +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, + 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 +); + +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, + 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 (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, + role VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +);