Fix database seeding, add security and complete missing endpoints

This commit is contained in:
2026-03-08 09:11:26 -06:00
parent 62ad3881a2
commit 3a93fea34f
18 changed files with 620 additions and 1 deletions

View File

@@ -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": []
}
}
]
},

View File

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

View File

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

View File

@@ -88,6 +88,7 @@ public class AuthController {
public ResponseEntity<?> logout() {
Map<String, String> 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);
}
}

View File

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

View File

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

View File

@@ -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<Map<String, String>> healthCheck() {
return ResponseEntity.ok(Map.of("status", "UP"));
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<StoreResponse> getStoreById(@PathVariable Long id) {
return ResponseEntity.ok(storeService.getStoreById(id));
}
@PostMapping
public ResponseEntity<StoreResponse> createStore(@Valid @RequestBody StoreRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(storeService.createStore(request));
}
@PutMapping("/{id}")
public ResponseEntity<StoreResponse> updateStore(
@PathVariable Long id,
@Valid @RequestBody StoreRequest request) {
return ResponseEntity.ok(storeService.updateStore(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteStore(@PathVariable Long id) {
storeService.deleteStore(id);
return ResponseEntity.noContent().build();
}
@DeleteMapping
public ResponseEntity<Void> bulkDeleteStores(@Valid @RequestBody BulkDeleteRequest request) {
storeService.bulkDeleteStores(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -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 + '\'' +
'}';
}
}

View File

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

View File

@@ -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(),

View File

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

205
src/main/resources/data.sql Normal file
View File

@@ -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');

View File

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