first commit

This commit is contained in:
Your NamebaishaliHolocron
2026-06-15 12:57:03 +05:30
commit b9ac5ae0b2
398 changed files with 49583 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ikon.projectmanagement</groupId>
<artifactId>projectmanagement</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>projectmanagement-server</artifactId>
<repositories>
<repository>
<id>confluent</id>
<url>https://packages.confluent.io/maven/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.ikon</groupId>
<artifactId>ikon-sdk</artifactId>
<version>${ikon.sdk.version}</version>
</dependency>
<dependency>
<groupId>com.ikon.projectmanagement</groupId>
<artifactId>projectmanagement-client</artifactId>
<version>${projectmanagement.version}</version>
</dependency>
<dependency>
<groupId>com.ikon</groupId>
<artifactId>ikon-client</artifactId>
<version>${ikon.sdk.version}</version>
</dependency>
<!-- <dependency>
<groupId>com.ikon</groupId>
<artifactId>ikon-processmanagement</artifactId>
<version>${ikon.sdk.version}</version>
</dependency> -->
<!-- <dependency>
<groupId>com.ikon</groupId>
<artifactId>ikon-job-executor</artifactId>
<version>${ikon.sdk.version}</version>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>io.confluent</groupId>
<artifactId>kafka-avro-serializer</artifactId>
<version>${avro-serializer.version}</version>
</dependency>
<dependency>
<groupId>com.ikon</groupId>
<artifactId>ikon-connector</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<!-- Attach Javadoc JAR -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,42 @@
package com.ikon.projectmanagement;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.cassandra.CassandraHealthContributorAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.ikon.autoconfigure.annotation.EnableIkonSdk;
import com.ikon.sdk.config.IkonSdkConfig;
@EntityScan(basePackages = {
"com.ikon.projectmanagement.entity",
})
@EnableJpaRepositories(basePackages = {
"com.ikon.projectmanagement.repository",
})
@EnableFeignClients(basePackages = {
"com.ikon.client",
})
@SpringBootApplication(scanBasePackages = {
"com.ikon.projectmanagement",
}, exclude = {
CassandraAutoConfiguration.class,
CassandraHealthContributorAutoConfiguration.class
})
@EnableJpaAuditing
@EnableIkonSdk(configuration = IkonSdkConfig.class)
public class ProjectManagementApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectManagementApplication.class, args);
}
}

View File

@@ -0,0 +1,15 @@
package com.ikon.projectmanagement.config;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModelMapperConfig {
@Bean
ModelMapper modelMapper() {
return new ModelMapper();
}
}

View File

