Skip to content

Commit b6ba645

Browse files
committed
Merge branch 'dev' into feat/implement-notificaiton-module
# Conflicts: # src/main/java/app/teamwize/api/user/service/UserService.java
2 parents cbc756a + ea6a4a9 commit b6ba645

File tree

10 files changed

+67
-26
lines changed

10 files changed

+67
-26
lines changed

build.gradle

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,13 @@ dependencies {
6060
testImplementation 'org.testcontainers:postgresql'
6161
}
6262

63-
//tasks.withType<Test> {
64-
// useJUnitPlatform()
65-
//}
6663

6764
jib {
6865
from {
6966
image = 'mcr.microsoft.com/openjdk/jdk:21-distroless'
7067
}
7168
to {
72-
image = 'ghcr.io/teamwize/api:dev'
69+
image = 'ghcr.io/teampilot-hq/api:dev'
7370
tags = ['dev']
7471
}
75-
}
72+
}

src/main/java/app/teamwize/api/leave/repository/LeaveRepository.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
public interface LeaveRepository extends BaseJpaRepository<Leave, Long>, JpaSpecificationExecutor<Leave> {
1919

2020
@EntityGraph(attributePaths = {"user", "user.team", "user.avatar", "type", "activatedType"}, type = EntityGraph.EntityGraphType.FETCH)
21-
Optional<Leave> findByUserIdAndId(Long userId, Long id);
22-
21+
Optional<Leave> findByOrganizationIdAndId(Long organizationId, Long id);
2322

2423
@EntityGraph(attributePaths = {"user", "user.team", "user.avatar", "type", "activatedType"}, type = EntityGraph.EntityGraphType.FETCH)
2524
Page<Leave> findByOrganizationIdAndUserId(Long organizationId, Long userId, Pageable page);

src/main/java/app/teamwize/api/leave/rest/controller/LeaveController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public LeaveResponse updateDayOff(@PathVariable Long id, @RequestBody LeaveUpdat
9494

9595
@GetMapping("{id}")
9696
public LeaveResponse getDayOff(@PathVariable Long id) throws LeaveNotFoundException {
97-
return leaveMapper.toLeaveResponse(leaveService.getLeave(securityService.getUserId(), id));
97+
return leaveMapper.toLeaveResponse(leaveService.getLeave(securityService.getUserOrganizationId(), id));
9898
}
9999

100100
@PostMapping("check")

src/main/java/app/teamwize/api/leave/service/LeaveService.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import app.teamwize.api.organization.domain.entity.Organization;
2727
import app.teamwize.api.organization.exception.OrganizationNotFoundException;
2828
import app.teamwize.api.organization.service.OrganizationService;
29+
import app.teamwize.api.user.domain.UserRole;
2930
import app.teamwize.api.user.domain.entity.User;
3031
import app.teamwize.api.user.exception.UserNotFoundException;
3132
import app.teamwize.api.user.service.UserService;
@@ -118,34 +119,40 @@ public Page<Leave> getLeaves(Long organizationId, Long userId, PaginationRequest
118119

119120

120121
@Transactional
121-
public Leave updateLeave(Long organizationId, Long userId, Long id, LeaveUpdateCommand request) throws LeaveNotFoundException, LeaveUpdateStatusFailedException, UserNotFoundException {
122-
var user = userService.getUser(organizationId, userId);
123-
var leave = getById(userId, id);
122+
public Leave updateLeave(Long organizationId, Long approverId, Long id, LeaveUpdateCommand request) throws LeaveNotFoundException, LeaveUpdateStatusFailedException, UserNotFoundException {
123+
var approverUser = userService.getUser(organizationId, approverId);
124+
if (approverUser.getRole() != UserRole.ORGANIZATION_ADMIN && approverUser.getRole() != UserRole.TEAM_ADMIN) {
125+
throw new LeaveUpdateStatusFailedException("Leave update failed because user is not authorized to update leave, id = " + id);
126+
}
127+
var leave = leaveRepository.findByOrganizationIdAndId(organizationId, id).orElseThrow(() -> new LeaveNotFoundException("Leave not found with id: " + id));
128+
if (approverUser.getRole() == UserRole.TEAM_ADMIN && !leave.getUser().getTeam().getId().equals(approverUser.getTeam().getId())) {
129+
throw new LeaveUpdateStatusFailedException("Leave update failed because user is not authorized to update leave, id = " + id);
130+
}
124131
if (leave.getStatus() != LeaveStatus.PENDING) {
125132
throw new LeaveUpdateStatusFailedException("Leave update failed because it is not in pending status, id = " + id);
126133
}
127134
leave.setStatus(request.status());
128-
eventService.emmit(organizationId, new LeaveStatusUpdatedEvent(new LeaveEventPayload(leave), new UserEventPayload(user)));
135+
eventService.emmit(organizationId, new LeaveStatusUpdatedEvent(new LeaveEventPayload(leave), new UserEventPayload(approverUser)));
129136
return leaveRepository.update(leave);
130137
}
131138

132-
public Leave getLeave(Long userId, Long id) throws LeaveNotFoundException {
133-
return getById(userId, id);
139+
public Leave getLeave(Long organizationId, Long id) throws LeaveNotFoundException {
140+
return getById(organizationId, id);
134141
}
135142

136143
public Float getTotalDuration(Long organizationId, Long userId, LeavePolicyActivatedTypeId activatedTypeId, LeaveStatus status) {
137144
var sum = leaveRepository.countByOrganizationIdAndUserIdAndTypeId(organizationId, userId, activatedTypeId, status);
138145
return sum != null ? sum : 0f;
139146
}
140147

141-
private Leave getById(Long userId, Long id) throws LeaveNotFoundException {
142-
return leaveRepository.findByUserIdAndId(userId, id).orElseThrow(() -> new LeaveNotFoundException("Leave not found with id: " + id));
148+
private Leave getById(Long OrganizationId, Long id) throws LeaveNotFoundException {
149+
return leaveRepository.findByOrganizationIdAndId(OrganizationId, id).orElseThrow(() -> new LeaveNotFoundException("Leave not found with id: " + id));
143150
}
144151

145152
public List<UserLeaveBalance> getLeaveBalance(Long organizationId, Long userId) throws UserNotFoundException, LeavePolicyNotFoundException {
146153
var user = userService.getUser(organizationId, userId);
147154
var policy = leavePolicyService.getLeavePolicy(organizationId, user.getLeavePolicy().getId());
148-
var startedAt = user.getCreatedAt();
155+
var startedAt = user.getJoinedAt();
149156
var result = new ArrayList<UserLeaveBalance>();
150157
for (var activatedType : policy.getActivatedTypes()) {
151158
var usedAmount = this.getTotalDuration(organizationId, userId, activatedType.getId(), LeaveStatus.ACCEPTED);
@@ -154,9 +161,9 @@ public List<UserLeaveBalance> getLeaveBalance(Long organizationId, Long userId)
154161
case PER_MONTH ->
155162
Period.between(startedAt.atZone(user.getTimeZoneId()).toLocalDate(), LocalDate.now()).toTotalMonths() * activatedType.getAmount();
156163
case PER_YEAR ->
157-
(Period.between(startedAt.atZone(user.getTimeZoneId()).toLocalDate(), LocalDate.now()).toTotalMonths() / 12) * activatedType.getAmount();
164+
(Period.between(startedAt.atZone(user.getTimeZoneId()).toLocalDate(), LocalDate.now()).toTotalMonths() / 12f) * activatedType.getAmount();
158165
};
159-
result.add(new UserLeaveBalance(activatedType, usedAmount.longValue(), totalAmount, startedAt.atZone(user.getTimeZoneId()).toLocalDate()));
166+
result.add(new UserLeaveBalance(activatedType, usedAmount.longValue(), (long) totalAmount, startedAt.atZone(user.getTimeZoneId()).toLocalDate()));
160167
}
161168
return result;
162169
}

src/main/java/app/teamwize/api/user/domain/entity/User.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import lombok.NoArgsConstructor;
1414
import lombok.Setter;
1515

16+
import java.time.Instant;
1617
import java.time.ZoneId;
1718
import java.util.Objects;
1819

@@ -43,7 +44,7 @@ public class User extends BaseAuditEntity {
4344
private UserStatus status;
4445
@ManyToOne(fetch = FetchType.LAZY)
4546
private Asset avatar;
46-
47+
private Instant joinedAt;
4748
@ManyToOne
4849
private LeavePolicy leavePolicy;
4950

src/main/java/app/teamwize/api/user/domain/request/UserCreateRequest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import app.teamwize.api.user.domain.UserRole;
44

5+
import java.time.Instant;
6+
57
public record UserCreateRequest(
68
String email,
79
String firstName,
@@ -12,5 +14,6 @@ public record UserCreateRequest(
1214
String timezone,
1315
String country,
1416
Long teamId,
15-
Long leavePolicyId) {
17+
Long leavePolicyId,
18+
Instant joinedAt) {
1619
}

src/main/java/app/teamwize/api/user/domain/request/UserUpdateRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33

44
import app.teamwize.api.base.validator.PhoneNumber;
55
import jakarta.validation.constraints.Email;
6+
import jakarta.validation.constraints.NotNull;
67
import jakarta.validation.constraints.Size;
78
import lombok.Data;
89
import org.openapitools.jackson.nullable.JsonNullable;
910

11+
import java.time.Instant;
12+
1013
@Data
1114
public class UserUpdateRequest {
1215

@@ -27,4 +30,7 @@ public class UserUpdateRequest {
2730
private JsonNullable<Long> leavePolicyId;
2831

2932
private JsonNullable<Long> teamId;
33+
34+
@NotNull
35+
private JsonNullable<Instant> joinedAt;
3036
}

src/main/java/app/teamwize/api/user/domain/response/UserResponse.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33

44
import app.teamwize.api.assets.domain.model.response.AssetCompactResponse;
55
import app.teamwize.api.leave.rest.model.response.LeavePolicyCompactResponse;
6+
import app.teamwize.api.organization.domain.response.OrganizationCompactResponse;
67
import app.teamwize.api.team.domain.response.TeamCompactResponse;
78
import app.teamwize.api.user.domain.UserRole;
89
import app.teamwize.api.user.domain.UserStatus;
9-
import app.teamwize.api.organization.domain.response.OrganizationCompactResponse;
10-
1110
import jakarta.annotation.Nonnull;
1211
import jakarta.annotation.Nullable;
1312

13+
import java.time.Instant;
14+
1415

1516
public record UserResponse(
1617
@Nonnull Long id,
@@ -25,6 +26,9 @@ public record UserResponse(
2526
@Nonnull OrganizationCompactResponse organization,
2627
@Nonnull TeamCompactResponse team,
2728
@Nonnull AssetCompactResponse avatar,
28-
@Nonnull LeavePolicyCompactResponse leavePolicy
29+
@Nonnull LeavePolicyCompactResponse leavePolicy,
30+
@Nonnull Instant joinedAt,
31+
@Nonnull Instant createdAt,
32+
@Nonnull Instant updatedAt
2933
) {
3034
}

src/main/java/app/teamwize/api/user/service/UserService.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.springframework.stereotype.Service;
3434
import org.springframework.transaction.annotation.Transactional;
3535

36+
import java.time.Instant;
37+
3638
import java.util.List;
3739

3840
import static app.teamwize.api.user.repository.UserSpecifications.*;
@@ -94,7 +96,8 @@ public User createOrganizationAdmin(Long organizationId, Long teamId, AdminUserC
9496
.setCountry(request.country())
9597
.setTeam(team)
9698
.setOrganization(organization)
97-
.setLeavePolicy(leavePolicy);
99+
.setLeavePolicy(leavePolicy)
100+
.setJoinedAt(Instant.now());
98101
user.setTeam(team);
99102
user.setPassword(passwordEncoder.encode(request.password()));
100103
return userRepository.merge(user);
@@ -123,7 +126,8 @@ public User createUser(Long organizationId, Long inviterUserId, UserCreateReques
123126
.setCountry(request.country())
124127
.setTeam(team)
125128
.setOrganization(organization)
126-
.setLeavePolicy(leavePolicy);
129+
.setLeavePolicy(leavePolicy)
130+
.setJoinedAt(request.joinedAt() == null ? Instant.now() : request.joinedAt());
127131

128132
if (request.password() != null && !request.password().isBlank()) {
129133
user.setPassword(passwordEncoder.encode(request.password()));
@@ -152,6 +156,10 @@ public User partiallyUpdateUser(Long organizationId, Long userId, UserUpdateRequ
152156
request.getPhone().ifPresent(user::setPhone);
153157
}
154158

159+
if (request.getJoinedAt() != null) {
160+
request.getJoinedAt().ifPresent(user::setJoinedAt);
161+
}
162+
155163
if (request.getAvatarAssetId() != null && request.getAvatarAssetId().isPresent()) {
156164
var asset = assetService.getAsset(organizationId, request.getAvatarAssetId().get());
157165
user.setAvatar(asset);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<databaseChangeLog
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
6+
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.5.xsd">
7+
8+
<changeSet id="20250305-2010-add-joined-at-to-users-table" author="M.Karimi">
9+
<addColumn tableName="users">
10+
<column name="joined_at" type="TIMESTAMP WITHOUT TIME ZONE" defaultValueDate="NOW()">
11+
<constraints nullable="false"/>
12+
</column>
13+
</addColumn>
14+
</changeSet>
15+
16+
</databaseChangeLog>

0 commit comments

Comments
 (0)