Skip to content

Commit

Permalink
feat: Implemented billable in TimeEntryTrackingService (Close #20)
Browse files Browse the repository at this point in the history
Key changes:
- Created `TimeBillingDTO` class.
- Modified `TimeEntry` domain.
- Modified unit tests.
  • Loading branch information
seyedali-dev committed May 14, 2024
1 parent e114226 commit f20d68c
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import jakarta.persistence.ManyToOne;
import lombok.*;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public record TimeEntryDTO(
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "End time of the time entry in the format yyyy-MM-dd HH:mm", example = "2024-05-12 10:00:00")
String endTime,

@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "A flag determining this time entry is billable", example = "true")
boolean billable,

@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "The hourly rate in BigDecimal format", example = "10.0")
String hourlyRate,

@Schema(requiredMode = Schema.RequiredMode.AUTO, description = "Duration of the time entry in the format HH:mm:ss", example = "02:00:00")
String duration
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ public record TimeEntryResponse(
@ArraySchema(schema = @Schema(implementation = TimeSegmentDTO.class))
List<TimeSegmentDTO> timeSegmentDTOList,

@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "A flag determining this time entry is billable", example = "true")
boolean billable,

@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "The hourly rate in BigDecimal format", example = "10.0")
String hourlyRate,

@Schema(requiredMode = Schema.RequiredMode.NOT_REQUIRED, description = "Total time recorded", example = "00:00:18")
String totalDuration
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public List<TimeEntryResponse> getTimeEntries() {

List<TimeEntry> timeEntryList = this.timeEntryRepository.findAll();
for (TimeEntry timeEntry : timeEntryList) {
String hourlyRate = null;
if (timeEntry.getHourlyRate() != null)
hourlyRate = timeEntry.getHourlyRate().toString();
List<TimeSegmentDTO> timeSegmentDTOList = new ArrayList<>();
List<TimeSegment> timeSegmentList = timeEntry.getTimeSegmentList();
Duration totalDuration = this.getTotalDuration(timeEntry);
Expand All @@ -55,7 +58,7 @@ public List<TimeEntryResponse> getTimeEntries() {
String totalDurationStr = this.timeParser.parseDurationToString(totalDuration);

TimeSegmentDTO timeSegmentDTO = new TimeSegmentDTO(timeSegment.getTimeSegmentId(), startTimeStr, endTimeStr, durationStr, timeEntry.getUserId());
timeEntryResponse = new TimeEntryResponse(timeEntry.getTimeEntryId(), timeSegmentDTOList, totalDurationStr);
timeEntryResponse = new TimeEntryResponse(timeEntry.getTimeEntryId(), timeSegmentDTOList, timeEntry.isBillable(), hourlyRate, totalDurationStr);
timeSegmentDTOList.add(timeSegmentDTO);
}
timeEntryResponseList.add(timeEntryResponse);
Expand All @@ -67,12 +70,17 @@ public List<TimeEntryResponse> getTimeEntries() {
public TimeEntryResponse getUsersTimeEntry(String userId) {
List<TimeSegmentDTO> timeSegmentDTOList = new ArrayList<>();
String totalDurationStr = null;
String hourlyRate = null;

TimeEntry timeEntry = this.timeEntryRepository.findByUserId(userId);

if (timeEntry.getHourlyRate() != null)
hourlyRate = timeEntry.getHourlyRate().toString();

List<TimeSegment> timeSegmentList = timeEntry.getTimeSegmentList();
if (timeSegmentList.isEmpty()) {
totalDurationStr = this.timeParser.parseDurationToString(timeEntry.getTimeSegmentList().getLast().getDuration());
return new TimeEntryResponse(timeEntry.getTimeEntryId(), null, totalDurationStr);
return new TimeEntryResponse(timeEntry.getTimeEntryId(), null, timeEntry.isBillable(), hourlyRate, totalDurationStr);
}
Duration totalDuration = timeSegmentList.stream()
.map(TimeSegment::getDuration)
Expand All @@ -91,7 +99,7 @@ public TimeEntryResponse getUsersTimeEntry(String userId) {
timeSegmentDTOList.add(timeSegmentDTO);
}

return new TimeEntryResponse(timeEntry.getTimeEntryId(), timeSegmentDTOList, totalDurationStr);
return new TimeEntryResponse(timeEntry.getTimeEntryId(), timeSegmentDTOList, timeEntry.isBillable(), hourlyRate, totalDurationStr);

}

Expand Down Expand Up @@ -133,7 +141,7 @@ public TimeEntryDTO updateTimeEntryManually(String id, TimeEntryDTO timeEntryDTO
this.timeEntryRepository.save(timeEntry);

String startTimeString = this.timeParser.parseLocalDateTimeToString(lastTimeSegment.getStartTime());
return createTimeEntryDTO(timeEntry, lastTimeSegment, startTimeString);
return this.createTimeEntryDTO(timeEntry, lastTimeSegment, startTimeString);
} else throw new IllegalArgumentException("The provided id does not exist");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public TimeEntryDTO stopTrackingTimeEntry(String timeEntryId) {
LocalDateTime startTime = lastTimeSegment.getStartTime();
Duration duration = Duration.between(startTime, endTime);

createTimeEntry(endTime, timeEntry, startTime, duration);
this.createTimeInfo(timeEntry, startTime, endTime, duration);
this.timeEntryRepository.save(timeEntry);

// Calculate total duration
Expand All @@ -81,7 +81,7 @@ public TimeEntryDTO stopTrackingTimeEntry(String timeEntryId) {
String startTimeStr = this.timeParser.parseLocalDateTimeToString(startTime);
String endTimeStr = this.timeParser.parseLocalDateTimeToString(endTime);
String durationStr = this.timeParser.parseDurationToString(totalDuration);
return new TimeEntryDTO(null, startTimeStr, endTimeStr, durationStr);
return new TimeEntryDTO(null, startTimeStr, endTimeStr, timeEntry.isBillable(), timeEntry.getHourlyRate().toString(), durationStr);
}

// TODO: Implement REDIS for getting the cached `start_time`
Expand All @@ -91,6 +91,10 @@ public TimeEntryDTO continueTrackingTimeEntry(String timeEntryId) {
String currentLoggedInUsersId = this.authenticationServiceClient.getCurrentLoggedInUsersId();
TimeEntry timeEntry = this.timeEntryRepository.findByUserIdAndTimeEntryId(currentLoggedInUsersId, timeEntryId);

String hourlyRate = null;
if (timeEntry.getHourlyRate() != null)
hourlyRate = timeEntry.getHourlyRate().toString();

TimeSegment timeSegment = TimeSegment.builder()
.timeSegmentId(UUID.randomUUID().toString())
.startTime(continueTime)
Expand All @@ -103,7 +107,7 @@ public TimeEntryDTO continueTrackingTimeEntry(String timeEntryId) {
this.timeEntryRepository.save(timeEntry);

String startTimeStr = this.timeParser.parseLocalDateTimeToString(timeSegment.getStartTime());
return new TimeEntryDTO(timeEntryId, startTimeStr, null, null);
return new TimeEntryDTO(timeEntryId, startTimeStr, null, timeEntry.isBillable(), hourlyRate, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.seyed.ali.timeentryservice.repository.TimeSegmentRepository;
import lombok.RequiredArgsConstructor;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Optional;
Expand All @@ -20,8 +21,14 @@ public abstract class TimeEntryServiceUtility {
private final TimeParser timeParser;

public TimeEntry createTimeEntry(TimeEntryDTO timeEntryDTO) {
boolean billable = timeEntryDTO.billable();
String hourlyRate = timeEntryDTO.hourlyRate() != null ? timeEntryDTO.hourlyRate() : null;

TimeEntry timeEntry = new TimeEntry();
timeEntry.setTimeEntryId(UUID.randomUUID().toString());
timeEntry.setBillable(billable);
if (hourlyRate != null)
timeEntry.setHourlyRate(new BigDecimal(hourlyRate));

LocalDateTime startTime = this.timeParser.parseStringToLocalDateTime(timeEntryDTO.startTime());
LocalDateTime endTime = this.timeParser.parseStringToLocalDateTime(timeEntryDTO.endTime());
Expand All @@ -37,13 +44,13 @@ public TimeEntry createTimeEntry(TimeEntryDTO timeEntryDTO) {
}
});

this.createTimeEntry(endTime, timeEntry, startTime, calculatedDuration);
this.createTimeInfo(timeEntry, startTime, endTime, calculatedDuration);
timeEntry.setUserId(this.authenticationServiceClient.getCurrentLoggedInUsersId());

return timeEntry;
}

public void createTimeEntry(LocalDateTime endTime, TimeEntry timeEntry, LocalDateTime startTime, Duration duration) {
public void createTimeInfo(TimeEntry timeEntry, LocalDateTime startTime, LocalDateTime endTime, Duration duration) {
TimeSegment timeSegment = new TimeSegment();
timeSegment.setTimeSegmentId(UUID.randomUUID().toString());
timeSegment.setStartTime(startTime);
Expand All @@ -58,8 +65,10 @@ public void createTimeEntry(LocalDateTime endTime, TimeEntry timeEntry, LocalDat
public TimeEntryDTO createTimeEntryDTO(TimeEntry timeEntry, TimeSegment lastTimeSegment, String startTimeString) {
String endTimeString = lastTimeSegment.getEndTime() != null ? this.timeParser.parseLocalDateTimeToString(lastTimeSegment.getEndTime()) : null;
String durationString = lastTimeSegment.getDuration() != null ? this.timeParser.parseDurationToString(lastTimeSegment.getDuration()) : null;
boolean billable = timeEntry.isBillable();
String hourlyRate = timeEntry.getHourlyRate().toString();

return new TimeEntryDTO(timeEntry.getTimeEntryId(), startTimeString, endTimeString, durationString);
return new TimeEntryDTO(timeEntry.getTimeEntryId(), startTimeString, endTimeString, billable, hourlyRate, durationString);
}

public Duration getTotalDuration(TimeEntry timeEntry) {
Expand Down

0 comments on commit f20d68c

Please sign in to comment.