@@ -0,0 +1,13 @@
package com.ikon.projectmanagement.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfigs {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,67 @@
package com.ikon.projectmanagement.config;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.ikon.webservice.security.IkonJwtTokenConverter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
private Converter<Jwt, JwtAuthenticationToken> ikonJwtTokenConverter = new IkonJwtTokenConverter(
new JwtGrantedAuthoritiesConverter());
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource())) // Enable CORS
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authorize) -> {
authorize
.requestMatchers("/actuator/health", "/actuator/health/**").permitAll()
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated();
})
.oauth2ResourceServer((oauth2) -> {
oauth2.jwt((jwt) -> jwt.jwtAuthenticationConverter(ikonJwtTokenConverter));
});
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("http://localhost:3000"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

View File

@@ -0,0 +1,31 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.DashboardApi;
import com.ikon.projectmanagement.dto.response.DashboardWidgetsResponseDto;
import com.ikon.projectmanagement.dto.response.StatusWiseProjectResponseData;
import com.ikon.projectmanagement.service.DashboardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class DashboardController implements DashboardApi {
private final DashboardService dashboardService;
@Override
public ResponseEntity<DashboardWidgetsResponseDto> getWidgetsData(String accessToken) {
DashboardWidgetsResponseDto widgetsData = dashboardService.getDashboardWidgetsData();
return ResponseEntity.ok(widgetsData);
}
@Override
public ResponseEntity<List<StatusWiseProjectResponseData>> getStatusWiseProjectData(String accessToken) {
List<StatusWiseProjectResponseData> statusWiseData = dashboardService.getStatusWiseProjectData();
return ResponseEntity.ok(statusWiseData);
}
}

View File

@@ -0,0 +1,63 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.EmployeeApi;
import com.ikon.projectmanagement.dto.request.EmployeeRequestDto;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.service.EmployeeService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class EmployeeController implements EmployeeApi {
private final EmployeeService employeeService;
@Override
public ResponseEntity<List<EmployeeResponseDto>> getAllEmployees() {
return ResponseEntity.ok(employeeService.getAllEmployees());
}
@Override
public ResponseEntity<EmployeeResponseDto> getEmployeeById(String id) {
try {
EmployeeResponseDto employee = employeeService.getEmployeeById(id);
return ResponseEntity.ok(employee);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<EmployeeResponseDto> createEmployee(EmployeeRequestDto dto) {
try {
EmployeeResponseDto createdEmployee = employeeService.createEmployee(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(createdEmployee);
} catch (RuntimeException e) {
return ResponseEntity.badRequest().build();
}
}
@Override
public ResponseEntity<EmployeeResponseDto> updateEmployee(String id, EmployeeRequestDto dto) {
try {
EmployeeResponseDto updatedEmployee = employeeService.updateEmployee(id, dto);
return ResponseEntity.ok(updatedEmployee);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<Void> deleteEmployee(String id) {
try {
employeeService.deleteEmployee(id);
return ResponseEntity.noContent().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
}

View File

@@ -0,0 +1,32 @@
package com.ikon.projectmanagement.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.ikon.projectmanagement.api.FxRateApi;
import com.ikon.projectmanagement.dto.response.FxRateResponseDto;
import com.ikon.projectmanagement.service.FxRateService;
@RestController
@RequiredArgsConstructor
public class FxRateController implements FxRateApi {
private final FxRateService fxRateService;
@Override
public ResponseEntity<Page<FxRateResponseDto>> getAllFxRates(
@RequestHeader("Authorization") String accessToken,
Pageable pageable) {
return ResponseEntity.ok(fxRateService.getAllFxRates(pageable));
}
public ResponseEntity<FxRateResponseDto> getFxRateByYear(
@RequestHeader("Authorization") String accessToken,
@PathVariable String year) {
return ResponseEntity.ok(fxRateService.getFxRateByYear(year));
}
}

View File

@@ -0,0 +1,64 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.GradeApi;
import com.ikon.projectmanagement.dto.request.GradeRequestDto;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import com.ikon.projectmanagement.service.GradeService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class GradeController implements GradeApi {
private final GradeService gradeService;
@Override
public ResponseEntity<List<GradeResponseDto>> getAllGrades() {
return ResponseEntity.ok(gradeService.getAllGrades());
}
@Override
public ResponseEntity<GradeResponseDto> getGradeById(UUID id) {
try {
GradeResponseDto grade = gradeService.getGradeById(id);
return ResponseEntity.ok(grade);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<GradeResponseDto> createGrade(GradeRequestDto dto) {
try {
GradeResponseDto createdGrade = gradeService.createGrade(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(createdGrade);
} catch (RuntimeException e) {
return ResponseEntity.badRequest().build();
}
}
@Override
public ResponseEntity<GradeResponseDto> updateGrade(UUID id, GradeRequestDto dto) {
try {
GradeResponseDto updatedGrade = gradeService.updateGrade(id, dto);
return ResponseEntity.ok(updatedGrade);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<Void> deleteGrade(UUID id) {
try {
gradeService.deleteGrade(id);
return ResponseEntity.noContent().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ImportEmployeeApi;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.service.ImportEmployeeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImportEmployeeController implements ImportEmployeeApi {
private final ImportEmployeeService importEmployeeService;
@Override
public ResponseEntity<List<EmployeeResponseDto>> fetchAllEmployees(String accessToken) {
return ResponseEntity.ok(importEmployeeService.importEmployees(accessToken));
}
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ImportFxRateApi;
import com.ikon.projectmanagement.dto.response.FxRateDto;
import com.ikon.projectmanagement.service.ImportFxRateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImportFxRateController implements ImportFxRateApi {
private final ImportFxRateService importFxRateService;
@Override
public ResponseEntity<List<FxRateDto>> fetchAllFxRates(String accessToken) {
return ResponseEntity.ok(importFxRateService.importFxRates(accessToken));
}
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ImportGradeApi;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import com.ikon.projectmanagement.service.ImportGradeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImportGradeController implements ImportGradeApi {
private final ImportGradeService importGradeService;
@Override
public ResponseEntity<List<GradeResponseDto>> fetchAllGrades(String accessToken) {
return ResponseEntity.ok(importGradeService.importGrades(accessToken));
}
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ImportRoleApi;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
import com.ikon.projectmanagement.service.ImportRoleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImportRoleController implements ImportRoleApi {
private final ImportRoleService importRoleService;
@Override
public ResponseEntity<List<RoleResponseDto>> fetchAllRoles(String accessToken) {
return ResponseEntity.ok(importRoleService.importRoles(accessToken));
}
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ImportWorkingDayApi;
import com.ikon.projectmanagement.dto.response.WorkingDayDto;
import com.ikon.projectmanagement.service.ImportWorkingDayService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ImportWorkingDayController implements ImportWorkingDayApi {
private final ImportWorkingDayService importWorkingDayService;
@Override
public ResponseEntity<List<WorkingDayDto>> fetchAllWorkingDays(String accessToken) {
return ResponseEntity.ok(importWorkingDayService.importWorkingDays(accessToken));
}
}

View File

@@ -0,0 +1,78 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.IssueApi;
import com.ikon.projectmanagement.dto.request.IssueCreateRequestDto;
import com.ikon.projectmanagement.dto.response.IssueResponseDto;
import com.ikon.projectmanagement.service.IssueService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class IssueController implements IssueApi {
private final IssueService issueService;
@Override
public ResponseEntity<IssueResponseDto> createIssue(
String accessToken,
IssueCreateRequestDto issueCreateRequestDto) {
IssueResponseDto createdIssue = issueService.createIssue(issueCreateRequestDto);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(createdIssue);
}
@Override
public ResponseEntity<List<IssueResponseDto>> getAllIssues(
String accessToken,UUID projectIdentifier) {
return ResponseEntity.ok(
issueService.getAllIssues(projectIdentifier));
}
@Override
public ResponseEntity<IssueResponseDto> getIssueById(
String accessToken,
UUID issueId) {
return ResponseEntity.ok(
issueService.getIssueById(issueId));
}
@Override
public ResponseEntity<IssueResponseDto> updateIssue(
String accessToken,
UUID issueId,
IssueCreateRequestDto issueCreateRequestDto) {
return ResponseEntity.ok(
issueService.updateIssue(issueId, issueCreateRequestDto));
}
// @Override
// public ResponseEntity<Void> deleteIssue(
// String accessToken,
// UUID issueId) {
// issueService.deleteIssue(issueId);
// return ResponseEntity.noContent().build();
// }
// @Override
// public ResponseEntity<List<IssueResponseDto>> getIssuesByProject(
// String accessToken,
// UUID projectIdentifier) {
// return ResponseEntity.ok(
// issueService.getIssuesByProject(projectIdentifier));
// }
}

View File

@@ -0,0 +1,72 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.MeetingApi;
import com.ikon.projectmanagement.dto.request.MeetingRequestDTO;
import com.ikon.projectmanagement.dto.response.MeetingResponseDTO;
import com.ikon.projectmanagement.service.MeetingService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class MeetingController implements MeetingApi {
private final MeetingService meetingService;
@Override
public ResponseEntity<MeetingResponseDTO> createMeeting(
String accessToken,
MeetingRequestDTO meetingRequestDTO) {
MeetingResponseDTO createdMeeting = meetingService.createMeeting(meetingRequestDTO);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(createdMeeting);
}
@Override
public ResponseEntity<List<MeetingResponseDTO>> getAllMeetings(
String accessToken,
UUID projectIdentifier) {
return ResponseEntity.ok(
meetingService.getAllMeetings(projectIdentifier));
}
@Override
public ResponseEntity<MeetingResponseDTO> getMeetingById(
String accessToken,
UUID meetingId) {
return ResponseEntity.ok(
meetingService.getMeetingById(meetingId));
}
@Override
public ResponseEntity<MeetingResponseDTO> updateMeeting(
String accessToken,
UUID meetingId,
MeetingRequestDTO meetingRequestDTO) {
return ResponseEntity.ok(
meetingService.updateMeeting(meetingId, meetingRequestDTO));
}
@Override
public ResponseEntity<Void> deleteMeeting(
String accessToken,
UUID meetingId) {
meetingService.deleteMeeting(meetingId);
return ResponseEntity.noContent().build();
}
}

View File

@@ -0,0 +1,67 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ProductOfProjectApi;
import com.ikon.projectmanagement.dto.request.ProductOfProjectRequestDto;
import com.ikon.projectmanagement.dto.request.WorkflowTransitionRequestDto;
import com.ikon.projectmanagement.dto.response.ProductOfProjectResponseDto;
import com.ikon.projectmanagement.service.ProductOfProjectService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class ProductOfProjectController implements ProductOfProjectApi {
private final ProductOfProjectService productOfProjectService;
@Override
public ResponseEntity<List<ProductOfProjectResponseDto>> getAllProductsByProject(
String accessToken,
UUID projectIdentifier) {
return ResponseEntity.ok(
productOfProjectService.getProductsByProject(projectIdentifier));
}
@Override
public ResponseEntity<ProductOfProjectResponseDto> getProductById(
String accessToken,
UUID projectIdentifier,
UUID productIdentifier) {
return ResponseEntity.ok(
productOfProjectService.getProduct(projectIdentifier, productIdentifier));
}
@Override
public ResponseEntity<ProductOfProjectResponseDto> updateProduct(
String accessToken,
UUID projectIdentifier,
UUID productIdentifier,
ProductOfProjectRequestDto productDto) {
return ResponseEntity.ok(
productOfProjectService.updateProduct(projectIdentifier, productIdentifier,
productDto));
}
@Override
public ResponseEntity<ProductOfProjectResponseDto> transitionProductStatus(
String accessToken,
UUID projectIdentifier,
UUID productIdentifier,
WorkflowTransitionRequestDto transitionDto) {
return ResponseEntity.ok(
productOfProjectService.transitionStatus(
projectIdentifier,
productIdentifier,
transitionDto.getTargetStatus()));
}
}

View File

@@ -0,0 +1,62 @@
package com.ikon.projectmanagement.controller;
import java.util.List;
import java.util.UUID;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.ProductResourceAllocationApi;
import com.ikon.projectmanagement.dto.request.ProductPSResourceAllocationRequestDto;
import com.ikon.projectmanagement.dto.response.ProductPSResourceAllocationResponseDto;
import com.ikon.projectmanagement.service.ProductResourceAllocationService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class ProductResourceAllocationController implements ProductResourceAllocationApi {
private final ProductResourceAllocationService allocationService;
@Override
public ResponseEntity<List<ProductPSResourceAllocationResponseDto>> getResourceAllocations(
String accessToken,
UUID productIdentifier) {
return ResponseEntity.ok(allocationService.getResourceAllocations(productIdentifier));
}
@Override
public ResponseEntity<ProductPSResourceAllocationResponseDto> getResourceAllocation(
String accessToken,
UUID productIdentifier,
UUID allocationIdentifier) {
return ResponseEntity.ok(allocationService.getResourceAllocation(productIdentifier, allocationIdentifier));
}
@Override
public ResponseEntity<ProductPSResourceAllocationResponseDto> createResourceAllocation(
String accessToken,
UUID productIdentifier,
ProductPSResourceAllocationRequestDto allocationRequestDto) {
return ResponseEntity.status(201).body(allocationService.createResourceAllocation(productIdentifier, allocationRequestDto));
}
@Override
public ResponseEntity<ProductPSResourceAllocationResponseDto> updateResourceAllocation(
String accessToken,
UUID productIdentifier,
UUID allocationIdentifier,
ProductPSResourceAllocationRequestDto allocationRequestDto) {
return ResponseEntity.ok(allocationService.updateResourceAllocation(productIdentifier, allocationIdentifier, allocationRequestDto));
}
@Override
public ResponseEntity<Void> deleteResourceAllocation(
String accessToken,
UUID productIdentifier,
UUID allocationIdentifier) {
allocationService.deleteResourceAllocation(productIdentifier, allocationIdentifier);
return ResponseEntity.noContent().build();
}
}

View File

@@ -0,0 +1,66 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.ProjectApi;
import com.ikon.projectmanagement.dto.request.ProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProjectResponseDto;
import com.ikon.projectmanagement.dto.response.ProjectTimelineResponseDto;
import com.ikon.projectmanagement.service.ProjectService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class ProjectController implements ProjectApi {
private final ProjectService projectService;
@Override
public ResponseEntity<ProjectResponseDto> createProject(
String accessToken,
ProjectRequestDto projectRequestDto) {
ProjectResponseDto createdProject = projectService.createProject(projectRequestDto);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(createdProject);
}
@Override
public ResponseEntity<List<ProjectResponseDto>> getAllProjects(
String accessToken) {
return ResponseEntity.ok(
projectService.getAllProjects());
}
@Override
public ResponseEntity<ProjectResponseDto> getProjectByProjectIdentifier(String accessToken,
UUID projectIdentifier) {
return ResponseEntity.ok(projectService.getProjectByProjectIdentifier(projectIdentifier));
}
@Override
public ResponseEntity<ProjectResponseDto> updateProject(
String accessToken,
UUID projectIdentifier,
ProjectRequestDto projectDto) {
return ResponseEntity.ok(
projectService.updateProject(projectIdentifier, projectDto));
}
@Override
public ResponseEntity<List<ProjectTimelineResponseDto>> getAllActiveProjectsTimeline(
String accessToken) {
return ResponseEntity.ok(projectService.getAllActiveProjectsTimeline());
}
}

View File

@@ -0,0 +1,71 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.RiskApi;
import com.ikon.projectmanagement.dto.request.RiskCreateRequestDto;
import com.ikon.projectmanagement.dto.response.RiskResponseDto;
import com.ikon.projectmanagement.service.RiskService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class RiskController implements RiskApi {
private final RiskService riskService;
@Override
public ResponseEntity<RiskResponseDto> createRisk(
String accessToken,
RiskCreateRequestDto riskCreateRequestDto) {
RiskResponseDto createdRisk = riskService.createRisk(riskCreateRequestDto);
return ResponseEntity
.status(HttpStatus.CREATED)
.body(createdRisk);
}
@Override
public ResponseEntity<List<RiskResponseDto>> getAllRisks(
String accessToken) {
return ResponseEntity.ok(
riskService.getAllRisks());
}
@Override
public ResponseEntity<RiskResponseDto> getRiskById(
String accessToken,
UUID riskId) {
return ResponseEntity.ok(
riskService.getRiskById(riskId));
}
@Override
public ResponseEntity<RiskResponseDto> updateRisk(
String accessToken,
UUID riskId,
RiskCreateRequestDto riskCreateRequestDto) {
return ResponseEntity.ok(
riskService.updateRisk(riskId, riskCreateRequestDto));
}
@Override
public ResponseEntity<List<RiskResponseDto>> getRisksByProject(
String accessToken,
String projectIdentifier) {
return ResponseEntity.ok(
riskService.getRisksByProject(projectIdentifier));
}
}

View File

@@ -0,0 +1,64 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.RoleApi;
import com.ikon.projectmanagement.dto.request.RoleRequestDto;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
import com.ikon.projectmanagement.service.RoleService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class RoleController implements RoleApi {
private final RoleService roleService;
@Override
public ResponseEntity<List<RoleResponseDto>> getAllRoles() {
return ResponseEntity.ok(roleService.getAllRoles());
}
@Override
public ResponseEntity<RoleResponseDto> getRoleById(UUID id) {
try {
RoleResponseDto role = roleService.getRoleById(id);
return ResponseEntity.ok(role);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<RoleResponseDto> createRole(RoleRequestDto dto) {
try {
RoleResponseDto createdRole = roleService.createRole(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(createdRole);
} catch (RuntimeException e) {
return ResponseEntity.badRequest().build();
}
}
@Override
public ResponseEntity<RoleResponseDto> updateRole(UUID id, RoleRequestDto dto) {
try {
RoleResponseDto updatedRole = roleService.updateRole(id, dto);
return ResponseEntity.ok(updatedRole);
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<Void> deleteRole(UUID id) {
try {
roleService.deleteRole(id);
return ResponseEntity.noContent().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
}
}
}

View File

@@ -0,0 +1,39 @@
package com.ikon.projectmanagement.controller;
import com.ikon.projectmanagement.api.ScheduleApi;
import com.ikon.projectmanagement.dto.request.ScheduleRequestDto;
import com.ikon.projectmanagement.dto.response.ScheduleResponseDto;
import com.ikon.projectmanagement.service.ScheduleService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
@RestController
@RequiredArgsConstructor
public class ScheduleController implements ScheduleApi {
private final ScheduleService scheduleService;
@Override
public ResponseEntity<ScheduleResponseDto> saveSchedule(
String accessToken,
UUID projectIdentifier,
ScheduleRequestDto scheduleRequestDto) {
return ResponseEntity.ok(scheduleService.saveSchedule(projectIdentifier, scheduleRequestDto, accessToken));
}
@Override
public ResponseEntity<ScheduleResponseDto> getSchedule(
String accessToken,
UUID projectIdentifier) {
return ResponseEntity.ok(scheduleService.getSchedule(projectIdentifier, accessToken));
}
@Override
public ResponseEntity<List<ScheduleResponseDto>> getAllSchedules(String accessToken) {
return ResponseEntity.ok(scheduleService.getAllSchedules());
}
}

View File

@@ -0,0 +1,36 @@
package com.ikon.projectmanagement.controller;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import com.ikon.projectmanagement.api.WorkingDayApi;
import com.ikon.projectmanagement.dto.response.WorkingDaysResponseDto;
import com.ikon.projectmanagement.service.WorkingDayService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class WorkingDayController implements WorkingDayApi {
private final WorkingDayService workingDayService;
@Override
public ResponseEntity<Page<WorkingDaysResponseDto>> getAllWorkingDays(
@RequestHeader("Authorization") String accessToken, Pageable pageable) {
Page<WorkingDaysResponseDto> workingDaysList = workingDayService.getAllWorkingDays(pageable);
return ResponseEntity.ok(workingDaysList);
}
@Override
public ResponseEntity<WorkingDaysResponseDto> getWorkingDaysByYear(
@RequestHeader("Authorization") String accessToken, @PathVariable String year) {
WorkingDaysResponseDto workingDays = workingDayService.getWorkingDaysByYear(year);
return ResponseEntity.ok(workingDays);
}
}

View File

@@ -0,0 +1,43 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "employees")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@Column(unique = true)
private String empId;
@JdbcTypeCode(SqlTypes.UUID)
private UUID accountId;
private String name;
private String email;
private String organizationEmail;
@Column(name = "\"role\"")
private String role;
private String grade;
private Boolean active;
private String highestQualification;
private String joiningDate;
private String confirmationDate;
}

View File

@@ -0,0 +1,41 @@
package com.ikon.projectmanagement.entity;
import java.util.UUID;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "fx_rate_details_v2")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FxRateDetailsEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "id")
private UUID id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "fx_rate_id", nullable = false)
@ToString.Exclude
@EqualsAndHashCode.Exclude
private FxRateEntity fxRateEntity;
@Column(name = "currency_id", nullable = false)
private String currencyId;
@Column(name = "currency")
private String currency;
@Column(name = "fx_rate")
private Double fxRate;
@Column(name = "active_status")
private Boolean activeStatus;
@Column(name = "year")
private String year;
}

View File

@@ -0,0 +1,36 @@
package com.ikon.projectmanagement.entity;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "fx_rates_v2")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FxRateEntity {
@Id
@Column(name = "fx_rate_id")
private UUID fxRateId;
@Column(name = "account_identifier")
private UUID accountIdentifier;
@OneToMany(mappedBy = "fxRateEntity", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@MapKey(name = "currencyId")
@Builder.Default
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Map<String, FxRateDetailsEntity> fxRateDetails = new HashMap<>();
public void addFxRateDetail(FxRateDetailsEntity detail) {
detail.setFxRateEntity(this);
this.fxRateDetails.put(detail.getCurrencyId(), detail);
}
}

View File

@@ -0,0 +1,32 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "grade")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Grade {
@Id
@JdbcTypeCode(SqlTypes.UUID)
private UUID id;
@JdbcTypeCode(SqlTypes.UUID)
private UUID accountId;
@Column(nullable = false)
private String grade;
}

View File

@@ -0,0 +1,93 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "issues")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Issue {
@Id
@GeneratedValue
@JdbcTypeCode(SqlTypes.UUID)
@Column(name = "issue_id", nullable = false)
private UUID issueId;
@Column(name = "issue_title", nullable = false)
private String issueTitle;
@Column(name = "issue_probability")
private Integer issueProbability;
@Column(name = "gross_issue_value")
private BigDecimal grossIssueValue;
@Column(name = "probable_issue_value")
private BigDecimal probableIssueValue;
@Column(name = "probable_issue_value_usd")
private BigDecimal probableIssueValueInUSD;
@Column(name = "issue_impact")
private String issueImpact;
@Column(name = "issue_owner")
private String issueOwner;
@Column(name = "issue_description", columnDefinition = "TEXT")
private String issueDescription;
@Column(name = "mitigation_action", columnDefinition = "TEXT")
private String mitigationAction;
@Column(name = "project_identifier", nullable = false)
private UUID projectIdentifier;
@Column(name = "financial_issue")
private Boolean financialIssue;
@Column(name = "issue_created_date")
private LocalDateTime issueCreatedDate;
@Column(name = "issue_options_select_id")
private String issueOptionsSelectId;
@Column(name = "issue_status")
private String issueStatus;
@Column(name = "issue_age")
private Integer issueAge;
@Column(name = "effected_sprint_id")
private String effectedSprintId;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
this.createdAt = LocalDateTime.now();
if (this.issueCreatedDate == null) {
this.issueCreatedDate = LocalDateTime.now();
}
}
@PreUpdate
protected void onUpdate() {
this.updatedAt = LocalDateTime.now();
}
}

View File

@@ -0,0 +1,139 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "meetings")
public class Meeting {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "id", updatable = false, nullable = false)
private UUID id;
@Column(name = "title")
private String title;
@Column(name = "project_name")
private String projectName;
@Column(nullable = true)
private UUID createdBy;
@Column(nullable = true)
private UUID accountId;
@Column(name = "place")
private String place;
@Column(name = "date")
private LocalDate date;
@Column(name = "time")
private String time;
@Column(name = "duration")
private String duration;
@Column(name = "called_by")
private String calledBy;
@ElementCollection
@CollectionTable(name = "meeting_attendees", joinColumns = @JoinColumn(name = "meeting_id"))
private List<Attendee> attendees;
@ElementCollection
@CollectionTable(name = "meeting_agenda", joinColumns = @JoinColumn(name = "meeting_id"))
private List<AgendaItem> agenda;
@ElementCollection
@CollectionTable(name = "meeting_decisions", joinColumns = @JoinColumn(name = "meeting_id"))
@Column(name = "decision", columnDefinition = "TEXT")
private List<String> decisions;
@ElementCollection
@CollectionTable(name = "meeting_actions", joinColumns = @JoinColumn(name = "meeting_id"))
private List<ActionItem> actions;
@Column(name = "notes", columnDefinition = "TEXT")
private String notes;
@Column(name = "project_identifier", nullable = false)
private UUID projectIdentifier;
@Column(name = "status")
private String status;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public static class Attendee {
@Column(name = "name")
private String name;
@Column(name = "role")
private String role;
@Column(name = "contact")
private String contact;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public static class AgendaItem {
@Column(name = "item", columnDefinition = "TEXT")
private String item;
@Column(name = "owner")
private String owner;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public static class ActionItem {
@Column(name = "item", columnDefinition = "TEXT")
private String item;
@Column(name = "responsible")
private String responsible;
@Column(name = "due_date")
private LocalDate dueDate;
}
}

View File

@@ -0,0 +1,80 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import java.util.*;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import com.ikon.projectmanagement.dto.response.ProductExpenseResponseDto;
@Entity
@Table(name = "product_project")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProductOfProject {
@Id
@GeneratedValue
@JdbcTypeCode(SqlTypes.UUID)
@Column(name = "product_identifier")
private UUID productIdentifier;
@Column(name = "project_identifier", nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
private UUID projectIdentifier;
@Column(name = "project_name", nullable = true)
private String projectName;
@Column(name = "project_manager", nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
private UUID projectManager;
@Column(nullable = true)
private UUID accountId;
@Column(nullable = true)
private UUID leadIdentifier;
@Column(nullable = true)
private String productStatus;
@Column(nullable = true)
private String updatedOn;
@Column(nullable = true)
private String projectStatus;
@Column(nullable = true)
private String productType;
@Column(columnDefinition = "TEXT", nullable = true)
private String productDescription;
@Column(nullable = true)
private Double discountPercent;
@Column(nullable = true)
private String createdOn;
@Column(nullable = true)
private UUID updatedBy;
@Column(nullable = true)
private UUID createdBy;
@OneToMany(mappedBy = "productPS", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductPSQuotation> quotation = new ArrayList<>();
@OneToMany(mappedBy = "productPS", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProductPSResourceAllocation> resourceDataWithAllocation = new ArrayList<>();
@OneToMany(mappedBy = "productPS", cascade = CascadeType.ALL, orphanRemoval = true)
@MapKey(name = "id")
private Map<UUID, ProductOfProjectExpenseDetail> expenseDetails = new HashMap<>();
}

View File

@@ -0,0 +1,33 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import java.util.UUID;
@Entity
@Table(name = "product_project_expense_detail")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProductOfProjectExpenseDetail {
@Id
@GeneratedValue
private UUID id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_prj_id", nullable = false)
private ProductOfProject productPS;
private String expenseName;
private String location;
private String currency;
private Double cost;
private Integer quantity;
private Double totalCost;
@Column(columnDefinition = "TEXT")
private String remarks;
}

View File

@@ -0,0 +1,32 @@
package com.ikon.projectmanagement.entity;
import java.util.UUID;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "product_ps_quotation")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProductPSQuotation {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String itemKey; // original map key
private String role;
private Double totalFTE;
private Double scr;
private Double expenses;
private Double otherCosts;
private Double billingAmount;
@ManyToOne
@JoinColumn(name = "deal_identifier" )
private ProductOfProject productPS;
}

View File

@@ -0,0 +1,48 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import java.util.Map;
import java.util.UUID;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "product_ps_resource_allocation")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProductPSResourceAllocation {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String resourceType;
private String role;
private Integer gradeId;
private String employeeName;
private String taskName;
private String resourceId;
private Long taskId;
// Storing FTE allotments per month: {"Aug_2025": 0.9, "Sep_2025": 0.5}
@ElementCollection
@CollectionTable(
name = "product_ps_resource_allocation_months",
joinColumns = @JoinColumn(name = "allocation_id")
)
@MapKeyColumn(name = "month")
@Column(name = "fte_value")
private Map<String, Double> allocation;
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "json", nullable = true)
private Map<String, Map<String, Object>> detailedAllocation;
@ManyToOne
@JoinColumn(name = "deal_identifier")
private ProductOfProject productPS;
}

View File

@@ -0,0 +1,180 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import java.util.ArrayList;
@Entity
@Table(name = "projects", uniqueConstraints = @UniqueConstraint(columnNames = "project_identifier"))
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Project {
@Id
@JdbcTypeCode(SqlTypes.UUID)
@Column(name = "project_identifier")
private UUID projectIdentifier;
@JdbcTypeCode(SqlTypes.UUID)
@Column(nullable = false)
private UUID projectManager;
@Column(nullable = false)
private String projectName;
@Column(nullable = false)
private UUID accountId;
@Column(nullable = true)
private String projectStatus;
@Column(nullable = true)
private String projectNumber;
@Column(nullable = true)
private String parentProjectNo;
@Column(nullable = true)
private String contractNumber;
@Column(nullable = true)
private String projectClient;
@Column(nullable = true)
private String projectCity;
@Column(nullable = true)
private String projectCountry;
@Column(nullable = true)
private String currency;
@Column(nullable = true)
private String projectImage;
@Column(nullable = true)
private String contractUpload;
@Column(nullable = true)
private String source;
@Column(nullable = true)
private String productType;
@Column(nullable = true)
private String expenses;
@Column(nullable = true)
private String formattedActualRevenueIncludingVAT_deal;
@Column(nullable = true)
private Boolean isCompleted;
@Column(nullable = true)
private Boolean groupNotExist;
@Column(nullable = true)
private Boolean isDebtRevenue_deal;
@Column(length = 2000, nullable = true)
private String projectDescription;
@JdbcTypeCode(SqlTypes.UUID)
@Column(nullable = true)
private UUID createdById;
@JdbcTypeCode(SqlTypes.UUID)
@Column(nullable = true)
private UUID updatedBy;
@JdbcTypeCode(SqlTypes.UUID)
@Column(nullable = true)
private UUID projectManagerDelegates;
@Column(nullable = true)
private LocalDate projectStartDate;
@Column(nullable = true)
private LocalDate contractedStartDate;
@Column(nullable = true)
private LocalDate contractedEndDate;
@Column(nullable = true)
private LocalDate updatedOn;
@Column(nullable = true)
private LocalDate createdOn;
@ElementCollection
@CollectionTable(name = "project_team", joinColumns = @JoinColumn(name = "project_id"))
@Column(name = "user_id", nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
@Builder.Default
private List<UUID> projectTeam = new ArrayList<>();
@ElementCollection
@CollectionTable(name = "project_team_pm", joinColumns = @JoinColumn(name = "project_id"))
@Column(nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
private List<UUID> projectTeamUnderProjectManager = new ArrayList<>();
@ElementCollection
@CollectionTable(name = "project_team_pm_delegates", joinColumns = @JoinColumn(name = "project_id"))
@Column(nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
private List<UUID> projectTeamUnderProjectManagerDelegates = new ArrayList<>();
@Column(length = 2000, nullable = true)
private String groupAssigneesEditStr;
@Column(length = 2000, nullable = true)
private String groupAssigneesViewStr;
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "json", nullable = true)
private Map<String, Object> participants;
@JdbcTypeCode(SqlTypes.JSON)
@Column(columnDefinition = "json", nullable = true)
private Map<String, Object> contractedProductIdentifierWiseDataObj;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "product_identifier", nullable = true)
private ProductOfProject product;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "project_identifier")
@Builder.Default
private List<Risks> risks = new ArrayList<>();
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "project_identifier")
@Builder.Default
private List<Meeting> meetings = new ArrayList<>();
}

View File

@@ -0,0 +1,51 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Entity
@Table(name = "project_schedule", uniqueConstraints = {
@UniqueConstraint(columnNames = "project_identifier")
})
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProjectSchedule {
@Id
@GeneratedValue
@JdbcTypeCode(SqlTypes.UUID)
@Column(name = "schedule_identifier")
private UUID scheduleIdentifier;
@Column(name = "project_identifier", nullable = false)
@JdbcTypeCode(SqlTypes.UUID)
private UUID projectIdentifier;
@Column(name = "product_identifier", nullable = true)
@JdbcTypeCode(SqlTypes.UUID)
private UUID productIdentifier;
@Column(name = "account_id", nullable = false)
@JdbcTypeCode(SqlTypes.UUID)
private UUID accountId;
@OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ProjectScheduleTask> tasks = new ArrayList<>();
@OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ProjectScheduleDependency> dependencies = new ArrayList<>();
@OneToMany(mappedBy = "schedule", cascade = CascadeType.ALL, orphanRemoval = true)
@Builder.Default
private List<ProjectScheduleGroup> groups = new ArrayList<>();
}

View File

@@ -0,0 +1,27 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "project_schedule_dependency")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProjectScheduleDependency {
@Id
@Column(name = "id")
private Long id;
@Column(name = "predecessor_id")
private Long predecessorId;
@Column(name = "dependency_type")
private Integer dependencyType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "schedule_identifier", nullable = false)
private ProjectSchedule schedule;
}

View File

@@ -0,0 +1,47 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Entity
@Table(name = "project_schedule_group")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProjectScheduleGroup {
@Id
@GeneratedValue
@JdbcTypeCode(SqlTypes.UUID)
@Column(name = "id")
private UUID id;
@Column(name = "group_key", nullable = false)
private String groupKey;
@Column(name = "group_name")
private String groupName;
@Column(name = "color")
private String color;
@ElementCollection
@CollectionTable(
name = "project_schedule_group_task",
joinColumns = @JoinColumn(name = "group_id")
)
@Column(name = "task_id")
@Builder.Default
private List<Long> taskIds = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "schedule_identifier", nullable = false)
private ProjectSchedule schedule;
}

View File

@@ -0,0 +1,54 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "project_schedule_task")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ProjectScheduleTask {
@Id
@Column(name = "id")
private Long id;
@Column(name = "parent_id")
private Long parentId;
@Column(name = "task_name")
private String taskName;
@Column(name = "task_duration")
private Double taskDuration;
@Column(name = "task_predecessor")
private String taskPredecessor;
@Column(name = "dependency_type")
private Integer dependencyType;
@Column(name = "task_colour")
private String taskColour;
@Column(name = "delay_duration")
private Double delayDuration;
@Column(name = "task_description", columnDefinition = "TEXT")
private String taskDescription;
@Column(name = "task_start")
private String taskStart;
@Column(name = "task_end")
private String taskEnd;
@Column(name = "milestone_task")
private Boolean milestoneTask;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "schedule_identifier", nullable = false)
private ProjectSchedule schedule;
}

View File

@@ -0,0 +1,91 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "risks")
public class Risks {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "risk_identifier", updatable = false, nullable = false)
private UUID riskIdentifier;
@Column(name = "risk_title", nullable = false)
private String riskTitle;
@Column(name = "risk_probability")
private Integer riskProbability;
@Column(name = "gross_risk_value", precision = 15, scale = 2)
private BigDecimal grossRiskValue;
@Column(name = "probable_risk_value", precision = 15, scale = 2)
private BigDecimal probableRiskValue;
@Column(name = "probable_risk_value_in_usd", precision = 15, scale = 2)
private BigDecimal probableRiskValueInUSD;
@Column(name = "risk_impact")
private String riskImpact;
@Column(name = "risk_owner")
private String riskOwner;
@Column(name = "risk_description", columnDefinition = "TEXT")
private String riskDescription;
@Column(name = "financial_risk")
private Boolean financialRisk;
@Column(name = "risk_created_date")
private LocalDateTime riskCreatedDate;
@Column(name = "risk_options_select_id")
private String riskOptionsSelectId;
@Column(name = "risk_status")
private String riskStatus;
@Column(name = "risk_age")
private Integer riskAge;
@Column(name = "effected_sprint_id")
private String effectedSprintId;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
// @Column(name = "project_identifier", insertable = false, updatable = false)
// private UUID projectIdentifier;
@Column(name = "project_identifier")
private UUID projectIdentifier;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
if (riskCreatedDate == null) {
riskCreatedDate = LocalDateTime.now();
}
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}

View File

@@ -0,0 +1,30 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Entity
@Table(name = "crm_role")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Role {
@Id
@JdbcTypeCode(SqlTypes.UUID)
private UUID id;
@JdbcTypeCode(SqlTypes.UUID)
private UUID accountId;
private String role;
}

View File

@@ -0,0 +1,34 @@
package com.ikon.projectmanagement.entity;
import java.util.UUID;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "working_days_details")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WorkingDaysDetailsEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "working_id", nullable = false)
@ToString.Exclude
@EqualsAndHashCode.Exclude
private WorkingDaysEntity workingDaysEntity;
@Column(name = "year")
private String year;
@Column(name = "month", nullable = false)
private String month;
@Column(name = "working_days")
private Integer workingDays;
}

View File

@@ -0,0 +1,36 @@
package com.ikon.projectmanagement.entity;
import jakarta.persistence.*;
import lombok.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Entity
@Table(name = "working_days")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WorkingDaysEntity {
@Id
@Column(name = "working_id")
private UUID workingId;
@Column(name = "account_identifier")
private UUID accountIdentifier;
@OneToMany(mappedBy = "workingDaysEntity", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@MapKey(name = "month")
@Builder.Default
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Map<String, WorkingDaysDetailsEntity> workingDaysDetails = new HashMap<>();
public void addWorkingDaysDetail(WorkingDaysDetailsEntity detail) {
detail.setWorkingDaysEntity(this);
this.workingDaysDetails.put(detail.getMonth(), detail);
}
}

View File

@@ -0,0 +1,79 @@
package com.ikon.projectmanagement.mapper;
import com.ikon.projectmanagement.dto.request.EmployeeRequestDto;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.entity.Employee;
import org.springframework.stereotype.Component;
/**
* Mapper for converting between Employee entity and DTO objects.
* Handles bidirectional mapping for request and response DTOs.
*/
@Component
public class EmployeeMapper {
/**
* Convert Employee entity to response DTO
*/
public EmployeeResponseDto toResponseDto(Employee employee) {
if (employee == null) {
return null;
}
return EmployeeResponseDto.builder()
.empId(employee.getEmpId())
.accountId(employee.getAccountId())
.name(employee.getName())
.email(employee.getEmail())
.organizationEmail(employee.getOrganizationEmail())
.role(employee.getRole())
.grade(employee.getGrade())
.active(employee.getActive())
.build();
}
/**
* Convert request DTO to Employee entity
*/
public Employee toEntity(EmployeeRequestDto dto) {
if (dto == null) {
return null;
}
return Employee.builder()
.empId(dto.getEmpId())
.name(dto.getName())
.email(dto.getEmail())
.organizationEmail(dto.getOrganizationEmail())
.role(dto.getRole())
.grade(dto.getGrade())
.active(dto.getActive())
.build();
}
/**
* Update Employee entity from request DTO
*/
public Employee updateEntity(Employee employee, EmployeeRequestDto dto) {
if (employee == null || dto == null) {
return employee;
}
if (dto.getName() != null) {
employee.setName(dto.getName());
}
if (dto.getEmail() != null) {
employee.setEmail(dto.getEmail());
}
if (dto.getOrganizationEmail() != null) {
employee.setOrganizationEmail(dto.getOrganizationEmail());
}
if (dto.getRole() != null) {
employee.setRole(dto.getRole());
}
if (dto.getGrade() != null) {
employee.setGrade(dto.getGrade());
}
if (dto.getActive() != null) {
employee.setActive(dto.getActive());
}
return employee;
}
}

View File

@@ -0,0 +1,60 @@
package com.ikon.projectmanagement.mapper;
import java.util.UUID;
import com.ikon.projectmanagement.dto.request.GradeRequestDto;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import com.ikon.projectmanagement.entity.Grade;
import org.springframework.stereotype.Component;
/**
* Mapper for converting between Grade entity and DTO objects.
* Handles bidirectional mapping for request and response DTOs.
*/
@Component
public class GradeMapper {
/**
* Convert Grade entity to response DTO
*/
public GradeResponseDto toResponseDto(Grade grade) {
if (grade == null) {
return null;
}
return GradeResponseDto.builder()
.id(grade.getId())
.accountId(grade.getAccountId())
.grade(grade.getGrade())
.build();
}
/**
* Convert request DTO to Grade entity
*/
public Grade toEntity(GradeRequestDto dto) {
if (dto == null) {
return null;
}
// Grade.id has no @GeneratedValue, so the PK must be supplied here or the
// insert fails with a null primary key. Honour a client-supplied id, else
// generate one. accountId is carried through from the request.
return Grade.builder()
.id(dto.getId() != null ? dto.getId() : UUID.randomUUID())
.accountId(dto.getAccountId())
.grade(dto.getGrade())
.build();
}
/**
* Update Grade entity from request DTO
*/
public Grade updateEntity(Grade grade, GradeRequestDto dto) {
if (grade == null || dto == null) {
return grade;
}
if (dto.getGrade() != null) {
grade.setGrade(dto.getGrade());
}
return grade;
}
}

View File

@@ -0,0 +1,112 @@
package com.ikon.projectmanagement.mapper;
import org.springframework.stereotype.Component;
import com.ikon.projectmanagement.dto.request.IssueCreateRequestDto;
import com.ikon.projectmanagement.dto.response.IssueResponseDto;
import com.ikon.projectmanagement.entity.Issue;
@Component
public class IssueMapper {
// ================= CREATE =================
public Issue toEntity(IssueCreateRequestDto dto) {
Issue issue = new Issue();
issue.setIssueTitle(dto.getIssueTitle());
issue.setIssueProbability(dto.getIssueProbability());
issue.setGrossIssueValue(dto.getGrossIssueValue());
issue.setProbableIssueValue(dto.getProbableIssueValue());
issue.setProbableIssueValueInUSD(dto.getProbableIssueValueInUSD());
issue.setIssueImpact(dto.getIssueImpact());
issue.setIssueOwner(dto.getIssueOwner());
issue.setIssueDescription(dto.getIssueDescription());
issue.setMitigationAction(dto.getMitigationAction());
issue.setFinancialIssue(dto.getFinancialIssue());
issue.setIssueCreatedDate(dto.getIssueCreatedDate());
issue.setIssueOptionsSelectId(dto.getIssueOptionsSelectId());
issue.setIssueStatus(dto.getIssueStatus());
issue.setIssueAge(dto.getIssueAge());
issue.setEffectedSprintId(dto.getEffectedSprintId());
issue.setProjectIdentifier(dto.getProjectIdentifier());
return issue;
}
// ================= RESPONSE =================
public IssueResponseDto toResponse(Issue issue) {
IssueResponseDto dto = new IssueResponseDto();
dto.setIssueId(issue.getIssueId());
dto.setIssueTitle(issue.getIssueTitle());
dto.setIssueProbability(issue.getIssueProbability());
dto.setGrossIssueValue(issue.getGrossIssueValue());
dto.setProbableIssueValue(issue.getProbableIssueValue());
dto.setProbableIssueValueInUSD(issue.getProbableIssueValueInUSD());
dto.setIssueImpact(issue.getIssueImpact());
dto.setIssueOwner(issue.getIssueOwner());
dto.setIssueDescription(issue.getIssueDescription());
dto.setMitigationAction(issue.getMitigationAction());
dto.setFinancialIssue(issue.getFinancialIssue());
dto.setIssueCreatedDate(issue.getIssueCreatedDate());
dto.setIssueOptionsSelectId(issue.getIssueOptionsSelectId());
dto.setIssueStatus(issue.getIssueStatus());
dto.setIssueAge(issue.getIssueAge());
dto.setEffectedSprintId(issue.getEffectedSprintId());
dto.setProjectIdentifier(issue.getProjectIdentifier());
return dto;
}
// ================= UPDATE =================
public void updateEntityFromDto(IssueCreateRequestDto dto, Issue issue) {
if (dto.getIssueTitle() != null)
issue.setIssueTitle(dto.getIssueTitle());
if (dto.getIssueProbability() != null)
issue.setIssueProbability(dto.getIssueProbability());
if (dto.getGrossIssueValue() != null)
issue.setGrossIssueValue(dto.getGrossIssueValue());
if (dto.getProbableIssueValue() != null)
issue.setProbableIssueValue(dto.getProbableIssueValue());
if (dto.getProbableIssueValueInUSD() != null)
issue.setProbableIssueValueInUSD(dto.getProbableIssueValueInUSD());
if (dto.getIssueImpact() != null)
issue.setIssueImpact(dto.getIssueImpact());
if (dto.getIssueOwner() != null)
issue.setIssueOwner(dto.getIssueOwner());
if (dto.getIssueDescription() != null)
issue.setIssueDescription(dto.getIssueDescription());
if (dto.getMitigationAction() != null)
issue.setMitigationAction(dto.getMitigationAction());
if (dto.getFinancialIssue() != null)
issue.setFinancialIssue(dto.getFinancialIssue());
if (dto.getIssueCreatedDate() != null)
issue.setIssueCreatedDate(dto.getIssueCreatedDate());
if (dto.getIssueOptionsSelectId() != null)
issue.setIssueOptionsSelectId(dto.getIssueOptionsSelectId());
if (dto.getIssueStatus() != null)
issue.setIssueStatus(dto.getIssueStatus());
if (dto.getIssueAge() != null)
issue.setIssueAge(dto.getIssueAge());
if (dto.getEffectedSprintId() != null)
issue.setEffectedSprintId(dto.getEffectedSprintId());
if (dto.getProjectIdentifier() != null)
issue.setProjectIdentifier(dto.getProjectIdentifier());
}
}

View File

@@ -0,0 +1,263 @@
package com.ikon.projectmanagement.mapper;
import com.ikon.projectmanagement.dto.request.*;
import com.ikon.projectmanagement.dto.response.MeetingResponseDTO;
import com.ikon.projectmanagement.entity.Meeting;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MeetingMapper {
// ================= DTO TO ENTITY =================
public Meeting toEntity(MeetingRequestDTO dto) {
Meeting meeting = new Meeting();
if (dto.getMeeting() != null) {
meeting.setTitle(dto.getMeeting().getTitle());
meeting.setProjectName(dto.getMeeting().getProjectName());
meeting.setPlace(dto.getMeeting().getPlace());
meeting.setDate(parseDate(dto.getMeeting().getDate()));
meeting.setTime(dto.getMeeting().getTime());
meeting.setDuration(dto.getMeeting().getDuration());
meeting.setCalledBy(dto.getMeeting().getCalledBy());
}
if (dto.getAttendees() != null && dto.getAttendees().getAttendees() != null) {
meeting.setAttendees(
dto.getAttendees().getAttendees().stream()
.map(this::toAttendeeEntity)
.collect(Collectors.toList())
);
}
if (dto.getAgenda() != null && dto.getAgenda().getAgenda() != null) {
meeting.setAgenda(
dto.getAgenda().getAgenda().stream()
.map(this::toAgendaItemEntity)
.collect(Collectors.toList())
);
}
if (dto.getDecisions() != null && dto.getDecisions().getDecisions() != null) {
meeting.setDecisions(dto.getDecisions().getDecisions());
}
if (dto.getActions() != null && dto.getActions().getActions() != null) {
meeting.setActions(
dto.getActions().getActions().stream()
.map(this::toActionItemEntity)
.collect(Collectors.toList())
);
}
if (dto.getOthers() != null) {
meeting.setNotes(dto.getOthers().getNotes());
}
if (dto.getProjectIdentifier() != null) {
meeting.setProjectIdentifier(dto.getProjectIdentifier());
}
meeting.setStatus(dto.getStatus());
return meeting;
}
// ================= UPDATE ENTITY FROM DTO =================
public void updateEntityFromDTO(Meeting existing, MeetingRequestDTO dto) {
if (dto.getMeeting() != null) {
existing.setTitle(dto.getMeeting().getTitle());
existing.setProjectName(dto.getMeeting().getProjectName());
existing.setPlace(dto.getMeeting().getPlace());
existing.setDate(parseDate(dto.getMeeting().getDate()));
existing.setTime(dto.getMeeting().getTime());
existing.setDuration(dto.getMeeting().getDuration());
existing.setCalledBy(dto.getMeeting().getCalledBy());
}
if (dto.getAttendees() != null && dto.getAttendees().getAttendees() != null) {
existing.setAttendees(
dto.getAttendees().getAttendees().stream()
.map(this::toAttendeeEntity)
.collect(Collectors.toList())
);
}
if (dto.getAgenda() != null && dto.getAgenda().getAgenda() != null) {
existing.setAgenda(
dto.getAgenda().getAgenda().stream()
.map(this::toAgendaItemEntity)
.collect(Collectors.toList())
);
}
if (dto.getDecisions() != null && dto.getDecisions().getDecisions() != null) {
existing.setDecisions(dto.getDecisions().getDecisions());
}
if (dto.getActions() != null && dto.getActions().getActions() != null) {
existing.setActions(
dto.getActions().getActions().stream()
.map(this::toActionItemEntity)
.collect(Collectors.toList())
);
}
if (dto.getOthers() != null) {
existing.setNotes(dto.getOthers().getNotes());
}
if (dto.getProjectIdentifier() != null) {
existing.setProjectIdentifier(dto.getProjectIdentifier());
}
existing.setStatus(dto.getStatus());
}
// ================= ENTITY TO DTO =================
public MeetingResponseDTO toResponseDTO(Meeting meeting) {
MeetingResponseDTO dto = new MeetingResponseDTO();
dto.setId(meeting.getId());
MeetingDetailsDTO detailsDTO = new MeetingDetailsDTO();
detailsDTO.setTitle(meeting.getTitle());
detailsDTO.setProjectName(meeting.getProjectName());
detailsDTO.setPlace(meeting.getPlace());
detailsDTO.setDate(formatDate(meeting.getDate()));
detailsDTO.setTime(meeting.getTime());
detailsDTO.setDuration(meeting.getDuration());
detailsDTO.setCalledBy(meeting.getCalledBy());
dto.setMeeting(detailsDTO);
AttendeesWrapperDTO attendeesWrapper = new AttendeesWrapperDTO();
attendeesWrapper.setAttendees(
meeting.getAttendees() != null
? meeting.getAttendees().stream()
.map(this::toAttendeeDTO)
.collect(Collectors.toList())
: Collections.emptyList()
);
dto.setAttendees(attendeesWrapper);
AgendaWrapperDTO agendaWrapper = new AgendaWrapperDTO();
agendaWrapper.setAgenda(
meeting.getAgenda() != null
? meeting.getAgenda().stream()
.map(this::toAgendaItemDTO)
.collect(Collectors.toList())
: Collections.emptyList()
);
dto.setAgenda(agendaWrapper);
DecisionsWrapperDTO decisionsWrapper = new DecisionsWrapperDTO();
decisionsWrapper.setDecisions(
meeting.getDecisions() != null
? meeting.getDecisions()
: Collections.emptyList()
);
dto.setDecisions(decisionsWrapper);
ActionsWrapperDTO actionsWrapper = new ActionsWrapperDTO();
actionsWrapper.setActions(
meeting.getActions() != null
? meeting.getActions().stream()
.map(this::toActionItemDTO)
.collect(Collectors.toList())
: Collections.emptyList()
);
dto.setActions(actionsWrapper);
OthersDTO othersDTO = new OthersDTO();
othersDTO.setNotes(meeting.getNotes());
dto.setOthers(othersDTO);
if (meeting.getProjectIdentifier() != null) {
dto.setProjectIdentifier(meeting.getProjectIdentifier());
}
dto.setStatus(meeting.getStatus());
dto.setCreatedAt(meeting.getCreatedAt());
dto.setUpdatedAt(meeting.getUpdatedAt());
return dto;
}
// ================= LIST MAPPING =================
public List<MeetingResponseDTO> toResponseDTOList(List<Meeting> meetings) {
return meetings.stream()
.map(this::toResponseDTO)
.collect(Collectors.toList());
}
// ================= PRIVATE HELPERS =================
private LocalDate parseDate(String dateStr) {
if (dateStr == null || dateStr.isBlank()) return null;
try {
return LocalDate.parse(dateStr);
} catch (Exception e) {
return null;
}
}
private String formatDate(LocalDate date) {
return date != null ? date.toString() : null;
}
private Meeting.Attendee toAttendeeEntity(AttendeeDTO dto) {
Meeting.Attendee attendee = new Meeting.Attendee();
attendee.setName(dto.getName());
attendee.setRole(dto.getRole());
attendee.setContact(dto.getContact());
return attendee;
}
private AttendeeDTO toAttendeeDTO(Meeting.Attendee attendee) {
AttendeeDTO dto = new AttendeeDTO();
dto.setName(attendee.getName());
dto.setRole(attendee.getRole());
dto.setContact(attendee.getContact());
return dto;
}
private Meeting.AgendaItem toAgendaItemEntity(AgendaItemDTO dto) {
Meeting.AgendaItem agendaItem = new Meeting.AgendaItem();
agendaItem.setItem(dto.getItem());
agendaItem.setOwner(dto.getOwner());
return agendaItem;
}
private AgendaItemDTO toAgendaItemDTO(Meeting.AgendaItem agendaItem) {
AgendaItemDTO dto = new AgendaItemDTO();
dto.setItem(agendaItem.getItem());
dto.setOwner(agendaItem.getOwner());
return dto;
}
private Meeting.ActionItem toActionItemEntity(ActionItemDTO dto) {
Meeting.ActionItem actionItem = new Meeting.ActionItem();
actionItem.setItem(dto.getItem());
actionItem.setResponsible(dto.getResponsible());
actionItem.setDueDate(parseDate(dto.getDueDate()));
return actionItem;
}
private ActionItemDTO toActionItemDTO(Meeting.ActionItem actionItem) {
ActionItemDTO dto = new ActionItemDTO();
dto.setItem(actionItem.getItem());
dto.setResponsible(actionItem.getResponsible());
dto.setDueDate(formatDate(actionItem.getDueDate()));
return dto;
}
}

View File

@@ -0,0 +1,198 @@
package com.ikon.projectmanagement.mapper;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.ikon.projectmanagement.dto.request.ProductOfProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProductExpenseResponseDto;
import com.ikon.projectmanagement.dto.response.ProductOfProjectResponseDto;
import com.ikon.projectmanagement.dto.response.ProductPSResourceAllocationResponseDto;
import com.ikon.projectmanagement.entity.ProductOfProject;
import com.ikon.projectmanagement.entity.ProductOfProjectExpenseDetail;
import com.ikon.projectmanagement.entity.ProductPSResourceAllocation;
@Component
public class ProductOfProjectMapper {
public ProductOfProject toEntity(ProductOfProjectRequestDto dto) {
if (dto == null)
return null;
ProductOfProject product = new ProductOfProject();
product.setProjectIdentifier(dto.getProjectIdentifier());
product.setProjectName(dto.getProjectName());
product.setProjectManager(dto.getProjectManager());
product.setAccountId(dto.getAccountId());
product.setLeadIdentifier(dto.getLeadIdentifier());
product.setProductStatus(dto.getProductStatus());
product.setProjectStatus(dto.getProjectStatus());
product.setProductType(dto.getProductType());
product.setProductDescription(dto.getProductDescription());
product.setDiscountPercent(dto.getDiscountPercent());
if (dto.getExpenseDetails() != null && !dto.getExpenseDetails().isEmpty()) {
Map<UUID, ProductOfProjectExpenseDetail> expenseMap = new HashMap<>();
for (Entry<UUID, ProductExpenseResponseDto> entry : dto.getExpenseDetails().entrySet()) {
UUID expenseId = entry.getKey();
ProductExpenseResponseDto expDto = entry.getValue();
ProductOfProjectExpenseDetail exp = toExpenseEntity(expDto);
exp.setId(expenseId);
exp.setProductPS(product);
expenseMap.put(expenseId, exp);
}
product.setExpenseDetails(expenseMap);
}
return product;
}
public ProductOfProjectResponseDto toResponse(ProductOfProject entity) {
if (entity == null)
return null;
ProductOfProjectResponseDto dto = new ProductOfProjectResponseDto();
dto.setProductIdentifier(entity.getProductIdentifier());
dto.setProjectIdentifier(entity.getProjectIdentifier());
dto.setProjectName(entity.getProjectName());
dto.setProjectManager(entity.getProjectManager());
dto.setAccountId(entity.getAccountId());
dto.setLeadIdentifier(entity.getLeadIdentifier());
dto.setProductStatus(entity.getProductStatus());
dto.setProjectStatus(entity.getProjectStatus());
dto.setProductType(entity.getProductType());
dto.setProductDescription(entity.getProductDescription());
dto.setDiscountPercent(entity.getDiscountPercent());
dto.setCreatedOn(entity.getCreatedOn());
dto.setUpdatedOn(entity.getUpdatedOn());
dto.setCreatedBy(entity.getCreatedBy());
dto.setUpdatedBy(entity.getUpdatedBy());
if (entity.getExpenseDetails() != null && !entity.getExpenseDetails().isEmpty()) {
Map<UUID, ProductExpenseResponseDto> expenseDtos = new HashMap<>();
for (Map.Entry<UUID, ProductOfProjectExpenseDetail> entry : entity.getExpenseDetails().entrySet()) {
ProductOfProjectExpenseDetail exp = entry.getValue();
ProductExpenseResponseDto expDto = new ProductExpenseResponseDto();
expDto.setExpenseName(exp.getExpenseName());
expDto.setLocation(exp.getLocation());
expDto.setCurrency(exp.getCurrency());
expDto.setCost(exp.getCost());
expDto.setQuantity(exp.getQuantity());
expDto.setTotalCost(exp.getTotalCost());
expDto.setRemarks(exp.getRemarks());
expenseDtos.put(entry.getKey(), expDto);
}
dto.setExpenseDetails(expenseDtos);
}
return dto;
}
public void updateEntity(ProductOfProject entity, ProductOfProjectRequestDto dto) {
if (dto == null || entity == null)
return;
if (dto.getProjectIdentifier() != null)
entity.setProjectIdentifier(dto.getProjectIdentifier());
if (dto.getProjectName() != null)
entity.setProjectName(dto.getProjectName());
if (dto.getProjectManager() != null)
entity.setProjectManager(dto.getProjectManager());
if (dto.getLeadIdentifier() != null)
entity.setLeadIdentifier(dto.getLeadIdentifier());
if (dto.getProductStatus() != null)
entity.setProductStatus(dto.getProductStatus());
if (dto.getProjectStatus() != null)
entity.setProjectStatus(dto.getProjectStatus());
if (dto.getProductType() != null)
entity.setProductType(dto.getProductType());
if (dto.getProductDescription() != null)
entity.setProductDescription(dto.getProductDescription());
if (dto.getDiscountPercent() != null)
entity.setDiscountPercent(dto.getDiscountPercent());
}
public ProductOfProjectExpenseDetail toExpenseEntity(ProductExpenseResponseDto expDto) {
if (expDto == null)
return null;
return ProductOfProjectExpenseDetail.builder()
.expenseName(expDto.getExpenseName())
.location(expDto.getLocation())
.currency(expDto.getCurrency())
.cost(expDto.getCost())
.quantity(expDto.getQuantity())
.totalCost(expDto.getTotalCost())
.remarks(expDto.getRemarks())
.build();
}
public void updateExpenseEntity(ProductOfProjectExpenseDetail entity,
ProductExpenseResponseDto expenseDto) {
if (entity == null || expenseDto == null)
return;
if (expenseDto.getExpenseName() != null)
entity.setExpenseName(expenseDto.getExpenseName());
if (expenseDto.getLocation() != null)
entity.setLocation(expenseDto.getLocation());
if (expenseDto.getCurrency() != null)
entity.setCurrency(expenseDto.getCurrency());
if (expenseDto.getCost() != null)
entity.setCost(expenseDto.getCost());
if (expenseDto.getQuantity() != null)
entity.setQuantity(expenseDto.getQuantity());
if (expenseDto.getTotalCost() != null)
entity.setTotalCost(expenseDto.getTotalCost());
if (expenseDto.getRemarks() != null)
entity.setRemarks(expenseDto.getRemarks());
}
private ProductPSResourceAllocationResponseDto toResourceAllocationResponseDto(ProductPSResourceAllocation allocation) {
ProductPSResourceAllocationResponseDto dto = new ProductPSResourceAllocationResponseDto();
dto.setId(allocation.getId());
dto.setResourceType(allocation.getResourceType());
dto.setRole(allocation.getRole());
dto.setGradeId(allocation.getGradeId());
dto.setEmployeeName(allocation.getEmployeeName());
dto.setTaskName(allocation.getTaskName());
dto.setResourceId(allocation.getResourceId());
dto.setTaskId(allocation.getTaskId());
dto.setAllocation(allocation.getAllocation());
dto.setDetailedAllocation(allocation.getDetailedAllocation());
return dto;
}
}

View File

@@ -0,0 +1,74 @@
package com.ikon.projectmanagement.mapper;
import java.util.HashMap;
import org.springframework.stereotype.Component;
import com.ikon.projectmanagement.dto.request.ProductPSResourceAllocationRequestDto;
import com.ikon.projectmanagement.dto.response.ProductPSResourceAllocationResponseDto;
import com.ikon.projectmanagement.entity.ProductOfProject;
import com.ikon.projectmanagement.entity.ProductPSResourceAllocation;
@Component
public class ProductResourceAllocationMapper {
public ProductPSResourceAllocationResponseDto toResponse(ProductPSResourceAllocation entity) {
if (entity == null)
return null;
ProductPSResourceAllocationResponseDto dto = new ProductPSResourceAllocationResponseDto();
dto.setId(entity.getId());
dto.setResourceType(entity.getResourceType());
dto.setRole(entity.getRole());
dto.setGradeId(entity.getGradeId());
dto.setEmployeeName(entity.getEmployeeName());
dto.setTaskName(entity.getTaskName());
dto.setResourceId(entity.getResourceId());
dto.setTaskId(entity.getTaskId());
dto.setAllocation(entity.getAllocation());
dto.setDetailedAllocation(entity.getDetailedAllocation());
return dto;
}
public ProductPSResourceAllocation toEntity(ProductPSResourceAllocationRequestDto dto, ProductOfProject product) {
if (dto == null)
return null;
ProductPSResourceAllocation allocation = new ProductPSResourceAllocation();
allocation.setResourceType(dto.getResourceType());
allocation.setRole(dto.getRole());
allocation.setGradeId(dto.getGradeId());
allocation.setEmployeeName(dto.getEmployeeName());
allocation.setTaskName(dto.getTaskName());
allocation.setResourceId(dto.getResourceId());
allocation.setTaskId(dto.getTaskId());
allocation.setAllocation(dto.getAllocation() != null ? dto.getAllocation() : new HashMap<>());
allocation.setDetailedAllocation(dto.getDetailedAllocation() != null ? dto.getDetailedAllocation() : new HashMap<>());
allocation.setProductPS(product);
return allocation;
}
public void updateEntity(ProductPSResourceAllocation entity, ProductPSResourceAllocationRequestDto dto) {
if (entity == null || dto == null)
return;
if (dto.getResourceType() != null)
entity.setResourceType(dto.getResourceType());
if (dto.getRole() != null)
entity.setRole(dto.getRole());
if (dto.getGradeId() != null)
entity.setGradeId(dto.getGradeId());
if (dto.getEmployeeName() != null)
entity.setEmployeeName(dto.getEmployeeName());
if (dto.getTaskName() != null)
entity.setTaskName(dto.getTaskName());
if (dto.getResourceId() != null)
entity.setResourceId(dto.getResourceId());
if (dto.getTaskId() != null)
entity.setTaskId(dto.getTaskId());
if (dto.getAllocation() != null)
entity.setAllocation(dto.getAllocation());
if (dto.getDetailedAllocation() != null)
entity.setDetailedAllocation(dto.getDetailedAllocation());
}
}

View File

@@ -0,0 +1,118 @@
package com.ikon.projectmanagement.mapper;
import org.springframework.stereotype.Component;
import com.ikon.projectmanagement.dto.request.ProductExpenseRequestDto;
import com.ikon.projectmanagement.dto.request.ProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProjectResponseDto;
import com.ikon.projectmanagement.entity.ProductOfProjectExpenseDetail;
import com.ikon.projectmanagement.entity.Project;
@Component
public class ProjectMapper {
public Project toEntity(ProjectRequestDto dto) {
Project project = new Project();
mapCommonFields(dto, project);
return project;
}
public void updateEntityFromDto(ProjectRequestDto dto, Project project) {
mapCommonFields(dto, project);
}
private void mapCommonFields(ProjectRequestDto dto, Project project) {
project.setProjectName(dto.getProjectName());
project.setProjectManager(dto.getProjectManager());
project.setProjectStatus(dto.getProjectStatus());
project.setProjectNumber(dto.getProjectNumber());
project.setParentProjectNo(dto.getParentProjectNo());
project.setContractNumber(dto.getContractNumber());
project.setProjectClient(dto.getProjectClient());
project.setProjectCity(dto.getProjectCity());
project.setProjectCountry(dto.getProjectCountry());
project.setCurrency(dto.getCurrency());
project.setProjectImage(dto.getProjectImage());
project.setContractUpload(dto.getContractUpload());
project.setSource(dto.getSource());
project.setProductType(dto.getProductType());
project.setExpenses(dto.getExpenses());
project.setFormattedActualRevenueIncludingVAT_deal(dto.getFormattedActualRevenueIncludingVAT_deal());
project.setIsCompleted(dto.getIsCompleted());
project.setGroupNotExist(dto.getGroupNotExist());
project.setIsDebtRevenue_deal(dto.getIsDebtRevenue_deal());
project.setProjectDescription(dto.getProjectDescription());
project.setProjectStartDate(dto.getProjectStartDate());
project.setContractedStartDate(dto.getContractedStartDate());
project.setContractedEndDate(dto.getContractedEndDate());
project.setProjectTeam(dto.getProjectTeam());
project.setProjectTeamUnderProjectManager(dto.getProjectTeamUnderProjectManager());
project.setProjectTeamUnderProjectManagerDelegates(dto.getProjectTeamUnderProjectManagerDelegates());
project.setParticipants(dto.getParticipants());
project.setContractedProductIdentifierWiseDataObj(dto.getContractedProductIdentifierWiseDataObj());
}
public ProjectResponseDto toResponse(Project project) {
if (project == null)
return null;
ProjectResponseDto dto = new ProjectResponseDto();
dto.setProjectIdentifier(project.getProjectIdentifier());
dto.setProjectName(project.getProjectName());
dto.setProjectManager(project.getProjectManager());
dto.setProjectStatus(project.getProjectStatus());
dto.setProjectNumber(project.getProjectNumber());
dto.setParentProjectNo(project.getParentProjectNo());
dto.setContractNumber(project.getContractNumber());
dto.setProjectClient(project.getProjectClient());
dto.setProjectCity(project.getProjectCity());
dto.setProjectCountry(project.getProjectCountry());
dto.setCurrency(project.getCurrency());
dto.setProjectImage(project.getProjectImage());
dto.setContractUpload(project.getContractUpload());
dto.setSource(project.getSource());
dto.setProductType(project.getProductType());
dto.setExpenses(project.getExpenses());
dto.setFormattedActualRevenueIncludingVAT_deal(
project.getFormattedActualRevenueIncludingVAT_deal());
dto.setIsCompleted(project.getIsCompleted());
dto.setGroupNotExist(project.getGroupNotExist());
dto.setIsDebtRevenue_deal(project.getIsDebtRevenue_deal());
dto.setProjectDescription(project.getProjectDescription());
dto.setProjectStartDate(project.getProjectStartDate());
dto.setContractedStartDate(project.getContractedStartDate());
dto.setContractedEndDate(project.getContractedEndDate());
dto.setProjectTeam(project.getProjectTeam());
dto.setProjectTeamUnderProjectManager(
project.getProjectTeamUnderProjectManager());
dto.setProjectTeamUnderProjectManagerDelegates(
project.getProjectTeamUnderProjectManagerDelegates());
dto.setParticipants(project.getParticipants());
dto.setContractedProductIdentifierWiseDataObj(
project.getContractedProductIdentifierWiseDataObj());
if (project.getProduct() != null) {
dto.setProductIdentifier(project.getProduct().getProductIdentifier());
dto.setProductType(project.getProduct().getProductType());
}
return dto;
}
}

View File

@@ -0,0 +1,88 @@
package com.ikon.projectmanagement.mapper;
import org.springframework.stereotype.Component;
import com.ikon.projectmanagement.dto.request.RiskCreateRequestDto;
import com.ikon.projectmanagement.dto.response.RiskResponseDto;
import com.ikon.projectmanagement.entity.Risks;
@Component
public class RiskMapper {
public Risks toEntity(RiskCreateRequestDto dto) {
Risks risk = new Risks();
risk.setRiskTitle(dto.getRiskTitle());
risk.setRiskProbability(dto.getRiskProbability());
risk.setGrossRiskValue(dto.getGrossRiskValue());
risk.setProbableRiskValue(dto.getProbableRiskValue());
risk.setProbableRiskValueInUSD(dto.getProbableRiskValueInUSD());
risk.setRiskImpact(dto.getRiskImpact());
risk.setRiskOwner(dto.getRiskOwner());
risk.setRiskDescription(dto.getRiskDescription());
risk.setFinancialRisk(dto.getFinancialRisk());
risk.setRiskCreatedDate(dto.getRiskCreatedDate());
risk.setRiskOptionsSelectId(dto.getRiskOptionsSelectId());
risk.setRiskStatus(dto.getRiskStatus());
risk.setRiskAge(dto.getRiskAge());
risk.setEffectedSprintId(dto.getEffectedSprintId());
risk.setProjectIdentifier(dto.getProjectIdentifier());
return risk;
}
public RiskResponseDto toResponse(Risks risk) {
RiskResponseDto dto = new RiskResponseDto();
dto.setRiskIdentifier(risk.getRiskIdentifier());
dto.setRiskTitle(risk.getRiskTitle());
dto.setRiskProbability(risk.getRiskProbability());
dto.setGrossRiskValue(risk.getGrossRiskValue());
dto.setProbableRiskValue(risk.getProbableRiskValue());
dto.setProbableRiskValueInUSD(risk.getProbableRiskValueInUSD());
dto.setRiskImpact(risk.getRiskImpact());
dto.setRiskOwner(risk.getRiskOwner());
dto.setRiskDescription(risk.getRiskDescription());
dto.setFinancialRisk(risk.getFinancialRisk());
dto.setRiskCreatedDate(risk.getRiskCreatedDate());
dto.setRiskOptionsSelectId(risk.getRiskOptionsSelectId());
dto.setRiskStatus(risk.getRiskStatus());
dto.setRiskAge(risk.getRiskAge());
dto.setEffectedSprintId(risk.getEffectedSprintId());
// Now using plain UUID field instead of
// risk.getProject().getProjectIdentifier()
dto.setProjectIdentifier(risk.getProjectIdentifier());
return dto;
}
public void updateEntityFromDto(RiskCreateRequestDto dto, Risks risk) {
if (dto.getRiskTitle() != null)
risk.setRiskTitle(dto.getRiskTitle());
if (dto.getRiskProbability() != null)
risk.setRiskProbability(dto.getRiskProbability());
if (dto.getGrossRiskValue() != null)
risk.setGrossRiskValue(dto.getGrossRiskValue());
if (dto.getProbableRiskValue() != null)
risk.setProbableRiskValue(dto.getProbableRiskValue());
if (dto.getProbableRiskValueInUSD() != null)
risk.setProbableRiskValueInUSD(dto.getProbableRiskValueInUSD());
if (dto.getRiskImpact() != null)
risk.setRiskImpact(dto.getRiskImpact());
if (dto.getRiskOwner() != null)
risk.setRiskOwner(dto.getRiskOwner());
if (dto.getRiskDescription() != null)
risk.setRiskDescription(dto.getRiskDescription());
if (dto.getFinancialRisk() != null)
risk.setFinancialRisk(dto.getFinancialRisk());
if (dto.getRiskCreatedDate() != null)
risk.setRiskCreatedDate(dto.getRiskCreatedDate());
if (dto.getRiskOptionsSelectId() != null)
risk.setRiskOptionsSelectId(dto.getRiskOptionsSelectId());
if (dto.getRiskStatus() != null)
risk.setRiskStatus(dto.getRiskStatus());
if (dto.getRiskAge() != null)
risk.setRiskAge(dto.getRiskAge());
if (dto.getEffectedSprintId() != null)
risk.setEffectedSprintId(dto.getEffectedSprintId());
if (dto.getProjectIdentifier() != null)
risk.setProjectIdentifier(dto.getProjectIdentifier());
}
}

View File

@@ -0,0 +1,59 @@
package com.ikon.projectmanagement.mapper;
import java.util.UUID;
import com.ikon.projectmanagement.dto.request.RoleRequestDto;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
import com.ikon.projectmanagement.entity.Role;
import org.springframework.stereotype.Component;
/**
* Mapper for converting between Role entity and DTO objects.
* Handles bidirectional mapping for request and response DTOs.
*/
@Component
public class RoleMapper {
/**
* Convert Role entity to response DTO
*/
public RoleResponseDto toResponseDto(Role role) {
if (role == null) {
return null;
}
return RoleResponseDto.builder()
.id(role.getId())
.accountId(role.getAccountId())
.role(role.getRole())
.build();
}
/**
* Convert request DTO to Role entity
*/
public Role toEntity(RoleRequestDto dto) {
if (dto == null) {
return null;
}
// Role.id has no @GeneratedValue, so the PK must be supplied here or the
// insert fails with a null primary key. Honour a client-supplied id, else
// generate one.
return Role.builder()
.id(dto.getId() != null ? dto.getId() : UUID.randomUUID())
.role(dto.getRole())
.build();
}
/**
* Update Role entity from request DTO
*/
public Role updateEntity(Role role, RoleRequestDto dto) {
if (role == null || dto == null) {
return role;
}
if (dto.getRole() != null) {
role.setRole(dto.getRole());
}
return role;
}
}

View File

@@ -0,0 +1,99 @@
package com.ikon.projectmanagement.mapper;
import com.ikon.projectmanagement.dto.request.ScheduleDependencyDto;
import com.ikon.projectmanagement.dto.request.ScheduleGroupDto;
import com.ikon.projectmanagement.dto.request.ScheduleTaskDto;
import com.ikon.projectmanagement.dto.response.ScheduleResponseDto;
import com.ikon.projectmanagement.entity.ProjectSchedule;
import com.ikon.projectmanagement.entity.ProjectScheduleDependency;
import com.ikon.projectmanagement.entity.ProjectScheduleGroup;
import com.ikon.projectmanagement.entity.ProjectScheduleTask;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component
public class ScheduleMapper {
public ScheduleResponseDto toResponse(ProjectSchedule schedule) {
List<ScheduleTaskDto> tasks = new ArrayList<>();
for (ProjectScheduleTask task : schedule.getTasks()) {
ScheduleTaskDto dto = new ScheduleTaskDto();
dto.setId(task.getId());
dto.setParentId(task.getParentId());
dto.setTaskName(task.getTaskName());
dto.setTaskDuration(task.getTaskDuration());
dto.setTaskPredecessor(task.getTaskPredecessor());
dto.setDependencyType(task.getDependencyType());
dto.setTaskColour(task.getTaskColour());
dto.setDelayDuration(task.getDelayDuration());
dto.setTaskDescription(task.getTaskDescription());
dto.setTaskStart(task.getTaskStart());
dto.setTaskEnd(task.getTaskEnd());
dto.setMilestoneTask(task.getMilestoneTask());
tasks.add(dto);
}
List<ScheduleDependencyDto> dependencies = new ArrayList<>();
for (ProjectScheduleDependency dep : schedule.getDependencies()) {
ScheduleDependencyDto dto = new ScheduleDependencyDto();
dto.setId(dep.getId());
dto.setPredecessorId(dep.getPredecessorId());
dto.setDependencyType(dep.getDependencyType());
dependencies.add(dto);
}
Map<String, ScheduleGroupDto> groups = new LinkedHashMap<>();
for (ProjectScheduleGroup g : schedule.getGroups()) {
ScheduleGroupDto dto = new ScheduleGroupDto();
dto.setId(g.getGroupKey());
dto.setGroupName(g.getGroupName());
dto.setColor(g.getColor());
dto.setTaskIds(new ArrayList<>(g.getTaskIds()));
groups.put(g.getGroupKey(), dto);
}
return new ScheduleResponseDto(schedule.getProjectIdentifier(), schedule.getProductIdentifier(), tasks, dependencies, groups);
}
public ProjectScheduleTask toTaskEntity(ScheduleTaskDto dto, ProjectSchedule schedule) {
ProjectScheduleTask task = new ProjectScheduleTask();
task.setId(dto.getId());
task.setParentId(dto.getParentId());
task.setTaskName(dto.getTaskName());
task.setTaskDuration(dto.getTaskDuration());
task.setTaskPredecessor(dto.getTaskPredecessor());
task.setDependencyType(dto.getDependencyType());
task.setTaskColour(dto.getTaskColour());
task.setDelayDuration(dto.getDelayDuration());
task.setTaskDescription(dto.getTaskDescription());
task.setTaskStart(dto.getTaskStart());
task.setTaskEnd(dto.getTaskEnd());
task.setMilestoneTask(dto.getMilestoneTask());
task.setSchedule(schedule);
return task;
}
public ProjectScheduleDependency toDependencyEntity(ScheduleDependencyDto dto, ProjectSchedule schedule) {
ProjectScheduleDependency dep = new ProjectScheduleDependency();
dep.setId(dto.getId());
dep.setPredecessorId(dto.getPredecessorId());
dep.setDependencyType(dto.getDependencyType());
dep.setSchedule(schedule);
return dep;
}
public ProjectScheduleGroup toGroupEntity(String groupKey, ScheduleGroupDto dto, ProjectSchedule schedule) {
ProjectScheduleGroup g = new ProjectScheduleGroup();
g.setGroupKey(groupKey);
g.setGroupName(dto.getGroupName());
g.setColor(dto.getColor());
g.setTaskIds(dto.getTaskIds() != null ? new ArrayList<>(dto.getTaskIds()) : new ArrayList<>());
g.setSchedule(schedule);
return g;
}
}

View File

@@ -0,0 +1,12 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.Employee;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, UUID> {
Optional<Employee> findByEmpId(String empId);
}

View File

@@ -0,0 +1,26 @@
package com.ikon.projectmanagement.repository;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.ikon.projectmanagement.entity.FxRateEntity;
@Repository
public interface FxRateRepository extends JpaRepository<FxRateEntity, UUID> {
@Query("SELECT f FROM FxRateEntity f LEFT JOIN FETCH f.fxRateDetails WHERE f.fxRateId = :id")
Optional<FxRateEntity> findByIdWithDetails(@Param("id") UUID id);
Optional<FxRateEntity> findByAccountIdentifier(UUID accountIdentifier);
@Query("SELECT f FROM FxRateEntity f JOIN f.fxRateDetails d WHERE d.year = :year")
Optional<FxRateEntity> findByYear(@Param("year") String year);
@Query("SELECT f FROM FxRateEntity f JOIN f.fxRateDetails d WHERE d.year = :year AND d.currency = :currency")
Optional<FxRateEntity> findByYearAndCurrency(@Param("year") String year, @Param("currency") String currency);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.Grade;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GradeRepository extends JpaRepository<Grade, UUID> {
}

View File

@@ -0,0 +1,16 @@
package com.ikon.projectmanagement.repository;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.ikon.projectmanagement.entity.Issue;
@Repository
public interface IssueRepository extends JpaRepository<Issue, UUID> {
List<Issue> findByProjectIdentifier(UUID projectIdentifier);
List<Issue> findByIssueOwner(String issueOwner);
}

View File

@@ -0,0 +1,16 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.Meeting;
import org.apache.kafka.common.Uuid;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface MeetingRepository extends JpaRepository<Meeting, UUID> {
List<Meeting> findByProjectIdentifier(UUID projectIdentifier);
}

View File

@@ -0,0 +1,24 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.ProductOfProject;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductOfProjectRepository extends JpaRepository<ProductOfProject, UUID> {
List<ProductOfProject> findByAccountIdAndProjectIdentifier(
UUID accountId,
UUID projectIdentifier);
Optional<ProductOfProject> findByAccountIdAndProductIdentifier(
UUID accountId,
UUID productIdentifier);
Optional<ProductOfProject> findByAccountIdAndProjectIdentifierAndProductIdentifier(UUID accountId, UUID projectIdentifier,
UUID productIdentifier);
}

View File

@@ -0,0 +1,36 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.dto.response.StatusWiseProjectResponseData;
import com.ikon.projectmanagement.entity.Project;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@Repository
public interface ProjectRepository extends JpaRepository<Project, UUID> {
List<Project> findByAccountId(UUID accountId);
Optional<Project> getProjectByProjectIdentifier(UUID projectIdentifier);
boolean existsByProjectIdentifier(UUID projectIdentifier);
Optional<Project> findByProjectIdentifier(UUID projectIdentifier);
Optional<Project> findByAccountIdAndProjectIdentifier(UUID accountId, UUID projectIdentifier);
@Query("""
SELECT new com.ikon.projectmanagement.dto.response.StatusWiseProjectResponseData (
p.projectStatus,
COUNT(p)
)FROM Project p GROUP BY p.projectStatus
""")
List<StatusWiseProjectResponseData> getProjectStatusCounts();
List<Project> findByProjectStatusIgnoreCase(String projectStatus);
}

View File

@@ -0,0 +1,18 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.ProjectSchedule;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface ProjectScheduleRepository extends JpaRepository<ProjectSchedule, UUID> {
Optional<ProjectSchedule> findByAccountIdAndProjectIdentifier(UUID accountId, UUID projectIdentifier);
Optional<ProjectSchedule> findByAccountIdAndProductIdentifier(UUID accountId, UUID productIdentifier);
List<ProjectSchedule> findAllByAccountId(UUID accountId);
}

View File

@@ -0,0 +1,22 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.Risks;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface RiskRepository extends JpaRepository<Risks, UUID> {
List<Risks> findByProjectIdentifier(UUID projectIdentifier);
List<Risks> findByRiskStatus(String riskStatus);
List<Risks> findByRiskOwner(String riskOwner);
boolean existsByRiskTitleAndProjectIdentifier(String riskTitle, UUID projectIdentifier);
Risks findByRiskIdentifierAndProjectIdentifier(UUID riskIdentifier, UUID projectIdentifier);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.repository;
import com.ikon.projectmanagement.entity.Role;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleRepository extends JpaRepository<Role, UUID> {
}

View File

@@ -0,0 +1,28 @@
package com.ikon.projectmanagement.repository;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.ikon.projectmanagement.entity.WorkingDaysEntity;
@Repository
public interface WorkingDaysRepository extends JpaRepository<WorkingDaysEntity, UUID> {
@Query("SELECT w FROM WorkingDaysEntity w LEFT JOIN FETCH w.workingDaysDetails WHERE w.workingId = :id")
Optional<WorkingDaysEntity> findByIdWithDetails(@Param("id") UUID id);
Optional<WorkingDaysEntity> findByAccountIdentifier(UUID accountIdentifier);
List<WorkingDaysEntity> findAllByAccountIdentifier(UUID accountIdentifier);
Optional<WorkingDaysEntity> findByWorkingId(UUID workingId);
@Query("SELECT w FROM WorkingDaysEntity w JOIN w.workingDaysDetails d WHERE d.year = :year")
Optional<WorkingDaysEntity> findByYear(@Param("year") String year);
}

View File

@@ -0,0 +1,11 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.DashboardWidgetsResponseDto;
import com.ikon.projectmanagement.dto.response.StatusWiseProjectResponseData;
public interface DashboardService {
DashboardWidgetsResponseDto getDashboardWidgetsData();
List<StatusWiseProjectResponseData> getStatusWiseProjectData();
}

View File

@@ -0,0 +1,33 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.EmployeeRequestDto;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import java.util.List;
public interface EmployeeService {
/**
* Retrieve all employees
*/
List<EmployeeResponseDto> getAllEmployees();
/**
* Retrieve employee by ID
*/
EmployeeResponseDto getEmployeeById(String id);
/**
* Create a new employee
*/
EmployeeResponseDto createEmployee(EmployeeRequestDto dto);
/**
* Update an existing employee
*/
EmployeeResponseDto updateEmployee(String id, EmployeeRequestDto dto);
/**
* Delete an employee by ID
*/
void deleteEmployee(String id);
}

View File

@@ -0,0 +1,11 @@
package com.ikon.projectmanagement.service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.ikon.projectmanagement.dto.response.FxRateResponseDto;
public interface FxRateService {
Page<FxRateResponseDto> getAllFxRates(Pageable pageable);
FxRateResponseDto getFxRateByYear(String year);
}

View File

@@ -0,0 +1,34 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.GradeRequestDto;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import java.util.List;
import java.util.UUID;
public interface GradeService {
/**
* Retrieve all grades
*/
List<GradeResponseDto> getAllGrades();
/**
* Retrieve grade by ID
*/
GradeResponseDto getGradeById(UUID id);
/**
* Create a new grade
*/
GradeResponseDto createGrade(GradeRequestDto dto);
/**
* Update an existing grade
*/
GradeResponseDto updateGrade(UUID id, GradeRequestDto dto);
/**
* Delete a grade by ID
*/
void deleteGrade(UUID id);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
public interface ImportEmployeeService {
List<EmployeeResponseDto> importEmployees(String accessToken);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.FxRateDto;
public interface ImportFxRateService {
List<FxRateDto> importFxRates(String accessToken);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
public interface ImportGradeService {
List<GradeResponseDto> importGrades(String accessToken);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
public interface ImportRoleService {
List<RoleResponseDto> importRoles(String accessToken);
}

View File

@@ -0,0 +1,10 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import com.ikon.projectmanagement.dto.response.WorkingDayDto;
public interface ImportWorkingDayService {
List<WorkingDayDto> importWorkingDays(String accessToken);
}

View File

@@ -0,0 +1,22 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.IssueCreateRequestDto;
import com.ikon.projectmanagement.dto.request.RiskCreateRequestDto;
import com.ikon.projectmanagement.dto.response.IssueResponseDto;
import java.util.List;
import java.util.UUID;
public interface IssueService {
IssueResponseDto createIssue(IssueCreateRequestDto issueCreateRequestDto);
List<IssueResponseDto> getAllIssues(UUID projectIdentifier);
IssueResponseDto getIssueById(UUID issueId);
IssueResponseDto updateIssue(UUID issueId, IssueCreateRequestDto issueCreateRequestDto);
List<IssueResponseDto> getIssuesByProject(String projectIdentifier);
}

View File

@@ -0,0 +1,22 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.MeetingRequestDTO;
import com.ikon.projectmanagement.dto.response.MeetingResponseDTO;
import java.util.List;
import java.util.UUID;
public interface MeetingService {
MeetingResponseDTO createMeeting(MeetingRequestDTO meetingRequestDTO);
List<MeetingResponseDTO> getAllMeetings(UUID projectIdentifier);
List<MeetingResponseDTO> getMeetingsByProjectIdentifier(String projectIdentifier);
MeetingResponseDTO getMeetingById(UUID meetingId);
MeetingResponseDTO updateMeeting(UUID meetingId, MeetingRequestDTO meetingRequestDTO);
void deleteMeeting(UUID meetingId);
}

View File

@@ -0,0 +1,27 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import java.util.UUID;
import com.ikon.projectmanagement.dto.request.ProductOfProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProductOfProjectResponseDto;
public interface ProductOfProjectService {
List<ProductOfProjectResponseDto> getProductsByProject(UUID projectIdentifier);
ProductOfProjectResponseDto getProduct(UUID projectIdentifier, UUID productIdentifier);
ProductOfProjectResponseDto updateProduct(
UUID projectIdentifier,
UUID productIdentifier,
ProductOfProjectRequestDto productDto
);
ProductOfProjectResponseDto transitionStatus(
UUID projectIdentifier,
UUID productIdentifier,
String targetStatus
);
}

View File

@@ -0,0 +1,20 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.ProductPSResourceAllocationRequestDto;
import com.ikon.projectmanagement.dto.response.ProductPSResourceAllocationResponseDto;
import java.util.List;
import java.util.UUID;
public interface ProductResourceAllocationService {
List<ProductPSResourceAllocationResponseDto> getResourceAllocations(UUID productIdentifier);
ProductPSResourceAllocationResponseDto getResourceAllocation(UUID productIdentifier, UUID allocationIdentifier);
ProductPSResourceAllocationResponseDto createResourceAllocation(UUID productIdentifier, ProductPSResourceAllocationRequestDto dto);
ProductPSResourceAllocationResponseDto updateResourceAllocation(UUID productIdentifier, UUID allocationIdentifier, ProductPSResourceAllocationRequestDto dto);
void deleteResourceAllocation(UUID productIdentifier, UUID allocationIdentifier);
}

View File

@@ -0,0 +1,21 @@
package com.ikon.projectmanagement.service;
import java.util.List;
import java.util.UUID;
import com.ikon.projectmanagement.dto.request.ProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProjectResponseDto;
import com.ikon.projectmanagement.dto.response.ProjectTimelineResponseDto;
public interface ProjectService {
ProjectResponseDto createProject(ProjectRequestDto projectDto);
List<ProjectResponseDto> getAllProjects();
ProjectResponseDto getProjectByProjectIdentifier(UUID projectIdentifier);
ProjectResponseDto updateProject(UUID projectIdentifier, ProjectRequestDto projectDto);
List<ProjectTimelineResponseDto> getAllActiveProjectsTimeline();
}

View File

@@ -0,0 +1,20 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.RiskCreateRequestDto;
import com.ikon.projectmanagement.dto.response.RiskResponseDto;
import java.util.List;
import java.util.UUID;
public interface RiskService {
RiskResponseDto createRisk(RiskCreateRequestDto riskCreateRequestDto);
List<RiskResponseDto> getAllRisks();
RiskResponseDto getRiskById(UUID riskId);
RiskResponseDto updateRisk(UUID riskId, RiskCreateRequestDto riskCreateRequestDto);
List<RiskResponseDto> getRisksByProject(String projectIdentifier);
}

View File

@@ -0,0 +1,34 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.RoleRequestDto;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
import java.util.List;
import java.util.UUID;
public interface RoleService {
/**
* Retrieve all roles
*/
List<RoleResponseDto> getAllRoles();
/**
* Retrieve role by ID
*/
RoleResponseDto getRoleById(UUID id);
/**
* Create a new role
*/
RoleResponseDto createRole(RoleRequestDto dto);
/**
* Update an existing role
*/
RoleResponseDto updateRole(UUID id, RoleRequestDto dto);
/**
* Delete a role by ID
*/
void deleteRole(UUID id);
}

View File

@@ -0,0 +1,13 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.request.ScheduleRequestDto;
import com.ikon.projectmanagement.dto.response.ScheduleResponseDto;
import java.util.List;
import java.util.UUID;
public interface ScheduleService {
ScheduleResponseDto saveSchedule(UUID projectIdentifier, ScheduleRequestDto dto, String accessToken);
ScheduleResponseDto getSchedule(UUID projectIdentifier, String accessToken);
List<ScheduleResponseDto> getAllSchedules();
}

View File

@@ -0,0 +1,11 @@
package com.ikon.projectmanagement.service;
import com.ikon.projectmanagement.dto.response.WorkingDaysResponseDto;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface WorkingDayService {
public Page<WorkingDaysResponseDto> getAllWorkingDays(Pageable pageable);
public WorkingDaysResponseDto getWorkingDaysByYear(String year);
}

View File

@@ -0,0 +1,366 @@
package com.ikon.projectmanagement.service.etl;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.ProductOfProject;
import com.ikon.projectmanagement.entity.Project;
import com.ikon.projectmanagement.repository.ProjectRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
@RequiredArgsConstructor
public class DealConnector implements ConnectorDataSync {
private static final Logger log = LoggerFactory.getLogger(DealConnector.class);
private final ProjectRepository projectRepository;
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig
.add(FieldsConfigDto.builder().key("dealIdentifier").label("Deal Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("dealName").label("Deal Name").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("projectName").label("Project Name").type("string").build());
fieldsConfig.add(
FieldsConfigDto.builder().key("accountIdentifier").label("Account Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("dealStatus").label("Deal Status").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("currency").label("Currency").type("string").build());
fieldsConfig
.add(FieldsConfigDto.builder().key("contractNumber").label("Contract Number").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("contractedStartDate").label("Contracted Start Date")
.type("string").build());
fieldsConfig.add(
FieldsConfigDto.builder().key("contractedEndDate").label("Contracted End Date").type("string").build());
fieldsConfig.add(
FieldsConfigDto.builder().key("parentProjectNo").label("Parent Project No").type("string").build());
fieldsConfig
.add(FieldsConfigDto.builder().key("isDebtRevenue").label("Is Debt Revenue").type("boolean").build());
fieldsConfig
.add(FieldsConfigDto.builder().key("leadIdentifier").label("Lead Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("formattedActualRevenueIncludingVAT")
.label("Formatted Actual Revenue Including VAT").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("dealTeam").label("Deal Team").type("array").build());
fieldsConfig.add(FieldsConfigDto.builder().key("updatedBy").label("Updated By").type("string").build());
fieldsConfig
.add(FieldsConfigDto.builder().key("dealStartDate").label("Deal Start Date").type("string").build());
fieldsConfig
.add(FieldsConfigDto.builder().key("productDetails").label("Product Details").type("object").build());
return fieldsConfig;
}
@Override
public String getModule() {
return "Deal";
}
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
log.info("[DealConnector] Received {} records for sync", payload.size());
payload.stream()
.filter(data -> {
String dealId = toStr(data.get("dealIdentifier"));
String status = toStr(data.get("dealStatus"));
log.info(
"[DealConnector] Evaluating deal [{}], status=[{}]",
dealId,
status);
return "WON".equalsIgnoreCase(status);
})
.forEach(data -> {
log.info("====================================================");
log.info("[DealConnector] Processing Deal");
log.info("====================================================");
log.info("[DealConnector] Full Payload : {}", data);
UUID dealIdentifier = parseUUID(data.get("dealIdentifier"));
UUID accountId = parseUUID(
data.get("accountIdentifier") != null
? data.get("accountIdentifier")
: data.get("accountId"));
UUID leadIdentifier = parseUUID(data.get("leadIdentifier"));
UUID updatedByUUID = parseUUID(data.get("updatedBy"));
log.info(
"[DealConnector] dealIdentifier=[{}], accountId=[{}], leadIdentifier=[{}], updatedBy=[{}]",
dealIdentifier,
accountId,
leadIdentifier,
updatedByUUID);
Object rawProductDetails = data.get("productDetails");
log.info(
"[DealConnector] productDetails type=[{}]",
rawProductDetails != null
? rawProductDetails.getClass().getName()
: "null");
log.info(
"[DealConnector] productDetails value=[{}]",
rawProductDetails);
ProductOfProject product = buildProduct(
rawProductDetails,
accountId,
dealIdentifier,
leadIdentifier);
if (product == null) {
log.warn(
"[DealConnector] Product creation FAILED for deal [{}]",
dealIdentifier);
log.warn(
"[DealConnector] Raw productDetails = {}",
rawProductDetails);
return;
}
log.info(
"[DealConnector] Product created successfully. productIdentifier=[{}], type=[{}]",
product.getProductIdentifier(),
product.getProductType());
String name = data.get("projectName") != null
? (String) data.get("projectName")
: (String) data.get("dealName");
UUID projectManager = product.getProjectManager() != null
? product.getProjectManager()
: accountId;
List<UUID> projectTeam = parseUUIDList(data.get("dealTeam"));
Project project = Project.builder()
.projectIdentifier(dealIdentifier)
.accountId(accountId)
.projectName(name)
.projectStatus((String) data.get("dealStatus"))
.currency((String) data.get("currency"))
.isDebtRevenue_deal((Boolean) data.get("isDebtRevenue"))
.formattedActualRevenueIncludingVAT_deal(
toStr(data.get("formattedActualRevenueIncludingVAT_contracted")))
.contractNumber(toStr(data.get("contractNumber")))
.contractedStartDate(parseDate((String) data.get("contractedStartDate")))
.contractedEndDate(parseDate((String) data.get("contractedEndDate")))
.projectStartDate(parseDate((String) data.get("dealStartDate")))
.parentProjectNo((String) data.get("parentProjectNo"))
.projectManager(projectManager)
.updatedBy(updatedByUUID)
.projectTeam(projectTeam)
.source("SalesCRM")
.product(product)
.build();
log.info(
"[DealConnector] Saving project for deal [{}] projectName=[{}]",
dealIdentifier,
name);
projectRepository.save(project);
log.info(
"[DealConnector] Project saved successfully for deal [{}]",
dealIdentifier);
});
}
private ProductOfProject buildProduct(
Object raw,
UUID accountId,
UUID projectIdentifier,
UUID leadIdentifier) {
log.info(
"[DealConnector] buildProduct() called. rawType=[{}]",
raw != null ? raw.getClass().getName() : "null");
if (raw == null) {
log.warn(
"[DealConnector] productDetails is NULL for deal [{}]",
projectIdentifier);
return null;
}
if (!(raw instanceof Map)) {
log.warn(
"[DealConnector] productDetails is NOT a Map. type=[{}], value=[{}]",
raw.getClass().getName(),
raw);
return null;
}
Map<?, ?> productMap = (Map<?, ?>) raw;
log.info(
"[DealConnector] productDetails contains {} entries",
productMap.size());
if (productMap.isEmpty()) {
log.warn(
"[DealConnector] productDetails map is EMPTY");
return null;
}
productMap.forEach((key, value) -> {
log.info(
"[DealConnector] Entry key=[{}], valueType=[{}]",
key,
value != null ? value.getClass().getName() : "null");
log.info(
"[DealConnector] Entry value=[{}]",
value);
});
for (Object value : productMap.values()) {
if (!(value instanceof Map)) {
log.warn(
"[DealConnector] Product entry is not Map : {}",
value);
continue;
}
Map<?, ?> pd = (Map<?, ?>) value;
String productType = toStr(pd.get("productType"));
log.info(
"[DealConnector] Checking productType=[{}]",
productType);
if (!"Professional Service".equalsIgnoreCase(productType)) {
log.info(
"[DealConnector] Skipping product because type is [{}]",
productType);
continue;
}
log.info(
"[DealConnector] Professional Service product FOUND");
UUID pmUuid = tryParseUUID(toStr(pd.get("projectManager")));
UUID productId = tryParseUUID(toStr(pd.get("productIdentifier")));
log.info(
"[DealConnector] productIdentifier=[{}], projectManager=[{}]",
productId,
pmUuid);
return ProductOfProject.builder()
.productIdentifier(productId)
.projectIdentifier(projectIdentifier)
.accountId(accountId)
.leadIdentifier(leadIdentifier)
.productType(productType)
.productDescription(
toStr(pd.get("productDescription")))
.discountPercent(
toDouble(pd.get("discountPercent")))
.projectManager(pmUuid)
.productStatus("Active")
.build();
}
log.warn(
"[DealConnector] No Professional Service product found in productDetails. productMap={}",
productMap);
return null;
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
private UUID parseUUID(Object value) {
if (value == null)
return null;
try {
return UUID.fromString(value.toString());
} catch (Exception e) {
return null;
}
}
private UUID tryParseUUID(String value) {
if (value == null)
return null;
try {
return UUID.fromString(value);
} catch (Exception e) {
return null;
}
}
private List<UUID> parseUUIDList(Object value) {
List<UUID> result = new ArrayList<>();
if (!(value instanceof List))
return result;
for (Object item : (List<?>) value) {
UUID uuid = parseUUID(item);
if (uuid != null)
result.add(uuid);
}
return result;
}
private LocalDate parseDate(String value) {
if (value == null || value.isBlank())
return null;
try {
return LocalDate.parse(value);
} catch (DateTimeParseException e) {
return null;
}
}
private String toStr(Object value) {
return value != null ? String.valueOf(value) : null;
}
private Double toDouble(Object value) {
if (value instanceof Number)
return ((Number) value).doubleValue();
return null;
}
}

View File

@@ -0,0 +1,71 @@
package com.ikon.projectmanagement.service.etl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.Employee;
import com.ikon.projectmanagement.repository.EmployeeRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class EmployeeConector implements ConnectorDataSync{
private final EmployeeRepository employeeRepository;
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig.add(FieldsConfigDto.builder().key("empId").label("Employee Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("name").label("Name").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("role").label("Role").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("grade").label("Grade").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("email").label("Email").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("organizationEmail").label("Organization Email").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("active").label("Active").type("boolean").build());
fieldsConfig.add(FieldsConfigDto.builder().key("highestQualification").label("Highest Qualification").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("joiningDate").label("Joining Date").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("confirmationDate").label("Confirmation Date").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("accountId").label("Account Identifier").type("string").build());
return fieldsConfig;
}
@Override
public String getModule() {
return "Employee";
}
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
payload.stream()
.forEach(data -> {
Employee employee = Employee.builder()
.name(data.get("name") != null ? data.get("name").toString() : null)
.accountId(data.get("accountId") != null ? UUID.fromString(data.get("accountId").toString()) : null)
.empId(data.get("empId") != null ? data.get("empId").toString() : null)
.email(data.get("email") != null ? data.get("email").toString() : null)
.organizationEmail(data.get("organizationEmail") != null ? data.get("organizationEmail").toString() : null)
.role(data.get("role") != null ? data.get("role").toString() : null)
.grade(data.get("grade") != null ? data.get("grade").toString() : null)
.active(data.get("active") != null ? Boolean.parseBoolean(data.get("active").toString()) : null)
.highestQualification(data.get("highestQualification") != null ? data.get("highestQualification").toString(): null)
.joiningDate(data.get("joiningDate") != null ? data.get("joiningDate").toString() : null)
.confirmationDate(data.get("confirmationDate") != null ? data.get("confirmationDate").toString() : null)
.build();
employeeRepository.save(employee);
});
}
}

View File

@@ -0,0 +1,184 @@
package com.ikon.projectmanagement.service.etl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.FxRateDetailsEntity;
import com.ikon.projectmanagement.entity.FxRateEntity;
import com.ikon.projectmanagement.repository.FxRateRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Component
@RequiredArgsConstructor
@Slf4j
public class FxRateConnector implements ConnectorDataSync {
private final FxRateRepository fxRateRepository;
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig.add(FieldsConfigDto.builder().key("fxRateId").label("FX Rate ID").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("accountIdentifier").label("Account Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("fxRateDetails").label("FX Rate Details").type("object").build());
return fieldsConfig;
}
@Override
public String getModule() {
return "FxRate";
}
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
if (payload == null || payload.isEmpty())
return;
List<FxRateEntity> fxRates = payload.stream()
.map(data -> {
UUID fxRateId = parseUUID(data.get("fxRateId"));
UUID accountIdentifier = parseUUID(data.get("accountIdentifier"));
if (fxRateId == null) {
log.warn("Skipping record with null fxRateId");
return null;
}
FxRateEntity fxRate = fxRateRepository.findByIdWithDetails(fxRateId)
.orElseGet(() -> FxRateEntity.builder()
.fxRateId(fxRateId)
.build());
fxRate.setAccountIdentifier(accountIdentifier);
Map<String, FxRateDetailsEntity> detailsMap = buildDetailsMap(data.get("fxRateDetails"));
log.info("Built detailsMap size: {}", detailsMap.size());
if (!detailsMap.isEmpty()) {
fxRate.getFxRateDetails().clear();
detailsMap.values().forEach(fxRate::addFxRateDetail);
log.info("fxRateDetails after repopulation: {}", fxRate.getFxRateDetails().size());
}
return fxRate;
})
.filter(Objects::nonNull)
.toList();
log.info("Saving {} FxRate entities", fxRates.size());
fxRateRepository.saveAll(fxRates);
}
private Map<String, FxRateDetailsEntity> buildDetailsMap(Object rawDetails) {
Map<String, FxRateDetailsEntity> resultMap = new HashMap<>();
// Convert to JsonNode — handles ObjectNode, String, or Map
JsonNode rootNode = toJsonNode(rawDetails);
if (rootNode == null || rootNode.isNull() || !rootNode.isObject()) {
log.warn("fxRateDetails could not be parsed as an object, type: {}",
rawDetails == null ? "NULL" : rawDetails.getClass().getName());
return resultMap;
}
// Outer loop: year keys e.g. "2026", "2025"
rootNode.fields().forEachRemaining(yearEntry -> {
JsonNode currencyMapNode = yearEntry.getValue();
if (!currencyMapNode.isObject())
return;
// Inner loop: currency keys e.g. "USD", "CNY"
currencyMapNode.fields().forEachRemaining(currencyEntry -> {
JsonNode fd = currencyEntry.getValue();
if (!fd.isObject())
return;
// currencyId is null in payload, fallback to currency field
String currencyId = !fd.path("currencyId").isNull() && fd.hasNonNull("currencyId")
? fd.path("currencyId").asText(null)
: fd.path("currency").asText(null);
if (currencyId == null) {
log.warn("Skipping detail with null currencyId and currency for year: {}", yearEntry.getKey());
return;
}
Boolean active = fd.hasNonNull("activeStatus")
? fd.path("activeStatus").asBoolean(true)
: Boolean.TRUE;
FxRateDetailsEntity detail = FxRateDetailsEntity.builder()
.currencyId(currencyId)
.currency(fd.path("currency").asText(null))
.fxRate(fd.hasNonNull("fxRate") ? fd.path("fxRate").asDouble() : null)
.activeStatus(active)
.year(fd.path("year").asText(null))
.build();
resultMap.put(currencyId, detail);
log.debug("Added detail: currencyId={}, fxRate={}, year={}",
currencyId, detail.getFxRate(), detail.getYear());
});
});
return resultMap;
}
/**
* Converts ObjectNode, Map, or JSON String → JsonNode
*/
private JsonNode toJsonNode(Object rawDetails) {
if (rawDetails == null)
return null;
// Already a JsonNode (e.g. ObjectNode from Jackson)
if (rawDetails instanceof JsonNode) {
return (JsonNode) rawDetails;
}
// Plain JSON String
if (rawDetails instanceof String) {
try {
return objectMapper.readTree((String) rawDetails);
} catch (Exception e) {
log.error("Failed to parse fxRateDetails JSON string: {}", e.getMessage());
return null;
}
}
// Plain Map (e.g. LinkedHashMap)
if (rawDetails instanceof Map) {
return objectMapper.valueToTree(rawDetails);
}
log.warn("Unrecognized fxRateDetails type: {}", rawDetails.getClass().getName());
return null;
}
// --- Utility Helpers ---
private UUID parseUUID(Object value) {
if (value == null)
return null;
try {
return UUID.fromString(value.toString());
} catch (Exception e) {
log.warn("Failed to parse UUID from value: {}", value);
return null;
}
}
}

View File

@@ -0,0 +1,134 @@
package com.ikon.projectmanagement.service.etl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.Grade;
import com.ikon.projectmanagement.repository.GradeRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
/**
* GradeConnector: Maps external grade/salary grade data to local Grade entity
*
* This connector syncs grade information from an external HR system or database.
* It handles field mapping, type conversion, and persistence of grade records.
*/
@Component
@RequiredArgsConstructor
public class GradeConnector implements ConnectorDataSync {
private final GradeRepository gradeRepository;
/**
* Define the configuration for fields that will be synced from external source.
* This specifies the external field names, labels, and their data types.
*/
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig.add(FieldsConfigDto.builder()
.key("id")
.label("Grade ID")
.type("string")
.build());
fieldsConfig.add(FieldsConfigDto.builder()
.key("accountId")
.label("Account ID")
.type("string")
.build());
fieldsConfig.add(FieldsConfigDto.builder()
.key("grade")
.label("Grade")
.type("string")
.build());
return fieldsConfig;
}
/**
* Returns the module name for this connector.
* Used for identification and logging purposes.
*/
@Override
public String getModule() {
return "Grade";
}
/**
* Core method that processes incoming grade data from external source.
*
* Process flow:
* 1. Extract and parse fields from Map
* 2. Convert data types using utility methods
* 3. Build Grade entity
* 4. Save to repository
*
* @param payload List of Map objects containing grade data from external source
*/
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
payload.stream()
.forEach(data -> {
try {
UUID gradeId = parseUUID(data.get("id"));
UUID accountId = parseUUID(data.get("accountId"));
String gradeName = toStr(data.get("grade"));
// Skip if required fields are missing
if (gradeId == null || gradeName == null) {
System.err.println("Skipping grade record: missing required fields (id or grade)");
return;
}
// Build Grade entity with mapped fields
Grade grade = Grade.builder()
.id(gradeId)
.accountId(accountId)
.grade(gradeName)
.build();
// Save the grade entity to repository
gradeRepository.save(grade);
} catch (Exception e) {
// Log error but continue processing other records
System.err.println("Error syncing grade: " + e.getMessage());
}
});
}
// ============ Utility Methods for Type Conversion ============
/**
* Parse UUID from Object safely.
* Returns null if value is null or cannot be parsed as UUID.
*/
private UUID parseUUID(Object value) {
if (value == null) return null;
try {
return UUID.fromString(value.toString());
} catch (Exception e) {
return null;
}
}
/**
* Convert Object to String safely.
* Returns null if value is null, otherwise returns String representation.
*/
private String toStr(Object value) {
return value != null ? String.valueOf(value) : null;
}
}

View File

@@ -0,0 +1,94 @@
package com.ikon.projectmanagement.service.etl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Component;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.Role;
import com.ikon.projectmanagement.repository.RoleRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
/**
* RoleConnector: Maps external role data to local Role entity.
*
* This connector syncs role information from an external system.
* It handles field mapping, type conversion, and persistence of Role records.
*/
@Component
@RequiredArgsConstructor
public class RoleConnector implements ConnectorDataSync {
private final RoleRepository roleRepository;
/**
* Define the configuration for fields that will be synced from the external
* source.
* This specifies the external field names, labels, and their data types.
*/
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig.add(FieldsConfigDto.builder()
.key("id")
.label("Role ID")
.type("string")
.build());
fieldsConfig.add(FieldsConfigDto.builder()
.key("accountId")
.label("Account ID")
.type("string")
.build());
fieldsConfig.add(FieldsConfigDto.builder()
.key("role")
.label("Role")
.type("string")
.build());
return fieldsConfig;
}
/**
* Returns the module name for this connector.
* Used for identification and logging purposes.
*/
@Override
public String getModule() {
return "Role";
}
/**
* Core method that processes incoming role data from an external source.
*
* Process flow:
* 1. Extract and parse fields from Map
* 2. Convert data types using utility methods
* 3. Build Role entity
* 4. Save to repository
*
* @param payload List of Map objects containing role data from external source
*/
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
payload.stream()
.forEach(data -> {
Role role = Role.builder()
.id(data.get("id") != null ? UUID.fromString(data.get("id").toString()) : null)
.accountId(data.get("id") != null ? UUID.fromString(data.get("id").toString()) : null)
.role(data.get("role") != null ? data.get("role").toString() : null)
.build();
roleRepository.save(role);
});
}
}

View File

@@ -0,0 +1,188 @@
package com.ikon.projectmanagement.service.etl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.WorkingDaysDetailsEntity;
import com.ikon.projectmanagement.entity.WorkingDaysEntity;
import com.ikon.projectmanagement.repository.WorkingDaysRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Component
@RequiredArgsConstructor
@Slf4j
public class WorkingDaysConnector implements ConnectorDataSync {
private final WorkingDaysRepository workingDaysRepository;
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public List<FieldsConfigDto> getFieldsConfig() {
List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
fieldsConfig.add(FieldsConfigDto.builder().key("workingId").label("Working ID").type("string").build());
fieldsConfig.add(
FieldsConfigDto.builder().key("accountIdentifier").label("Account Identifier").type("string").build());
fieldsConfig.add(FieldsConfigDto.builder().key("workingDaysDetails").label("Working Days Details")
.type("object").build());
return fieldsConfig;
}
@Override
public String getModule() {
return "WorkingDays";
}
@Override
@Transactional
public void syncBatch(List<Map<String, Object>> payload) {
if (payload == null || payload.isEmpty())
return;
List<WorkingDaysEntity> workingDaysList = payload.stream()
.map(data -> {
UUID workingId = parseUUID(data.get("workingId"));
UUID accountIdentifier = parseUUID(data.get("accountIdentifier"));
if (workingId == null) {
log.warn("Skipping record with null workingId");
return null;
}
// Fetch existing entity with details eagerly, or create new
WorkingDaysEntity workingDays = workingDaysRepository.findByIdWithDetails(workingId)
.orElseGet(() -> WorkingDaysEntity.builder()
.workingId(workingId)
.build());
workingDays.setAccountIdentifier(accountIdentifier);
log.info("RAW workingDaysDetails type: {}",
data.get("workingDaysDetails") == null ? "NULL"
: data.get("workingDaysDetails").getClass().getName());
log.info("RAW workingDaysDetails value: {}", data.get("workingDaysDetails"));
Map<String, WorkingDaysDetailsEntity> detailsMap = buildDetailsMap(data.get("workingDaysDetails"));
log.info("Built detailsMap size: {}", detailsMap.size());
if (!detailsMap.isEmpty()) {
// Clear existing managed collection (orphanRemoval deletes old rows)
workingDays.getWorkingDaysDetails().clear();
// Repopulate via addWorkingDaysDetail to keep parent link intact
detailsMap.values().forEach(workingDays::addWorkingDaysDetail);
log.info("workingDaysDetails after repopulation: {}",
workingDays.getWorkingDaysDetails().size());
}
return workingDays;
})
.filter(Objects::nonNull)
.toList();
log.info("Saving {} WorkingDays entities", workingDaysList.size());
workingDaysRepository.saveAll(workingDaysList);
}
private Map<String, WorkingDaysDetailsEntity> buildDetailsMap(Object rawDetails) {
Map<String, WorkingDaysDetailsEntity> resultMap = new HashMap<>();
JsonNode rootNode = toJsonNode(rawDetails);
if (rootNode == null || rootNode.isNull() || !rootNode.isObject()) {
log.warn("workingDaysDetails could not be parsed as an object, type: {}",
rawDetails == null ? "NULL" : rawDetails.getClass().getName());
return resultMap;
}
// Outer loop: year keys e.g. "2025", "2026"
rootNode.fields().forEachRemaining(yearEntry -> {
String year = yearEntry.getKey();
JsonNode monthMapNode = yearEntry.getValue();
if (!monthMapNode.isObject())
return;
// Inner loop: month keys e.g. "January", "February"
monthMapNode.fields().forEachRemaining(monthEntry -> {
JsonNode fd = monthEntry.getValue();
if (!fd.isObject())
return;
String month = fd.hasNonNull("month")
? fd.path("month").asText(null)
: monthEntry.getKey();
if (month == null) {
log.warn("Skipping detail with null month for year: {}", year);
return;
}
Integer workingDays = fd.hasNonNull("workingDays")
? fd.path("workingDays").asInt()
: null;
WorkingDaysDetailsEntity detail = WorkingDaysDetailsEntity.builder()
.year(fd.hasNonNull("year") ? fd.path("year").asText(null) : year)
.month(month)
.workingDays(workingDays)
.build();
resultMap.put(month, detail);
log.debug("Added detail: year={}, month={}, workingDays={}", year, month, workingDays);
});
});
return resultMap;
}
/**
* Converts ObjectNode, Map, or JSON String → JsonNode
*/
private JsonNode toJsonNode(Object rawDetails) {
if (rawDetails == null)
return null;
if (rawDetails instanceof JsonNode) {
return (JsonNode) rawDetails;
}
if (rawDetails instanceof String) {
try {
return objectMapper.readTree((String) rawDetails);
} catch (Exception e) {
log.error("Failed to parse workingDaysDetails JSON string: {}", e.getMessage());
return null;
}
}
if (rawDetails instanceof Map) {
return objectMapper.valueToTree(rawDetails);
}
log.warn("Unrecognized workingDaysDetails type: {}", rawDetails.getClass().getName());
return null;
}
// --- Utility Helpers ---
private UUID parseUUID(Object value) {
if (value == null)
return null;
try {
return UUID.fromString(value.toString());
} catch (Exception e) {
log.warn("Failed to parse UUID from value: {}", value);
return null;
}
}
}

View File

@@ -0,0 +1,35 @@
package com.ikon.projectmanagement.service.impl;
import java.util.List;
import org.springframework.stereotype.Service;
import com.ikon.projectmanagement.dto.response.DashboardWidgetsResponseDto;
import com.ikon.projectmanagement.dto.response.StatusWiseProjectResponseData;
import com.ikon.projectmanagement.repository.IssueRepository;
import com.ikon.projectmanagement.repository.ProjectRepository;
import com.ikon.projectmanagement.repository.RiskRepository;
import com.ikon.projectmanagement.service.DashboardService;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {
private final ProjectRepository projectRepository;
private final IssueRepository issueRepository;
private final RiskRepository riskRepository;
@Override
public DashboardWidgetsResponseDto getDashboardWidgetsData() {
DashboardWidgetsResponseDto widgetsData = new DashboardWidgetsResponseDto();
widgetsData.setTotalProjects(projectRepository.count());
widgetsData.setTotalIssues(issueRepository.count());
widgetsData.setTotalRisksCount(riskRepository.count());
return widgetsData;
}
@Override
public List<StatusWiseProjectResponseData> getStatusWiseProjectData() {
return projectRepository.getProjectStatusCounts();
}
}

View File

@@ -0,0 +1,61 @@
package com.ikon.projectmanagement.service.impl;
import com.ikon.projectmanagement.dto.request.EmployeeRequestDto;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.entity.Employee;
import com.ikon.projectmanagement.mapper.EmployeeMapper;
import com.ikon.projectmanagement.repository.EmployeeRepository;
import com.ikon.projectmanagement.service.EmployeeService;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeRepository employeeRepository;
private final EmployeeMapper employeeMapper;
@Override
public List<EmployeeResponseDto> getAllEmployees() {
return employeeRepository.findAll().stream()
.map(employeeMapper::toResponseDto)
.collect(Collectors.toList());
}
@Override
public EmployeeResponseDto getEmployeeById(String id) {
Employee employee = employeeRepository.findByEmpId(id)
.orElseThrow(() -> new RuntimeException("Employee not found with ID: " + id));
return employeeMapper.toResponseDto(employee);
}
@Override
public EmployeeResponseDto createEmployee(EmployeeRequestDto dto) {
// Check if employee already exists
if (dto.getEmpId() != null && employeeRepository.findByEmpId(dto.getEmpId()).isPresent()) {
throw new RuntimeException("Employee already exists with ID: " + dto.getEmpId());
}
Employee employee = employeeMapper.toEntity(dto);
Employee savedEmployee = employeeRepository.save(employee);
return employeeMapper.toResponseDto(savedEmployee);
}
@Override
public EmployeeResponseDto updateEmployee(String id, EmployeeRequestDto dto) {
Employee employee = employeeRepository.findByEmpId(id)
.orElseThrow(() -> new RuntimeException("Employee not found with ID: " + id));
employeeMapper.updateEntity(employee, dto);
Employee updatedEmployee = employeeRepository.save(employee);
return employeeMapper.toResponseDto(updatedEmployee);
}
@Override
public void deleteEmployee(String id) {
Employee employee = employeeRepository.findByEmpId(id)
.orElseThrow(() -> new RuntimeException("Employee not found with ID: " + id));
employeeRepository.delete(employee);
}
}

View File

@@ -0,0 +1,64 @@
package com.ikon.projectmanagement.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.ikon.projectmanagement.dto.response.FxRateDetailsDto;
import com.ikon.projectmanagement.dto.response.FxRateResponseDto;
import com.ikon.projectmanagement.entity.FxRateEntity;
import com.ikon.projectmanagement.repository.FxRateRepository;
import com.ikon.projectmanagement.service.FxRateService;
import com.ikon.webservice.WebService;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class FxRateServiceImpl extends WebService implements FxRateService {
private final FxRateRepository fxRateRepository;
@Override
public Page<FxRateResponseDto> getAllFxRates(Pageable pageable) {
return fxRateRepository.findAll(pageable)
.map(this::mapToResponseDto);
}
@Override
public FxRateResponseDto getFxRateByYear(String year) {
FxRateEntity entity = fxRateRepository.findByYear(year)
.orElseThrow(() -> new EntityNotFoundException(
"FX rate not found for year: " + year));
return mapToResponseDto(entity);
}
// -------------------------------------------------------------------------
// Private helpers
// ------------------------------------------------------------------------
private FxRateResponseDto mapToResponseDto(FxRateEntity entity) {
Map<String, Map<String, FxRateDetailsDto>> detailsMap = new HashMap<>();
entity.getFxRateDetails().values().forEach(detail -> {
FxRateDetailsDto dto = new FxRateDetailsDto();
dto.setId(detail.getCurrencyId());
dto.setCurrency(detail.getCurrency());
dto.setFxRate(detail.getFxRate());
dto.setActiveStatus(detail.getActiveStatus());
dto.setYear(detail.getYear());
detailsMap
.computeIfAbsent(detail.getYear(), y -> new HashMap<>())
.put(detail.getCurrencyId(), dto);
});
FxRateResponseDto responseDto = new FxRateResponseDto();
responseDto.setFxRateId(entity.getFxRateId());
responseDto.setAccountIdentifier(entity.getAccountIdentifier());
responseDto.setFxRateDetails(detailsMap);
return responseDto;
}
}

View File

@@ -0,0 +1,58 @@
package com.ikon.projectmanagement.service.impl;
import com.ikon.projectmanagement.dto.request.GradeRequestDto;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import com.ikon.projectmanagement.entity.Grade;
import com.ikon.projectmanagement.mapper.GradeMapper;
import com.ikon.projectmanagement.repository.GradeRepository;
import com.ikon.projectmanagement.service.GradeService;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class GradeServiceImpl implements GradeService {
private final GradeRepository gradeRepository;
private final GradeMapper gradeMapper;
@Override
public List<GradeResponseDto> getAllGrades() {
return gradeRepository.findAll().stream()
.map(gradeMapper::toResponseDto)
.collect(Collectors.toList());
}
@Override
public GradeResponseDto getGradeById(UUID id) {
Grade grade = gradeRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Grade not found with ID: " + id));
return gradeMapper.toResponseDto(grade);
}
@Override
public GradeResponseDto createGrade(GradeRequestDto dto) {
Grade grade = gradeMapper.toEntity(dto);
Grade savedGrade = gradeRepository.save(grade);
return gradeMapper.toResponseDto(savedGrade);
}
@Override
public GradeResponseDto updateGrade(UUID id, GradeRequestDto dto) {
Grade grade = gradeRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Grade not found with ID: " + id));
gradeMapper.updateEntity(grade, dto);
Grade updatedGrade = gradeRepository.save(grade);
return gradeMapper.toResponseDto(updatedGrade);
}
@Override
public void deleteGrade(UUID id) {
Grade grade = gradeRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Grade not found with ID: " + id));
gradeRepository.delete(grade);
}
}

View File

@@ -0,0 +1,132 @@
package com.ikon.projectmanagement.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.dto.request.EmployeeRequestDto;
import com.ikon.projectmanagement.dto.response.EmployeeResponseDto;
import com.ikon.projectmanagement.entity.Employee;
import com.ikon.projectmanagement.repository.EmployeeRepository;
import com.ikon.projectmanagement.mapper.EmployeeMapper;
import com.ikon.projectmanagement.service.ImportEmployeeService;
import com.ikon.webservice.WebService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@Slf4j
public class ImportEmployeeServiceImpl extends WebService implements ImportEmployeeService {
private final EmployeeMapper employeeMapper;
private final RestTemplate restTemplate;
private final EmployeeRepository employeeRepository;
@Value("${salescrm.api.base-url}")
private String salesCrmBaseUrl;
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
if (accessToken != null && !accessToken.isEmpty()) {
headers.set("Authorization", accessToken);
}
headers.set("Content-Type", "application/json");
return headers;
}
private static final int PAGE_SIZE = 200;
@Override
@Transactional
public List<EmployeeResponseDto> importEmployees(String accessToken) {
String baseUrl = salesCrmBaseUrl + "/employee";
UUID accountId = getActiveAccountId();
log.info("Fetching all employees from Sales CRM: {}", baseUrl);
try {
HttpEntity<Void> entity = new HttpEntity<>(createHeaders(accessToken));
List<EmployeeRequestDto> allDtos = new ArrayList<>();
List<EmployeeResponseDto> savedResponses = new ArrayList<>();
int page = 0;
int totalPages = 1;
while (page < totalPages) {
String url = baseUrl + "?page=" + page + "&size=" + PAGE_SIZE;
ResponseEntity<PageResponse<EmployeeRequestDto>> response = restTemplate.exchange(
url, HttpMethod.GET, entity, new ParameterizedTypeReference<PageResponse<EmployeeRequestDto>>() {});
if (response.getBody() == null || response.getBody().getContent() == null) {
break;
}
PageResponse<EmployeeRequestDto> body = response.getBody();
allDtos.addAll(body.getContent());
totalPages = body.getTotalPages();
log.info("Fetched employees page {}/{}", page + 1, totalPages);
page++;
}
for (EmployeeRequestDto dto : allDtos) {
Optional<Employee> existingOpt = employeeRepository.findByEmpId(dto.getEmpId());
Employee emp;
if (existingOpt.isPresent()) {
emp = existingOpt.get();
emp.setAccountId(accountId);
emp.setName(dto.getName());
emp.setEmail(dto.getEmail());
emp.setOrganizationEmail(dto.getOrganizationEmail());
emp.setRole(dto.getRole());
emp.setGrade(dto.getGrade());
emp.setActive(dto.getActive());
} else {
emp = Employee.builder()
.empId(dto.getEmpId())
.accountId(accountId)
.name(dto.getName())
.email(dto.getEmail())
.organizationEmail(dto.getOrganizationEmail())
.role(dto.getRole())
.grade(dto.getGrade())
.active(dto.getActive())
.build();
}
employeeRepository.save(emp);
savedResponses.add(employeeMapper.toResponseDto(emp));
}
if (!allDtos.isEmpty()) {
log.info("Saved/Updated {} employees in total", allDtos.size());
}
return savedResponses;
} catch (RestClientException e) {
log.error("Failed to fetch employees from Sales CRM: {}", e.getMessage());
return Collections.emptyList();
}
}
@Data
public static class PageResponse<T> {
private List<T> content;
private int totalPages;
private long totalElements;
}
}

View File

@@ -0,0 +1,141 @@
package com.ikon.projectmanagement.service.impl;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.ikon.projectmanagement.dto.response.FxRateDto;
import com.ikon.projectmanagement.entity.FxRateEntity;
import com.ikon.projectmanagement.entity.FxRateEntity;
import com.ikon.projectmanagement.repository.FxRateRepository;
import com.ikon.projectmanagement.service.ImportFxRateService;
import com.ikon.webservice.WebService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class ImportFxRateServiceImpl extends WebService implements ImportFxRateService {
private final RestTemplate restTemplate;
private final FxRateRepository fxRateRepository;
@Value("${salescrm.api.base-url}")
private String salesCrmBaseUrl;
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
if (accessToken != null && !accessToken.isEmpty()) {
headers.set("Authorization", accessToken);
}
headers.set("Content-Type", "application/json");
return headers;
}
private static final int PAGE_SIZE = 200;
@Override
@Transactional
public List<FxRateDto> importFxRates(String accessToken) {
String baseUrl = salesCrmBaseUrl + "/fxrate";
log.info("Fetching all FX rates from Sales CRM: {}", baseUrl);
try {
HttpEntity<Void> entity = new HttpEntity<>(createHeaders(accessToken));
List<SalesCrmFxRateDto> allRaw = new ArrayList<>();
int page = 0;
int totalPages = 1;
while (page < totalPages) {
String url = baseUrl + "?page=" + page + "&size=" + PAGE_SIZE;
ResponseEntity<PageResponse<SalesCrmFxRateDto>> response = restTemplate.exchange(
url, HttpMethod.GET, entity,
new ParameterizedTypeReference<PageResponse<SalesCrmFxRateDto>>() {});
if (response.getBody() == null || response.getBody().getContent() == null) {
break;
}
PageResponse<SalesCrmFxRateDto> body = response.getBody();
allRaw.addAll(body.getContent());
totalPages = body.getTotalPages();
log.info("Fetched FX rates page {}/{}", page + 1, totalPages);
page++;
}
List<FxRateDto> dtos = allRaw.stream()
.map(this::mapToFxRateDto)
.collect(Collectors.toList());
List<FxRateEntity> entities = dtos.stream().map(dto -> FxRateEntity.builder()
.fxRateId(dto.getId())
.accountIdentifier(dto.getAccountId())
.build()).collect(Collectors.toList());
if (!entities.isEmpty()) {
fxRateRepository.saveAll(entities);
log.info("Saved/Updated {} FX rates in total", entities.size());
}
return dtos;
} catch (RestClientException e) {
log.error("Failed to fetch FX rates from Sales CRM: {}", e.getMessage());
return Collections.emptyList();
}
}
private FxRateDto mapToFxRateDto(SalesCrmFxRateDto source) {
BigDecimal rateValue = BigDecimal.ZERO;
if (source.getRate() != null) {
try {
rateValue = new BigDecimal(source.getRate());
} catch (NumberFormatException e) {
log.warn("Invalid rate format for id {}: {}", source.getId(), source.getRate());
}
}
return FxRateDto.builder()
.id(source.getId())
.accountId(source.getAccountId())
.toCurrency(source.getCurrency())
.fromCurrency("USD")
.rate(rateValue)
.effectiveDate(source.getYear())
.build();
}
@Data
public static class PageResponse<T> {
private List<T> content;
private int totalPages;
private long totalElements;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class SalesCrmFxRateDto {
private UUID id;
private UUID accountId;
private String year;
private String rate;
private String currency;
}
}

View File

@@ -0,0 +1,85 @@
package com.ikon.projectmanagement.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.ikon.projectmanagement.dto.request.GradeRequestDto;
import com.ikon.projectmanagement.dto.response.GradeResponseDto;
import com.ikon.projectmanagement.entity.Grade;
import com.ikon.projectmanagement.mapper.GradeMapper;
import com.ikon.projectmanagement.repository.GradeRepository;
import com.ikon.projectmanagement.service.ImportGradeService;
import com.ikon.webservice.WebService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@Slf4j
public class ImportGradeServiceImpl extends WebService implements ImportGradeService {
private final GradeMapper gradeMapper;
private final RestTemplate restTemplate;
private final GradeRepository gradeRepository;
@Value("${salescrm.api.base-url}")
private String salesCrmBaseUrl;
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
if (accessToken != null && !accessToken.isEmpty()) {
headers.set("Authorization", accessToken);
}
headers.set("Content-Type", "application/json");
return headers;
}
@Override
@Transactional
public List<GradeResponseDto> importGrades(String accessToken) {
String url = salesCrmBaseUrl + "/grade";
UUID accountId = getActiveAccountId();
log.info("Fetching all grades from Sales CRM: {}", url);
try {
HttpEntity<Void> entity = new HttpEntity<>(createHeaders(accessToken));
ResponseEntity<List<GradeRequestDto>> response = restTemplate.exchange(
url, HttpMethod.GET, entity, new ParameterizedTypeReference<List<GradeRequestDto>>() {});
List<GradeRequestDto> dtos = response.getBody() != null ? response.getBody() : Collections.emptyList();
List<GradeResponseDto> savedResponses = new ArrayList<>();
List<Grade> entities = dtos.stream().map(dto -> Grade.builder()
.id(dto.getId())
.accountId(accountId)
.grade(dto.getGrade())
.build()).collect(Collectors.toList());
if (!entities.isEmpty()) {
gradeRepository.saveAll(entities);
log.info("Saved/Updated {} grades", entities.size());
}
entities.forEach(g -> savedResponses.add(gradeMapper.toResponseDto(g)));
return savedResponses;
} catch (RestClientException e) {
log.error("Failed to fetch grades from Sales CRM: {}", e.getMessage());
return Collections.emptyList();
}
}
}

View File

@@ -0,0 +1,85 @@
package com.ikon.projectmanagement.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.ikon.projectmanagement.dto.request.RoleRequestDto;
import com.ikon.projectmanagement.dto.response.RoleResponseDto;
import com.ikon.projectmanagement.entity.Role;
import com.ikon.projectmanagement.mapper.RoleMapper;
import com.ikon.projectmanagement.repository.RoleRepository;
import com.ikon.projectmanagement.service.ImportRoleService;
import com.ikon.webservice.WebService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@Slf4j
public class ImportRoleServiceImpl extends WebService implements ImportRoleService {
private final RoleMapper roleMapper;
private final RestTemplate restTemplate;
private final RoleRepository roleRepository;
@Value("${salescrm.api.base-url}")
private String salesCrmBaseUrl;
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
if (accessToken != null && !accessToken.isEmpty()) {
headers.set("Authorization", accessToken);
}
headers.set("Content-Type", "application/json");
return headers;
}
@Override
@Transactional
public List<RoleResponseDto> importRoles(String accessToken) {
String url = salesCrmBaseUrl + "/crm-role";
UUID accountId = getActiveAccountId();
log.info("Fetching all roles from Sales CRM: {}", url);
try {
HttpEntity<Void> entity = new HttpEntity<>(createHeaders(accessToken));
ResponseEntity<List<RoleRequestDto>> response = restTemplate.exchange(
url, HttpMethod.GET, entity, new ParameterizedTypeReference<List<RoleRequestDto>>() {});
List<RoleRequestDto> dtos = response.getBody() != null ? response.getBody() : Collections.emptyList();
List<RoleResponseDto> savedResponses = new ArrayList<>();
List<Role> entities = dtos.stream().map(dto -> Role.builder()
.id(dto.getId())
.accountId(accountId)
.role(dto.getRole())
.build()).collect(Collectors.toList());
if (!entities.isEmpty()) {
roleRepository.saveAll(entities);
log.info("Saved/Updated {} roles", entities.size());
}
entities.forEach(r -> savedResponses.add(roleMapper.toResponseDto(r)));
return savedResponses;
} catch (RestClientException e) {
log.error("Failed to fetch roles from Sales CRM: {}", e.getMessage());
return Collections.emptyList();
}
}
}

View File

@@ -0,0 +1,109 @@
package com.ikon.projectmanagement.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.ikon.projectmanagement.dto.response.WorkingDayDto;
import com.ikon.projectmanagement.entity.WorkingDaysEntity;
import com.ikon.projectmanagement.repository.WorkingDaysRepository;
import com.ikon.projectmanagement.service.ImportWorkingDayService;
import com.ikon.webservice.WebService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@Slf4j
public class ImportWorkingDayServiceImpl extends WebService implements ImportWorkingDayService {
private final RestTemplate restTemplate;
private final WorkingDaysRepository workingDayRepository;
@Value("${salescrm.api.base-url}")
private String salesCrmBaseUrl;
private HttpHeaders createHeaders(String accessToken) {
HttpHeaders headers = new HttpHeaders();
if (accessToken != null && !accessToken.isEmpty()) {
headers.set("Authorization", accessToken);
}
headers.set("Content-Type", "application/json");
return headers;
}
private static final int PAGE_SIZE = 200;
@Override
@Transactional
public List<WorkingDayDto> importWorkingDays(String accessToken) {
String baseUrl = salesCrmBaseUrl + "/workingday";
UUID accountId = getActiveAccountId();
log.info("Fetching all working days from Sales CRM: {}", baseUrl);
try {
HttpEntity<Void> entity = new HttpEntity<>(createHeaders(accessToken));
List<WorkingDayDto> allDtos = new ArrayList<>();
int page = 0;
int totalPages = 1;
while (page < totalPages) {
String url = baseUrl + "?page=" + page + "&size=" + PAGE_SIZE;
ResponseEntity<PageResponse<WorkingDayDto>> response = restTemplate.exchange(
url, HttpMethod.GET, entity, new ParameterizedTypeReference<PageResponse<WorkingDayDto>>() {
});
if (response.getBody() == null || response.getBody().getContent() == null) {
break;
}
PageResponse<WorkingDayDto> body = response.getBody();
allDtos.addAll(body.getContent());
totalPages = body.getTotalPages();
log.info("Fetched working days page {}/{}", page + 1, totalPages);
page++;
}
// List<WorkingDaysEntity> entities = allDtos.stream().map(dto ->
// WorkingDaysEntity.builder()
// .id(dto.getId())
// .accountId(accountId)
// .year(dto.getYear())
// .month(dto.getMonth())
// .workingDays(dto.getWorkingDays())
// .build()).collect(Collectors.toList());
// if (!entities.isEmpty()) {
// workingDayRepository.saveAll(entities);
// log.info("Saved/Updated {} working days in total", entities.size());
// }
return allDtos;
} catch (RestClientException e) {
log.error("Failed to fetch working days from Sales CRM: {}", e.getMessage());
return Collections.emptyList();
}
}
@Data
public static class PageResponse<T> {
private List<T> content;
private int totalPages;
private long totalElements;
}
}

View File

@@ -0,0 +1,112 @@
package com.ikon.projectmanagement.service.impl;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
import com.ikon.projectmanagement.dto.request.IssueCreateRequestDto;
import com.ikon.projectmanagement.dto.response.IssueResponseDto;
import com.ikon.projectmanagement.entity.Issue;
import com.ikon.projectmanagement.mapper.IssueMapper;
import com.ikon.projectmanagement.repository.IssueRepository;
import com.ikon.projectmanagement.repository.ProjectRepository;
import com.ikon.projectmanagement.service.IssueService;
import com.ikon.webservice.WebService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.common.errors.ResourceNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Slf4j
public class IssueServiceImpl extends WebService implements IssueService {
private final IssueRepository issueRepository;
private final ProjectRepository projectRepository;
private final IssueMapper issueMapper;
@Override
@Transactional
public IssueResponseDto createIssue(IssueCreateRequestDto issueCreateRequestDto) {
log.info("Creating issue for project: {}", issueCreateRequestDto.getProjectIdentifier());
UUID projectId = issueCreateRequestDto.getProjectIdentifier();
projectRepository.findByProjectIdentifier(projectId)
.orElseThrow(() -> new NoSuchElementException(
"Project not found with identifier: " + issueCreateRequestDto.getProjectIdentifier()));
Issue issue = issueMapper.toEntity(issueCreateRequestDto);
issue.setProjectIdentifier(projectId);
issue.setIssueStatus("Active");
Issue savedIssue = issueRepository.save(issue);
return issueMapper.toResponse(savedIssue);
}
@Override
public List<IssueResponseDto> getAllIssues(UUID projectIdentifier) {
log.info("Fetching all issues");
return issueRepository.findByProjectIdentifier(projectIdentifier)
.stream()
.map(issueMapper::toResponse)
.toList();
}
@Override
public IssueResponseDto getIssueById(UUID issueIdentifier) {
log.info("Fetching issue with identifier: {}", issueIdentifier);
Issue issue = issueRepository.findById(issueIdentifier)
.orElseThrow(() -> new NoSuchElementException(
"Issue not found with identifier: " + issueIdentifier));
return issueMapper.toResponse(issue);
}
@Override
@Transactional
public IssueResponseDto updateIssue(UUID issueIdentifier, IssueCreateRequestDto issueCreateRequestDto) {
log.info("Updating issue with identifier: {}", issueIdentifier);
Issue existingIssue = issueRepository.findById(issueIdentifier)
.orElseThrow(() -> new ResourceNotFoundException(
"Issue not found with identifier: " + issueIdentifier));
issueMapper.updateEntityFromDto(issueCreateRequestDto, existingIssue);
Issue updatedIssue = issueRepository.save(existingIssue);
log.info("Issue updated successfully with identifier: {}", issueIdentifier);
return issueMapper.toResponse(updatedIssue);
}
@Override
public List<IssueResponseDto> getIssuesByProject(String projectIdentifier) {
log.info("Fetching all issues for project: {}", projectIdentifier);
UUID projectId = UUID.fromString(projectIdentifier);
// Validate project exists
projectRepository.findByProjectIdentifier(projectId)
.orElseThrow(() -> new NoSuchElementException(
"Project not found with identifier: " + projectIdentifier));
return issueRepository.findByProjectIdentifier(projectId)
.stream()
.map(issueMapper::toResponse)
.toList();
}
}

View File

@@ -0,0 +1,98 @@
package com.ikon.projectmanagement.service.impl;
import com.ikon.projectmanagement.dto.request.MeetingRequestDTO;
import com.ikon.projectmanagement.dto.response.MeetingResponseDTO;
import com.ikon.projectmanagement.entity.Meeting;
import com.ikon.projectmanagement.mapper.MeetingMapper;
import com.ikon.projectmanagement.repository.MeetingRepository;
import com.ikon.projectmanagement.service.MeetingService;
import com.ikon.webservice.WebService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class MeetingServiceImpl extends WebService implements MeetingService {
@Autowired
private MeetingMapper meetingMapper;
@Autowired
private MeetingRepository meetingRepository;
@Override
@Transactional
public MeetingResponseDTO createMeeting(MeetingRequestDTO meetingRequestDTO) {
UUID accountId = getActiveAccountId();
UUID createdBy = getCurrentUserId();
Meeting meeting = meetingMapper.toEntity(meetingRequestDTO);
meeting.setAccountId(accountId);
meeting.setCreatedBy(createdBy);
Meeting savedMeeting = meetingRepository.save(meeting);
return meetingMapper.toResponseDTO(savedMeeting);
}
@Override
@Transactional(readOnly = true)
public List<MeetingResponseDTO> getAllMeetings(UUID projectIdentifier) {
List<Meeting> meetings = meetingRepository.findByProjectIdentifier(projectIdentifier);
return meetingMapper.toResponseDTOList(meetings);
}
@Override
@Transactional(readOnly = true)
public List<MeetingResponseDTO> getMeetingsByProjectIdentifier(String projectIdentifier) {
List<Meeting> meetings = meetingRepository.findByProjectIdentifier(UUID.fromString(projectIdentifier));
return meetingMapper.toResponseDTOList(meetings);
}
@Override
@Transactional(readOnly = true)
public MeetingResponseDTO getMeetingById(UUID meetingId) {
Meeting meeting = meetingRepository.findById(meetingId)
.orElseThrow(() -> new RuntimeException("Meeting not found with id: " + meetingId));
return meetingMapper.toResponseDTO(meeting);
}
@Override
@Transactional
public MeetingResponseDTO updateMeeting(UUID meetingId, MeetingRequestDTO meetingRequestDTO) {
Meeting existingMeeting = meetingRepository.findById(meetingId)
.orElseThrow(() -> new RuntimeException("Meeting not found with id: " + meetingId));
meetingMapper.updateEntityFromDTO(existingMeeting, meetingRequestDTO);
Meeting updatedMeeting = meetingRepository.save(existingMeeting);
return meetingMapper.toResponseDTO(updatedMeeting);
}
@Override
@Transactional
public void deleteMeeting(UUID meetingId) {
Meeting meeting = meetingRepository.findById(meetingId)
.orElseThrow(() -> new RuntimeException("Meeting not found with id: " + meetingId));
meetingRepository.delete(meeting);
}
}

View File

@@ -0,0 +1,213 @@
package com.ikon.projectmanagement.service.impl;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ikon.projectmanagement.dto.request.ProductOfProjectRequestDto;
import com.ikon.projectmanagement.dto.response.ProductExpenseResponseDto;
import com.ikon.projectmanagement.dto.response.ProductOfProjectResponseDto;
import com.ikon.projectmanagement.entity.ProductOfProject;
import com.ikon.projectmanagement.entity.ProductOfProjectExpenseDetail;
import com.ikon.projectmanagement.mapper.ProductOfProjectMapper;
import com.ikon.projectmanagement.repository.ProductOfProjectRepository;
import com.ikon.projectmanagement.repository.ProjectScheduleRepository;
import com.ikon.projectmanagement.service.ProductOfProjectService;
import com.ikon.projectmanagement.service.ScheduleService;
import com.ikon.webservice.WebService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class ProductOfProjectServiceImpl extends WebService implements ProductOfProjectService {
private final ProductOfProjectMapper productMapper;
private final ProductOfProjectRepository productRepository;
private final ScheduleService scheduleService;
private final ProjectScheduleRepository projectScheduleRepository;
// Valid workflow transitions: currentStatus -> Set of allowed targetStatuses
private static final Map<String, Set<String>> VALID_TRANSITIONS = Map.of(
// Forward transitions
"Product Created", Set.of("Schedule Submitted From Product"),
"Schedule Submitted From Product", Set.of("Submitted Resources and Expenses For Product",
"Recall Schedule from Resource & Expense"),
// "Submitted Resources and Expenses For Product", Set.of("Closed",
// "Recall Schedule from Resource & Expense"),
// "Closed", Set.of("Recall Resources & Expenses from Closed"),
// Also allow transitions from recall statuses
// "Recall Product Created from Schedule", Set.of("Schedule Submitted From
// Product"),
"Recall Schedule from Resource & Expense", Set.of("Schedule Submitted From Product")
// "Recall Resources & Expenses from Closed", Set.of("Closed",
// "Recall Schedule from Resource & Expense")
);
// The target status that requires schedule data to exist
private static final String REQUIRES_SCHEDULE = "Schedule Submitted From Product";
@Override
@Transactional(readOnly = true)
public List<ProductOfProjectResponseDto> getProductsByProject(UUID projectIdentifier) {
UUID accountId = getActiveAccountId();
var scheduleData = scheduleService.getSchedule(projectIdentifier, null);
return productRepository
.findByAccountIdAndProjectIdentifier(accountId, projectIdentifier)
.stream()
.map(p -> {
ProductOfProjectResponseDto dto = productMapper.toResponse(p);
dto.setScheduleData(scheduleData);
return dto;
})
.toList();
}
@Override
@Transactional(readOnly = true)
public ProductOfProjectResponseDto getProduct(UUID projectIdentifier, UUID productIdentifier) {
UUID accountId = getActiveAccountId();
ProductOfProject product = productRepository
.findByAccountIdAndProjectIdentifierAndProductIdentifier(
accountId, projectIdentifier, productIdentifier)
.orElseThrow(() -> new RuntimeException("Product not found"));
ProductOfProjectResponseDto dto = productMapper.toResponse(product);
dto.setScheduleData(scheduleService.getSchedule(projectIdentifier, null));
return dto;
}
@Override
@Transactional
public ProductOfProjectResponseDto updateProduct(UUID projectIdentifier, UUID productIdentifier,
ProductOfProjectRequestDto dto) {
UUID accountId = getActiveAccountId();
ProductOfProject product = productRepository
.findByAccountIdAndProjectIdentifierAndProductIdentifier(accountId, projectIdentifier,
productIdentifier)
.orElseThrow(() -> new RuntimeException("Product not found"));
productMapper.updateEntity(product, dto);
if (dto.getExpenseDetails() != null) {
Map<UUID, ProductOfProjectExpenseDetail> existingExpenses = product.getExpenseDetails();
Map<UUID, ProductExpenseResponseDto> incomingExpenses = dto.getExpenseDetails();
existingExpenses.keySet().removeIf(id -> !incomingExpenses.containsKey(id));
for (Map.Entry<UUID, ProductExpenseResponseDto> entry : incomingExpenses.entrySet()) {
UUID incomingId = entry.getKey();
ProductExpenseResponseDto expenseDto = entry.getValue();
if (incomingId != null && existingExpenses.containsKey(incomingId)) {
ProductOfProjectExpenseDetail existing = existingExpenses.get(incomingId);
productMapper.updateExpenseEntity(existing, expenseDto);
} else {
ProductOfProjectExpenseDetail newExpense = productMapper
.toExpenseEntity(expenseDto);
newExpense.setProductPS(product);
existingExpenses.put(UUID.randomUUID(), newExpense);
}
}
}
ProductOfProject saved = productRepository.save(product);
return productMapper.toResponse(saved);
}
@Override
@Transactional
public ProductOfProjectResponseDto transitionStatus(
UUID projectIdentifier,
UUID productIdentifier,
String targetStatus) {
UUID accountId = getActiveAccountId();
ProductOfProject product = productRepository
.findByAccountIdAndProjectIdentifierAndProductIdentifier(
accountId, projectIdentifier, productIdentifier)
.orElseThrow(() -> new RuntimeException("Product not found"));
String currentStatus = product.getProductStatus();
log.info("Workflow transition requested: '{}' -> '{}' for product {}",
currentStatus, targetStatus, productIdentifier);
// Validate that this transition is allowed
Set<String> allowedTargets = VALID_TRANSITIONS.get(currentStatus);
log.info("Allowed target statuses for current status '{}': {}", currentStatus, allowedTargets);
if (allowedTargets == null || !allowedTargets.contains(targetStatus)) {
throw new IllegalStateException(
"Transition from '" + currentStatus + "' to '" + targetStatus
+ "' is not allowed.");
}
// Special validation: Schedule -> Resources & Expenses requires schedule data
if (REQUIRES_SCHEDULE.equals(targetStatus)) {
boolean hasScheduleData = checkScheduleDataExists(projectIdentifier, productIdentifier,
accountId);
if (!hasScheduleData) {
throw new IllegalStateException(
"Cannot move to Resources & Expenses: No schedule data found. "
+ "Please create a schedule before proceeding.");
}
}
// Perform the transition
product.setProductStatus(targetStatus);
ProductOfProject saved = productRepository.save(product);
log.info("Workflow transition completed: '{}' -> '{}' for product {}",
currentStatus, targetStatus, productIdentifier);
return productMapper.toResponse(saved);
}
/**
* Checks if schedule data exists by looking up:
* 1. Project schedule (by projectIdentifier)
* 2. Product schedule data (by productIdentifier — stored on the product
* entity)
* If either has tasks, returns true.
*/
private boolean checkScheduleDataExists(UUID projectIdentifier, UUID productIdentifier, UUID accountId) {
// Check 1: Project-level schedule
boolean hasProjectSchedule = projectScheduleRepository
.findByAccountIdAndProjectIdentifier(accountId, projectIdentifier)
.map(schedule -> schedule.getTasks() != null && !schedule.getTasks().isEmpty())
.orElse(false);
if (hasProjectSchedule) {
log.debug("Schedule data found via project schedule for project {}", projectIdentifier);
return true;
}
// Check 2: Product-level schedule data
boolean hasProductSchedule = projectScheduleRepository
.findByAccountIdAndProductIdentifier(accountId, productIdentifier)
.map(schedule -> schedule.getTasks() != null && !schedule.getTasks().isEmpty())
.orElse(false);
if (hasProductSchedule) {
log.debug("Schedule data found via product schedule for product {}", productIdentifier);
return true;
}
log.warn("No schedule data found for project {} or product {}", projectIdentifier, productIdentifier);
return false;
}
}

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