Move backend to root directory

This commit is contained in:
2026-03-04 17:34:43 -07:00
parent 3f82142d3a
commit aeb8002b2b
112 changed files with 181 additions and 338 deletions

View File

@@ -1,11 +0,0 @@
package com.petshop.backend;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}

View File

@@ -1,55 +0,0 @@
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.service.AdoptionService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/adoptions")
@RequiredArgsConstructor
public class AdoptionController {
private final AdoptionService adoptionService;
@GetMapping
public ResponseEntity<Page<AdoptionResponse>> getAllAdoptions(Pageable pageable) {
return ResponseEntity.ok(adoptionService.getAllAdoptions(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<AdoptionResponse> getAdoptionById(@PathVariable Long id) {
return ResponseEntity.ok(adoptionService.getAdoptionById(id));
}
@PostMapping
public ResponseEntity<AdoptionResponse> createAdoption(@Valid @RequestBody AdoptionRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request));
}
@PutMapping("/{id}")
public ResponseEntity<AdoptionResponse> updateAdoption(
@PathVariable Long id,
@Valid @RequestBody AdoptionRequest request) {
return ResponseEntity.ok(adoptionService.updateAdoption(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteAdoption(@PathVariable Long id) {
adoptionService.deleteAdoption(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteAdoptions(@Valid @RequestBody BulkDeleteRequest request) {
adoptionService.bulkDeleteAdoptions(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,55 +0,0 @@
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.service.AppointmentService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/appointments")
@RequiredArgsConstructor
public class AppointmentController {
private final AppointmentService appointmentService;
@GetMapping
public ResponseEntity<Page<AppointmentResponse>> getAllAppointments(Pageable pageable) {
return ResponseEntity.ok(appointmentService.getAllAppointments(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<AppointmentResponse> getAppointmentById(@PathVariable Long id) {
return ResponseEntity.ok(appointmentService.getAppointmentById(id));
}
@PostMapping
public ResponseEntity<AppointmentResponse> createAppointment(@Valid @RequestBody AppointmentRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(appointmentService.createAppointment(request));
}
@PutMapping("/{id}")
public ResponseEntity<AppointmentResponse> updateAppointment(
@PathVariable Long id,
@Valid @RequestBody AppointmentRequest request) {
return ResponseEntity.ok(appointmentService.updateAppointment(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteAppointment(@PathVariable Long id) {
appointmentService.deleteAppointment(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteAppointments(@Valid @RequestBody BulkDeleteRequest request) {
appointmentService.bulkDeleteAppointments(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,89 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.auth.LoginRequest;
import com.petshop.backend.dto.auth.LoginResponse;
import com.petshop.backend.dto.auth.UserInfoResponse;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import com.petshop.backend.security.JwtUtil;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {
private final AuthenticationManager authenticationManager;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
User user = userRepository.findByUsername(request.getUsername())
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
UserDetails userDetails = new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
java.util.Collections.emptyList()
);
String token = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new LoginResponse(
token,
user.getUsername(),
user.getFullName(),
user.getRole().name()
));
} catch (BadCredentialsException e) {
Map<String, String> error = new HashMap<>();
error.put("message", "Invalid username or password");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(error);
}
}
@GetMapping("/me")
public ResponseEntity<UserInfoResponse> getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String username = authentication.getName();
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return ResponseEntity.ok(new UserInfoResponse(
user.getId(),
user.getUsername(),
user.getFullName(),
user.getEmail(),
user.getRole().name()
));
}
@PostMapping("/logout")
public ResponseEntity<?> logout() {
Map<String, String> response = new HashMap<>();
response.put("message", "Logged out successfully");
return ResponseEntity.ok(response);
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.category.CategoryRequest;
import com.petshop.backend.dto.category.CategoryResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.service.CategoryService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/categories")
@RequiredArgsConstructor
public class CategoryController {
private final CategoryService categoryService;
@GetMapping
public ResponseEntity<Page<CategoryResponse>> getAllCategories(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(categoryService.getAllCategories(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<CategoryResponse> getCategoryById(@PathVariable Long id) {
return ResponseEntity.ok(categoryService.getCategoryById(id));
}
@PostMapping
public ResponseEntity<CategoryResponse> createCategory(@Valid @RequestBody CategoryRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(categoryService.createCategory(request));
}
@PutMapping("/{id}")
public ResponseEntity<CategoryResponse> updateCategory(
@PathVariable Long id,
@Valid @RequestBody CategoryRequest request) {
return ResponseEntity.ok(categoryService.updateCategory(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCategory(@PathVariable Long id) {
categoryService.deleteCategory(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteCategories(@Valid @RequestBody BulkDeleteRequest request) {
categoryService.bulkDeleteCategories(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,57 +0,0 @@
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 jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/customers")
@RequiredArgsConstructor
public class CustomerController {
private final CustomerService customerService;
@GetMapping
public ResponseEntity<Page<CustomerResponse>> getAllCustomers(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(customerService.getAllCustomers(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<CustomerResponse> getCustomerById(@PathVariable Long id) {
return ResponseEntity.ok(customerService.getCustomerById(id));
}
@PostMapping
public ResponseEntity<CustomerResponse> createCustomer(@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(customerService.createCustomer(request));
}
@PutMapping("/{id}")
public ResponseEntity<CustomerResponse> updateCustomer(
@PathVariable Long id,
@Valid @RequestBody CustomerRequest request) {
return ResponseEntity.ok(customerService.updateCustomer(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) {
customerService.bulkDeleteCustomers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,91 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.DropdownOption;
import com.petshop.backend.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/dropdowns")
@RequiredArgsConstructor
public class DropdownController {
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
private final ServiceRepository serviceRepository;
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
private final StoreRepository storeRepository;
private final SupplierRepository supplierRepository;
@GetMapping("/pets")
public ResponseEntity<List<DropdownOption>> getPets() {
return ResponseEntity.ok(
petRepository.findAll().stream()
.map(p -> new DropdownOption(p.getId(), p.getPetName()))
.collect(Collectors.toList())
);
}
@GetMapping("/customers")
public ResponseEntity<List<DropdownOption>> getCustomers() {
return ResponseEntity.ok(
customerRepository.findAll().stream()
.map(c -> new DropdownOption(c.getId(), c.getCustomerName()))
.collect(Collectors.toList())
);
}
@GetMapping("/services")
public ResponseEntity<List<DropdownOption>> getServices() {
return ResponseEntity.ok(
serviceRepository.findAll().stream()
.map(s -> new DropdownOption(s.getId(), s.getServiceName()))
.collect(Collectors.toList())
);
}
@GetMapping("/products")
public ResponseEntity<List<DropdownOption>> getProducts() {
return ResponseEntity.ok(
productRepository.findAll().stream()
.map(p -> new DropdownOption(p.getId(), p.getProductName()))
.collect(Collectors.toList())
);
}
@GetMapping("/categories")
public ResponseEntity<List<DropdownOption>> getCategories() {
return ResponseEntity.ok(
categoryRepository.findAll().stream()
.map(c -> new DropdownOption(c.getId(), c.getCategoryName()))
.collect(Collectors.toList())
);
}
@GetMapping("/stores")
public ResponseEntity<List<DropdownOption>> getStores() {
return ResponseEntity.ok(
storeRepository.findAll().stream()
.map(s -> new DropdownOption(s.getId(), s.getStoreName()))
.collect(Collectors.toList())
);
}
@GetMapping("/suppliers")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<List<DropdownOption>> getSuppliers() {
return ResponseEntity.ok(
supplierRepository.findAll().stream()
.map(s -> new DropdownOption(s.getId(), s.getSupplierName()))
.collect(Collectors.toList())
);
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.inventory.InventoryRequest;
import com.petshop.backend.dto.inventory.InventoryResponse;
import com.petshop.backend.service.InventoryService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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/inventory")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class InventoryController {
private final InventoryService inventoryService;
@GetMapping
public ResponseEntity<Page<InventoryResponse>> getAllInventory(Pageable pageable) {
return ResponseEntity.ok(inventoryService.getAllInventory(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<InventoryResponse> getInventoryById(@PathVariable Long id) {
return ResponseEntity.ok(inventoryService.getInventoryById(id));
}
@PostMapping
public ResponseEntity<InventoryResponse> createInventory(@Valid @RequestBody InventoryRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(inventoryService.createInventory(request));
}
@PutMapping("/{id}")
public ResponseEntity<InventoryResponse> updateInventory(
@PathVariable Long id,
@Valid @RequestBody InventoryRequest request) {
return ResponseEntity.ok(inventoryService.updateInventory(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteInventory(@PathVariable Long id) {
inventoryService.deleteInventory(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteInventory(@Valid @RequestBody BulkDeleteRequest request) {
inventoryService.bulkDeleteInventory(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
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.service.PetService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/pets")
@RequiredArgsConstructor
public class PetController {
private final PetService petService;
@GetMapping
public ResponseEntity<Page<PetResponse>> getAllPets(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(petService.getAllPets(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<PetResponse> getPetById(@PathVariable Long id) {
return ResponseEntity.ok(petService.getPetById(id));
}
@PostMapping
public ResponseEntity<PetResponse> createPet(@Valid @RequestBody PetRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(petService.createPet(request));
}
@PutMapping("/{id}")
public ResponseEntity<PetResponse> updatePet(
@PathVariable Long id,
@Valid @RequestBody PetRequest request) {
return ResponseEntity.ok(petService.updatePet(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePet(@PathVariable Long id) {
petService.deletePet(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeletePets(@Valid @RequestBody BulkDeleteRequest request) {
petService.bulkDeletePets(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.product.ProductRequest;
import com.petshop.backend.dto.product.ProductResponse;
import com.petshop.backend.service.ProductService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping
public ResponseEntity<Page<ProductResponse>> getAllProducts(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(productService.getAllProducts(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<ProductResponse> getProductById(@PathVariable Long id) {
return ResponseEntity.ok(productService.getProductById(id));
}
@PostMapping
public ResponseEntity<ProductResponse> createProduct(@Valid @RequestBody ProductRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(productService.createProduct(request));
}
@PutMapping("/{id}")
public ResponseEntity<ProductResponse> updateProduct(
@PathVariable Long id,
@Valid @RequestBody ProductRequest request) {
return ResponseEntity.ok(productService.updateProduct(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteProducts(@Valid @RequestBody BulkDeleteRequest request) {
productService.bulkDeleteProducts(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,62 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.productsupplier.BulkDeleteProductSupplierRequest;
import com.petshop.backend.dto.productsupplier.ProductSupplierRequest;
import com.petshop.backend.dto.productsupplier.ProductSupplierResponse;
import com.petshop.backend.service.ProductSupplierService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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/product-suppliers")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class ProductSupplierController {
private final ProductSupplierService productSupplierService;
@GetMapping
public ResponseEntity<Page<ProductSupplierResponse>> getAllProductSuppliers(Pageable pageable) {
return ResponseEntity.ok(productSupplierService.getAllProductSuppliers(pageable));
}
@GetMapping("/{productId}/{supplierId}")
public ResponseEntity<ProductSupplierResponse> getProductSupplierById(
@PathVariable Long productId,
@PathVariable Long supplierId) {
return ResponseEntity.ok(productSupplierService.getProductSupplierById(productId, supplierId));
}
@PostMapping
public ResponseEntity<ProductSupplierResponse> createProductSupplier(@Valid @RequestBody ProductSupplierRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(productSupplierService.createProductSupplier(request));
}
@PutMapping("/{productId}/{supplierId}")
public ResponseEntity<ProductSupplierResponse> updateProductSupplier(
@PathVariable Long productId,
@PathVariable Long supplierId,
@Valid @RequestBody ProductSupplierRequest request) {
return ResponseEntity.ok(productSupplierService.updateProductSupplier(productId, supplierId, request));
}
@DeleteMapping("/{productId}/{supplierId}")
public ResponseEntity<Void> deleteProductSupplier(
@PathVariable Long productId,
@PathVariable Long supplierId) {
productSupplierService.deleteProductSupplier(productId, supplierId);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteProductSuppliers(@Valid @RequestBody BulkDeleteProductSupplierRequest request) {
productSupplierService.bulkDeleteProductSuppliers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,29 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.purchaseorder.PurchaseOrderResponse;
import com.petshop.backend.service.PurchaseOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/purchase-orders")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class PurchaseOrderController {
private final PurchaseOrderService purchaseOrderService;
@GetMapping
public ResponseEntity<Page<PurchaseOrderResponse>> getAllPurchaseOrders(Pageable pageable) {
return ResponseEntity.ok(purchaseOrderService.getAllPurchaseOrders(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<PurchaseOrderResponse> getPurchaseOrderById(@PathVariable Long id) {
return ResponseEntity.ok(purchaseOrderService.getPurchaseOrderById(id));
}
}

View File

@@ -1,25 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.refund.RefundRequest;
import com.petshop.backend.dto.refund.RefundResponse;
import com.petshop.backend.service.RefundService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/sales")
@RequiredArgsConstructor
public class RefundController {
private final RefundService refundService;
@PostMapping("/{saleId}/refunds")
public ResponseEntity<RefundResponse> createRefund(
@PathVariable Long saleId,
@Valid @RequestBody RefundRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(refundService.createRefund(saleId, request));
}
}

View File

@@ -1,35 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.sale.SaleRequest;
import com.petshop.backend.dto.sale.SaleResponse;
import com.petshop.backend.service.SaleService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/sales")
@RequiredArgsConstructor
public class SaleController {
private final SaleService saleService;
@GetMapping
public ResponseEntity<Page<SaleResponse>> getAllSales(Pageable pageable) {
return ResponseEntity.ok(saleService.getAllSales(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<SaleResponse> getSaleById(@PathVariable Long id) {
return ResponseEntity.ok(saleService.getSaleById(id));
}
@PostMapping
public ResponseEntity<SaleResponse> createSale(@Valid @RequestBody SaleRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(saleService.createSale(request));
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.service.ServiceRequest;
import com.petshop.backend.dto.service.ServiceResponse;
import com.petshop.backend.service.ServiceService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/services")
@RequiredArgsConstructor
public class ServiceController {
private final ServiceService serviceService;
@GetMapping
public ResponseEntity<Page<ServiceResponse>> getAllServices(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(serviceService.getAllServices(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<ServiceResponse> getServiceById(@PathVariable Long id) {
return ResponseEntity.ok(serviceService.getServiceById(id));
}
@PostMapping
public ResponseEntity<ServiceResponse> createService(@Valid @RequestBody ServiceRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(serviceService.createService(request));
}
@PutMapping("/{id}")
public ResponseEntity<ServiceResponse> updateService(
@PathVariable Long id,
@Valid @RequestBody ServiceRequest request) {
return ResponseEntity.ok(serviceService.updateService(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteService(@PathVariable Long id) {
serviceService.deleteService(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteServices(@Valid @RequestBody BulkDeleteRequest request) {
serviceService.bulkDeleteServices(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,59 +0,0 @@
package com.petshop.backend.controller;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.supplier.SupplierRequest;
import com.petshop.backend.dto.supplier.SupplierResponse;
import com.petshop.backend.service.SupplierService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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/suppliers")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class SupplierController {
private final SupplierService supplierService;
@GetMapping
public ResponseEntity<Page<SupplierResponse>> getAllSuppliers(
@RequestParam(required = false) String q,
Pageable pageable) {
return ResponseEntity.ok(supplierService.getAllSuppliers(q, pageable));
}
@GetMapping("/{id}")
public ResponseEntity<SupplierResponse> getSupplierById(@PathVariable Long id) {
return ResponseEntity.ok(supplierService.getSupplierById(id));
}
@PostMapping
public ResponseEntity<SupplierResponse> createSupplier(@Valid @RequestBody SupplierRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(supplierService.createSupplier(request));
}
@PutMapping("/{id}")
public ResponseEntity<SupplierResponse> updateSupplier(
@PathVariable Long id,
@Valid @RequestBody SupplierRequest request) {
return ResponseEntity.ok(supplierService.updateSupplier(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteSupplier(@PathVariable Long id) {
supplierService.deleteSupplier(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteSuppliers(@Valid @RequestBody BulkDeleteRequest request) {
supplierService.bulkDeleteSuppliers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.controller;
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.service.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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/users")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<UserResponse>> getAllUsers(Pageable pageable) {
return ResponseEntity.ok(userService.getAllUsers(pageable));
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody UserRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(request));
}
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.updateUser(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/bulk-delete")
public ResponseEntity<Void> bulkDeleteUsers(@Valid @RequestBody BulkDeleteRequest request) {
userService.bulkDeleteUsers(request);
return ResponseEntity.noContent().build();
}
}

View File

@@ -1,26 +0,0 @@
package com.petshop.backend.dto.adoption;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class AdoptionRequest {
@NotNull(message = "Pet ID is required")
private Long petId;
@NotNull(message = "Customer ID is required")
private Long customerId;
@NotNull(message = "Adoption date is required")
private LocalDate adoptionDate;
@NotNull(message = "Adoption fee is required")
@Positive(message = "Adoption fee must be positive")
private BigDecimal adoptionFee;
private String notes;
}

View File

@@ -1,25 +0,0 @@
package com.petshop.backend.dto.adoption;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AdoptionResponse {
private Long id;
private Long petId;
private String petName;
private Long customerId;
private String customerName;
private LocalDate adoptionDate;
private BigDecimal adoptionFee;
private String notes;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,57 +0,0 @@
package com.petshop.backend.dto.analytics;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DashboardResponse {
private SalesSummary salesSummary;
private InventorySummary inventorySummary;
private List<TopProduct> topProducts;
private List<DailySales> dailySales;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class SalesSummary {
private BigDecimal totalRevenue;
private Long totalSales;
private BigDecimal totalRefunds;
private Long totalRefundCount;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class InventorySummary {
private Long totalProducts;
private Long lowStockProducts;
private Long outOfStockProducts;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class TopProduct {
private Long productId;
private String productName;
private Long quantitySold;
private BigDecimal revenue;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class DailySales {
private String date;
private BigDecimal revenue;
private Long salesCount;
}

View File

@@ -1,33 +0,0 @@
package com.petshop.backend.dto.appointment;
import com.petshop.backend.entity.Appointment;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
@Data
public class AppointmentRequest {
@NotNull(message = "Customer ID is required")
private Long customerId;
@NotNull(message = "Service ID is required")
private Long serviceId;
@NotNull(message = "Appointment date is required")
private LocalDate appointmentDate;
@NotNull(message = "Appointment time is required")
private LocalTime appointmentTime;
@NotNull(message = "Status is required")
private Appointment.AppointmentStatus status;
@NotEmpty(message = "At least one pet must be specified")
private List<Long> petIds;
private String notes;
}

View File

@@ -1,29 +0,0 @@
package com.petshop.backend.dto.appointment;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AppointmentResponse {
private Long id;
private Long customerId;
private String customerName;
private Long serviceId;
private String serviceName;
private LocalDate appointmentDate;
private LocalTime appointmentTime;
private String status;
private List<String> petNames;
private List<Long> petIds;
private String notes;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,13 +0,0 @@
package com.petshop.backend.dto.auth;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class LoginRequest {
@NotBlank(message = "Username is required")
private String username;
@NotBlank(message = "Password is required")
private String password;
}

View File

@@ -1,13 +0,0 @@
package com.petshop.backend.dto.auth;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class LoginResponse {
private String token;
private String username;
private String fullName;
private String role;
}

View File

@@ -1,14 +0,0 @@
package com.petshop.backend.dto.auth;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class UserInfoResponse {
private Long id;
private String username;
private String fullName;
private String email;
private String role;
}

View File

@@ -1,12 +0,0 @@
package com.petshop.backend.dto.category;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class CategoryRequest {
@NotBlank(message = "Category name is required")
private String categoryName;
private String categoryDescription;
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.dto.category;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CategoryResponse {
private Long id;
private String categoryName;
private String categoryDescription;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,12 +0,0 @@
package com.petshop.backend.dto.common;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class BulkDeleteRequest {
@NotEmpty(message = "IDs list cannot be empty")
private List<Long> ids;
}

View File

@@ -1,11 +0,0 @@
package com.petshop.backend.dto.common;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class DropdownOption {
private Long id;
private String label;
}

View File

@@ -1,17 +0,0 @@
package com.petshop.backend.dto.customer;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class CustomerRequest {
@NotBlank(message = "Customer name is required")
private String customerName;
@Email(message = "Invalid email format")
private String customerEmail;
private String customerPhone;
private String customerAddress;
}

View File

@@ -1,20 +0,0 @@
package com.petshop.backend.dto.customer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerResponse {
private Long id;
private String customerName;
private String customerEmail;
private String customerPhone;
private String customerAddress;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,21 +0,0 @@
package com.petshop.backend.dto.inventory;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Data;
@Data
public class InventoryRequest {
@NotNull(message = "Product ID is required")
private Long productId;
@NotNull(message = "Store ID is required")
private Long storeId;
@NotNull(message = "Quantity is required")
@PositiveOrZero(message = "Quantity must be zero or positive")
private Integer quantity;
@PositiveOrZero(message = "Reorder level must be zero or positive")
private Integer reorderLevel = 10;
}

View File

@@ -1,24 +0,0 @@
package com.petshop.backend.dto.inventory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InventoryResponse {
private Long id;
private Long productId;
private String productName;
private String categoryName;
private Long storeId;
private String storeName;
private Integer quantity;
private Integer reorderLevel;
private LocalDateTime lastRestocked;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,28 +0,0 @@
package com.petshop.backend.dto.pet;
import com.petshop.backend.entity.Pet;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class PetRequest {
@NotBlank(message = "Pet name is required")
private String petName;
@NotBlank(message = "Species is required")
private String petSpecies;
private String petBreed;
@Positive(message = "Age must be positive")
private Integer petAge;
@NotNull(message = "Status is required")
private Pet.PetStatus petStatus;
private BigDecimal petPrice;
}

View File

@@ -1,23 +0,0 @@
package com.petshop.backend.dto.pet;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PetResponse {
private Long id;
private String petName;
private String petSpecies;
private String petBreed;
private Integer petAge;
private String petStatus;
private BigDecimal petPrice;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,25 +0,0 @@
package com.petshop.backend.dto.product;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ProductRequest {
@NotBlank(message = "Product name is required")
private String productName;
@NotNull(message = "Category ID is required")
private Long categoryId;
private String productDescription;
@NotNull(message = "Product price is required")
@Positive(message = "Price must be positive")
private BigDecimal productPrice;
private Boolean active = true;
}

View File

@@ -1,23 +0,0 @@
package com.petshop.backend.dto.product;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductResponse {
private Long id;
private String productName;
private Long categoryId;
private String categoryName;
private String productDescription;
private BigDecimal productPrice;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,12 +0,0 @@
package com.petshop.backend.dto.productsupplier;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class BulkDeleteProductSupplierRequest {
@NotEmpty(message = "Keys list cannot be empty")
private List<ProductSupplierKey> keys;
}

View File

@@ -1,13 +0,0 @@
package com.petshop.backend.dto.productsupplier;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
public class ProductSupplierKey {
private Long productId;
private Long supplierId;
}

View File

@@ -1,26 +0,0 @@
package com.petshop.backend.dto.productsupplier;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ProductSupplierRequest {
@NotNull(message = "Product ID is required")
private Long productId;
@NotNull(message = "Supplier ID is required")
private Long supplierId;
@NotNull(message = "Cost price is required")
@Positive(message = "Cost price must be positive")
private BigDecimal costPrice;
@PositiveOrZero(message = "Lead time must be zero or positive")
private Integer leadTimeDays;
private Boolean isPreferred = false;
}

View File

@@ -1,23 +0,0 @@
package com.petshop.backend.dto.productsupplier;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ProductSupplierResponse {
private Long productId;
private String productName;
private Long supplierId;
private String supplierName;
private BigDecimal costPrice;
private Integer leadTimeDays;
private Boolean isPreferred;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,39 +0,0 @@
package com.petshop.backend.dto.purchaseorder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PurchaseOrderResponse {
private Long id;
private Long supplierId;
private String supplierName;
private LocalDate orderDate;
private LocalDate expectedDelivery;
private String status;
private BigDecimal totalAmount;
private String notes;
private List<PurchaseOrderItemResponse> items;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class PurchaseOrderItemResponse {
private Long id;
private Long productId;
private String productName;
private Integer quantity;
private BigDecimal unitCost;
private BigDecimal subtotal;
}
}

View File

@@ -1,15 +0,0 @@
package com.petshop.backend.dto.refund;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
@Data
public class RefundItemRequest {
@NotNull(message = "Sale item ID is required")
private Long saleItemId;
@NotNull(message = "Quantity is required")
@Positive(message = "Quantity must be positive")
private Integer quantity;
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.dto.refund;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.util.List;
@Data
public class RefundRequest {
@NotEmpty(message = "At least one item is required")
@Valid
private List<RefundItemRequest> items;
private String refundReason;
}

View File

@@ -1,36 +0,0 @@
package com.petshop.backend.dto.refund;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RefundResponse {
private Long id;
private Long saleId;
private LocalDateTime refundDate;
private BigDecimal refundAmount;
private String refundReason;
private Long processedBy;
private String processedByName;
private List<RefundItemResponse> items;
private LocalDateTime createdAt;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class RefundItemResponse {
private Long id;
private Long saleItemId;
private Long productId;
private String productName;
private Integer quantity;
private BigDecimal refundAmount;
}
}

View File

@@ -1,15 +0,0 @@
package com.petshop.backend.dto.sale;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
@Data
public class SaleItemRequest {
@NotNull(message = "Product ID is required")
private Long productId;
@NotNull(message = "Quantity is required")
@Positive(message = "Quantity must be positive")
private Integer quantity;
}

View File

@@ -1,28 +0,0 @@
package com.petshop.backend.dto.sale;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class SaleRequest {
private Long customerId;
@NotNull(message = "Store ID is required")
private Long storeId;
private String paymentMethod;
private BigDecimal tax = BigDecimal.ZERO;
@NotEmpty(message = "At least one item is required")
@Valid
private List<SaleItemRequest> items;
private String notes;
}

View File

@@ -1,42 +0,0 @@
package com.petshop.backend.dto.sale;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SaleResponse {
private Long id;
private LocalDateTime saleDate;
private Long employeeId;
private String employeeName;
private Long customerId;
private String customerName;
private Long storeId;
private String storeName;
private BigDecimal subtotal;
private BigDecimal tax;
private BigDecimal total;
private String paymentMethod;
private String notes;
private List<SaleItemResponse> items;
private LocalDateTime createdAt;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class SaleItemResponse {
private Long id;
private Long productId;
private String productName;
private Integer quantity;
private BigDecimal unitPrice;
private BigDecimal subtotal;
}
}

View File

@@ -1,25 +0,0 @@
package com.petshop.backend.dto.service;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ServiceRequest {
@NotBlank(message = "Service name is required")
private String serviceName;
private String serviceDescription;
@NotNull(message = "Service price is required")
@Positive(message = "Price must be positive")
private BigDecimal servicePrice;
@Positive(message = "Duration must be positive")
private Integer serviceDurationMinutes;
private Boolean active = true;
}

View File

@@ -1,22 +0,0 @@
package com.petshop.backend.dto.service;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServiceResponse {
private Long id;
private String serviceName;
private String serviceDescription;
private BigDecimal servicePrice;
private Integer serviceDurationMinutes;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,20 +0,0 @@
package com.petshop.backend.dto.supplier;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class SupplierRequest {
@NotBlank(message = "Supplier name is required")
private String supplierName;
private String supplierContact;
@Email(message = "Invalid email format")
private String supplierEmail;
private String supplierPhone;
private String supplierAddress;
private Boolean active = true;
}

View File

@@ -1,22 +0,0 @@
package com.petshop.backend.dto.supplier;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SupplierResponse {
private Long id;
private String supplierName;
private String supplierContact;
private String supplierEmail;
private String supplierPhone;
private String supplierAddress;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,29 +0,0 @@
package com.petshop.backend.dto.user;
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;
import lombok.Data;
@Data
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")
private String fullName;
@Email(message = "Invalid email format")
private String email;
@NotNull(message = "Role is required")
private User.Role role;
private Boolean active = true;
}

View File

@@ -1,21 +0,0 @@
package com.petshop.backend.dto.user;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserResponse {
private Long id;
private String username;
private String fullName;
private String email;
private String role;
private Boolean active;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

View File

@@ -1,49 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Entity
@Table(name = "adoptions")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Adoption {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "pet_id", nullable = false)
private Pet pet;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@Column(name = "adoption_date", nullable = false)
private LocalDate adoptionDate;
@Column(name = "adoption_fee", nullable = false, precision = 10, scale = 2)
private BigDecimal adoptionFee;
@Column(columnDefinition = "TEXT")
private String notes;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,67 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "appointments")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Appointment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
@ManyToOne
@JoinColumn(name = "service_id", nullable = false)
private Service service;
@Column(name = "appointment_date", nullable = false)
private LocalDate appointmentDate;
@Column(name = "appointment_time", nullable = false)
private LocalTime appointmentTime;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private AppointmentStatus status = AppointmentStatus.Scheduled;
@Column(columnDefinition = "TEXT")
private String notes;
@ManyToMany
@JoinTable(
name = "appointment_pets",
joinColumns = @JoinColumn(name = "appointment_id"),
inverseJoinColumns = @JoinColumn(name = "pet_id")
)
private Set<Pet> pets = new HashSet<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public enum AppointmentStatus {
Scheduled, Completed, Cancelled
}
}

View File

@@ -1,36 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "categories")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "category_name", nullable = false, unique = true, length = 100)
private String categoryName;
@Column(name = "category_description", columnDefinition = "TEXT")
private String categoryDescription;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,42 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "customers")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "customer_name", nullable = false, length = 100)
private String customerName;
@Column(name = "customer_email", length = 100)
private String customerEmail;
@Column(name = "customer_phone", length = 20)
private String customerPhone;
@Column(name = "customer_address", columnDefinition = "TEXT")
private String customerAddress;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,49 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "inventory", uniqueConstraints = {
@UniqueConstraint(name = "unique_product_store", columnNames = {"product_id", "store_id"})
})
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Inventory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@ManyToOne
@JoinColumn(name = "store_id", nullable = false)
private Store store;
@Column(nullable = false)
private Integer quantity = 0;
@Column(name = "reorder_level")
private Integer reorderLevel = 10;
@Column(name = "last_restocked")
private LocalDateTime lastRestocked;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,54 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "pets")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pet {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "pet_name", nullable = false, length = 100)
private String petName;
@Column(name = "pet_species", nullable = false, length = 50)
private String petSpecies;
@Column(name = "pet_breed", length = 50)
private String petBreed;
@Column(name = "pet_age")
private Integer petAge;
@Enumerated(EnumType.STRING)
@Column(name = "pet_status", nullable = false)
private PetStatus petStatus = PetStatus.Available;
@Column(name = "pet_price", precision = 10, scale = 2)
private BigDecimal petPrice;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public enum PetStatus {
Available, Adopted, Under_Care
}
}

View File

@@ -1,47 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_name", nullable = false, length = 100)
private String productName;
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private Category category;
@Column(name = "product_description", columnDefinition = "TEXT")
private String productDescription;
@Column(name = "product_price", nullable = false, precision = 10, scale = 2)
private BigDecimal productPrice;
@Column(nullable = false)
private Boolean active = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,56 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "product_suppliers")
@Data
@NoArgsConstructor
@AllArgsConstructor
@IdClass(ProductSupplier.ProductSupplierId.class)
public class ProductSupplier {
@Id
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@Id
@ManyToOne
@JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
@Column(name = "cost_price", nullable = false, precision = 10, scale = 2)
private BigDecimal costPrice;
@Column(name = "lead_time_days")
private Integer leadTimeDays;
@Column(name = "is_preferred")
private Boolean isPreferred = false;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ProductSupplierId implements Serializable {
private Long product;
private Long supplier;
}
}

View File

@@ -1,61 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
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;
@Entity
@Table(name = "purchase_orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PurchaseOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
@Column(name = "order_date", nullable = false)
private LocalDate orderDate;
@Column(name = "expected_delivery")
private LocalDate expectedDelivery;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OrderStatus status = OrderStatus.Pending;
@Column(name = "total_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal totalAmount;
@Column(columnDefinition = "TEXT")
private String notes;
@OneToMany(mappedBy = "purchaseOrder", cascade = CascadeType.ALL)
private List<PurchaseOrderItem> items = new ArrayList<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public enum OrderStatus {
Pending, Delivered, Cancelled
}
}

View File

@@ -1,37 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Entity
@Table(name = "purchase_order_items")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PurchaseOrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "purchase_order_id", nullable = false)
private PurchaseOrder purchaseOrder;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@Column(nullable = false)
private Integer quantity;
@Column(name = "unit_cost", nullable = false, precision = 10, scale = 2)
private BigDecimal unitCost;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal subtotal;
}

View File

@@ -1,48 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "refunds")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Refund {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "sale_id", nullable = false)
private Sale sale;
@Column(name = "refund_date", nullable = false)
private LocalDateTime refundDate = LocalDateTime.now();
@Column(name = "refund_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal refundAmount;
@Column(name = "refund_reason", columnDefinition = "TEXT")
private String refundReason;
@ManyToOne
@JoinColumn(name = "processed_by", nullable = false)
private User processedBy;
@OneToMany(mappedBy = "refund", cascade = CascadeType.ALL)
private List<RefundItem> items = new ArrayList<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
}

View File

@@ -1,34 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Entity
@Table(name = "refund_items")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RefundItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "refund_id", nullable = false)
private Refund refund;
@ManyToOne
@JoinColumn(name = "sale_item_id", nullable = false)
private SaleItem saleItem;
@Column(nullable = false)
private Integer quantity;
@Column(name = "refund_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal refundAmount;
}

View File

@@ -1,61 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "sales")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Sale {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "sale_date", nullable = false)
private LocalDateTime saleDate = LocalDateTime.now();
@ManyToOne
@JoinColumn(name = "employee_id", nullable = false)
private User employee;
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
@ManyToOne
@JoinColumn(name = "store_id")
private Store store;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal subtotal;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal tax = BigDecimal.ZERO;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal total;
@Column(name = "payment_method", length = 50)
private String paymentMethod;
@Column(columnDefinition = "TEXT")
private String notes;
@OneToMany(mappedBy = "sale", cascade = CascadeType.ALL)
private List<SaleItem> items = new ArrayList<>();
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
}

View File

@@ -1,37 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Entity
@Table(name = "sale_items")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SaleItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "sale_id", nullable = false)
private Sale sale;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
@Column(nullable = false)
private Integer quantity;
@Column(name = "unit_price", nullable = false, precision = 10, scale = 2)
private BigDecimal unitPrice;
@Column(nullable = false, precision = 10, scale = 2)
private BigDecimal subtotal;
}

View File

@@ -1,46 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Table(name = "services")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Service {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "service_name", nullable = false, length = 100)
private String serviceName;
@Column(name = "service_description", columnDefinition = "TEXT")
private String serviceDescription;
@Column(name = "service_price", nullable = false, precision = 10, scale = 2)
private BigDecimal servicePrice;
@Column(name = "service_duration_minutes")
private Integer serviceDurationMinutes;
@Column(nullable = false)
private Boolean active = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,31 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "stores")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "store_name", nullable = false, length = 100)
private String storeName;
@Column(name = "store_location", length = 200)
private String storeLocation;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
}

View File

@@ -1,48 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "suppliers")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Supplier {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "supplier_name", nullable = false, length = 100)
private String supplierName;
@Column(name = "supplier_contact", length = 100)
private String supplierContact;
@Column(name = "supplier_email", length = 100)
private String supplierEmail;
@Column(name = "supplier_phone", length = 20)
private String supplierPhone;
@Column(name = "supplier_address", columnDefinition = "TEXT")
private String supplierAddress;
@Column(nullable = false)
private Boolean active = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@@ -1,53 +0,0 @@
package com.petshop.backend.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false)
private String password;
@Column(name = "full_name", nullable = false, length = 100)
private String fullName;
@Column(length = 100)
private String email;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
@Column(nullable = false)
private Boolean active = true;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
public enum Role {
STAFF, ADMIN
}
}

View File

@@ -1,7 +0,0 @@
package com.petshop.backend.exception;
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}

View File

@@ -1,65 +0,0 @@
package com.petshop.backend.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
Map<String, Object> response = new HashMap<>();
response.put("status", HttpStatus.BAD_REQUEST.value());
response.put("errors", errors);
response.put("timestamp", LocalDateTime.now());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"An unexpected error occurred: " + ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
record ErrorResponse(int status, String message, LocalDateTime timestamp) {}

View File

@@ -1,7 +0,0 @@
package com.petshop.backend.exception;
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Adoption;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AdoptionRepository extends JpaRepository<Adoption, Long> {
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Appointment;
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.time.LocalDate;
import java.time.LocalTime;
import java.util.List;
@Repository
public interface AppointmentRepository extends JpaRepository<Appointment, Long> {
@Query("SELECT a FROM Appointment a WHERE a.appointmentDate = :date AND a.appointmentTime = :time")
List<Appointment> findByDateAndTime(@Param("date") LocalDate date, @Param("time") LocalTime time);
}

View File

@@ -1,22 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Category;
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.Optional;
@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
Optional<Category> findByCategoryName(String categoryName);
@Query("SELECT c FROM Category c WHERE " +
"LOWER(c.categoryName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.categoryDescription) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Category> searchCategories(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,19 +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;
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
@Query("SELECT c FROM Customer c WHERE " +
"LOWER(c.customerName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.customerEmail) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(c.customerPhone) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Customer> searchCustomers(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,16 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Inventory;
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.Optional;
@Repository
public interface InventoryRepository extends JpaRepository<Inventory, Long> {
@Query("SELECT i FROM Inventory i WHERE i.product.id = :productId AND i.store.id = :storeId")
Optional<Inventory> findByProductIdAndStoreId(@Param("productId") Long productId, @Param("storeId") Long storeId);
}

View File

@@ -1,19 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Pet;
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;
@Repository
public interface PetRepository extends JpaRepository<Pet, Long> {
@Query("SELECT p FROM Pet p WHERE " +
"LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.petBreed) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Pet> searchPets(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Product;
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;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("SELECT p FROM Product p WHERE " +
"LOWER(p.productName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(p.productDescription) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Product> searchProducts(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.ProductSupplier;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductSupplierRepository extends JpaRepository<ProductSupplier, ProductSupplier.ProductSupplierId> {
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.PurchaseOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PurchaseOrderRepository extends JpaRepository<PurchaseOrder, Long> {
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Refund;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RefundRepository extends JpaRepository<Refund, Long> {
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.SaleItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SaleItemRepository extends JpaRepository<SaleItem, Long> {
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Sale;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SaleRepository extends JpaRepository<Sale, Long> {
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Service;
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;
@Repository
public interface ServiceRepository extends JpaRepository<Service, Long> {
@Query("SELECT s FROM Service s WHERE " +
"LOWER(s.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(s.serviceDescription) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Service> searchServices(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,9 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Store;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StoreRepository extends JpaRepository<Store, Long> {
}

View File

@@ -1,18 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.Supplier;
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;
@Repository
public interface SupplierRepository extends JpaRepository<Supplier, Long> {
@Query("SELECT s FROM Supplier s WHERE " +
"LOWER(s.supplierName) LIKE LOWER(CONCAT('%', :q, '%')) OR " +
"LOWER(s.supplierContact) LIKE LOWER(CONCAT('%', :q, '%'))")
Page<Supplier> searchSuppliers(@Param("q") String query, Pageable pageable);
}

View File

@@ -1,13 +0,0 @@
package com.petshop.backend.repository;
import com.petshop.backend.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
}

View File

@@ -1,58 +0,0 @@
package com.petshop.backend.security;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
final String jwt;
final String username;
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
jwt = authHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
filterChain.doFilter(request, response);
}
}

View File

@@ -1,74 +0,0 @@
package com.petshop.backend.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser()
.verifyWith(getSigningKey())
.build()
.parseSignedClaims(token)
.getPayload();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder()
.claims(claims)
.subject(subject)
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}

View File

@@ -1,71 +0,0 @@
package com.petshop.backend.security;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
private final UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/auth/login").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")
.requestMatchers("/api/v1/suppliers/**").hasRole("ADMIN")
.requestMatchers("/api/v1/product-suppliers/**").hasRole("ADMIN")
.requestMatchers("/api/v1/purchase-orders/**").hasRole("ADMIN")
.requestMatchers("/api/v1/users/**").hasRole("ADMIN")
.requestMatchers("/api/v1/analytics/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -1,35 +0,0 @@
package com.petshop.backend.security;
import com.petshop.backend.entity.User;
import com.petshop.backend.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Collections;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
if (!user.getActive()) {
throw new UsernameNotFoundException("User is inactive: " + username);
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + user.getRole().name()))
);
}
}

View File

@@ -1,104 +0,0 @@
package com.petshop.backend.service;
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.Pet;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AdoptionRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.PetRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class AdoptionService {
private final AdoptionRepository adoptionRepository;
private final PetRepository petRepository;
private final CustomerRepository customerRepository;
public Page<AdoptionResponse> getAllAdoptions(Pageable pageable) {
return adoptionRepository.findAll(pageable).map(this::mapToResponse);
}
public AdoptionResponse getAdoptionById(Long id) {
Adoption adoption = adoptionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id));
return mapToResponse(adoption);
}
@Transactional
public AdoptionResponse createAdoption(AdoptionRequest request) {
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
Adoption adoption = new Adoption();
adoption.setPet(pet);
adoption.setCustomer(customer);
adoption.setAdoptionDate(request.getAdoptionDate());
adoption.setAdoptionFee(request.getAdoptionFee());
adoption.setNotes(request.getNotes());
adoption = adoptionRepository.save(adoption);
return mapToResponse(adoption);
}
@Transactional
public AdoptionResponse updateAdoption(Long id, AdoptionRequest request) {
Adoption adoption = adoptionRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id));
Pet pet = petRepository.findById(request.getPetId())
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId()));
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
adoption.setPet(pet);
adoption.setCustomer(customer);
adoption.setAdoptionDate(request.getAdoptionDate());
adoption.setAdoptionFee(request.getAdoptionFee());
adoption.setNotes(request.getNotes());
adoption = adoptionRepository.save(adoption);
return mapToResponse(adoption);
}
@Transactional
public void deleteAdoption(Long id) {
if (!adoptionRepository.existsById(id)) {
throw new ResourceNotFoundException("Adoption not found with id: " + id);
}
adoptionRepository.deleteById(id);
}
@Transactional
public void bulkDeleteAdoptions(BulkDeleteRequest request) {
adoptionRepository.deleteAllById(request.getIds());
}
private AdoptionResponse mapToResponse(Adoption adoption) {
return new AdoptionResponse(
adoption.getId(),
adoption.getPet().getId(),
adoption.getPet().getPetName(),
adoption.getCustomer().getId(),
adoption.getCustomer().getCustomerName(),
adoption.getAdoptionDate(),
adoption.getAdoptionFee(),
adoption.getNotes(),
adoption.getCreatedAt(),
adoption.getUpdatedAt()
);
}
}

View File

@@ -1,140 +0,0 @@
package com.petshop.backend.service;
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.Pet;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.AppointmentRepository;
import com.petshop.backend.repository.CustomerRepository;
import com.petshop.backend.repository.PetRepository;
import com.petshop.backend.repository.ServiceRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class AppointmentService {
private final AppointmentRepository appointmentRepository;
private final CustomerRepository customerRepository;
private final ServiceRepository serviceRepository;
private final PetRepository petRepository;
public Page<AppointmentResponse> getAllAppointments(Pageable pageable) {
return appointmentRepository.findAll(pageable).map(this::mapToResponse);
}
public AppointmentResponse getAppointmentById(Long id) {
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
return mapToResponse(appointment);
}
@Transactional
public AppointmentResponse createAppointment(AppointmentRequest request) {
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
Set<Pet> pets = fetchPets(request.getPetIds());
Appointment appointment = new Appointment();
appointment.setCustomer(customer);
appointment.setService(service);
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setStatus(request.getStatus());
appointment.setPets(pets);
appointment.setNotes(request.getNotes());
appointment = appointmentRepository.save(appointment);
return mapToResponse(appointment);
}
@Transactional
public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) {
Appointment appointment = appointmentRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id));
Customer customer = customerRepository.findById(request.getCustomerId())
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId()));
com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId())
.orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId()));
Set<Pet> pets = fetchPets(request.getPetIds());
appointment.setCustomer(customer);
appointment.setService(service);
appointment.setAppointmentDate(request.getAppointmentDate());
appointment.setAppointmentTime(request.getAppointmentTime());
appointment.setStatus(request.getStatus());
appointment.setPets(pets);
appointment.setNotes(request.getNotes());
appointment = appointmentRepository.save(appointment);
return mapToResponse(appointment);
}
@Transactional
public void deleteAppointment(Long id) {
if (!appointmentRepository.existsById(id)) {
throw new ResourceNotFoundException("Appointment not found with id: " + id);
}
appointmentRepository.deleteById(id);
}
@Transactional
public void bulkDeleteAppointments(BulkDeleteRequest request) {
appointmentRepository.deleteAllById(request.getIds());
}
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 AppointmentResponse mapToResponse(Appointment appointment) {
List<String> petNames = appointment.getPets().stream()
.map(Pet::getPetName)
.collect(Collectors.toList());
List<Long> petIds = appointment.getPets().stream()
.map(Pet::getId)
.collect(Collectors.toList());
return new AppointmentResponse(
appointment.getId(),
appointment.getCustomer().getId(),
appointment.getCustomer().getCustomerName(),
appointment.getService().getId(),
appointment.getService().getServiceName(),
appointment.getAppointmentDate(),
appointment.getAppointmentTime(),
appointment.getStatus() != null ? appointment.getStatus().toString() : null,
petNames,
petIds,
appointment.getNotes(),
appointment.getCreatedAt(),
appointment.getUpdatedAt()
);
}
}

View File

@@ -1,81 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.category.CategoryRequest;
import com.petshop.backend.dto.category.CategoryResponse;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.entity.Category;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CategoryRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class CategoryService {
private final CategoryRepository categoryRepository;
public Page<CategoryResponse> getAllCategories(String query, Pageable pageable) {
Page<Category> categories;
if (query != null && !query.trim().isEmpty()) {
categories = categoryRepository.searchCategories(query, pageable);
} else {
categories = categoryRepository.findAll(pageable);
}
return categories.map(this::mapToResponse);
}
public CategoryResponse getCategoryById(Long id) {
Category category = categoryRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + id));
return mapToResponse(category);
}
@Transactional
public CategoryResponse createCategory(CategoryRequest request) {
Category category = new Category();
category.setCategoryName(request.getCategoryName());
category.setCategoryDescription(request.getCategoryDescription());
category = categoryRepository.save(category);
return mapToResponse(category);
}
@Transactional
public CategoryResponse updateCategory(Long id, CategoryRequest request) {
Category category = categoryRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + id));
category.setCategoryName(request.getCategoryName());
category.setCategoryDescription(request.getCategoryDescription());
category = categoryRepository.save(category);
return mapToResponse(category);
}
@Transactional
public void deleteCategory(Long id) {
if (!categoryRepository.existsById(id)) {
throw new ResourceNotFoundException("Category not found with id: " + id);
}
categoryRepository.deleteById(id);
}
@Transactional
public void bulkDeleteCategories(BulkDeleteRequest request) {
categoryRepository.deleteAllById(request.getIds());
}
private CategoryResponse mapToResponse(Category category) {
return new CategoryResponse(
category.getId(),
category.getCategoryName(),
category.getCategoryDescription(),
category.getCreatedAt(),
category.getUpdatedAt()
);
}
}

View File

@@ -1,87 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.common.BulkDeleteRequest;
import com.petshop.backend.dto.customer.CustomerRequest;
import com.petshop.backend.dto.customer.CustomerResponse;
import com.petshop.backend.entity.Customer;
import com.petshop.backend.exception.ResourceNotFoundException;
import com.petshop.backend.repository.CustomerRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class CustomerService {
private final CustomerRepository customerRepository;
public Page<CustomerResponse> getAllCustomers(String query, Pageable pageable) {
Page<Customer> customers;
if (query != null && !query.trim().isEmpty()) {
customers = customerRepository.searchCustomers(query, pageable);
} else {
customers = customerRepository.findAll(pageable);
}
return customers.map(this::mapToResponse);
}
public CustomerResponse getCustomerById(Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
return mapToResponse(customer);
}
@Transactional
public CustomerResponse createCustomer(CustomerRequest request) {
Customer customer = new Customer();
customer.setCustomerName(request.getCustomerName());
customer.setCustomerEmail(request.getCustomerEmail());
customer.setCustomerPhone(request.getCustomerPhone());
customer.setCustomerAddress(request.getCustomerAddress());
customer = customerRepository.save(customer);
return mapToResponse(customer);
}
@Transactional
public CustomerResponse updateCustomer(Long id, CustomerRequest request) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id));
customer.setCustomerName(request.getCustomerName());
customer.setCustomerEmail(request.getCustomerEmail());
customer.setCustomerPhone(request.getCustomerPhone());
customer.setCustomerAddress(request.getCustomerAddress());
customer = customerRepository.save(customer);
return mapToResponse(customer);
}
@Transactional
public void deleteCustomer(Long id) {
if (!customerRepository.existsById(id)) {
throw new ResourceNotFoundException("Customer not found with id: " + id);
}
customerRepository.deleteById(id);
}
@Transactional
public void bulkDeleteCustomers(BulkDeleteRequest request) {
customerRepository.deleteAllById(request.getIds());
}
private CustomerResponse mapToResponse(Customer customer) {
return new CustomerResponse(
customer.getId(),
customer.getCustomerName(),
customer.getCustomerEmail(),
customer.getCustomerPhone(),
customer.getCustomerAddress(),
customer.getCreatedAt(),
customer.getUpdatedAt()
);
}
}

View File

@@ -1,107 +0,0 @@
package com.petshop.backend.service;
import com.petshop.backend.dto.common.BulkDeleteRequest;
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.Store;
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 lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
public class InventoryService {
private final InventoryRepository inventoryRepository;
private final ProductRepository productRepository;
private final StoreRepository storeRepository;
public Page<InventoryResponse> getAllInventory(Pageable pageable) {
return inventoryRepository.findAll(pageable).map(this::mapToResponse);
}
public InventoryResponse getInventoryById(Long id) {
Inventory inventory = inventoryRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found with id: " + id));
return mapToResponse(inventory);
}
@Transactional
public InventoryResponse createInventory(InventoryRequest request) {
Product product = productRepository.findById(request.getProductId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + request.getProductId()));
Store store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
Inventory inventory = new Inventory();
inventory.setProduct(product);
inventory.setStore(store);
inventory.setQuantity(request.getQuantity());
inventory.setReorderLevel(request.getReorderLevel());
inventory.setLastRestocked(LocalDateTime.now());
inventory = inventoryRepository.save(inventory);
return mapToResponse(inventory);
}
@Transactional
public InventoryResponse updateInventory(Long id, InventoryRequest request) {
Inventory inventory = inventoryRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Inventory not found with id: " + id));
Product product = productRepository.findById(request.getProductId())
.orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + request.getProductId()));
Store store = storeRepository.findById(request.getStoreId())
.orElseThrow(() -> new ResourceNotFoundException("Store not found with id: " + request.getStoreId()));
inventory.setProduct(product);
inventory.setStore(store);
inventory.setQuantity(request.getQuantity());
inventory.setReorderLevel(request.getReorderLevel());
inventory.setLastRestocked(LocalDateTime.now());
inventory = inventoryRepository.save(inventory);
return mapToResponse(inventory);
}
@Transactional
public void deleteInventory(Long id) {
if (!inventoryRepository.existsById(id)) {
throw new ResourceNotFoundException("Inventory not found with id: " + id);
}
inventoryRepository.deleteById(id);
}
@Transactional
public void bulkDeleteInventory(BulkDeleteRequest request) {
inventoryRepository.deleteAllById(request.getIds());
}
private InventoryResponse mapToResponse(Inventory inventory) {
return new InventoryResponse(
inventory.getId(),
inventory.getProduct().getId(),
inventory.getProduct().getProductName(),
inventory.getProduct().getCategory().getCategoryName(),
inventory.getStore().getId(),
inventory.getStore().getStoreName(),
inventory.getQuantity(),
inventory.getReorderLevel(),
inventory.getLastRestocked(),
inventory.getCreatedAt(),
inventory.getUpdatedAt()
);
}
}

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