From ea38bf0f0be8b9c2cd10d163e8122ab6c25caaf8 Mon Sep 17 00:00:00 2001 From: Dennis Eckerskorn Date: Thu, 29 May 2025 21:40:02 +0200 Subject: [PATCH] Added and revised javadoc --- docker/docker-compose.yml | 10 -- .../libs/memberflow-data-1.0-SNAPSHOT.jar | Bin 261798 -> 261831 bytes .../MemberFlowApplication.java | 3 + .../config/SecurityConfig.java | 26 ++++ .../config/SwaggerConfig.java | 21 ++- .../denniseckerskorn/config/WebConfig.java | 4 + .../controllers/AuthController.java | 17 +++ .../AssistanceController.java | 41 +++++- .../MembershipController.java | 35 +++++ .../TrainingGroupController.java | 67 +++++++++- .../TrainingSessionController.java | 26 ++++ .../finance_management/IVATypeController.java | 45 +++++++ .../finance_management/InvoiceController.java | 104 ++++++++++++++- .../InvoiceLineController.java | 44 ++++++- .../finance_management/PaymentController.java | 56 +++++++- .../ProductServiceController.java | 47 ++++++- .../AdminController.java | 60 ++++++++- .../NotificationController.java | 59 ++++++++- .../PermissionController.java | 53 +++++++- .../RoleController.java | 62 ++++++++- .../StudentController.java | 123 +++++++++++++++++- .../StudentHistoryController.java | 55 +++++++- .../TeacherController.java | 60 ++++++++- .../UserController.java | 79 ++++++++++- .../class_managment_dtos/AssistanceDTO.java | 1 - .../CreateInvoiceLineDTO.java | 4 +- .../CreateInvoiceRequestDTO.java | 4 - .../finance_management_dtos/IVATypeDTO.java | 6 +- .../finance_management_dtos/InvoiceDTO.java | 3 - .../InvoiceLineDTO.java | 6 +- .../finance_management_dtos/PaymentDTO.java | 4 - .../ProductServiceDTO.java | 2 - .../dtos/user_managment_dtos/AdminDTO.java | 2 - .../user_managment_dtos/NotificationDTO.java | 3 +- .../NotificationMiniDTO.java | 1 - .../dtos/user_managment_dtos/StudentDTO.java | 2 - .../StudentHistoryMiniDTO.java | 1 - .../user_managment_dtos/StudentMiniDTO.java | 5 +- .../StudentRegisterDTO.java | 40 ++++-- .../dtos/user_managment_dtos/TeacherDTO.java | 5 +- .../dtos/user_managment_dtos/UserDTO.java | 5 - .../exceptions/GlobalExceptionHandler.java | 4 + .../finance_service/InvoicePdfGenerator.java | 14 +- .../src/main/resources/Checklist.txt | 7 - .../config/HibernateConfig.java | 28 ++++ .../AssistanceRepository.java | 5 + .../MembershipRepository.java | 4 + .../TrainingGroupRepository.java | 4 + .../TrainingSessionRepository.java | 4 + .../IVATypeRepository.java | 4 + .../InvoiceLineRepository.java | 4 + .../InvoiceRepository.java | 4 + .../PaymentRepository.java | 4 + .../ProductServiceRepository.java | 4 + .../AdminRepository.java | 4 + .../NotificationRepository.java | 5 + .../PermissionRepository.java | 4 + .../RoleRepository.java | 4 + .../StudentHistoryRepository.java | 4 + .../StudentRepository.java | 4 + .../TeacherRepository.java | 4 + .../UserRepository.java | 5 + .../seeder/TestDataSeeder.java | 78 +++++++---- .../AssistanceService.java | 54 +++++++- .../TrainingGroupService.java | 95 ++++++++++++++ .../TrainingSessionService.java | 77 +++++++++++ .../finance_services/IVATypeService.java | 55 ++++++++ .../finance_services/InvoiceLineService.java | 45 ++++++- .../finance_services/InvoiceService.java | 115 +++++++++++++++- .../finance_services/PaymentService.java | 68 +++++++++- .../ProductServiceService.java | 50 ++++++- .../NotificationService.java | 8 ++ .../StudentService.java | 4 +- .../TeacherService.java | 1 + .../user_managment_services/UserService.java | 14 ++ 75 files changed, 1828 insertions(+), 151 deletions(-) delete mode 100644 memberflow-api/src/main/resources/Checklist.txt diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 39e24a2..adb9fe7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -45,13 +45,3 @@ services: - backend ports: - "3000:80" - - db-restore: - image: mysql:8.0 - depends_on: - mysql: - condition: service_healthy - volumes: - - ./backup.sql:/backup.sql - - ./docker/initdb.sh:/docker-entrypoint-initdb.d/initdb.sh - entrypoint: ["/bin/bash", "/docker-entrypoint-initdb.d/initdb.sh"] diff --git a/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar b/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar index dfe6bb17250612eab10a35cf78bd1cd34854114e..aa2c3a8d3e687262d88be656e759d85f4516f21d 100644 GIT binary patch delta 45072 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ë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 diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/MemberFlowApplication.java b/memberflow-api/src/main/java/com/denniseckerskorn/MemberFlowApplication.java index 6722e5d..9646420 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/MemberFlowApplication.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/MemberFlowApplication.java @@ -3,6 +3,9 @@ package com.denniseckerskorn; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * Main application class for the MemberFlow application. + */ @SpringBootApplication public class MemberFlowApplication { public static void main(String[] args) { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java b/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java index a7ce3ff..5563b1d 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java @@ -14,27 +14,53 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +/** + * Security configuration class for the application. + */ @EnableMethodSecurity @Configuration public class SecurityConfig { private final JwtAuthFilter jwtAuthFilter; private final CustomUserDetailsService customUserDetailsService; + /** + * Constructor for SecurityConfig. + * + * @param jwtAuthFilter the JWT authentication filter + * @param customUserDetailsService the custom user details service + */ public SecurityConfig(JwtAuthFilter jwtAuthFilter, CustomUserDetailsService customUserDetailsService) { this.jwtAuthFilter = jwtAuthFilter; this.customUserDetailsService = customUserDetailsService; } + /** + * Bean for PasswordEncoder. + * + * @return a BCryptPasswordEncoder instance + */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + /** + * Bean for CustomUserDetailsService. + * + * @return the custom user details service + */ @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } + /** + * Bean for SecurityFilterChain. + * + * @param http the HttpSecurity object + * @return a SecurityFilterChain instance + * @throws Exception if an error occurs during configuration + */ @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/config/SwaggerConfig.java b/memberflow-api/src/main/java/com/denniseckerskorn/config/SwaggerConfig.java index e15e52d..56e4a18 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/config/SwaggerConfig.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/config/SwaggerConfig.java @@ -8,9 +8,18 @@ import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Swagger configuration class for the MemberFlow API. + * This class sets up the OpenAPI documentation for the API, including security schemes and metadata. + */ @Configuration public class SwaggerConfig { + /** + * Configures the OpenAPI documentation for the MemberFlow API. + * + * @return an OpenAPI instance with the API information and security scheme. + */ @Bean public OpenAPI customOpenAPI() { final String securitySchemeName = "bearerAuth"; @@ -20,12 +29,12 @@ public class SwaggerConfig { .title("MemberFlow API") .version("1.0.0") .description(""" - Documentación de la API de MemberFlow. - **Usuarios de prueba:** - - Admin: admin@example.com / admin123 - - Teacher: teacher@example.com / 123456789 - - Student: student@example.com / 12345678 - """) + Documentación de la API de MemberFlow. + **Usuarios de prueba:** + - Admin: admin@example.com / admin123 + - Teacher: teacher@example.com / 123456789 + - Student: student@example.com / 12345678 + """) ) .addSecurityItem(new SecurityRequirement().addList(securitySchemeName)) .components(new Components() diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/config/WebConfig.java b/memberflow-api/src/main/java/com/denniseckerskorn/config/WebConfig.java index 7cbf3fd..1307d9d 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/config/WebConfig.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/config/WebConfig.java @@ -4,6 +4,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +/** + * Web configuration class for setting up CORS mappings. + * This class allows cross-origin requests from specified origins. + */ @Configuration public class WebConfig implements WebMvcConfigurer { @Override diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/AuthController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/AuthController.java index 3475e29..4a06d95 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/AuthController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/AuthController.java @@ -14,6 +14,10 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; +/** + * AuthController handles user authentication requests. + * It provides an endpoint for user login, which returns a JWT token upon successful authentication. + */ @RestController @RequestMapping("/api/v1/auth") @Tag(name = "Authentication", description = "Operations related to user authentication") @@ -23,12 +27,25 @@ public class AuthController { private final JwtUtil jwtUtil; private final UserService userService; + /** + * Constructor for AuthController. + * + * @param authenticationManager Authentication manager for handling authentication requests. + * @param jwtUtil Utility class for generating JWT tokens. + * @param userService Service for handling user-related operations. + */ public AuthController(AuthenticationManager authenticationManager, JwtUtil jwtUtil, UserService userService) { this.authenticationManager = authenticationManager; this.jwtUtil = jwtUtil; this.userService = userService; } + /** + * API response codes for the login endpoint. + * + * @param loginRequest The login request containing user credentials. + * @return ResponseEntity containing the JWT token or an error message. + */ @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successful login"), @ApiResponse(responseCode = "401", description = "Unauthorized"), diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/AssistanceController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/AssistanceController.java index 8a17be6..2a42e3a 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/AssistanceController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/AssistanceController.java @@ -17,6 +17,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * AssistanceController handles requests related to assistance management. + * It provides endpoints for creating, updating, retrieving, and deleting assistance records. + */ @RestController @RequestMapping("/api/v1/assistances") @Tag(name = "Assistance Management", description = "Operations related to assistance management") @@ -26,6 +30,13 @@ public class AssistanceController { private final StudentService studentService; private final TrainingSessionService trainingSessionService; + /** + * Constructor for AssistanceController. + * + * @param assistanceService Service for handling assistance records. + * @param studentService Service for handling student records. + * @param trainingSessionService Service for handling training session records. + */ public AssistanceController( AssistanceService assistanceService, StudentService studentService, @@ -36,6 +47,12 @@ public class AssistanceController { this.trainingSessionService = trainingSessionService; } + /** + * Creates a new assistance record. + * + * @param dto The AssistanceDTO containing the details of the assistance to be created. + * @return ResponseEntity containing the created AssistanceDTO. + */ @Operation(summary = "Create a new assistance record") @PostMapping("/create") public ResponseEntity create(@RequestBody AssistanceDTO dto) { @@ -52,7 +69,12 @@ public class AssistanceController { return ResponseEntity.ok(AssistanceDTO.fromEntity(saved)); } - + /** + * Updates an existing assistance record. + * + * @param dto The AssistanceDTO containing the updated details of the assistance. + * @return ResponseEntity containing the updated AssistanceDTO. + */ @Operation(summary = "Update an existing assistance record", description = "Update an existing assistance record") @PutMapping("/update") public ResponseEntity update(@RequestBody AssistanceDTO dto) { @@ -64,6 +86,11 @@ public class AssistanceController { return ResponseEntity.ok(AssistanceDTO.fromEntity(updated)); } + /** + * Retrieves all assistance records. + * + * @return ResponseEntity containing a list of AssistanceDTOs. + */ @Operation(summary = "Get all assistance records", description = "Retrieve a list of all assistance records") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -74,6 +101,12 @@ public class AssistanceController { return ResponseEntity.ok(list); } + /** + * Retrieves assistance records by student ID. + * + * @param id The ID of the student whose assistance records are to be retrieved. + * @return ResponseEntity containing a list of AssistanceDTOs for the specified student. + */ @Operation(summary = "Get assistance records by student ID", description = "Retrieve assistance records for a specific student") @GetMapping("/getById/{id}") public ResponseEntity getAssistanceById(@PathVariable Integer id) { @@ -81,6 +114,12 @@ public class AssistanceController { return ResponseEntity.ok(AssistanceDTO.fromEntity(assistance)); } + /** + * Deletes an assistance record by ID. + * + * @param id The ID of the assistance record to be deleted. + * @return ResponseEntity indicating the result of the deletion operation. + */ @Operation(summary = "Delete an assistance record by ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/MembershipController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/MembershipController.java index 6f287e8..075dfd3 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/MembershipController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/MembershipController.java @@ -24,10 +24,21 @@ public class MembershipController { private final MembershipService membershipService; + /** + * Constructor for MembershipController. + * + * @param membershipService Service for handling membership records. + */ public MembershipController(MembershipService membershipService) { this.membershipService = membershipService; } + /** + * Creates a new membership. + * + * @param membershipDTO The MembershipDTO containing the details of the membership to be created. + * @return ResponseEntity containing the created MembershipDTO. + */ @Operation(summary = "Create a new membership") @PostMapping("/create") public ResponseEntity createMembership(@RequestBody MembershipDTO membershipDTO) { @@ -36,6 +47,11 @@ public class MembershipController { return ResponseEntity.status(HttpStatus.CREATED).body(MembershipDTO.fromEntity(savedMembership)); } + /** + * Retrieves all memberships. + * + * @return ResponseEntity containing a list of MembershipDTOs. + */ @Operation(summary = "Get all memberships", description = "Retrieve a list of all memberships") @GetMapping("/getAll") public ResponseEntity> getAllMemberships() { @@ -49,6 +65,12 @@ public class MembershipController { return ResponseEntity.ok(dtos); } + /** + * Retrieves a membership by its ID. + * + * @param id The ID of the membership to retrieve. + * @return ResponseEntity containing the MembershipDTO if found, or 404 Not Found if not found. + */ @Operation(summary = "Get a membership by ID", description = "Retrieve a membership by its ID") @GetMapping("/getById/{id}") public ResponseEntity getMembershipById(@PathVariable Integer id) { @@ -56,6 +78,13 @@ public class MembershipController { return ResponseEntity.ok(MembershipDTO.fromEntity(membership)); } + /** + * Updates an existing membership. + * + * @param id The ID of the membership to update. + * @param dto The MembershipDTO containing the updated details of the membership. + * @return ResponseEntity containing the updated MembershipDTO. + */ @Operation(summary = "Update a membership", description = "Update an existing membership") @Transactional @PutMapping("/update/{id}") @@ -65,6 +94,12 @@ public class MembershipController { return ResponseEntity.ok(MembershipDTO.fromEntity(updated)); } + /** + * Deletes a membership by its ID. + * + * @param id The ID of the membership to delete. + * @return ResponseEntity indicating the result of the deletion operation. + */ @Operation(summary = "Delete a membership", description = "Delete a membership by its ID") @Transactional @DeleteMapping("/delete/{id}") diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingGroupController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingGroupController.java index 76dde1e..928140f 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingGroupController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingGroupController.java @@ -29,7 +29,6 @@ import java.util.stream.Collectors; * Controller for managing training groups. * Provides endpoints for creating, updating, retrieving, and deleting training groups. */ - @RestController @RequestMapping("/api/v1/training-groups") @Tag(name = "Training Group Management", description = "Operations related to training group management") @@ -40,7 +39,14 @@ public class TrainingGroupController { private final StudentService studentService; private final TrainingSessionService trainingSessionService; - + /** + * Constructor for TrainingGroupController. + * + * @param trainingGroupService Service for handling training group records. + * @param teacherService Service for handling teacher records. + * @param studentService Service for handling student records. + * @param trainingSessionService Service for handling training session records. + */ public TrainingGroupController(TrainingGroupService trainingGroupService, TeacherService teacherService, StudentService studentService, TrainingSessionService trainingSessionService) { this.trainingGroupService = trainingGroupService; this.teacherService = teacherService; @@ -48,6 +54,12 @@ public class TrainingGroupController { this.trainingSessionService = trainingSessionService; } + /** + * Creates a new training group with a teacher. + * + * @param dto The TrainingGroupDTO containing the details of the group to be created. + * @return ResponseEntity containing the created TrainingGroupDTO. + */ @Operation(summary = "Create a training group with a teacher") @PostMapping("/create") public ResponseEntity createGroup(@RequestBody TrainingGroupDTO dto) { @@ -68,7 +80,13 @@ public class TrainingGroupController { return ResponseEntity.status(HttpStatus.CREATED).body(new TrainingGroupDTO(createdGroup)); } - + /** + * Assigns a student to a training group. + * + * @param groupId The ID of the training group. + * @param studentId The ID of the student to be assigned. + * @return ResponseEntity indicating the result of the operation. + */ @Operation(summary = "Assign a student to a group") @PutMapping("/assign-student") public ResponseEntity assignStudent(@RequestParam Integer groupId, @RequestParam Integer studentId) { @@ -79,6 +97,13 @@ public class TrainingGroupController { return ResponseEntity.ok().build(); } + /** + * Removes a student from a training group. + * + * @param groupId The ID of the training group. + * @param studentId The ID of the student to be removed. + * @return ResponseEntity indicating the result of the operation. + */ @Operation(summary = "Remove a student from a group") @PutMapping("/remove-student") public ResponseEntity removeStudent(@RequestParam Integer groupId, @RequestParam Integer studentId) { @@ -89,7 +114,14 @@ public class TrainingGroupController { return ResponseEntity.ok().build(); } - @Operation(summary = "Actualizar un grupo de entrenamiento existente", description = "Actualiza un grupo de entrenamiento por su ID") + /** + * Updates an existing training group. + * + * @param id The ID of the training group to update. + * @param dto The TrainingGroupDTO containing the updated details of the group. + * @return ResponseEntity containing the updated TrainingGroupDTO. + */ + @Operation(summary = "Updates a training-group by its ID", description = "Update a training group with the specified ID") @Transactional @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody TrainingGroupDTO dto) { @@ -113,13 +145,23 @@ public class TrainingGroupController { return ResponseEntity.ok(new TrainingGroupDTO(updatedGroup)); } - + /** + * Finds a training group by its ID. + * + * @param id The ID of the training group to retrieve. + * @return ResponseEntity containing the TrainingGroupDTO if found, or 404 Not Found if not found. + */ @Operation(summary = "Find a training group by ID", description = "Retrieve a training group with the specified ID") @GetMapping("findById/{id}") public ResponseEntity findGroupById(@PathVariable Integer id) { return ResponseEntity.ok(new TrainingGroupDTO(trainingGroupService.findById(id))); } + /** + * Retrieves all training groups. + * + * @return ResponseEntity containing a list of TrainingGroupDTOs. + */ @Operation(summary = "Get all training groups", description = "Retrieve a list of all training groups") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -130,13 +172,19 @@ public class TrainingGroupController { ); } + /** + * Deletes a training group by its ID. + * + * @param id The ID of the training group to delete. + * @return ResponseEntity indicating the result of the deletion. + */ @Operation(summary = "Delete a training group by ID", description = "Delete a training group with the specified ID") @Transactional @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { TrainingGroup group = trainingGroupService.findById(id); if (group == null) { - throw new EntityNotFoundException("Group not found"); + throw new EntityNotFoundException("Training group not found with ID: " + id); } for (TrainingSession session : group.getTrainingSessions()) { @@ -162,6 +210,12 @@ public class TrainingGroupController { return ResponseEntity.noContent().build(); } + /** + * Generates recurring training sessions for a group. + * + * @param dto The TrainingGroupDTO containing the group ID and recurrence details. + * @return ResponseEntity containing the updated TrainingGroupDTO. + */ @Operation(summary = "Generate recurring training sessions for a group", description = "Generates recurring training sessions for a specified number of months") @Transactional @PostMapping("/generate-recurring-sessions") @@ -176,5 +230,4 @@ public class TrainingGroupController { return ResponseEntity.ok(new TrainingGroupDTO(group)); } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingSessionController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingSessionController.java index 7b34dd3..c43b8cf 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingSessionController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/class_management_controllers/TrainingSessionController.java @@ -16,6 +16,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing training sessions. + * Provides endpoints for creating, retrieving, and deleting training sessions. + */ @RestController @RequestMapping("/api/v1/training-sessions") @Tag(name = "Training Session Management", description = "Operations related to training session management") @@ -23,17 +27,34 @@ public class TrainingSessionController { private final TrainingSessionService trainingSessionService; private final TrainingGroupService trainingGroupService; + /** + * Constructor for TrainingSessionController. + * + * @param trainingSessionService Service for handling training session records. + * @param trainingGroupService Service for handling training group records. + */ public TrainingSessionController(TrainingSessionService trainingSessionService, TrainingGroupService trainingGroupService) { this.trainingSessionService = trainingSessionService; this.trainingGroupService = trainingGroupService; } + /** + * Finds a training session by its ID and returns it as a DTO. + * + * @param id The ID of the training session to find. + * @return TrainingSessionDTO containing the details of the found training session. + */ @Operation(summary = "Find a training session by ID", description = "Retrieve a training session by its ID") @GetMapping("findById/{id}") public TrainingSessionDTO findTrainingSessionById(@PathVariable Integer id) { return TrainingSessionDTO.fromEntity(trainingSessionService.findById(id)); } + /** + * Gets all training sessions and returns them as a list of DTOs. + * + * @return List of TrainingSessionDTOs containing all training sessions. + */ @Operation(summary = "Get all training sessions", description = "Retrieve a list of all training sessions") @GetMapping("/getAll") public List getAll() { @@ -43,6 +64,11 @@ public class TrainingSessionController { .collect(Collectors.toList()); } + /** + * Gets all training sessions by group ID and returns them as a list of DTOs. + * + * @param id The ID of the training group to find sessions for. + */ @Operation(summary = "Get all training sessions by group ID", description = "Retrieve a list of all training sessions by group ID") @DeleteMapping("delete/{id}") public void delete(@PathVariable Integer id) { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java index 116b397..cd3f531 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java @@ -16,6 +16,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing IVA types. + * Provides endpoints for creating, retrieving, updating, and deleting IVA types. + */ @RestController @RequestMapping("/api/v1/iva-types") @Tag(name = "IVA Types", description = "Operations related to IVA type management") @@ -23,10 +27,22 @@ public class IVATypeController { private final IVATypeService ivaTypeService; + /** + * Constructor for IVATypeController. + * + * @param ivaTypeService Service for handling IVA type records. + */ public IVATypeController(IVATypeService ivaTypeService) { this.ivaTypeService = ivaTypeService; } + /** + * Creates a new IVA type. + * + * @param dto The IVATypeDTO containing the details of the IVA type to be created. + * @return ResponseEntity containing the created IVATypeDTO. + * @throws DuplicateEntityException if an IVA type with the same name already exists. + */ @PostMapping("/create") @Operation(summary = "Create a new IVA type") public ResponseEntity create(@Valid @RequestBody IVATypeDTO dto) throws DuplicateEntityException { @@ -34,6 +50,14 @@ public class IVATypeController { return new ResponseEntity<>(new IVATypeDTO(saved), HttpStatus.CREATED); } + /** + * Updates an existing IVA type. + * + * @param dto The IVATypeDTO containing the updated details of the IVA type. + * @return ResponseEntity containing the updated IVATypeDTO. + * @throws EntityNotFoundException if the IVA type to update does not exist. + * @throws InvalidDataException if the provided data is invalid. + */ @PutMapping("/update") @Operation(summary = "Update an existing IVA type") public ResponseEntity update(@Valid @RequestBody IVATypeDTO dto) throws EntityNotFoundException, InvalidDataException { @@ -41,6 +65,14 @@ public class IVATypeController { return new ResponseEntity<>(new IVATypeDTO(updated), HttpStatus.OK); } + /** + * Retrieves an IVA type by its ID. + * + * @param id The ID of the IVA type to retrieve. + * @return ResponseEntity containing the IVATypeDTO if found. + * @throws EntityNotFoundException if the IVA type with the specified ID does not exist. + * @throws InvalidDataException if the provided ID is invalid. + */ @GetMapping("/getById/{id}") @Operation(summary = "Get IVA type by ID") public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -48,6 +80,11 @@ public class IVATypeController { return new ResponseEntity<>(new IVATypeDTO(entity), HttpStatus.OK); } + /** + * Retrieves all IVA types. + * + * @return ResponseEntity containing a list of IVATypeDTOs. + */ @GetMapping("/getAll") @Operation(summary = "Get all IVA types") public ResponseEntity> getAll() { @@ -56,6 +93,14 @@ public class IVATypeController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Deletes an IVA type by its ID. + * + * @param id The ID of the IVA type to delete. + * @return ResponseEntity with no content if deletion is successful. + * @throws EntityNotFoundException if the IVA type with the specified ID does not exist. + * @throws InvalidDataException if the IVA type is in use and cannot be deleted. + */ @DeleteMapping("/deleteById/{id}") @Operation(summary = "Delete IVA type by ID (if not in use)") public ResponseEntity delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java index bf2e8aa..6916479 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java @@ -27,6 +27,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing invoices. + * Provides endpoints for creating, retrieving, updating, and deleting invoices, + */ @RestController @RequestMapping("/api/v1/invoices") @Tag(name = "Invoices", description = "Operations related to invoice management") @@ -37,6 +41,14 @@ public class InvoiceController { private final ProductServiceService productServiceService; private final InvoicePdfGenerator invoicePdfGenerator; + /** + * Constructor for InvoiceController. + * + * @param invoiceService Service for handling invoice records. + * @param userService Service for handling user records. + * @param productServiceService Service for handling product/service records. + * @param invoicePdfGenerator Service for generating PDF invoices. + */ public InvoiceController(InvoiceService invoiceService, UserService userService, ProductServiceService productServiceService, InvoicePdfGenerator invoicePdfGenerator) { this.invoiceService = invoiceService; this.userService = userService; @@ -44,6 +56,14 @@ public class InvoiceController { this.invoicePdfGenerator = invoicePdfGenerator; } + /** + * Creates a new invoice with lines. + * + * @param dto The CreateInvoiceRequestDTO containing the details of the invoice and its lines. + * @return ResponseEntity containing the created InvoiceDTO. + * @throws EntityNotFoundException If the user or product/service is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @PostMapping("/createInvoiceWithLines") @Operation(summary = "Create a new invoice with lines") public ResponseEntity createInvoiceWithLines(@Valid @RequestBody CreateInvoiceRequestDTO dto) throws EntityNotFoundException, InvalidDataException { @@ -59,7 +79,7 @@ public class InvoiceController { for (CreateInvoiceLineDTO lineDTO : dto.getLines()) { ProductService product = productServiceService.findById(lineDTO.getProductServiceId()); if (product == null) { - throw new EntityNotFoundException("Product/Service not found"); + throw new EntityNotFoundException("Product/Service not found with ID: " + lineDTO.getProductServiceId()); } InvoiceLine line = lineDTO.toEntity(invoice, product); invoiceService.addLineToInvoiceById(invoice.getId(), line); @@ -69,6 +89,13 @@ public class InvoiceController { return new ResponseEntity<>(new InvoiceDTO(invoice), HttpStatus.CREATED); } + /** + * Generates a PDF for an invoice by its ID. + * + * @param id The ID of the invoice to generate the PDF for. + * @return ResponseEntity containing the PDF file as a byte array. + * @throws EntityNotFoundException If the invoice is not found. + */ @GetMapping("/generatePDFById/{id}") @Operation(summary = "Generate PDF for invoice by ID") public ResponseEntity downloadInvoicePdf(@PathVariable Integer id) throws EntityNotFoundException { @@ -82,7 +109,13 @@ public class InvoiceController { .body(pdf); } - + /** + * Creates a new invoice. + * + * @param dto The InvoiceDTO containing the details of the invoice to be created. + * @return ResponseEntity containing the created InvoiceDTO. + * @throws DuplicateEntityException If an invoice with the same details already exists. + */ @PostMapping("/create") @Operation(summary = "Create a new invoice") public ResponseEntity createInvoice(@Valid @RequestBody InvoiceDTO dto) throws DuplicateEntityException { @@ -90,6 +123,14 @@ public class InvoiceController { return new ResponseEntity<>(new InvoiceDTO(saved), HttpStatus.CREATED); } + /** + * Updates an existing invoice. + * + * @param dto The InvoiceDTO containing the updated details of the invoice. + * @return ResponseEntity containing the updated InvoiceDTO. + * @throws EntityNotFoundException If the invoice to update is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @PutMapping("/update") @Operation(summary = "Update an existing invoice") public ResponseEntity updateInvoice(@Valid @RequestBody InvoiceDTO dto) throws EntityNotFoundException, InvalidDataException { @@ -97,6 +138,14 @@ public class InvoiceController { return new ResponseEntity<>(new InvoiceDTO(updated), HttpStatus.OK); } + /** + * Retrieves an invoice by its ID. + * + * @param id The ID of the invoice to retrieve. + * @return ResponseEntity containing the InvoiceDTO if found, or 404 Not Found if not found. + * @throws EntityNotFoundException If the invoice is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @GetMapping("/getById/{id}") @Operation(summary = "Get invoice by ID") public ResponseEntity getInvoiceById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -104,6 +153,11 @@ public class InvoiceController { return new ResponseEntity<>(new InvoiceDTO(invoice), HttpStatus.OK); } + /** + * Retrieves all invoices. + * + * @return ResponseEntity containing a list of InvoiceDTOs. + */ @GetMapping("/getAll") @Operation(summary = "Get all invoices") public ResponseEntity> getAllInvoices() { @@ -112,6 +166,14 @@ public class InvoiceController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Deletes an invoice by its ID. + * + * @param id The ID of the invoice to delete. + * @return ResponseEntity with status NO_CONTENT if successful. + * @throws EntityNotFoundException If the invoice to delete is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @DeleteMapping("/deleteById/{id}") @Operation(summary = "Delete invoice by ID") public ResponseEntity deleteInvoice(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -119,6 +181,13 @@ public class InvoiceController { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + /** + * Retrieves all invoices associated with a specific user ID. + * + * @param userId The ID of the user whose invoices are to be retrieved. + * @return ResponseEntity containing a list of InvoiceDTOs for the specified user. + * @throws InvalidDataException If the provided data is invalid. + */ @GetMapping("/getAllInvoicesByUserId/{userId}") @Operation(summary = "Get all invoices by user ID") public ResponseEntity> getInvoicesByUserId(@PathVariable Integer userId) throws InvalidDataException { @@ -127,6 +196,13 @@ public class InvoiceController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Adds a line to an invoice by its ID. + * + * @param invoiceId The ID of the invoice to add the line to. + * @param line The InvoiceLine to be added to the invoice. + * @return ResponseEntity with status OK if successful. + */ @PostMapping("/addLinesByInvoiceId/{invoiceId}") @Operation(summary = "Add a line to an invoice by invoice ID") public ResponseEntity addLineToInvoice(@PathVariable Integer invoiceId, @RequestBody @Valid InvoiceLine line) { @@ -134,6 +210,14 @@ public class InvoiceController { return new ResponseEntity<>(HttpStatus.OK); } + /** + * Removes a line from an invoice by its ID. + * + * @param invoiceId The ID of the invoice to remove the line from. + * @param lineId The ID of the line to be removed. + * @return ResponseEntity with status NO_CONTENT if successful. + * @throws EntityNotFoundException If the invoice or line is not found. + */ @DeleteMapping("/removeLineFromInvoiceById/{invoiceId}") @Operation(summary = "Remove a line from an invoice by IDs") public ResponseEntity removeLineFromInvoice(@PathVariable Integer invoiceId, @PathVariable Integer lineId) { @@ -141,6 +225,14 @@ public class InvoiceController { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + /** + * Recalculates the total of an invoice by its ID. + * + * @param invoiceId The ID of the invoice to recalculate the total for. + * @return ResponseEntity with status OK if successful. + * @throws EntityNotFoundException If the invoice is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @PutMapping("/recalculateTotalOfInvoiceById/{invoiceId}") @Operation(summary = "Recalculate the total of an invoice") public ResponseEntity recalculateTotal(@PathVariable Integer invoiceId) throws EntityNotFoundException, InvalidDataException { @@ -149,6 +241,14 @@ public class InvoiceController { return new ResponseEntity<>(HttpStatus.OK); } + /** + * Clears all lines from an invoice by its ID. + * + * @param invoiceId The ID of the invoice to clear lines from. + * @return ResponseEntity with status NO_CONTENT if successful. + * @throws EntityNotFoundException If the invoice is not found. + * @throws InvalidDataException If the provided data is invalid. + */ @DeleteMapping("/clearAllLinesFromInvoiceById/{invoiceId}") @Operation(summary = "Clear all invoice lines from an invoice") public ResponseEntity clearInvoiceLines(@PathVariable Integer invoiceId) throws EntityNotFoundException, InvalidDataException { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java index 19965a5..8facc6b 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java @@ -17,7 +17,8 @@ import java.util.List; import java.util.stream.Collectors; /** - * InvoiceLineController handles operations related to invoice lines (items in invoices). + * Controller for managing invoice lines. + * Provides endpoints for creating, retrieving, updating, and deleting invoice lines. */ @RestController @RequestMapping("/api/v1/invoice-lines") @@ -26,10 +27,22 @@ public class InvoiceLineController { private final InvoiceLineService invoiceLineService; + /** + * Constructor for InvoiceLineController. + * + * @param invoiceLineService Service for handling invoice line records. + */ public InvoiceLineController(InvoiceLineService invoiceLineService) { this.invoiceLineService = invoiceLineService; } + /** + * Creates a new invoice line. + * + * @param dto The InvoiceLineDTO containing the details of the invoice line to be created. + * @return ResponseEntity containing the created InvoiceLineDTO. + * @throws DuplicateEntityException if an invoice line with the same details already exists. + */ @PostMapping("/create") @Operation(summary = "Create a new invoice line") public ResponseEntity create(@Valid @RequestBody InvoiceLineDTO dto) throws DuplicateEntityException { @@ -37,6 +50,14 @@ public class InvoiceLineController { return new ResponseEntity<>(new InvoiceLineDTO(saved), HttpStatus.CREATED); } + /** + * Updates an existing invoice line. + * + * @param dto The InvoiceLineDTO containing the updated details of the invoice line. + * @return ResponseEntity containing the updated InvoiceLineDTO. + * @throws InvalidDataException if the provided data is invalid. + * @throws EntityNotFoundException if the invoice line to update does not exist. + */ @PutMapping("/update") @Operation(summary = "Update an existing invoice line") public ResponseEntity update(@Valid @RequestBody InvoiceLineDTO dto) throws InvalidDataException, EntityNotFoundException { @@ -44,6 +65,14 @@ public class InvoiceLineController { return new ResponseEntity<>(new InvoiceLineDTO(updated), HttpStatus.OK); } + /** + * Retrieves an invoice line by its ID. + * + * @param id The ID of the invoice line to retrieve. + * @return ResponseEntity containing the InvoiceLineDTO of the found invoice line. + * @throws InvalidDataException if the provided ID is invalid. + * @throws EntityNotFoundException if the invoice line with the given ID does not exist. + */ @GetMapping("/getById/{id}") @Operation(summary = "Get invoice line by ID") public ResponseEntity getById(@PathVariable Integer id) throws InvalidDataException, EntityNotFoundException { @@ -51,6 +80,11 @@ public class InvoiceLineController { return new ResponseEntity<>(new InvoiceLineDTO(line), HttpStatus.OK); } + /** + * Retrieves all invoice lines. + * + * @return ResponseEntity containing a list of InvoiceLineDTOs representing all invoice lines. + */ @GetMapping("/getAll") @Operation(summary = "Get all invoice lines") public ResponseEntity> getAll() { @@ -59,6 +93,14 @@ public class InvoiceLineController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Deletes an invoice line by its ID. + * + * @param id The ID of the invoice line to delete. + * @return ResponseEntity with no content status. + * @throws InvalidDataException if the provided ID is invalid. + * @throws EntityNotFoundException if the invoice line with the given ID does not exist. + */ @DeleteMapping("/deleteById/{id}") @Operation(summary = "Delete invoice line by ID") public ResponseEntity delete(@PathVariable Integer id) throws InvalidDataException, EntityNotFoundException { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java index ff95ced..1c55385 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java @@ -17,6 +17,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * PaymentController handles requests related to payment management. + * It provides endpoints for creating, updating, retrieving, and deleting payments. + */ @RestController @RequestMapping("/api/v1/payments") @Tag(name = "Payments", description = "Operations related to payment management") @@ -24,10 +28,23 @@ public class PaymentController { private final PaymentService paymentService; + /** + * Constructor for PaymentController. + * + * @param paymentService Service for handling payment records. + */ public PaymentController(PaymentService paymentService) { this.paymentService = paymentService; } + /** + * Creates a new payment and marks the associated invoice as PAID. + * + * @param dto The PaymentDTO containing the details of the payment to be created. + * @return ResponseEntity containing the created PaymentDTO. + * @throws DuplicateEntityException if a payment already exists for the given invoice ID. + * @throws EntityNotFoundException if the invoice with the given ID does not exist. + */ @PostMapping("/create") @Operation(summary = "Create a new payment and mark invoice as PAID") public ResponseEntity create(@Valid @RequestBody PaymentDTO dto) @@ -46,7 +63,14 @@ public class PaymentController { return new ResponseEntity<>(new PaymentDTO(saved), HttpStatus.CREATED); } - + /** + * Updates an existing payment and adjusts the invoice status accordingly. + * + * @param dto The PaymentDTO containing the updated details of the payment. + * @return ResponseEntity containing the updated PaymentDTO. + * @throws EntityNotFoundException if the invoice with the given ID does not exist. + * @throws InvalidDataException if the provided data is invalid. + */ @PutMapping("/update") @Operation(summary = "Update an existing payment and adjust invoice status") public ResponseEntity update(@Valid @RequestBody PaymentDTO dto) @@ -61,11 +85,18 @@ public class PaymentController { if (updated == null) { throw new InvalidDataException("Payment update failed for invoice ID: " + dto.getInvoiceId()); } - + return new ResponseEntity<>(new PaymentDTO(updated), HttpStatus.OK); } - + /** + * Retrieves a payment by its ID. + * + * @param id The ID of the payment to retrieve. + * @return ResponseEntity containing the PaymentDTO if found. + * @throws EntityNotFoundException if the payment with the given ID does not exist. + * @throws InvalidDataException if the provided ID is invalid. + */ @GetMapping("/getById/{id}") @Operation(summary = "Get payment by ID") public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -73,6 +104,11 @@ public class PaymentController { return new ResponseEntity<>(new PaymentDTO(payment), HttpStatus.OK); } + /** + * Retrieves all payments. + * + * @return ResponseEntity containing a list of PaymentDTOs. + */ @GetMapping("/getAll") @Operation(summary = "Get all payments") public ResponseEntity> getAll() { @@ -81,6 +117,14 @@ public class PaymentController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Deletes a payment by its ID and sets the associated invoice status to NOT_PAID. + * + * @param id The ID of the payment to delete. + * @return ResponseEntity with no content status. + * @throws EntityNotFoundException if the payment with the given ID does not exist. + * @throws InvalidDataException if the provided ID is invalid. + */ @DeleteMapping("/deleteById/{id}") @Operation(summary = "Remove a payment and set invoice status to NOT_PAID") public ResponseEntity removePayment(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -88,6 +132,12 @@ public class PaymentController { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + /** + * Retrieves all payments made by a specific user. + * + * @param userId The ID of the user whose payments are to be retrieved. + * @return ResponseEntity containing a list of PaymentDTOs for the specified user. + */ @GetMapping("/getAllByUserId/{userId}") @Operation(summary = "Get all payments by user ID") public ResponseEntity> getAllByUserId(@PathVariable Integer userId) { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java index 1b5eb90..cbb5b00 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java @@ -18,6 +18,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing products and services. + * Provides endpoints for creating, updating, retrieving, and deleting products/services. + */ @RestController @RequestMapping("/api/v1/products-services") @Tag(name = "Product Services", description = "Operations related to product/service management") @@ -26,11 +30,24 @@ public class ProductServiceController { private final ProductServiceService productServiceService; private final IVATypeService ivaTypeService; + /** + * Constructor for ProductServiceController. + * + * @param productServiceService Service for handling product/service records. + * @param ivaTypeService Service for handling IVA type records. + */ public ProductServiceController(ProductServiceService productServiceService, IVATypeService ivaTypeService) { this.productServiceService = productServiceService; this.ivaTypeService = ivaTypeService; } + /** + * Creates a new product/service. + * + * @param dto The ProductServiceDTO containing the details of the product/service to be created. + * @return ResponseEntity containing the created ProductServiceDTO. + * @throws DuplicateEntityException if a product/service with the same name already exists. + */ @PostMapping("/create") @Operation(summary = "Create a new product/service") public ResponseEntity create(@Valid @RequestBody ProductServiceDTO dto) throws DuplicateEntityException { @@ -39,6 +56,14 @@ public class ProductServiceController { return new ResponseEntity<>(new ProductServiceDTO(saved), HttpStatus.CREATED); } + /** + * Updates an existing product/service. + * + * @param dto The ProductServiceDTO containing the updated details of the product/service. + * @return ResponseEntity containing the updated ProductServiceDTO. + * @throws EntityNotFoundException if the product/service to update does not exist. + * @throws InvalidDataException if the provided data is invalid. + */ @PutMapping("/update") @Operation(summary = "Update an existing product/service") public ResponseEntity update(@Valid @RequestBody ProductServiceDTO dto) throws EntityNotFoundException, InvalidDataException { @@ -47,7 +72,14 @@ public class ProductServiceController { return new ResponseEntity<>(new ProductServiceDTO(updated), HttpStatus.OK); } - + /** + * Retrieves a product/service by its ID. + * + * @param id The ID of the product/service to retrieve. + * @return ResponseEntity containing the ProductServiceDTO if found. + * @throws EntityNotFoundException if the product/service with the specified ID does not exist. + * @throws InvalidDataException if the provided ID is invalid. + */ @GetMapping("/getById/{id}") @Operation(summary = "Get product/service by ID") public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { @@ -55,6 +87,11 @@ public class ProductServiceController { return new ResponseEntity<>(new ProductServiceDTO(entity), HttpStatus.OK); } + /** + * Retrieves all products/services. + * + * @return ResponseEntity containing a list of ProductServiceDTOs. + */ @GetMapping("/getAll") @Operation(summary = "Get all products/services") public ResponseEntity> getAll() { @@ -63,6 +100,14 @@ public class ProductServiceController { return new ResponseEntity<>(dtos, HttpStatus.OK); } + /** + * Deletes a product/service by its ID. + * + * @param id The ID of the product/service to delete. + * @return ResponseEntity with no content if deletion is successful. + * @throws EntityNotFoundException if the product/service with the specified ID does not exist. + * @throws InvalidDataException if the provided ID is invalid. + */ @DeleteMapping("/deleteById/{id}") @Operation(summary = "Delete product/service by ID") public ResponseEntity delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java index 509ef80..ea01225 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java @@ -18,6 +18,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing administrators. + * Provides endpoints for creating, retrieving, updating, and deleting administrators. + */ @RestController @RequestMapping("/api/v1/admins") @Tag(name = "Administrator Management", description = "Operations related to administrator management") @@ -26,14 +30,28 @@ public class AdminController { private final AdminService adminService; private final RoleService roleService; + /** + * Password encoder for encoding passwords. + */ @Autowired private PasswordEncoder passwordEncoder; + /** + * Constructor for AdminController. + * + * @param adminService Service for handling administrator records. + * @param roleService Service for handling roles. + */ public AdminController(AdminService adminService, RoleService roleService) { this.adminService = adminService; this.roleService = roleService; } + /** + * Gets all administrators and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of AdminDTOs or no content if none found. + */ @Operation(summary = "Get all administrators") @GetMapping("/getAll") public ResponseEntity> getAllAdmins() { @@ -45,6 +63,12 @@ public class AdminController { return dtos.isEmpty() ? ResponseEntity.noContent().build() : ResponseEntity.ok(dtos); } + /** + * Gets administrator by ID and returns it as a DTO. + * + * @param id The ID of the administrator to find. + * @return ResponseEntity containing the AdminDTO or not found if the ID does not exist. + */ @Operation(summary = "Get administrator by ID") @GetMapping("/getById/{id}") public ResponseEntity getById(@PathVariable Integer id) { @@ -52,6 +76,12 @@ public class AdminController { return ResponseEntity.ok(convertToDTO(admin)); } + /** + * Creates a new administrator. + * + * @param dto The AdminDTO containing the details of the administrator to be created. + * @return ResponseEntity containing the created AdminDTO with status 201 Created. + */ @Operation(summary = "Create a new administrator") @PostMapping("/create") public ResponseEntity create(@RequestBody AdminDTO dto) { @@ -60,6 +90,13 @@ public class AdminController { return ResponseEntity.status(201).body(convertToDTO(saved)); } + /** + * Updates an existing administrator. + * + * @param id The ID of the administrator to update. + * @param dto The AdminDTO containing the updated details of the administrator. + * @return ResponseEntity containing the updated AdminDTO. + */ @Operation(summary = "Update an existing administrator") @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody AdminDTO dto) { @@ -69,6 +106,12 @@ public class AdminController { return ResponseEntity.ok(convertToDTO(updated)); } + /** + * Deletes an administrator by ID. + * + * @param id The ID of the administrator to delete. + * @return ResponseEntity with a message indicating successful deletion. + */ @Operation(summary = "Delete an administrator by ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { @@ -76,12 +119,26 @@ public class AdminController { return ResponseEntity.ok("Admin deleted"); } - // -------------------- Utils -------------------- + /* -------------------- Utils -------------------- */ + /** + * Converts an Admin entity to an AdminDTO. + * + * @param admin The Admin entity to convert. + * @return The converted AdminDTO. + */ private AdminDTO convertToDTO(Admin admin) { return AdminDTO.fromEntity(admin); } + /** + * Converts an AdminDTO to an Admin entity. + * + * @param dto The AdminDTO to convert. + * @param isCreate Indicates if the conversion is for creation (true) or update (false). + * @return The converted Admin entity. + * @throws InvalidDataException if the provided data is invalid. + */ private Admin convertToEntity(AdminDTO dto, boolean isCreate) { if (dto == null || dto.getUser() == null) { throw new InvalidDataException("Admin and User data are required"); @@ -118,5 +175,4 @@ public class AdminController { admin.setUser(user); return admin; } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/NotificationController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/NotificationController.java index 6468507..1503aba 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/NotificationController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/NotificationController.java @@ -15,6 +15,10 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +/** + * NotificationController handles requests related to notification management. + * It provides endpoints for creating, retrieving, updating, and deleting notifications. + */ @RestController @RequestMapping("/api/v1/notifications") @Tag(name = "Notification Management", description = "Operations related to notifications") @@ -23,11 +27,22 @@ public class NotificationController { private final NotificationService notificationService; private final UserService userService; + /** + * Constructor for NotificationController. + * + * @param notificationService Service for handling notification records. + * @param userService Service for handling user records. + */ public NotificationController(NotificationService notificationService, UserService userService) { this.notificationService = notificationService; this.userService = userService; } + /** + * Gets all notifications and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of NotificationDTOs or no content if none found. + */ @Operation(summary = "Get all notifications", description = "Retrieve a list of all notifications") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -38,6 +53,12 @@ public class NotificationController { return ResponseEntity.ok(dtos); } + /** + * Gets a notification by its ID and returns it as a DTO. + * + * @param id The ID of the notification to find. + * @return ResponseEntity containing the NotificationDTO or not found if it doesn't exist. + */ @Operation(summary = "Get a notification by ID", description = "Retrieve a notification by its ID") @GetMapping("/getById/{id}") public ResponseEntity getById(@PathVariable Integer id) { @@ -45,6 +66,12 @@ public class NotificationController { return ResponseEntity.ok(toDTO(notification)); } + /** + * Creates a new notification. + * + * @param dto The NotificationDTO containing the details of the notification to be created. + * @return ResponseEntity containing the created NotificationDTO. + */ @Operation(summary = "Create a new notification", description = "Create a new notification") @PostMapping("/create") public ResponseEntity create(@RequestBody NotificationDTO dto) { @@ -57,11 +84,16 @@ public class NotificationController { notificationService.addNotificationToUser(saved, user); } } - return ResponseEntity.status(201).body(toDTO(saved)); } - + /** + * Updates an existing notification by its ID. + * + * @param id The ID of the notification to update. + * @param dto The NotificationDTO containing the updated details of the notification. + * @return ResponseEntity containing the updated NotificationDTO. + */ @Operation(summary = "Update a notification by ID", description = "Update a notification by its ID") @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody NotificationDTO dto) { @@ -87,7 +119,12 @@ public class NotificationController { return ResponseEntity.ok(toDTO(updated)); } - + /** + * Deletes a notification by its ID. + * + * @param id The ID of the notification to delete. + * @return ResponseEntity with a message indicating successful deletion. + */ @Operation(summary = "Delete a notification by ID", description = "Delete a notification by its ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { @@ -95,8 +132,14 @@ public class NotificationController { return ResponseEntity.ok("Notification deleted successfully."); } - // -------------------- Mapping -------------------- + /* -------------------- Utils -------------------- */ + /** + * Converts a Notification entity to a NotificationDTO. + * + * @param notification The Notification entity to convert. + * @return The converted NotificationDTO. + */ private NotificationDTO toDTO(Notification notification) { Set userIds = notification.getUsers() != null ? notification.getUsers().stream().map(User::getId).collect(Collectors.toSet()) @@ -113,6 +156,13 @@ public class NotificationController { ); } + /** + * Converts a NotificationDTO to a Notification entity. + * + * @param dto The NotificationDTO to convert. + * @param includeUsers Whether to include users in the conversion. + * @return The converted Notification entity. + */ private Notification toEntity(NotificationDTO dto, boolean includeUsers) { Notification notification = (dto.getId() != null) ? notificationService.findById(dto.getId()) @@ -126,5 +176,4 @@ public class NotificationController { return notification; } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/PermissionController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/PermissionController.java index 9640ab6..f7e48c2 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/PermissionController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/PermissionController.java @@ -11,6 +11,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * PermissionController handles requests related to permission management. + * It provides endpoints for creating, retrieving, updating, and deleting permissions. + */ @RestController @RequestMapping("/api/v1/permissions") @Tag(name = "Permission Management", description = "Operations related to permissions") @@ -18,10 +22,20 @@ public class PermissionController { private final PermissionService permissionService; + /** + * Constructor for PermissionController. + * + * @param permissionService Service for handling permission records. + */ public PermissionController(PermissionService permissionService) { this.permissionService = permissionService; } + /** + * Retrieves all permissions and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of PermissionDTOs or no content if none found. + */ @Operation(summary = "Get all permissions", description = "Retrieve a list of all permissions") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -34,6 +48,12 @@ public class PermissionController { return ResponseEntity.ok(dtos); } + /** + * Retrieves a permission by its ID and returns it as a DTO. + * + * @param id The ID of the permission to find. + * @return ResponseEntity containing the PermissionDTO or not found if it doesn't exist. + */ @Operation(summary = "Get permission by ID", description = "Retrieve a permission by its ID") @GetMapping("/getById/{id}") public ResponseEntity getById(@PathVariable Integer id) { @@ -41,6 +61,12 @@ public class PermissionController { return ResponseEntity.ok(toDTO(permission)); } + /** + * Creates a new permission and returns the created permission as a DTO. + * + * @param dto The PermissionDTO containing the details of the permission to be created. + * @return ResponseEntity containing the created PermissionDTO. + */ @Operation(summary = "Create a new permission", description = "Create a new permission") @PostMapping("/create") public ResponseEntity create(@RequestBody PermissionDTO dto) { @@ -49,6 +75,13 @@ public class PermissionController { return ResponseEntity.status(201).body(toDTO(saved)); } + /** + * Updates an existing permission and returns the updated permission as a DTO. + * + * @param id The ID of the permission to update. + * @param dto The PermissionDTO containing the updated details of the permission. + * @return ResponseEntity containing the updated PermissionDTO. + */ @Operation(summary = "Update an existing permission", description = "Update an existing permission") @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody PermissionDTO dto) { @@ -57,6 +90,12 @@ public class PermissionController { return ResponseEntity.ok(toDTO(updated)); } + /** + * Deletes a permission by its ID. + * + * @param id The ID of the permission to delete. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Delete a permission", description = "Delete a permission by its ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { @@ -64,12 +103,24 @@ public class PermissionController { return ResponseEntity.ok("Permission deleted successfully."); } - // -------------------- Mapping -------------------- + /* -------------------- Utils -------------------- */ + /** + * Converts a Permission entity to a PermissionDTO. + * + * @param permission The Permission entity to convert. + * @return The converted PermissionDTO. + */ private PermissionDTO toDTO(Permission permission) { return new PermissionDTO(permission.getId(), permission.getPermissionName()); } + /** + * Converts a PermissionDTO to a Permission entity. + * + * @param dto The PermissionDTO to convert. + * @return The converted Permission entity. + */ private Permission toEntity(PermissionDTO dto) { Permission permission = (dto.getId() != null) ? permissionService.findById(dto.getId()) diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/RoleController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/RoleController.java index ffe3857..cd3565f 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/RoleController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/RoleController.java @@ -14,6 +14,10 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +/** + * RoleController handles requests related to role management. + * It provides endpoints for creating, retrieving, updating, and deleting roles. + */ @RestController @RequestMapping("/api/v1/roles") @Tag(name = "Role Management", description = "Operations related to roles") @@ -22,11 +26,22 @@ public class RoleController { private final RoleService roleService; private final PermissionService permissionService; + /** + * Constructor for RoleController. + * + * @param roleService Service for handling role records. + * @param permissionService Service for handling permission records. + */ public RoleController(RoleService roleService, PermissionService permissionService) { this.roleService = roleService; this.permissionService = permissionService; } + /** + * Retrieves all roles and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of RoleDTOs or no content if none found. + */ @Operation(summary = "Get all roles", description = "Retrieve a list of all roles") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -37,6 +52,12 @@ public class RoleController { return ResponseEntity.ok(dtos); } + /** + * Retrieves a role by its ID and returns it as a DTO. + * + * @param id The ID of the role to find. + * @return ResponseEntity containing the RoleDTO or not found if it doesn't exist. + */ @Operation(summary = "Get role by ID", description = "Retrieve a role by its ID") @GetMapping("/getById/{id}") public ResponseEntity getById(@PathVariable Integer id) { @@ -44,6 +65,12 @@ public class RoleController { return ResponseEntity.ok(toDTO(role)); } + /** + * Creates a new role and returns the created role as a DTO. + * + * @param dto The RoleDTO containing the details of the role to be created. + * @return ResponseEntity containing the created RoleDTO. + */ @Operation(summary = "Create a new role", description = "Create a new role") @PostMapping("/create") public ResponseEntity create(@RequestBody RoleDTO dto) { @@ -61,6 +88,13 @@ public class RoleController { return ResponseEntity.status(201).body(toDTO(saved)); } + /** + * Updates an existing role and returns the updated role as a DTO. + * + * @param id The ID of the role to update. + * @param dto The RoleDTO containing the updated details of the role. + * @return ResponseEntity containing the updated RoleDTO. + */ @Operation(summary = "Update a role", description = "Update an existing role") @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody RoleDTO dto) { @@ -79,6 +113,12 @@ public class RoleController { return ResponseEntity.ok(toDTO(updated)); } + /** + * Deletes a role by its ID. + * + * @param id The ID of the role to delete. + * @return ResponseEntity with a message indicating successful deletion. + */ @Operation(summary = "Delete a role", description = "Delete a role by its ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { @@ -86,6 +126,13 @@ public class RoleController { return ResponseEntity.ok("Role deleted successfully."); } + /** + * Adds a permission to a role. + * + * @param roleId The ID of the role to which the permission will be added. + * @param permissionId The ID of the permission to add to the role. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Add permission to role", description = "Add a permission to a role") @PostMapping("/permissions/add/{roleId}/{permissionId}") public ResponseEntity addPermissionToRole(@PathVariable Integer roleId, @PathVariable Integer permissionId) { @@ -95,6 +142,13 @@ public class RoleController { return ResponseEntity.ok("Permission added to role."); } + /** + * Removes a permission from a role. + * + * @param roleId The ID of the role from which the permission will be removed. + * @param permissionId The ID of the permission to remove from the role. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Remove permission from role", description = "Remove a permission from a role") @DeleteMapping("/permissions/remove/{permissionId}/{roleId}") public ResponseEntity removePermissionFromRole(@PathVariable Integer roleId, @PathVariable Integer permissionId) { @@ -104,8 +158,14 @@ public class RoleController { return ResponseEntity.ok("Permission removed from role."); } - // -------------------- Mapping -------------------- + /* -------------------- Utils -------------------- */ + /** + * Converts a Role entity to a RoleDTO. + * + * @param role The Role entity to convert. + * @return The converted RoleDTO. + */ private RoleDTO toDTO(Role role) { Set permissionIds = role.getPermissions().stream() .map(Permission::getId) diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentController.java index 4bdc443..3c137fd 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentController.java @@ -31,6 +31,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing students. + * Provides endpoints for creating, retrieving, updating, and deleting students. + */ @RestController @RequestMapping("/api/v1/students") @Tag(name = "Student Management", description = "Operations related to student management") @@ -42,9 +46,21 @@ public class StudentController { private final MembershipService membershipService; private final TrainingSessionService trainingSessionService; + /** + * Password encoder for encoding passwords. + */ @Autowired private PasswordEncoder passwordEncoder; + /** + * Constructor for StudentController. + * + * @param studentService Service for handling student records. + * @param roleService Service for handling roles. + * @param userService Service for handling user records. + * @param membershipService Service for handling membership records. + * @param trainingSessionService Service for handling training session records. + */ public StudentController(StudentService studentService, RoleService roleService, UserService userService, MembershipService membershipService, TrainingSessionService trainingSessionService) { this.studentService = studentService; this.roleService = roleService; @@ -53,6 +69,12 @@ public class StudentController { this.trainingSessionService = trainingSessionService; } + /** + * Registers a new student with the provided details. + * + * @param studentRegisterDTO The DTO containing the student's registration details. + * @return ResponseEntity containing the created StudentDTO. + */ @Operation(summary = "Register a new Student", description = "Registers a new student with the provided details.") @PostMapping("/register") public ResponseEntity registerStudent(@RequestBody @Valid StudentRegisterDTO studentRegisterDTO) { @@ -100,7 +122,11 @@ public class StudentController { return ResponseEntity.status(HttpStatus.CREATED).body(response); } - + /** + * Retrieves all students. + * + * @return ResponseEntity containing a list of StudentDTOs. + */ @Operation(summary = "Get all Students", description = "Retrieves a list of all students.") @GetMapping("/getAll") public ResponseEntity> getAllStudents() { @@ -114,6 +140,12 @@ public class StudentController { return ResponseEntity.ok(dtos); } + /** + * Creates a new student with the provided details. + * + * @param dto The StudentDTO containing the details of the student to be created. + * @return ResponseEntity containing the created StudentDTO. + */ @Operation(summary = "Create a new Student", description = "Creates a new student with the provided details.") @PostMapping("/create") public ResponseEntity createStudent(@RequestBody StudentDTO dto) { @@ -122,6 +154,13 @@ public class StudentController { return ResponseEntity.status(HttpStatus.CREATED).body(convertToDTO(saved)); } + /** + * Updates an existing student with the provided details. + * + * @param id The ID of the student to update. + * @param dto The StudentDTO containing the updated details of the student. + * @return ResponseEntity containing the updated StudentDTO. + */ @Operation(summary = "Update an existing Student", description = "Updates the details of an existing student.") @PutMapping("/update/{id}") public ResponseEntity updateStudent(@PathVariable Integer id, @RequestBody StudentDTO dto) { @@ -131,6 +170,12 @@ public class StudentController { return ResponseEntity.ok(convertToDTO(updated)); } + /** + * Retrieves a student by their ID. + * + * @param id The ID of the student to find. + * @return ResponseEntity containing the StudentDTO or not found if the ID does not exist. + */ @Operation(summary = "Find a Student by ID", description = "Retrieves a student by their ID.") @GetMapping("/findById/{id}") public ResponseEntity getStudentById(@PathVariable Integer id) { @@ -138,6 +183,12 @@ public class StudentController { return ResponseEntity.ok(convertToDTO(student)); } + /** + * Deletes a student by their ID. + * + * @param id The ID of the student to delete. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Delete a Student", description = "Deletes a student by their ID.") @DeleteMapping("/delete/{id}") public ResponseEntity deleteStudent(@PathVariable Integer id) { @@ -145,6 +196,13 @@ public class StudentController { return ResponseEntity.ok("Student deleted successfully"); } + /** + * Adds a history record to a student by their ID. + * + * @param id The ID of the student to whom the history will be added. + * @param history The StudentHistory object containing the history details to be added. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Add a Student History", description = "Adds a history record to a student by their ID.") @PostMapping("/addStudentHistory/{id}") public ResponseEntity addStudentHistory(@PathVariable Integer id, @RequestBody StudentHistory history) { @@ -153,6 +211,12 @@ public class StudentController { return ResponseEntity.ok("Student history added successfully"); } + /** + * Removes a history record from a student by their ID. + * + * @param id The ID of the history record to remove. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Remove a Student History", description = "Removes a history record from a student.") @DeleteMapping("/removeStudentHistory/{id}") public ResponseEntity removeStudentHistory(@PathVariable Integer id) { @@ -160,6 +224,13 @@ public class StudentController { return ResponseEntity.ok("Student history removed successfully"); } + /** + * Adds a membership to a student by their ID. + * + * @param id The ID of the student to whom the membership will be added. + * @param membership The Membership object containing the membership details to be added. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Add a Membership to a Student", description = "Adds a membership to a student by their ID.") @PostMapping("/addMembershipToStudent/{id}") public ResponseEntity addMembershipToStudent(@PathVariable Integer id, @RequestBody Membership membership) { @@ -168,6 +239,12 @@ public class StudentController { return ResponseEntity.ok("Membership added to student successfully"); } + /** + * Removes a membership from a student by their ID. + * + * @param id The ID of the student from whom the membership will be removed. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Remove a Membership from a Student", description = "Removes a membership from a student by their ID.") @DeleteMapping("/removeMembershipFromStudent/{id}") public ResponseEntity removeMembershipFromStudent(@PathVariable Integer id) { @@ -175,6 +252,13 @@ public class StudentController { return ResponseEntity.ok("Membership removed from student successfully"); } + /** + * Adds an assistance record to a student by their ID. + * + * @param id The ID of the student to whom the assistance will be added. + * @param assistanceDTO The AssistanceDTO containing the details of the assistance to be added. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Add an Assistance to a Student", description = "Adds an assistance record to a student by their ID.") @PostMapping("/addAssistanceToStudent/{id}") public ResponseEntity addAssistanceToStudent(@PathVariable Integer id, @RequestBody AssistanceDTO assistanceDTO) { @@ -185,6 +269,13 @@ public class StudentController { return ResponseEntity.ok("Assistance added to student successfully"); } + /** + * Removes an assistance record from a student by their ID. + * + * @param studentId The ID of the student from whom the assistance will be removed. + * @param assistanceId The ID of the assistance record to remove. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Remove an Assistance from a Student", description = "Removes an assistance record from a student by their ID.") @DeleteMapping("/deleteAssistanceFromStudent/{id}") public ResponseEntity deleteAssistanceFromStudent(@PathVariable Integer studentId, @PathVariable Integer assistanceId) { @@ -192,6 +283,13 @@ public class StudentController { return ResponseEntity.ok("Assistance removed from student successfully"); } + /** + * Adds a training group to a student by their ID. + * + * @param id The ID of the student to whom the training group will be added. + * @param group The TrainingGroup object containing the details of the group to be added. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Add a Training Group to a Student", description = "Adds a training group to a student by their ID.") @PostMapping("/addGroupToStudent/{id}") public ResponseEntity addGroupToStudent(@PathVariable Integer id, @RequestBody TrainingGroup group) { @@ -199,6 +297,13 @@ public class StudentController { return ResponseEntity.ok("Training group added to student successfully"); } + /** + * Updates a student's membership by their ID. + * + * @param studentId The ID of the student whose membership will be updated. + * @param membershipId The ID of the membership to assign to the student. + * @return ResponseEntity indicating the result of the operation. + */ @Operation(summary = "Update a student's membership") @PutMapping("/updateMembership/{studentId}") public ResponseEntity updateMembership( @@ -216,13 +321,26 @@ public class StudentController { - /* ----------------- Mapping Methods ------------------ */ + /* ----------------- Utils ------------------ */ + /** + * Converts a Student entity to a StudentDTO. + * + * @param student The Student entity to convert. + * @return The converted StudentDTO. + */ private StudentDTO convertToDTO(Student student) { return StudentDTO.fromEntity(student); } + /** + * Converts a StudentDTO to a Student entity. + * + * @param dto The StudentDTO to convert. + * @param isCreate Indicates if the conversion is for creating a new student. + * @return The converted Student entity. + */ private Student convertToEntity(StudentDTO dto, boolean isCreate) { if (dto == null || dto.getUser() == null) { throw new InvalidDataException("User data is required"); @@ -271,5 +389,4 @@ public class StudentController { return student; } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentHistoryController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentHistoryController.java index c7aade3..03843fb 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentHistoryController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/StudentHistoryController.java @@ -13,6 +13,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.stream.Collectors; +/** + * StudentHistoryController handles requests related to student history management. + * It provides endpoints for creating, retrieving, updating, and deleting student histories. + */ @RestController @RequestMapping("/api/v1/student-history") @Tag(name = "Student History Management", description = "Operations related to student history") @@ -21,11 +25,22 @@ public class StudentHistoryController { private final StudentHistoryService studentHistoryService; private final StudentService studentService; + /** + * Constructor for StudentHistoryController. + * + * @param studentHistoryService Service for handling student history records. + * @param studentService Service for handling student records. + */ public StudentHistoryController(StudentHistoryService studentHistoryService, StudentService studentService) { this.studentHistoryService = studentHistoryService; this.studentService = studentService; } + /** + * Retrieves all student histories and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of StudentHistoryDTOs or no content if none found. + */ @Operation(summary = "Get all student histories", description = "Retrieve a list of all student histories") @GetMapping("/getAll") public ResponseEntity> getAll() { @@ -36,6 +51,12 @@ public class StudentHistoryController { return ResponseEntity.ok(dtos); } + /** + * Retrieves a student history by its ID and returns it as a DTO. + * + * @param id The ID of the student history to find. + * @return ResponseEntity containing the StudentHistoryDTO or not found if it doesn't exist. + */ @Operation(summary = "find student history by ID", description = "Retrieve a student history by its ID") @GetMapping("/findById/{id}") public ResponseEntity findById(@PathVariable Integer id) { @@ -43,6 +64,12 @@ public class StudentHistoryController { return ResponseEntity.ok(toDTO(history)); } + /** + * Creates a new student history and returns the created history as a DTO. + * + * @param dto The StudentHistoryDTO containing the details of the history to be created. + * @return ResponseEntity containing the created StudentHistoryDTO. + */ @Operation(summary = "Create a new student history", description = "Create a new student history") @PostMapping("/create") public ResponseEntity create(@RequestBody StudentHistoryDTO dto) { @@ -51,6 +78,13 @@ public class StudentHistoryController { return ResponseEntity.status(201).body(toDTO(saved)); } + /** + * Updates an existing student history and returns the updated history as a DTO. + * + * @param id The ID of the student history to update. + * @param dto The StudentHistoryDTO containing the updated details. + * @return ResponseEntity containing the updated StudentHistoryDTO. + */ @Operation(summary = "Update an existing student history", description = "Update an existing student history") @PutMapping("/update/{id}") public ResponseEntity update(@PathVariable Integer id, @RequestBody StudentHistoryDTO dto) { @@ -60,6 +94,12 @@ public class StudentHistoryController { return ResponseEntity.ok(toDTO(updated)); } + /** + * Deletes a student history by its ID. + * + * @param id The ID of the student history to delete. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Delete a student history", description = "Delete a student history by its ID") @DeleteMapping("/delete/{id}") public ResponseEntity delete(@PathVariable Integer id) { @@ -67,8 +107,14 @@ public class StudentHistoryController { return ResponseEntity.ok("Student history deleted successfully."); } - // -------------------- Mapping -------------------- + /* -------------------- Utils -------------------- */ + /** + * Converts a StudentHistory entity to a StudentHistoryDTO. + * + * @param history The StudentHistory entity to convert. + * @return The converted StudentHistoryDTO. + */ private StudentHistoryDTO toDTO(StudentHistory history) { return new StudentHistoryDTO( history.getId(), @@ -79,6 +125,13 @@ public class StudentHistoryController { ); } + /** + * Converts a StudentHistoryDTO to a StudentHistory entity. + * + * @param dto The StudentHistoryDTO to convert. + * @param includeExistingStudent Whether to include the existing student in the history. + * @return The converted StudentHistory entity. + */ private StudentHistory toEntity(StudentHistoryDTO dto, boolean includeExistingStudent) { StudentHistory history = (dto.getId() != null) ? studentHistoryService.findById(dto.getId()) diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/TeacherController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/TeacherController.java index 2145c0c..377f8f3 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/TeacherController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/TeacherController.java @@ -18,6 +18,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +/** + * Controller for managing teachers. + * Provides endpoints for creating, retrieving, updating, and deleting teachers. + */ @RestController @RequestMapping("/api/v1/teachers") @Tag(name = "Teacher Management", description = "Operations related to teachers") @@ -26,14 +30,28 @@ public class TeacherController { private final TeacherService teacherService; private final RoleService roleService; + /** + * Password encoder for encoding passwords. + */ @Autowired private PasswordEncoder passwordEncoder; + /** + * Constructor for TeacherController. + * + * @param teacherService Service for handling teacher records. + * @param roleService Service for handling roles. + */ public TeacherController(TeacherService teacherService, RoleService roleService) { this.teacherService = teacherService; this.roleService = roleService; } + /** + * Gets all teachers and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of TeacherDTOs or no content if none found. + */ @Operation(summary = "Get all teachers", description = "Retrieve a list of all teachers") @GetMapping("/getAll") public ResponseEntity> getAllTeachers() { @@ -49,6 +67,12 @@ public class TeacherController { return ResponseEntity.ok(dtos); } + /** + * Creates a new teacher and returns the created teacher as a DTO. + * + * @param dto The TeacherDTO containing the details of the teacher to be created. + * @return ResponseEntity containing the created TeacherDTO. + */ @Operation(summary = "Create a new teacher", description = "Create a new teacher") @PostMapping("/create") public ResponseEntity createTeacher(@RequestBody TeacherDTO dto) { @@ -57,6 +81,12 @@ public class TeacherController { return ResponseEntity.status(HttpStatus.CREATED).body(convertToDTO(saved)); } + /** + * Finds a teacher by their ID and returns it as a DTO. + * + * @param id The ID of the teacher to find. + * @return ResponseEntity containing the TeacherDTO or not found if it doesn't exist. + */ @Operation(summary = "Find a teacher by ID", description = "Retrieve a teacher by their ID") @GetMapping("/findById/{id}") public ResponseEntity findById(@PathVariable Integer id) { @@ -67,6 +97,13 @@ public class TeacherController { return ResponseEntity.ok(convertToDTO(teacher)); } + /** + * Updates an existing teacher and returns the updated teacher as a DTO. + * + * @param id The ID of the teacher to update. + * @param dto The TeacherDTO containing the updated details of the teacher. + * @return ResponseEntity containing the updated TeacherDTO. + */ @Operation(summary = "Update a teacher", description = "Update an existing teacher") @PutMapping("/update/{id}") public ResponseEntity updateTeacher(@PathVariable Integer id, @RequestBody TeacherDTO dto) { @@ -76,7 +113,12 @@ public class TeacherController { return ResponseEntity.ok(convertToDTO(updated)); } - + /** + * Deletes a teacher by their ID. + * + * @param id The ID of the teacher to delete. + * @return ResponseEntity with a success message. + */ @Operation(summary = "Delete a teacher", description = "Delete a teacher by their ID") @DeleteMapping("/delete/{id}") public ResponseEntity deleteTeacher(@PathVariable Integer id) { @@ -84,12 +126,25 @@ public class TeacherController { return ResponseEntity.ok("Teacher deleted successfully"); } - // ----------------- Mapping ------------------ + /* ----------------- Utils ------------------ */ + /** + * Converts a Teacher entity to a TeacherDTO. + * + * @param teacher The Teacher entity to convert. + * @return The converted TeacherDTO. + */ private TeacherDTO convertToDTO(Teacher teacher) { return TeacherDTO.fromEntity(teacher); } + /** + * Converts a TeacherDTO to a Teacher entity. + * + * @param dto The TeacherDTO to convert. + * @param isCreate Indicates if this is a creation operation (true) or an update (false). + * @return The converted Teacher entity. + */ private Teacher convertToEntity(TeacherDTO dto, boolean isCreate) { if (dto == null || dto.getUser() == null) { throw new InvalidDataException("User data is required"); @@ -133,5 +188,4 @@ public class TeacherController { return teacher; } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/UserController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/UserController.java index ef5acb3..fbf9c7c 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/UserController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/UserController.java @@ -38,15 +38,30 @@ public class UserController { private final RoleService roleService; private final JwtUtil jwtUtil; + /** + * Password encoder for encoding passwords. + */ @Autowired private PasswordEncoder passwordEncoder; + /** + * Constructor for UserController. + * + * @param userService Service for handling user records. + * @param roleService Service for handling roles. + * @param jwtUtil Utility for JWT operations. + */ public UserController(UserService userService, RoleService roleService, JwtUtil jwtUtil) { this.userService = userService; this.roleService = roleService; this.jwtUtil = jwtUtil; } + /** + * Gets all users and returns them as a list of DTOs. + * + * @return ResponseEntity containing a list of UserDTOs or no content if none found. + */ @Operation(summary = "Get all users", description = "Retrieve a list of all users") @GetMapping("/getAll") public ResponseEntity> getAllUsers() { @@ -62,6 +77,12 @@ public class UserController { return ResponseEntity.ok(userDTOs); } + /** + * Finds a user by their ID and returns it as a DTO. + * + * @param id The ID of the user to find. + * @return ResponseEntity containing the UserDTO or not found if the ID does not exist. + */ @Operation(summary = "Find a User by ID", description = "Retrieve a user by their ID") @GetMapping("/getById/{id}") public ResponseEntity getUserById(@PathVariable Integer id) { @@ -69,7 +90,13 @@ public class UserController { return ResponseEntity.ok(convertToDTO(user)); } - @Operation( summary = "Find a User by email", description = "Retrieve a user by their email") + /** + * Creates a new user and returns the created user as a DTO. + * + * @param userDTO The UserDTO containing the details of the user to be created. + * @return ResponseEntity containing the created UserDTO with status 201 Created. + */ + @Operation(summary = "Create a new User", description = "Create a new user") @PostMapping("/create") public ResponseEntity createUser(@RequestBody UserDTO userDTO) { User user = convertToEntity(userDTO, true); @@ -77,6 +104,13 @@ public class UserController { return ResponseEntity.status(HttpStatus.CREATED).body(convertToDTO(saved)); } + /** + * Updates an existing user by ID and returns the updated user as a DTO. + * + * @param id The ID of the user to update. + * @param userDTO The UserDTO containing the updated details of the user. + * @return ResponseEntity containing the updated UserDTO. + */ @Operation(summary = "Update a user by ID", description = "Update an existing user") @PutMapping("/update/{id}") public ResponseEntity updateUser(@PathVariable Integer id, @RequestBody UserDTO userDTO) { @@ -86,13 +120,25 @@ public class UserController { return ResponseEntity.ok(convertToDTO(updated)); } - @Operation(summary = "Update a user by email", description = "Update an existing user") + /** + * Deletes a user by ID. + * + * @param id The ID of the user to delete. + * @return ResponseEntity with a success message. + */ + @Operation(summary = "Delete User by Id", description = "Delete a user by ID") @DeleteMapping("/delete/{id}") public ResponseEntity deleteUser(@PathVariable Integer id) { userService.deleteById(id); return ResponseEntity.ok("User deleted successfully"); } + /** + * Retrieves the currently authenticated user based on the JWT token from the request. + * + * @param request The HTTP request containing the JWT token. + * @return ResponseEntity containing the UserDTO of the current user. + */ @Operation(summary = "Get current user", description = "Retrieve the currently authenticated user") @GetMapping("/me") public ResponseEntity getCurrentUser(HttpServletRequest request) { @@ -103,8 +149,15 @@ public class UserController { return ResponseEntity.ok(convertToDTO(user)); } - // ------------------ UTILS ------------------ + /* ------------------ UTILS ------------------ */ + /** + * Extracts the JWT token from the Authorization header of the request. + * + * @param request The HTTP request containing the Authorization header. + * @return The extracted JWT token. + * @throws InvalidDataException if the Authorization header is missing or invalid. + */ private String extractTokenFromRequest(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); if (authHeader == null || !authHeader.startsWith("Bearer ")) { @@ -113,6 +166,12 @@ public class UserController { return authHeader.substring(7); // Remove "Bearer " } + /** + * Converts a User entity to a UserDTO. + * + * @param user The User entity to convert. + * @return The converted UserDTO. + */ private UserDTO convertToDTO(User user) { UserDTO dto = new UserDTO( user.getId(), @@ -156,6 +215,12 @@ public class UserController { return dto; } + /** + * Converts a Notification entity to a NotificationMiniDTO. + * + * @param notification The Notification entity to convert. + * @return The converted NotificationMiniDTO. + */ private NotificationMiniDTO convertNotificationToMiniDTO(Notification notification) { return new NotificationMiniDTO( notification.getId(), @@ -166,6 +231,14 @@ public class UserController { ); } + /** + * Converts a UserDTO to a User entity. + * + * @param dto The UserDTO to convert. + * @param isCreate Indicates if the conversion is for creation (true) or update (false). + * @return The converted User entity. + * @throws InvalidDataException if the provided data is invalid. + */ private User convertToEntity(UserDTO dto, boolean isCreate) { User user; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/class_managment_dtos/AssistanceDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/class_managment_dtos/AssistanceDTO.java index bde1a28..9a2905f 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/class_managment_dtos/AssistanceDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/class_managment_dtos/AssistanceDTO.java @@ -1,6 +1,5 @@ package com.denniseckerskorn.dtos.class_managment_dtos; - import com.denniseckerskorn.entities.class_managment.Assistance; import com.denniseckerskorn.entities.class_managment.TrainingSession; import com.denniseckerskorn.entities.user_managment.users.Student; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceLineDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceLineDTO.java index d81f895..d6fac5d 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceLineDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceLineDTO.java @@ -39,7 +39,6 @@ public class CreateInvoiceLineDTO { this.unitPrice = unitPrice; } - // Convierte a entidad con parámetros necesarios public InvoiceLine toEntity(Invoice invoice, ProductService productService) { InvoiceLine entity = new InvoiceLine(); entity.setInvoice(invoice); @@ -47,11 +46,10 @@ public class CreateInvoiceLineDTO { entity.setQuantity(this.quantity); entity.setUnitPrice(this.unitPrice); entity.setSubtotal(this.unitPrice.multiply(BigDecimal.valueOf(this.quantity))); - entity.setDescription(productService.getName()); // opcional + entity.setDescription(productService.getName()); return entity; } - // Convierte desde una entidad (opcional) public static CreateInvoiceLineDTO fromEntity(InvoiceLine entity) { CreateInvoiceLineDTO dto = new CreateInvoiceLineDTO(); dto.setProductServiceId(entity.getProductService() != null ? entity.getProductService().getId() : null); diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceRequestDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceRequestDTO.java index 5d34acb..e1012b6 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceRequestDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/CreateInvoiceRequestDTO.java @@ -20,8 +20,6 @@ public class CreateInvoiceRequestDTO { public CreateInvoiceRequestDTO() { } - // Getters y setters - public Integer getUserId() { return userId; } @@ -62,7 +60,6 @@ public class CreateInvoiceRequestDTO { this.lines = lines; } - // Convierte el DTO a entidad base (sin líneas, requiere setUser) public Invoice toEntity(User user) { Invoice invoice = new Invoice(); invoice.setUser(user); @@ -72,7 +69,6 @@ public class CreateInvoiceRequestDTO { return invoice; } - // Crea el DTO desde una entidad (opcional) public static CreateInvoiceRequestDTO fromEntity(Invoice invoice) { CreateInvoiceRequestDTO dto = new CreateInvoiceRequestDTO(); dto.setUserId(invoice.getUser() != null ? invoice.getUser().getId().intValue() : null); diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java index 643ecdb..8556b47 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java @@ -14,7 +14,8 @@ public class IVATypeDTO { private String description; - public IVATypeDTO() {} + public IVATypeDTO() { + } public IVATypeDTO(IVAType entity) { this.id = entity.getId(); @@ -34,9 +35,6 @@ public class IVATypeDTO { return iva; } - // Getters y setters... - - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java index b3410ef..44d00df 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java @@ -63,9 +63,6 @@ public class InvoiceDTO { return invoice; } - // Getters y setters (puedes generarlos automáticamente) - - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java index 551be37..1bd59c4 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java @@ -25,7 +25,8 @@ public class InvoiceLineDTO { private BigDecimal subtotal; - public InvoiceLineDTO() {} + public InvoiceLineDTO() { + } public InvoiceLineDTO(InvoiceLine entity) { this.id = entity.getId(); @@ -51,9 +52,6 @@ public class InvoiceLineDTO { return entity; } - // Getters y setters... - - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java index 0690699..101733b 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java @@ -67,10 +67,6 @@ public class PaymentDTO { return payment; } - - // Getters y setters... - - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java index dbba695..53fa4fb 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java @@ -70,8 +70,6 @@ public class ProductServiceDTO { return entity; } - // Getters y setters - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/AdminDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/AdminDTO.java index eaa43cf..3d03692 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/AdminDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/AdminDTO.java @@ -30,8 +30,6 @@ public class AdminDTO { this.user = user; } - // ------------ Métodos de conversión ------------ - public static AdminDTO fromEntity(Admin admin) { if (admin == null || admin.getUser() == null) { return null; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationDTO.java index e50e4be..7f1a907 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationDTO.java @@ -14,7 +14,8 @@ public class NotificationDTO { private StatusValues status; private Set userIds; - public NotificationDTO() {} + public NotificationDTO() { + } public NotificationDTO(Integer id, String title, String message, LocalDateTime shippingDate, String type, StatusValues status, Set userIds) { this.id = id; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationMiniDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationMiniDTO.java index 3386648..4353270 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationMiniDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/NotificationMiniDTO.java @@ -20,7 +20,6 @@ public class NotificationMiniDTO { this.type = type; } - // Getters y setters public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentDTO.java index 87a488e..04783f0 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentDTO.java @@ -109,8 +109,6 @@ public class StudentDTO { return student; } - // Getters y Setters... - public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentHistoryMiniDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentHistoryMiniDTO.java index 25aeedf..ccd6034 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentHistoryMiniDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentHistoryMiniDTO.java @@ -18,7 +18,6 @@ public class StudentHistoryMiniDTO { this.description = description; } - // Getters y setters public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentMiniDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentMiniDTO.java index f55d990..b22d4b6 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentMiniDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentMiniDTO.java @@ -4,9 +4,10 @@ import java.util.Set; public class StudentMiniDTO { private Integer id; - private Set histories; // 🔥 mini DTO + private Set histories; - public StudentMiniDTO() {} + public StudentMiniDTO() { + } public Integer getId() { return id; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentRegisterDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentRegisterDTO.java index adbbf55..828a67a 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentRegisterDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/StudentRegisterDTO.java @@ -1,21 +1,19 @@ package com.denniseckerskorn.dtos.user_managment_dtos; import java.time.LocalDate; + import com.fasterxml.jackson.annotation.JsonFormat; public class StudentRegisterDTO { - - // Datos de usuario private String name; private String surname; private String email; private String password; private String phoneNumber; private String address; - private String roleName; // ejemplo: "ROLE_STUDENT" - private String status; // ejemplo: "ACTIVE" + private String roleName; + private String status; - // Datos de estudiante private String dni; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") @@ -25,91 +23,117 @@ public class StudentRegisterDTO { private String progress; private String medicalReport; private String parentName; - private Integer membershipId; + private Integer membershipId; - // --- Getters y Setters --- public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getSurname() { return surname; } + public void setSurname(String surname) { this.surname = surname; } + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + public String getPassword() { return password; } + public void setPassword(String password) { this.password = password; } + public String getPhoneNumber() { return phoneNumber; } + public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } + public String getAddress() { return address; } + public void setAddress(String address) { this.address = address; } + public String getRoleName() { return roleName; } + public void setRoleName(String roleName) { this.roleName = roleName; } + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + public String getDni() { return dni; } + public void setDni(String dni) { this.dni = dni; } + public LocalDate getBirthdate() { return birthdate; } + public void setBirthdate(LocalDate birthdate) { this.birthdate = birthdate; } + public String getBelt() { return belt; } + public void setBelt(String belt) { this.belt = belt; } + public String getProgress() { return progress; } + public void setProgress(String progress) { this.progress = progress; } + public String getMedicalReport() { return medicalReport; } + public void setMedicalReport(String medicalReport) { this.medicalReport = medicalReport; } + public String getParentName() { return parentName; } + public void setParentName(String parentName) { this.parentName = parentName; } @@ -117,9 +141,9 @@ public class StudentRegisterDTO { public Integer getMembershipId() { return membershipId; } + public void setMembershipId(Integer membershipId) { this.membershipId = membershipId; } - } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/TeacherDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/TeacherDTO.java index a8c34c9..0023b7d 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/TeacherDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/TeacherDTO.java @@ -16,10 +16,7 @@ public class TeacherDTO { this.user = user; this.discipline = discipline; } - - // Getters y Setters - - + public Integer getId() { return id; } diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/UserDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/UserDTO.java index 0e7473d..1e96c84 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/UserDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/user_managment_dtos/UserDTO.java @@ -20,8 +20,6 @@ public class UserDTO { private LocalDateTime registerDate; private String roleName; private StatusValues status; - - // 🔥 Nuevos campos para el perfil completo private Set notifications; private StudentMiniDTO student; @@ -54,8 +52,6 @@ public class UserDTO { this.status = user.getStatus(); } - - // Getters y Setters public Integer getId() { return id; } @@ -152,7 +148,6 @@ public class UserDTO { this.student = student; } - // Conversión (opcional) public static UserDTO fromEntity(User user) { if (user == null) return null; diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/exceptions/GlobalExceptionHandler.java b/memberflow-api/src/main/java/com/denniseckerskorn/exceptions/GlobalExceptionHandler.java index fbf96e7..a24820a 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/exceptions/GlobalExceptionHandler.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/exceptions/GlobalExceptionHandler.java @@ -24,6 +24,10 @@ import org.springframework.web.servlet.resource.NoResourceFoundException; import java.util.HashMap; import java.util.Map; +/** + * GlobalExceptionHandler handles exceptions thrown by the application. + * It provides a centralized way to manage error responses for various exceptions. + */ @ControllerAdvice public class GlobalExceptionHandler { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/services/finance_service/InvoicePdfGenerator.java b/memberflow-api/src/main/java/com/denniseckerskorn/services/finance_service/InvoicePdfGenerator.java index cb4fcd8..382cd6f 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/services/finance_service/InvoicePdfGenerator.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/services/finance_service/InvoicePdfGenerator.java @@ -13,6 +13,10 @@ import org.springframework.stereotype.Service; import java.io.ByteArrayOutputStream; import java.math.BigDecimal; +/** + * InvoicePdfGenerator is a service that generates a PDF representation of an Invoice. + * It uses iText library to create the PDF document with invoice details. + */ @Service public class InvoicePdfGenerator { @@ -23,20 +27,20 @@ public class InvoicePdfGenerator { PdfDocument pdf = new PdfDocument(writer); Document doc = new Document(pdf); - // Cabecera + // Document title and metadata doc.add(new Paragraph("FACTURA #" + invoice.getId()).setFontSize(16).setBold()); doc.add(new Paragraph("Fecha: " + invoice.getDate())); doc.add(new Paragraph("Estado: " + invoice.getStatus())); doc.add(new Paragraph(" ")); - // Información del cliente + // Cliente information var user = invoice.getUser(); doc.add(new Paragraph("Cliente: " + user.getName() + " " + user.getSurname())); doc.add(new Paragraph("Email: " + user.getEmail())); doc.add(new Paragraph("Teléfono: " + user.getPhoneNumber())); doc.add(new Paragraph("Dirección: " + user.getAddress())); - // Si es estudiante, mostrar info básica (sin tutor ni cinturón) + // Student information Student student = user.getStudent(); if (student != null) { doc.add(new Paragraph("DNI: " + student.getDni())); @@ -45,7 +49,7 @@ public class InvoicePdfGenerator { doc.add(new Paragraph(" ")); - // Detalle de productos + // Products and services details doc.add(new Paragraph("Detalle de productos:").setBold()); doc.add(new Paragraph("Producto | Cant. | Precio | IVA % | Subtotal (sin IVA) | IVA")); @@ -74,7 +78,7 @@ public class InvoicePdfGenerator { )); } - // Totales + // Totals doc.add(new Paragraph(" ")); doc.add(new Paragraph("Subtotal (sin IVA): " + totalSubtotal + " €").setBold()); doc.add(new Paragraph("IVA total: " + totalIVA + " €").setBold()); diff --git a/memberflow-api/src/main/resources/Checklist.txt b/memberflow-api/src/main/resources/Checklist.txt deleted file mode 100644 index e0e1dbf..0000000 --- a/memberflow-api/src/main/resources/Checklist.txt +++ /dev/null @@ -1,7 +0,0 @@ -1. Comprobar seguridad y permisos en cada endpoint. -2. Comprobar o arreglar el tema de los ID automáticos y los DTOs. -3. Documentar los endpoints. -4. Crear javadoc de cada metodo. -5. Crear un README.md con la información del proyecto. -7. Comprobar como funciona logger en cada endpoint. -8. Mejorar excepciones y mensajes de error. \ No newline at end of file diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/config/HibernateConfig.java b/memberflow-data/src/main/java/com/denniseckerskorn/config/HibernateConfig.java index 5c228cf..6a6d0a7 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/config/HibernateConfig.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/config/HibernateConfig.java @@ -13,16 +13,31 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource; import java.util.Properties; +/** + * Hibernate configuration class for setting up the EntityManagerFactory and DataSource. + * It uses HikariCP for connection pooling and configures JPA properties. + */ @Configuration @EnableTransactionManagement public class HibernateConfig { private final Environment env; + /** + * Constructor to inject the Environment object for accessing properties. + * + * @param env the Environment object containing application properties + */ public HibernateConfig(Environment env) { this.env = env; } + /** + * Bean definition for DataSource using HikariCP. + * Reads properties from the application environment. + * + * @return a configured DataSource instance + */ @Bean public DataSource dataSource() { HikariDataSource dataSource = new HikariDataSource(); @@ -33,6 +48,12 @@ public class HibernateConfig { return dataSource; } + /** + * Bean definition for LocalContainerEntityManagerFactoryBean. + * + * @param dataSource the DataSource to be used by the EntityManagerFactory + * @return a configured LocalContainerEntityManagerFactoryBean instance + */ @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean(); @@ -50,6 +71,13 @@ public class HibernateConfig { return emf; } + /** + * Bean definition for PlatformTransactionManager. + * Uses JpaTransactionManager to manage transactions. + * + * @param emf the LocalContainerEntityManagerFactoryBean to be used by the transaction manager + * @return a configured PlatformTransactionManager instance + */ @Bean public PlatformTransactionManager transactionManager(LocalContainerEntityManagerFactoryBean emf) { JpaTransactionManager transactionManager = new JpaTransactionManager(); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/AssistanceRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/AssistanceRepository.java index b261b09..7c79679 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/AssistanceRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/AssistanceRepository.java @@ -8,6 +8,11 @@ import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; import java.util.List; +/** + * Repository interface for managing Assistance entities. + * Provides methods to find assistance records by training session ID, student ID, + * and date range, as well as a method to find assistance by both training session ID and student ID. + */ public interface AssistanceRepository extends JpaRepository { Assistance findByTrainingSessionId(Integer trainingSessionId); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/MembershipRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/MembershipRepository.java index d285639..ed261c1 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/MembershipRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/MembershipRepository.java @@ -8,6 +8,10 @@ import org.springframework.data.repository.query.Param; import java.time.LocalDate; +/** + * Repository interface for managing Membership entities. + * Provides methods to find memberships by various attributes. + */ public interface MembershipRepository extends JpaRepository { Membership findByType(MembershipTypeValues type); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingGroupRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingGroupRepository.java index 1b82567..592aaed 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingGroupRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingGroupRepository.java @@ -3,5 +3,9 @@ package com.denniseckerskorn.repositories.class_managment_repositories; import com.denniseckerskorn.entities.class_managment.TrainingGroup; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing TrainingGroup entities. + * Provides methods to perform CRUD operations on TrainingGroup entities. + */ public interface TrainingGroupRepository extends JpaRepository { } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingSessionRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingSessionRepository.java index 211b83e..c2c5c89 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingSessionRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/class_managment_repositories/TrainingSessionRepository.java @@ -7,6 +7,10 @@ import java.time.LocalDateTime; import java.util.List; +/** + * Repository interface for managing TrainingSession entities. + * Provides methods to find training sessions by date and date range. + */ public interface TrainingSessionRepository extends JpaRepository { TrainingSession findByDate(LocalDateTime date); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/IVATypeRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/IVATypeRepository.java index 59c5245..84726cb 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/IVATypeRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/IVATypeRepository.java @@ -5,6 +5,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.math.BigDecimal; +/** + * Repository interface for managing IVAType entities. + * Provides methods to perform CRUD operations on IVAType entities. + */ public interface IVATypeRepository extends JpaRepository { boolean existsByPercentage(BigDecimal percentage); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceLineRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceLineRepository.java index ad579c0..9dac0ca 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceLineRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceLineRepository.java @@ -3,5 +3,9 @@ package com.denniseckerskorn.repositories.finance_repositories; import com.denniseckerskorn.entities.finance.InvoiceLine; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing InvoiceLine entities. + * Provides methods to perform CRUD operations on InvoiceLine entities. + */ public interface InvoiceLineRepository extends JpaRepository { } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceRepository.java index e2cc959..607edd4 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/InvoiceRepository.java @@ -3,5 +3,9 @@ package com.denniseckerskorn.repositories.finance_repositories; import com.denniseckerskorn.entities.finance.Invoice; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Invoice entities. + * Provides methods to perform CRUD operations on Invoice entities. + */ public interface InvoiceRepository extends JpaRepository { } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java index f49de6d..81869a3 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java @@ -5,6 +5,10 @@ import com.denniseckerskorn.entities.finance.Payment; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +/** + * Repository interface for managing Payment entities. + * Provides methods to check if a payment exists by invoice ID and to find payments by user ID. + */ public interface PaymentRepository extends JpaRepository { boolean existsByInvoiceId(Integer invoiceId); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/ProductServiceRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/ProductServiceRepository.java index 984052a..bd32fe7 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/ProductServiceRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/ProductServiceRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.finance_repositories; import com.denniseckerskorn.entities.finance.ProductService; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing ProductService entities. + * Provides methods to perform CRUD operations on ProductService entities. + */ public interface ProductServiceRepository extends JpaRepository { boolean existsByName(String name); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/AdminRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/AdminRepository.java index 1336fd4..7832c21 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/AdminRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/AdminRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.user_managment_repositories; import com.denniseckerskorn.entities.user_managment.users.Admin; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Admin entities. + * Provides methods to perform CRUD operations on Admin entities. + */ public interface AdminRepository extends JpaRepository { boolean existsByUserEmail(String email); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/NotificationRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/NotificationRepository.java index 039bd94..1a0437a 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/NotificationRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/NotificationRepository.java @@ -6,6 +6,11 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.time.LocalDateTime; import java.util.List; +/** + * Repository interface for managing Notification entities. + * Provides methods to check if a notification exists by title and shipping date, + * and to find notifications by their ID. + */ public interface NotificationRepository extends JpaRepository { boolean existsByTitleAndShippingDate(String title, LocalDateTime shippingDate); List findNotificationsById(Integer id); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/PermissionRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/PermissionRepository.java index 7e47c07..fdc0c36 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/PermissionRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/PermissionRepository.java @@ -4,6 +4,10 @@ import com.denniseckerskorn.entities.user_managment.Permission; import com.denniseckerskorn.enums.PermissionValues; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Permission entities. + * Provides methods to perform CRUD operations on Permission entities. + */ public interface PermissionRepository extends JpaRepository { Permission findByPermissionName(PermissionValues permissionName); boolean existsByPermissionName(PermissionValues permissionName); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/RoleRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/RoleRepository.java index 4b5c97c..467d292 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/RoleRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/RoleRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.user_managment_repositories; import com.denniseckerskorn.entities.user_managment.Role; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Role entities. + * Provides methods to perform CRUD operations on Role entities. + */ public interface RoleRepository extends JpaRepository { Role findByName(String name); } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentHistoryRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentHistoryRepository.java index 78df28d..cce8da1 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentHistoryRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentHistoryRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.user_managment_repositories; import com.denniseckerskorn.entities.user_managment.StudentHistory; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing StudentHistory entities. + * Provides methods to perform CRUD operations on StudentHistory entities. + */ public interface StudentHistoryRepository extends JpaRepository { boolean existsByEventType(String eventType); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentRepository.java index 31f9ba0..6ad1d30 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/StudentRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.user_managment_repositories; import com.denniseckerskorn.entities.user_managment.users.Student; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Student entities. + * Provides methods to perform CRUD operations on Student entities. + */ public interface StudentRepository extends JpaRepository { boolean existsByDni(String dni); Student findByDni(String dni); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/TeacherRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/TeacherRepository.java index 46f7f8a..4b79850 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/TeacherRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/TeacherRepository.java @@ -3,6 +3,10 @@ package com.denniseckerskorn.repositories.user_managment_repositories; import com.denniseckerskorn.entities.user_managment.users.Teacher; import org.springframework.data.jpa.repository.JpaRepository; +/** + * Repository interface for managing Teacher entities. + * Provides methods to perform CRUD operations on Teacher entities. + */ public interface TeacherRepository extends JpaRepository { boolean existsByUserEmail(String mail); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/UserRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/UserRepository.java index 720045a..655f837 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/UserRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/user_managment_repositories/UserRepository.java @@ -6,6 +6,11 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +/** + * Repository interface for managing User entities. + * Provides methods to check if a user exists by email, find a user by email, + * and retrieve all users with a specific role. + */ public interface UserRepository extends JpaRepository { boolean existsByEmail(String email); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/seeder/TestDataSeeder.java b/memberflow-data/src/main/java/com/denniseckerskorn/seeder/TestDataSeeder.java index 8ae44a5..8eb77e5 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/seeder/TestDataSeeder.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/seeder/TestDataSeeder.java @@ -33,6 +33,12 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +/** + * TestDataSeeder is a component that seeds the database with initial test data + * when the application starts in the "dev" profile. + * It creates roles, permissions, users, memberships, training groups, sessions, + * and other entities to facilitate development and testing. + */ @Component @Profile("dev") public class TestDataSeeder implements CommandLineRunner { @@ -58,7 +64,27 @@ public class TestDataSeeder implements CommandLineRunner { private final ProductServiceService productServiceService; private final PaymentService paymentService; - + /** + * Constructor for TestDataSeeder. + * + * @param adminService Service for managing admin entities. + * @param notificationService Service for managing notifications. + * @param permissionService Service for managing permissions. + * @param roleService Service for managing roles. + * @param studentHistoryService Service for managing student history. + * @param studentService Service for managing student entities. + * @param teacherService Service for managing teacher entities. + * @param userService Service for managing user entities. + * @param membershipService Service for managing memberships. + * @param assistanceService Service for managing assistance records. + * @param trainingGroupService Service for managing training groups. + * @param trainingSessionService Service for managing training sessions. + * @param invoiceService Service for managing invoices. + * @param invoiceLineService Service for managing invoice lines. + * @param ivaTypeService Service for managing IVA types. + * @param productServiceService Service for managing product/services. + * @param paymentService Service for managing payments. + */ @Autowired public TestDataSeeder(AdminService adminService, NotificationService notificationService, @@ -96,6 +122,14 @@ public class TestDataSeeder implements CommandLineRunner { this.paymentService = paymentService; } + /** + * This method is executed when the application starts. + * It seeds the database with initial data for roles, permissions, users, + * memberships, training groups, sessions, and other entities. + * + * @param args command line arguments + * @throws Exception if an error occurs during seeding + */ @Override public void run(String... args) throws Exception { // Roles @@ -111,32 +145,30 @@ public class TestDataSeeder implements CommandLineRunner { adminRole.setName("ROLE_ADMIN"); this.roleService.save(adminRole); - // Permisos + // Permissions for (PermissionValues value : PermissionValues.values()) { Permission p = new Permission(); p.setPermissionName(value); this.permissionService.save(p); } - // Obtener permisos de la base de datos + // Obtain permissions Permission fullAccess = this.permissionService.findPermissionByName(PermissionValues.FULL_ACCESS); Permission manageUsers = this.permissionService.findPermissionByName(PermissionValues.MANAGE_STUDENTS); Permission viewOwnData = this.permissionService.findPermissionByName(PermissionValues.VIEW_OWN_DATA); - // Asignar permisos a roles + // Assign permissions to roles studentRole.addPermission(viewOwnData); teacherRole.addPermission(manageUsers); teacherRole.addPermission(viewOwnData); adminRole.addPermission(fullAccess); - - // Guardar roles actualizados + // Update roles with permissions this.roleService.update(studentRole); this.roleService.update(teacherRole); this.roleService.update(adminRole); - - // Membresías + // Memberships Membership basicMembership = new Membership(); basicMembership.setType(MembershipTypeValues.BASIC); basicMembership.setStartDate(LocalDate.now()); @@ -165,7 +197,7 @@ public class TestDataSeeder implements CommandLineRunner { noLimitMembership.setStatus(StatusValues.ACTIVE); this.membershipService.save(noLimitMembership); - // Usuarios + // Users and their roles User studentUser = new User(); studentUser.setName("Student"); studentUser.setSurname("One"); @@ -178,7 +210,6 @@ public class TestDataSeeder implements CommandLineRunner { studentUser.setRole(studentRole); this.userService.save(studentUser); - Student student = new Student(); student.setUser(studentUser); student.setBirthdate(LocalDate.of(2005, 5, 20)); @@ -210,7 +241,6 @@ public class TestDataSeeder implements CommandLineRunner { teacherUser.setRole(teacherRole); this.userService.save(teacherUser); - Teacher teacher = new Teacher(); teacher.setUser(teacherUser); teacher.setDiscipline("Jiu-Jitsu"); @@ -228,7 +258,6 @@ public class TestDataSeeder implements CommandLineRunner { adminUser.setRole(adminRole); this.userService.save(adminUser); - Admin admin = new Admin(); admin.setUser(adminUser); this.adminService.save(admin); @@ -246,32 +275,31 @@ public class TestDataSeeder implements CommandLineRunner { notification.addUser(adminUser); this.notificationService.update(notification); - // Grupo de entrenamiento + // Create a training group TrainingGroup trainingGroup = new TrainingGroup(); trainingGroup.setName("Grupo JiuJitsu Avanzado"); trainingGroup.setSchedule(LocalDateTime.now().plusDays(1)); trainingGroup.setTeacher(teacher); this.trainingGroupService.save(trainingGroup); - // Asignar grupo al estudiante + // Assign the training group to the student this.studentService.addGroupToStudent(student.getId(), trainingGroup); - // Sesión de entrenamiento + // Create a training session TrainingSession trainingSession = new TrainingSession(); trainingSession.setTrainingGroup(trainingGroup); trainingSession.setDate(LocalDateTime.now().plusDays(2)); trainingSession.setStatus(StatusValues.ACTIVE); this.trainingSessionService.save(trainingSession); - - // Crear asistencia con sesión válida + // Add the training session to the group Assistance validAssistance = new Assistance(); validAssistance.setDate(LocalDateTime.now()); validAssistance.setStudent(student); validAssistance.setTrainingSession(trainingSession); this.studentService.addAssistanceToStudent(student, validAssistance); - // Factura para el estudiante + // Create an invalid assistance Invoice invoice = new Invoice(); invoice.setUser(studentUser); invoice.setDate(LocalDateTime.now().minusDays(1)); @@ -280,13 +308,13 @@ public class TestDataSeeder implements CommandLineRunner { this.invoiceService.save(invoice); this.userService.addInvoiceToUser(studentUser, invoice); - // IVA TYPE + // Create an IVA type IVAType ivaGeneral = new IVAType(); ivaGeneral.setPercentage(new BigDecimal("21.00")); ivaGeneral.setDescription("IVA General"); this.ivaTypeService.save(ivaGeneral); - // PRODUCTO / SERVICIO + // Create a product/service ProductService jiuJitsuClass = new ProductService(); jiuJitsuClass.setName("Clase de JiuJitsu"); jiuJitsuClass.setType("Servicio"); @@ -295,7 +323,7 @@ public class TestDataSeeder implements CommandLineRunner { jiuJitsuClass.setStatus(StatusValues.ACTIVE); this.productServiceService.save(jiuJitsuClass); - // LÍNEA DE FACTURA + // Add the product/service to the invoice InvoiceLine line = new InvoiceLine(); line.setInvoice(invoice); line.setProductService(jiuJitsuClass); @@ -304,18 +332,16 @@ public class TestDataSeeder implements CommandLineRunner { line.setSubtotal(jiuJitsuClass.getPrice().multiply(BigDecimal.valueOf(line.getQuantity()))); this.invoiceService.addLineToInvoice(invoice, line); - - // PAGO (PAYMENT) + // Calculate the total with IVA Payment payment = new Payment(); payment.setInvoice(invoice); - payment.setAmount(invoice.getTotal()); // Debe incluir el IVA si se calcula externamente + payment.setAmount(invoice.getTotal()); payment.setPaymentMethod(PaymentMethodValues.CREDIT_CARD); payment.setPaymentDate(LocalDateTime.now()); payment.setStatus(StatusValues.PAID); this.paymentService.save(payment); - - // Mostrar todo + // Show all data in the console System.out.println("--- ROLES ---"); this.roleService.findAll().forEach(System.out::println); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/AssistanceService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/AssistanceService.java index e52efe6..4919ad9 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/AssistanceService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/AssistanceService.java @@ -23,15 +23,23 @@ public class AssistanceService extends AbstractService { private final AssistanceRepository assistanceRepository; /** - * Constructor for AssitanceService. + * Constructor for AssistanceService. * - * @param assistanceRepository the assistance repository + * @param assistanceRepository the repository for assistance records */ public AssistanceService(AssistanceRepository assistanceRepository) { super(assistanceRepository); this.assistanceRepository = assistanceRepository; } + /** + * Creates a new Assistance entity with the current date and time. + * + * @param entity the assistance to create + * @return the created assistance entity + * @throws IllegalArgumentException if the assistance is null + * @throws DuplicateEntityException if the assistance already exists + */ @Override public Assistance save(Assistance entity) throws IllegalArgumentException, DuplicateEntityException { logger.info("Saving assistance: {}", entity); @@ -42,6 +50,15 @@ public class AssistanceService extends AbstractService { return super.save(entity); } + /** + * Updates an existing Assistance entity. + * + * @param entity the entity to update + * @return the updated assistance entity + * @throws IllegalArgumentException if the assistance is null + * @throws InvalidDataException if the assistance data is invalid + * @throws EntityNotFoundException if the assistance does not exist + */ @Override public Assistance update(Assistance entity) throws IllegalArgumentException, InvalidDataException, EntityNotFoundException { logger.info("Updating assistance: {}", entity); @@ -49,11 +66,26 @@ public class AssistanceService extends AbstractService { return super.update(entity); } + /** + * Gets the ID of the Assistance entity. + * + * @param entity the assistance entity + * @return the ID of the assistance entity + * @throws IllegalStateException if the entity does not have an ID + */ @Override protected Integer getEntityId(Assistance entity) throws IllegalStateException { return super.getEntityId(entity); } + /** + * Finds an Assistance entity by its ID. + * + * @param id the ID of the assistance + * @return the found assistance entity + * @throws InvalidDataException if the ID is null + * @throws EntityNotFoundException if the assistance does not exist + */ @Override public Assistance findById(Integer id) throws InvalidDataException, EntityNotFoundException { logger.info("Finding assistance by ID: {}", id); @@ -64,6 +96,13 @@ public class AssistanceService extends AbstractService { return super.findById(id); } + /** + * Deletes an Assistance entity by its ID. + * + * @param id the ID of the assistance to delete + * @throws InvalidDataException if the ID is null + * @throws EntityNotFoundException if the assistance does not exist + */ @Override public void deleteById(Integer id) throws InvalidDataException, EntityNotFoundException { logger.info("Deleting assistance by ID: {}", id); @@ -75,12 +114,23 @@ public class AssistanceService extends AbstractService { super.deleteById(id); } + /** + * Finds all Assistance records. + * + * @return a list of all assistance records + */ @Override public List findAll() { logger.info("Finding all assistance records"); return super.findAll(); } + /** + * Checks if an Assistance entity exists. + * + * @param entity the assistance entity to check + * @return true if the assistance exists, false otherwise + */ @Override protected boolean exists(Assistance entity) { logger.info("Checking if assistance exists: {}", entity); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/TrainingGroupService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/TrainingGroupService.java index 864c6f8..a46a091 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/TrainingGroupService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/class_managment_services/TrainingGroupService.java @@ -15,17 +15,33 @@ import org.springframework.stereotype.Service; import java.util.List; +/** + * Service class for managing training groups. + */ @Service public class TrainingGroupService extends AbstractService { private static final Logger logger = LoggerFactory.getLogger(TrainingGroupService.class); private final TrainingGroupRepository trainingGroupRepository; + /** + * Constructor for TrainingGroupService. + * + * @param trainingGroupRepository the repository for training groups + */ public TrainingGroupService(TrainingGroupRepository trainingGroupRepository) { super(trainingGroupRepository); this.trainingGroupRepository = trainingGroupRepository; } + /** + * Saves a new TrainingGroup entity. + * + * @param entity the training group to save + * @return the saved training group + * @throws IllegalArgumentException if the training group is null + * @throws DuplicateEntityException if the training group already exists + */ @Override public TrainingGroup save(TrainingGroup entity) throws IllegalArgumentException, DuplicateEntityException { logger.info("Saving training group: {}", entity); @@ -36,6 +52,15 @@ public class TrainingGroupService extends AbstractService findAll() { logger.info("Finding all training groups"); return super.findAll(); } + /** + * Checks if a TrainingGroup exists in the database. + * + * @param entity the training group to check + * @return true if the training group exists, false otherwise + */ @Override protected boolean exists(TrainingGroup entity) { logger.info("Checking if training group exists: {}", entity); return super.exists(entity); } + /** + * Validates the TrainingGroup entity. + * + * @param group the training group to validate + * @throws InvalidDataException if the training group data is invalid + */ private void validateTrainingGroup(TrainingGroup group) throws InvalidDataException { if (group.getTeacher() == null) { throw new InvalidDataException("Teacher cannot be null"); @@ -89,6 +152,14 @@ public class TrainingGroupService extends AbstractService findAll() { logger.info("Finding all training sessions"); return super.findAll(); } + /** + * Checks if a training session exists in the database. + * + * @param entity the training session to check + * @return true if the training session exists, false otherwise + */ @Override protected boolean exists(TrainingSession entity) { logger.info("Checking if training session exists: {}", entity); @@ -98,6 +153,14 @@ public class TrainingSessionService extends AbstractService getStudentsFromSession(TrainingSession session) throws EntityNotFoundException, InvalidDataException { if (session == null || session.getId() == null) { throw new InvalidDataException("Session cannot be null"); @@ -107,6 +170,13 @@ public class TrainingSessionService extends AbstractService { @@ -21,12 +25,25 @@ public class IVATypeService extends AbstractService { private final IVATypeRepository ivaTypeRepository; private final ProductServiceRepository productServiceRepository; + /** + * Constructor for IVATypeService. + * + * @param ivaTypeRepository the repository for IVA types + * @param productServiceRepository the repository for product services + */ public IVATypeService(IVATypeRepository ivaTypeRepository, ProductServiceRepository productServiceRepository) { super(ivaTypeRepository); this.ivaTypeRepository = ivaTypeRepository; this.productServiceRepository = productServiceRepository; } + /** + * Saves a new IVAType entity. + * + * @param entity the IVAType to save + * @return the saved IVAType + * @throws DuplicateEntityException if an entity with the same percentage already exists + */ @Override public IVAType save(IVAType entity) throws DuplicateEntityException { logger.info("Saving IVAType: {}", entity); @@ -37,6 +54,14 @@ public class IVATypeService extends AbstractService { return super.save(entity); } + /** + * Updates an existing IVAType entity. + * + * @param entity the IVAType to update + * @return the updated IVAType + * @throws InvalidDataException if the entity data is invalid + * @throws EntityNotFoundException if the entity does not exist + */ @Override public IVAType update(IVAType entity) throws InvalidDataException, EntityNotFoundException { logger.info("Updating IVAType: {}", entity); @@ -44,22 +69,46 @@ public class IVATypeService extends AbstractService { return super.update(entity); } + /** + * Verifies if an IVAType entity exists in the database. + * + * @param entity the entity to check + * @return true if the entity exists, false otherwise + */ @Override protected boolean exists(IVAType entity) { return entity != null && entity.getId() != null && ivaTypeRepository.existsById(entity.getId()); } + /** + * Gets the ID of the IVAType entity. + * + * @param entity the IVAType entity + * @return the ID of the IVAType + */ @Override protected Integer getEntityId(IVAType entity) { return entity.getId(); } + /** + * Finds all IVAType entities. + * + * @return a list of all IVAType entities + */ @Override public List findAll() { logger.info("Retrieving all IVA types"); return super.findAll(); } + /** + * Deletes an IVAType entity by its ID. + * + * @param id the ID of the entity to delete + * @throws InvalidDataException if the ID is null or invalid + * @throws EntityNotFoundException if the entity does not exist + */ @Override public void deleteById(Integer id) throws InvalidDataException, EntityNotFoundException { if (productServiceRepository.existsByIvaTypeId(id)) { @@ -68,6 +117,12 @@ public class IVATypeService extends AbstractService { super.deleteById(id); } + /** + * Validates the IVAType entity before saving or updating. + * + * @param ivaType the IVAType to validate + * @throws InvalidDataException if the IVAType data is invalid + */ private void validate(IVAType ivaType) { if (ivaType.getPercentage() == null || ivaType.getPercentage().compareTo(BigDecimal.ZERO) < 0) { throw new InvalidDataException("IVA percentage must be >= 0"); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceLineService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceLineService.java index 9a61858..ed4f044 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceLineService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceLineService.java @@ -23,11 +23,24 @@ public class InvoiceLineService extends AbstractService { private static final Logger logger = LoggerFactory.getLogger(InvoiceLineService.class); private final InvoiceLineRepository invoiceLineRepository; + /** + * Constructor for InvoiceLineService. + * + * @param invoiceLineRepository the repository for InvoiceLine entities + */ public InvoiceLineService(InvoiceLineRepository invoiceLineRepository) { super(invoiceLineRepository); this.invoiceLineRepository = invoiceLineRepository; } + /** + * Saves a new InvoiceLine entity. + * + * @param entity the InvoiceLine to save + * @return the saved InvoiceLine + * @throws IllegalArgumentException if the entity is null + * @throws DuplicateEntityException if an entity with the same ID already exists + */ @Override public InvoiceLine save(InvoiceLine entity) throws IllegalArgumentException, DuplicateEntityException { logger.info("Saving invoice line: {}", entity); @@ -35,6 +48,15 @@ public class InvoiceLineService extends AbstractService { return super.save(entity); } + /** + * Updates an existing InvoiceLine entity. + * + * @param entity the InvoiceLine to update + * @return the updated InvoiceLine + * @throws IllegalArgumentException if the entity is null + * @throws InvalidDataException if the entity data is invalid + * @throws EntityNotFoundException if the entity does not exist + */ @Override public InvoiceLine update(InvoiceLine entity) throws IllegalArgumentException, InvalidDataException, EntityNotFoundException { logger.info("Updating invoice line: {}", entity); @@ -42,22 +64,44 @@ public class InvoiceLineService extends AbstractService { return super.update(entity); } + /** + * Finds all InvoiceLine entities. + * + * @return a list of all InvoiceLine entities + */ @Override public List findAll() { logger.info("Retrieving all invoice lines"); return super.findAll(); } + /** + * Verifies if an InvoiceLine entity exists in the repository. + * + * @param entity the entity to check + * @return true if the entity exists, false otherwise + */ @Override protected boolean exists(InvoiceLine entity) { return entity != null && entity.getId() != null && invoiceLineRepository.existsById(entity.getId()); } + /** + * Retrieves the ID of an InvoiceLine entity. + * + * @param entity the entity from which to retrieve the ID + * @return the ID of the InvoiceLine entity + */ @Override protected Integer getEntityId(InvoiceLine entity) { return entity.getId(); } + /** + * Validates the InvoiceLine entity before saving or updating. + * + * @param line the InvoiceLine to validate + */ private void validateInvoiceLine(InvoiceLine line) { if (line.getInvoice() == null) { throw new InvalidDataException("InvoiceLine must be linked to an invoice"); @@ -72,7 +116,6 @@ public class InvoiceLineService extends AbstractService { throw new InvalidDataException("Unit price must be greater than 0"); } - // Validación automática de subtotal BigDecimal expected = line.getUnitPrice().multiply(new BigDecimal(line.getQuantity())); line.setSubtotal(expected); } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceService.java index 53b6776..5efc95c 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/InvoiceService.java @@ -17,17 +17,34 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Objects; +/** + * Service class for managing invoices. + * This class provides methods to perform CRUD operations on Invoice entities. + */ @Service public class InvoiceService extends AbstractService { private static final Logger logger = LoggerFactory.getLogger(InvoiceService.class); private final InvoiceRepository invoiceRepository; + /** + * Constructor for InvoiceService. + * + * @param invoiceRepository the repository for Invoice entities + */ public InvoiceService(InvoiceRepository invoiceRepository) { super(invoiceRepository); this.invoiceRepository = invoiceRepository; } + /** + * Saves a new invoice in the database. + * + * @param entity the invoice to save + * @return the saved invoice + * @throws IllegalArgumentException if the invoice is null + * @throws DuplicateEntityException if an invoice with the same ID already exists + */ @Override public Invoice save(Invoice entity) throws IllegalArgumentException, DuplicateEntityException { logger.info("Saving invoice: {}", entity); @@ -38,6 +55,15 @@ public class InvoiceService extends AbstractService { return super.save(entity); } + /** + * Updates an existing invoice in the database. + * + * @param entity the invoice to update + * @return the updated invoice + * @throws IllegalArgumentException if the invoice is null + * @throws InvalidDataException if the invoice data is invalid + * @throws EntityNotFoundException if the invoice does not exist + */ @Override public Invoice update(Invoice entity) throws IllegalArgumentException, InvalidDataException, EntityNotFoundException { logger.info("Updating invoice: {}", entity); @@ -45,12 +71,27 @@ public class InvoiceService extends AbstractService { return super.update(entity); } + /** + * Finds an invoice by its ID. + * + * @param id the ID of the entity to find + * @return the found invoice + * @throws InvalidDataException if the ID is null or invalid + * @throws EntityNotFoundException if the invoice does not exist + */ @Override public Invoice findById(Integer id) throws InvalidDataException, EntityNotFoundException { logger.info("Finding invoice by ID: {}", id); return super.findById(id); } + /** + * Deletes an invoice by its ID. + * + * @param id the ID of the invoice to delete + * @throws InvalidDataException if the ID is null or invalid + * @throws EntityNotFoundException if the invoice does not exist + */ @Override public void deleteById(Integer id) throws InvalidDataException, EntityNotFoundException { logger.info("Deleting invoice by ID: {}", id); @@ -61,24 +102,45 @@ public class InvoiceService extends AbstractService { super.deleteById(id); } + /** + * Retrieves all invoices. + * + * @return a list of all invoices + */ @Override public List findAll() { logger.info("Retrieving all invoices"); return super.findAll(); } + /** + * Checks if an invoice exists in the database. + * + * @param entity the invoice to check + * @return true if the invoice exists, false otherwise + */ @Override protected boolean exists(Invoice entity) { return entity != null && entity.getId() != null && invoiceRepository.existsById(entity.getId()); } + /** + * Gets the ID of the invoice entity. + * + * @param entity the invoice entity + * @return the ID of the invoice + */ @Override protected Integer getEntityId(Invoice entity) { return entity.getId(); } /** - * Obtiene todas las facturas de un usuario específico. + * Finds all invoices associated with a specific user ID. + * + * @param userId the ID of the user + * @return a list of invoices associated with the user + * @throws InvalidDataException if the user ID is null */ public List findAllInvoicesByUserId(Integer userId) throws InvalidDataException { if (userId == null) { @@ -91,7 +153,10 @@ public class InvoiceService extends AbstractService { /** - * Validates the invoice before saving/updating. + * Validates the invoice entity. + * + * @param invoice the invoice to validate + * @throws InvalidDataException if the invoice data is invalid */ private void validateInvoice(Invoice invoice) throws InvalidDataException { if (invoice.getUser() == null) { @@ -108,6 +173,13 @@ public class InvoiceService extends AbstractService { } } + /** + * Adds a line to an invoice. + * + * @param invoice the invoice to which the line will be added + * @param line the line to add + * @throws InvalidDataException if the invoice or line is null + */ @Transactional public void addLineToInvoice(Invoice invoice, InvoiceLine line) { if (invoice == null || line == null) { @@ -119,6 +191,13 @@ public class InvoiceService extends AbstractService { invoiceRepository.save(invoice); } + /** + * Removes a line from an invoice. + * + * @param invoice the invoice from which the line will be removed + * @param line the line to remove + * @throws InvalidDataException if the invoice or line is null + */ @Transactional public void removeLineFromInvoice(Invoice invoice, InvoiceLine line) { if (invoice == null || line == null) { @@ -130,6 +209,11 @@ public class InvoiceService extends AbstractService { invoiceRepository.save(invoice); } + /** + * Updates the total amount of an invoice based on its lines. + * + * @param invoice the invoice to update + */ public void updateInvoiceTotal(Invoice invoice) { BigDecimal total = BigDecimal.ZERO; @@ -152,13 +236,25 @@ public class InvoiceService extends AbstractService { invoice.setTotal(total); } - + /** + * Adds a line to an invoice by its ID. + * + * @param invoiceId the ID of the invoice to which the line will be added + * @param line the line to add + */ @Transactional public void addLineToInvoiceById(Integer invoiceId, InvoiceLine line) { Invoice invoice = findById(invoiceId); addLineToInvoice(invoice, line); } + /** + * Removes a line from an invoice by its ID. + * + * @param invoiceId the ID of the invoice from which the line will be removed + * @param lineId the ID of the line to remove + * @throws EntityNotFoundException if the line does not exist in the invoice + */ @Transactional public void removeLineFromInvoiceById(Integer invoiceId, Integer lineId) { Invoice invoice = findById(invoiceId); @@ -169,12 +265,21 @@ public class InvoiceService extends AbstractService { removeLineFromInvoice(invoice, line); } + /** + * Recalculates the total of an invoice and saves it. + * + * @param invoice the invoice to recalculate + */ public void recalculateTotal(Invoice invoice) { updateInvoiceTotal(invoice); invoiceRepository.save(invoice); } - - + + /** + * Clears all lines from an invoice and updates the total. + * + * @param invoice the invoice to clear lines from + */ @Transactional public void clearInvoiceLines(Invoice invoice) { invoice.getInvoiceLines().clear(); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java index 83b99ca..d70e8be 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java @@ -17,6 +17,10 @@ import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +/** + * Service class for managing Payment entities. + * This class provides methods to perform CRUD operations on Payment entities. + */ @Service public class PaymentService extends AbstractService { @@ -24,12 +28,27 @@ public class PaymentService extends AbstractService { private final PaymentRepository paymentRepository; private final InvoiceService invoiceService; + /** + * Constructor for PaymentService. + * + * @param paymentRepository the repository for Payment entities + * @param invoiceService the service for Invoice entities + */ public PaymentService(PaymentRepository paymentRepository, InvoiceService invoiceService) { super(paymentRepository); this.paymentRepository = paymentRepository; this.invoiceService = invoiceService; } + /** + * Saves a new Payment entity. + * + * @param payment the payment to save + * @return the saved payment + * @throws DuplicateEntityException if a payment already exists for the invoice + * @throws EntityNotFoundException if the associated invoice does not exist + * @throws InvalidDataException if the payment data is invalid + */ @Override @Transactional public Payment save(Payment payment) throws DuplicateEntityException { @@ -50,14 +69,20 @@ public class PaymentService extends AbstractService { Payment savedPayment = super.save(payment); - //invoice.setPayment(savedPayment); invoice.setStatus(StatusValues.PAID); invoiceService.update(invoice); return savedPayment; } - + /** + * Updates an existing Payment entity. + * + * @param payment the entity to update + * @return the updated payment + * @throws EntityNotFoundException if the payment does not exist + * @throws InvalidDataException if the payment data is invalid + */ @Override @Transactional public Payment update(Payment payment) throws EntityNotFoundException, InvalidDataException { @@ -82,22 +107,44 @@ public class PaymentService extends AbstractService { return super.update(payment); } + /** + * Verifies if a Payment entity exists in the database. + * + * @param entity the entity to check + * @return true if the entity exists, false otherwise + */ @Override protected boolean exists(Payment entity) { return entity != null && entity.getId() != null && paymentRepository.existsById(entity.getId()); } + /** + * Obtains the ID of a Payment entity. + * + * @param entity the entity from which to retrieve the ID + * @return the ID of the Payment entity + */ @Override protected Integer getEntityId(Payment entity) { return entity.getId(); } + /** + * Obtains all Payment entities. + * + * @return a list of all Payment entities + */ @Override public List findAll() { logger.info("Retrieving all payments"); return super.findAll(); } + /** + * Validates the Payment entity before saving or updating. + * + * @param payment the Payment to validate + */ private void validate(Payment payment) { if (payment.getInvoice() == null) { throw new InvalidDataException("Payment must be linked to an invoice"); @@ -116,6 +163,11 @@ public class PaymentService extends AbstractService { } } + /** + * Removes a Payment entity by its ID. + * + * @param paymentId the ID of the payment to remove + */ @Transactional public void removePayment(Integer paymentId) { Payment payment = findById(paymentId); @@ -131,10 +183,22 @@ public class PaymentService extends AbstractService { invoiceService.update(invoice); } + /** + * Finds all Payment entities associated with a specific user ID. + * + * @param userId the ID of the user + * @return a list of payments associated with the user + */ public List findAllByUserId(Integer userId) { return paymentRepository.findByInvoice_User_Id(userId); } + /** + * Retrieves an Invoice by its ID. + * + * @param id the ID of the invoice + * @return the Invoice entity + */ public Invoice getInvoiceById(Integer id) { return invoiceService.findById(id); } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/ProductServiceService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/ProductServiceService.java index 7bb320b..0fe0789 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/ProductServiceService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/ProductServiceService.java @@ -15,6 +15,10 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.List; +/** + * Service class for managing ProductService entities. + * This class provides methods to perform CRUD operations on ProductService entities. + */ @Service public class ProductServiceService extends AbstractService { @@ -22,12 +26,26 @@ public class ProductServiceService extends AbstractService findAll() { logger.info("Retrieving all products/services"); return super.findAll(); } + /** + * Exists method to check if a ProductService entity exists in the repository. + * + * @param entity the entity to check + * @return true if the entity exists, false otherwise + */ @Override protected boolean exists(ProductService entity) { return entity != null && entity.getId() != null && productServiceRepository.existsById(entity.getId()); } + /** + * Gets the ID of a ProductService entity. + * + * @param entity the entity from which to retrieve the ID + * @return the ID of the ProductService entity + */ @Override protected Integer getEntityId(ProductService entity) { return entity.getId(); } + /** + * Validates the ProductService entity before saving or updating. + * + * @param product the ProductService to validate + */ private void validateProduct(ProductService product) { if (product.getName() == null || product.getName().isBlank()) { throw new InvalidDataException("Product name cannot be null or blank"); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/NotificationService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/NotificationService.java index 9536fa6..8ec5a92 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/NotificationService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/NotificationService.java @@ -67,6 +67,14 @@ public class NotificationService extends AbstractService return super.findById(id); } + /** + * Updates an existing notification. + * + * @param entity the notification to update + * @return the updated notification + * @throws EntityNotFoundException if the notification is not found + * @throws InvalidDataException if the entity data is invalid + */ @Transactional @Override public Notification update(Notification entity) throws EntityNotFoundException { diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/StudentService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/StudentService.java index f8c7901..3e38ba3 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/StudentService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/StudentService.java @@ -34,7 +34,9 @@ public class StudentService extends AbstractService { /** * Constructor for StudentService. * - * @param studentRepository the student repository + * @param studentRepository the student repository + * @param userRepository the user repository + * @param assistanceRepository the assistance repository */ public StudentService(StudentRepository studentRepository, UserRepository userRepository, AssistanceRepository assistanceRepository) { super(studentRepository); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/TeacherService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/TeacherService.java index fbf47fa..c9e2e7a 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/TeacherService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/TeacherService.java @@ -33,6 +33,7 @@ public class TeacherService extends AbstractService { * Constructor for TeacherService. * * @param teacherRepository the teacher repository + * @param userRepository the user repository */ public TeacherService(TeacherRepository teacherRepository, UserRepository userRepository) { super(teacherRepository); diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/UserService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/UserService.java index 2387fc8..0ca2179 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/UserService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/user_managment_services/UserService.java @@ -156,6 +156,13 @@ public class UserService extends AbstractService { return userRepository.existsByEmail(email); } + /** + * Adds an invoice to a user. + * + * @param user the user to whom the invoice will be added + * @param invoice the invoice to add + * @throws InvalidDataException if the user or invoice is null + */ @Transactional public void addInvoiceToUser(User user, Invoice invoice) throws InvalidDataException { if (user == null || invoice == null) { @@ -167,6 +174,13 @@ public class UserService extends AbstractService { userRepository.save(user); } + /** + * Removes an invoice from a user. + * + * @param user the user from whom the invoice will be removed + * @param invoice the invoice to remove + * @throws InvalidDataException if the user or invoice is null + */ @Transactional public void removeInvoiceFromUser(User user, Invoice invoice) throws InvalidDataException { if (user == null || invoice == null) {