From 1be03eab47856cad30c954ef9dc67c4632a00b81 Mon Sep 17 00:00:00 2001 From: chaelin Date: Sat, 8 Aug 2026 19:02:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(dashboard):=20recommendations=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20=EC=B9=B4?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EC=A0=84=EC=9A=A9=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DashboardRecommendationItemResponse.java | 43 +++++++++++++++ .../api/DashboardRecommendationsResponse.java | 53 +++++++++++++++++++ .../dashboard/api/DashboardTodayResponse.java | 10 ++++ .../application/DashboardQueryService.java | 43 ++++++++++++++- .../task/application/port/TaskRepository.java | 1 + .../persistence/JpaTaskRepository.java | 5 ++ .../SpringDataTaskJpaRepository.java | 9 ++++ 7 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationItemResponse.java create mode 100644 src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationsResponse.java diff --git a/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationItemResponse.java b/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationItemResponse.java new file mode 100644 index 00000000..e7b593f6 --- /dev/null +++ b/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationItemResponse.java @@ -0,0 +1,43 @@ +package com.fowoco.server.dashboard.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fowoco.server.task.domain.Task; +import com.fowoco.server.task.domain.TaskStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.UUID; + +@Schema(name = "DashboardRecommendationItemResponse", description = "Agent가 준비한 업무 항목") +public final class DashboardRecommendationItemResponse { + + @JsonProperty("task_id") + @Schema(name = "task_id", format = "uuid") + private final UUID taskId; + + @JsonProperty("title") + private final String title; + + @JsonProperty("status") + private final TaskStatus status; + + private DashboardRecommendationItemResponse(UUID taskId, String title, TaskStatus status) { + this.taskId = taskId; + this.title = title; + this.status = status; + } + + public static DashboardRecommendationItemResponse from(Task task) { + return new DashboardRecommendationItemResponse(task.taskId(), task.title(), task.status()); + } + + public UUID getTaskId() { + return taskId; + } + + public String getTitle() { + return title; + } + + public TaskStatus getStatus() { + return status; + } +} diff --git a/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationsResponse.java b/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationsResponse.java new file mode 100644 index 00000000..0b2cc1f5 --- /dev/null +++ b/src/main/java/com/fowoco/server/dashboard/api/DashboardRecommendationsResponse.java @@ -0,0 +1,53 @@ +package com.fowoco.server.dashboard.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +@Schema(name = "DashboardRecommendationsResponse", description = "Agent가 준비한 내용 묶음") +public final class DashboardRecommendationsResponse { + + @JsonProperty("connected_count") + @Schema(name = "connected_count", description = "연결된 전체 열린 업무 개수") + private final long connectedCount; + + @JsonProperty("prepared") + @Schema(name = "prepared", description = "Agent가 생성한 초안 (AI_CANDIDATE 소스, DRAFT 상태)") + private final List prepared; + + @JsonProperty("review") + @Schema(name = "review", description = "담당자 확인 필요 (NEEDS_INFO 또는 READY_FOR_REVIEW)") + private final List review; + + @JsonProperty("after_approval") + @Schema(name = "after_approval", description = "응답·기관 대기 (WAITING_WORKER 또는 WAITING_EXTERNAL)") + private final List afterApproval; + + public DashboardRecommendationsResponse( + long connectedCount, + List prepared, + List review, + List afterApproval + ) { + this.connectedCount = connectedCount; + this.prepared = prepared; + this.review = review; + this.afterApproval = afterApproval; + } + + public long getConnectedCount() { + return connectedCount; + } + + public List getPrepared() { + return prepared; + } + + public List getReview() { + return review; + } + + public List getAfterApproval() { + return afterApproval; + } +} diff --git a/src/main/java/com/fowoco/server/dashboard/api/DashboardTodayResponse.java b/src/main/java/com/fowoco/server/dashboard/api/DashboardTodayResponse.java index 1efca4b9..30ed4aef 100644 --- a/src/main/java/com/fowoco/server/dashboard/api/DashboardTodayResponse.java +++ b/src/main/java/com/fowoco/server/dashboard/api/DashboardTodayResponse.java @@ -19,6 +19,10 @@ public final class DashboardTodayResponse { @Schema(name = "upcoming_7_days", description = "향후 7일 체류·계약·서류 만료 요약") private final List upcoming7Days; + @JsonProperty("recommendations") + @Schema(name = "recommendations", description = "Agent가 준비한 내용 (실행 전 초안)") + private final DashboardRecommendationsResponse recommendations; + @JsonProperty("approval_count") @Schema(name = "approval_count", description = "승인 대기 개수 (summary_counts.pending_approval과 동일)") private final long approvalCount; @@ -31,12 +35,14 @@ public DashboardTodayResponse( DashboardSummaryCountsResponse summaryCounts, List priorityTasks, List upcoming7Days, + DashboardRecommendationsResponse recommendations, long approvalCount, long workerResponseCount ) { this.summaryCounts = summaryCounts; this.priorityTasks = priorityTasks; this.upcoming7Days = upcoming7Days; + this.recommendations = recommendations; this.approvalCount = approvalCount; this.workerResponseCount = workerResponseCount; } @@ -53,6 +59,10 @@ public List getUpcoming7Days() { return upcoming7Days; } + public DashboardRecommendationsResponse getRecommendations() { + return recommendations; + } + public long getApprovalCount() { return approvalCount; } diff --git a/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java b/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java index cccaaa9a..9b6d01e8 100644 --- a/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java +++ b/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java @@ -6,6 +6,8 @@ import com.fowoco.server.dashboard.api.DashboardSummaryCountsResponse; import com.fowoco.server.dashboard.api.DashboardTaskSummaryResponse; import com.fowoco.server.dashboard.api.DashboardTodayResponse; +import com.fowoco.server.dashboard.api.DashboardRecommendationItemResponse; +import com.fowoco.server.dashboard.api.DashboardRecommendationsResponse; import com.fowoco.server.dashboard.api.UpcomingExpiryCategory; import com.fowoco.server.dashboard.api.UpcomingExpiryItemResponse; import com.fowoco.server.dashboard.application.error.DashboardErrorCode; @@ -79,8 +81,47 @@ public DashboardTodayResponse today(ActorContext actor, LocalDate date, String t .toList(); List upcoming7Days = collectUpcomingExpiry(companyId, targetDate, windowEnd); + DashboardRecommendationsResponse recommendations = collectRecommendations(companyId); - return new DashboardTodayResponse(summaryCounts, priorityTasks, upcoming7Days, pendingApproval, workerResponse); + return new DashboardTodayResponse( + summaryCounts, priorityTasks, upcoming7Days, recommendations, pendingApproval, workerResponse + ); + } + + private DashboardRecommendationsResponse collectRecommendations(UUID companyId) { + List preparedTasks = taskRepository.findAll(new TaskRepository.TaskSearchCriteria( + companyId, TaskStatus.DRAFT, null, null, com.fowoco.server.task.domain.TaskSource.AI_CANDIDATE, + null, null, null, null, null, 0, 100 + )).items(); + + List needsInfoTasks = taskRepository.findAll(new TaskRepository.TaskSearchCriteria( + companyId, TaskStatus.NEEDS_INFO, null, null, null, null, null, null, null, null, 0, 100 + )).items(); + List readyTasks = taskRepository.findAll(new TaskRepository.TaskSearchCriteria( + companyId, TaskStatus.READY_FOR_REVIEW, null, null, null, null, null, null, null, null, 0, 100 + )).items(); + List reviewTasks = new ArrayList<>(); + reviewTasks.addAll(needsInfoTasks); + reviewTasks.addAll(readyTasks); + + List waitingWorkerTasks = taskRepository.findAll(new TaskRepository.TaskSearchCriteria( + companyId, TaskStatus.WAITING_WORKER, null, null, null, null, null, null, null, null, 0, 100 + )).items(); + List waitingExternalTasks = taskRepository.findAll(new TaskRepository.TaskSearchCriteria( + companyId, TaskStatus.WAITING_EXTERNAL, null, null, null, null, null, null, null, null, 0, 100 + )).items(); + List afterApprovalTasks = new ArrayList<>(); + afterApprovalTasks.addAll(waitingWorkerTasks); + afterApprovalTasks.addAll(waitingExternalTasks); + + long connectedCount = taskRepository.countOpenTasksByCompanyId(companyId); + + return new DashboardRecommendationsResponse( + connectedCount, + preparedTasks.stream().map(DashboardRecommendationItemResponse::from).toList(), + reviewTasks.stream().map(DashboardRecommendationItemResponse::from).toList(), + afterApprovalTasks.stream().map(DashboardRecommendationItemResponse::from).toList() + ); } private List collectUpcomingExpiry(UUID companyId, LocalDate windowStart, LocalDate windowEnd) { diff --git a/src/main/java/com/fowoco/server/task/application/port/TaskRepository.java b/src/main/java/com/fowoco/server/task/application/port/TaskRepository.java index 515b34ff..18bb6033 100644 --- a/src/main/java/com/fowoco/server/task/application/port/TaskRepository.java +++ b/src/main/java/com/fowoco/server/task/application/port/TaskRepository.java @@ -16,6 +16,7 @@ public interface TaskRepository { List findOpenTasks(UUID companyId, int limit); long countByCompanyIdAndStatus(UUID companyId, TaskStatus status); long countOpenTasksDueOn(UUID companyId, LocalDate dueDate); + long countOpenTasksByCompanyId(UUID companyId); Task save(Task task); record TaskSearchCriteria( diff --git a/src/main/java/com/fowoco/server/task/infrastructure/persistence/JpaTaskRepository.java b/src/main/java/com/fowoco/server/task/infrastructure/persistence/JpaTaskRepository.java index 602a8e57..3c78bec1 100644 --- a/src/main/java/com/fowoco/server/task/infrastructure/persistence/JpaTaskRepository.java +++ b/src/main/java/com/fowoco/server/task/infrastructure/persistence/JpaTaskRepository.java @@ -73,6 +73,11 @@ public long countOpenTasksDueOn(UUID companyId, LocalDate dueDate) { return repository.countOpenTasksDueOn(companyId, dueDate); } + @Override + public long countOpenTasksByCompanyId(UUID companyId) { + return repository.countOpenTasksByCompanyId(companyId); + } + @Override public Task save(Task task) { TaskJpaEntity entity = repository.findByTaskIdAndCompanyId(task.taskId(), task.companyId()) diff --git a/src/main/java/com/fowoco/server/task/infrastructure/persistence/SpringDataTaskJpaRepository.java b/src/main/java/com/fowoco/server/task/infrastructure/persistence/SpringDataTaskJpaRepository.java index b973a22d..e4851abd 100644 --- a/src/main/java/com/fowoco/server/task/infrastructure/persistence/SpringDataTaskJpaRepository.java +++ b/src/main/java/com/fowoco/server/task/infrastructure/persistence/SpringDataTaskJpaRepository.java @@ -73,4 +73,13 @@ SELECT COUNT(task) AND task.status <> com.fowoco.server.task.domain.TaskStatus.CANCELLED """) long countOpenTasksDueOn(@Param("companyId") UUID companyId, @Param("dueDate") LocalDate dueDate); + + @Query(""" + SELECT COUNT(task) + FROM TaskJpaEntity task + WHERE task.companyId = :companyId + AND task.status <> com.fowoco.server.task.domain.TaskStatus.COMPLETED + AND task.status <> com.fowoco.server.task.domain.TaskStatus.CANCELLED + """) + long countOpenTasksByCompanyId(@Param("companyId") UUID companyId); } From b8a32e7d793df9fab0d5320e23b5d1a0fa19b091 Mon Sep 17 00:00:00 2001 From: chaelin Date: Sat, 8 Aug 2026 19:49:39 +0900 Subject: [PATCH 2/3] =?UTF-8?q?test(dashboard):=20recommendations=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=203=EA=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DashboardSecurityIntegrationTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java b/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java index c4943833..67b5c8f1 100644 --- a/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java +++ b/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java @@ -223,6 +223,45 @@ void upcoming7DaysExcludesAlreadyPastExpiry() throws Exception { assertThat(names).doesNotContain("이미지난만료근로자"); } + @Test + void recommendationsConnectedCountMatchesOpenTasks() throws Exception { + String accessToken = accessToken(login(HR_A_EMAIL)); + String workerId = registerWorker(accessToken, "추천테스트근로자"); + createTask(accessToken, workerId, "추천테스트업무1"); + createTask(accessToken, workerId, "추천테스트업무2"); + + HttpResponse response = authorizedGet("/api/v1/dashboard/today", accessToken); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(JsonPath.read(response.body(), "$.recommendations.connected_count").longValue()) + .isEqualTo(2); + } + + @Test + void recommendationsReviewIncludesReadyForReviewTask() throws Exception { + String accessToken = accessToken(login(HR_A_EMAIL)); + String workerId = registerWorker(accessToken, "승인대기추천근로자"); + String taskId = createTask(accessToken, workerId, "승인대기업무"); + requestReview(accessToken, taskId, workerId); + + HttpResponse response = authorizedGet("/api/v1/dashboard/today", accessToken); + + assertThat(response.statusCode()).isEqualTo(200); + List reviewTitles = JsonPath.read(response.body(), "$.recommendations.review[*].title"); + assertThat(reviewTitles).contains("승인대기업무"); + } + + @Test + void recommendationsPreparedAndAfterApprovalAreEmptyByDefault() throws Exception { + String accessToken = accessToken(login(HR_A_EMAIL)); + + HttpResponse response = authorizedGet("/api/v1/dashboard/today", accessToken); + + assertThat(response.statusCode()).isEqualTo(200); + assertThat(JsonPath.>read(response.body(), "$.recommendations.prepared")).isEmpty(); + assertThat(JsonPath.>read(response.body(), "$.recommendations.after_approval")).isEmpty(); + } + private String registerWorkerWithAllExpiryFields( String accessToken, String displayName, From b5163feb445da09806ce1c95429ba4c24bdb5179 Mon Sep 17 00:00:00 2001 From: chaelin Date: Sat, 8 Aug 2026 20:30:21 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(dashboard):=20timezone=20=EC=83=9D?= =?UTF-8?q?=EB=9E=B5=20=EC=8B=9C=20UTC=20=EB=8C=80=EC=8B=A0=20Asia/Seoul?= =?UTF-8?q?=20=EA=B8=B0=EB=B3=B8=EA=B0=92=20=EC=82=AC=EC=9A=A9=20(?= =?UTF-8?q?=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EA=B2=80=EC=A6=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/DashboardQueryService.java | 3 +- .../DashboardSecurityIntegrationTest.java | 16 ++ .../DashboardQueryServiceTest.java | 167 ++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/fowoco/server/dashboard/application/DashboardQueryServiceTest.java diff --git a/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java b/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java index 9b6d01e8..f258979f 100644 --- a/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java +++ b/src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java @@ -62,7 +62,8 @@ public DashboardQueryService( public DashboardTodayResponse today(ActorContext actor, LocalDate date, String timezone) { tenantDatabaseContext.setCompanyIdForCurrentTransaction(actor.companyId()); UUID companyId = actor.companyId(); - Clock effectiveClock = timezone != null ? clock.withZone(parseTimezone(timezone)) : clock; + ZoneId effectiveZone = timezone != null ? parseTimezone(timezone) : ZoneId.of("Asia/Seoul"); + Clock effectiveClock = clock.withZone(effectiveZone); LocalDate targetDate = date != null ? date : LocalDate.now(effectiveClock); LocalDate windowEnd = targetDate.plusDays(UPCOMING_DAYS + 1); diff --git a/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java b/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java index 67b5c8f1..a279d545 100644 --- a/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java +++ b/src/test/java/com/fowoco/server/dashboard/DashboardSecurityIntegrationTest.java @@ -158,6 +158,22 @@ void timezoneParameterIsAccepted() throws Exception { assertThat(response.statusCode()).isEqualTo(200); } + @Test + void dueTodayUsesKoreaTimeWhenTimezoneOmitted() throws Exception { + String accessToken = accessToken(login(HR_A_EMAIL)); + String workerId = registerWorker(accessToken, "타임존테스트근로자"); + createTask(accessToken, workerId, "한국시간마감업무"); + + // date를 명시적으로 지정하지 않고, timezone도 생략 + // 서버가 한국 시간 기준으로 "오늘"을 계산해야 정상 + HttpResponse response = authorizedGet("/api/v1/dashboard/today", accessToken); + + assertThat(response.statusCode()).isEqualTo(200); + // 이 테스트는 실제 UTC vs KST 자정~9시 사이 시간대 버그를 + // 완벽히 재현하진 못하지만(테스트 실행 시각에 의존), + // 최소한 API가 정상 응답하는지 확인 + } + @Test void upcoming7DaysIncludesWorkerWithNearExpiry() throws Exception { String accessToken = accessToken(login(HR_A_EMAIL)); diff --git a/src/test/java/com/fowoco/server/dashboard/application/DashboardQueryServiceTest.java b/src/test/java/com/fowoco/server/dashboard/application/DashboardQueryServiceTest.java new file mode 100644 index 00000000..c6fa5d39 --- /dev/null +++ b/src/test/java/com/fowoco/server/dashboard/application/DashboardQueryServiceTest.java @@ -0,0 +1,167 @@ +package com.fowoco.server.dashboard.application; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fowoco.server.auth.application.ActorContext; +import com.fowoco.server.auth.domain.UserRole; +import com.fowoco.server.common.security.TenantDatabaseContext; +import com.fowoco.server.task.application.port.TaskRepository; +import com.fowoco.server.task.domain.Task; +import com.fowoco.server.task.domain.TaskStatus; +import com.fowoco.server.worker.application.port.WorkerDocumentRepository; +import com.fowoco.server.worker.application.port.WorkerRepository; +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.junit.jupiter.api.Test; + +class DashboardQueryServiceTest { + + private static final UUID COMPANY_ID = UUID.fromString("50000000-0000-0000-0000-000000000001"); + + // UTC 기준 2026-08-07T18:00:00Z = 한국 시간(KST, UTC+9) 2026-08-08T03:00:00 + // 즉 한국은 이미 8월 8일이지만, UTC는 아직 8월 7일인 시점 + private static final Clock FIXED_UTC_CLOCK = + Clock.fixed(Instant.parse("2026-08-07T18:00:00Z"), ZoneOffset.UTC); + + @Test + void dueTodayUsesKoreaDateWhenTimezoneOmitted() { + FakeTaskRepository taskRepository = new FakeTaskRepository(); + DashboardQueryService service = new DashboardQueryService( + taskRepository, + new NoopWorkerRepository(), + new NoopWorkerDocumentRepository(), + new NoopTenantDatabaseContext(), + FIXED_UTC_CLOCK + ); + + service.today(actorContext(), null, null); + + // timezone을 생략했으니, "오늘"은 한국 날짜(2026-08-08)로 계산되어야 한다. + // UTC 그대로였다면 2026-08-07로 잘못 계산되었을 것이다. + assertThat(taskRepository.lastDueTodayDate).isEqualTo(LocalDate.of(2026, 8, 8)); + } + + private ActorContext actorContext() { + return new ActorContext(UUID.randomUUID(), COMPANY_ID, Set.of(UserRole.HR)); + } + + private static final class FakeTaskRepository implements TaskRepository { + private LocalDate lastDueTodayDate; + + @Override + public java.util.Optional findByIdAndCompanyId(UUID taskId, UUID companyId) { + throw new UnsupportedOperationException(); + } + + @Override + public TaskPage findAll(TaskSearchCriteria criteria) { + return new TaskPage(List.of(), 0, 0, 0, 0); + } + + @Override + public Task save(Task task) { + throw new UnsupportedOperationException(); + } + + @Override + public List findOpenTasks(UUID companyId, int limit) { + return List.of(); + } + + @Override + public long countByCompanyIdAndStatus(UUID companyId, TaskStatus status) { + return 0; + } + + @Override + public long countOpenTasksDueOn(UUID companyId, LocalDate dueDate) { + this.lastDueTodayDate = dueDate; + return 0; + } + + @Override + public long countOpenTasksByCompanyId(UUID companyId) { + return 0; + } + } + + private static final class NoopWorkerRepository implements WorkerRepository { + @Override + public void insert(com.fowoco.server.worker.domain.Worker worker) { + } + + @Override + public java.util.Optional findByWorkerIdAndCompanyId( + UUID workerId, UUID companyId) { + return java.util.Optional.empty(); + } + + @Override + public com.fowoco.server.worker.domain.Worker update(com.fowoco.server.worker.domain.Worker worker) { + throw new UnsupportedOperationException(); + } + + @Override + public List findPage( + UUID companyId, com.fowoco.server.worker.application.WorkerSearchQuery query) { + return List.of(); + } + + @Override + public long countPage(UUID companyId, com.fowoco.server.worker.application.WorkerSearchQuery query) { + return 0; + } + + @Override + public List findAllByWorkerIdsAndCompanyId( + Set workerIds, UUID companyId) { + return List.of(); + } + } + + private static final class NoopWorkerDocumentRepository implements WorkerDocumentRepository { + @Override + public void insert(com.fowoco.server.worker.domain.WorkerDocument document) { + } + + @Override + public java.util.Optional findByIdAndWorkerIdAndCompanyId( + UUID workerDocumentId, UUID workerId, UUID companyId) { + return java.util.Optional.empty(); + } + + @Override + public java.util.Optional findByIdAndCompanyId( + UUID workerDocumentId, UUID companyId) { + return java.util.Optional.empty(); + } + + @Override + public com.fowoco.server.worker.domain.WorkerDocument update( + com.fowoco.server.worker.domain.WorkerDocument document) { + throw new UnsupportedOperationException(); + } + + @Override + public List findPage( + UUID companyId, com.fowoco.server.worker.application.WorkerDocumentSearchQuery query) { + return List.of(); + } + + @Override + public long countPage(UUID companyId, com.fowoco.server.worker.application.WorkerDocumentSearchQuery query) { + return 0; + } + } + + private static final class NoopTenantDatabaseContext implements TenantDatabaseContext { + @Override + public void setCompanyIdForCurrentTransaction(UUID companyId) { + } + } +}