diff --git a/wallet-api/src/main/java/io/meeds/wallet/model/RewardReportStatus.java b/wallet-api/src/main/java/io/meeds/wallet/model/RewardReportStatus.java new file mode 100644 index 000000000..0c8b9ced2 --- /dev/null +++ b/wallet-api/src/main/java/io/meeds/wallet/model/RewardReportStatus.java @@ -0,0 +1,45 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.wallet.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RewardReportStatus { + + private long sentDate; + + private RewardPeriod period; + + private long participantsCount; + + private long recipientsCount; + + private int achievementsCount; + + private double tokensSent; + + private double tokensToSend; + + private boolean completelyProceeded; + +} diff --git a/wallet-api/src/main/java/io/meeds/wallet/reward/service/RewardReportService.java b/wallet-api/src/main/java/io/meeds/wallet/reward/service/RewardReportService.java index a78385190..ce568ee71 100644 --- a/wallet-api/src/main/java/io/meeds/wallet/reward/service/RewardReportService.java +++ b/wallet-api/src/main/java/io/meeds/wallet/reward/service/RewardReportService.java @@ -17,26 +17,30 @@ package io.meeds.wallet.reward.service; import java.time.LocalDate; +import java.time.ZoneId; import java.util.List; +import java.util.Map; import java.util.Set; +import io.meeds.wallet.model.*; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.exoplatform.social.core.identity.model.Identity; -import io.meeds.wallet.model.DistributionForecast; -import io.meeds.wallet.model.RewardPeriod; -import io.meeds.wallet.model.RewardPeriodType; -import io.meeds.wallet.model.RewardReport; -import io.meeds.wallet.model.RewardSettings; -import io.meeds.wallet.model.WalletReward; - /** * A storage service to save/load reward settings */ public interface RewardReportService { + /** + * Gets rewards report switch {@link RewardPeriod} + * + * @param rewardPeriod a {@link RewardPeriod} + * @return a {@link RewardReportStatus} + */ + RewardReportStatus getReport(RewardPeriod rewardPeriod); + /** * Compute rewards swicth configurations for the list of identities passed in * parameters @@ -88,6 +92,14 @@ public interface RewardReportService { */ RewardPeriod getRewardPeriod(RewardPeriodType periodType, LocalDate date); + /** + * Return the stored reward period by id + * + * @param rewardPeriodId reward period id + * @return {@link RewardPeriod} + */ + RewardPeriod getRewardPeriodById(long rewardPeriodId); + /** * Retrieve a {@link RewardReport} corresponding to a period identified by its id * @@ -164,4 +176,28 @@ public interface RewardReportService { */ void replaceRewardTransactions(String oldHash, String newHash); + /** + * Gets wallet rewards by PeriodId and status + * first one + * + * @param periodId Reward Period id + * @param status Wallet reward status + * @param zoneId Wallet reward status + */ + Page findWalletRewardsByPeriodIdAndStatus(long periodId, String status, ZoneId zoneId, Pageable pageable); + + + /** + * Set isChanged data map for periods not sent + * + * @param updatedSettings map of isChanged data for periods not sent + * + */ + void setRewardSettingChanged(Map updatedSettings); + + /** + * Gets isChanged data map for periods not sent + * + */ + Map getRewardSettingChanged(); } diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/dao/RewardDAO.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/dao/RewardDAO.java index d3dcd93e5..541831e77 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/dao/RewardDAO.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/dao/RewardDAO.java @@ -22,6 +22,7 @@ import jakarta.transaction.Transactional; import io.meeds.wallet.reward.entity.WalletRewardEntity; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -37,6 +38,14 @@ public interface RewardDAO extends JpaRepository { """) List findRewardsByPeriodId(@Param("periodId") long periodId); + @Query(""" + SELECT rw FROM Reward rw WHERE rw.period.id = :periodId AND + (:isValid = TRUE AND (rw.tokensSent > 0 OR rw.tokensToSend > 0) OR :isValid = FALSE AND (rw.tokensSent <= 0 AND rw.tokensToSend <= 0)) + """) + Page findWalletRewardsByPeriodIdAndStatus(@Param("periodId") long periodId, + @Param("isValid") boolean isValid, + Pageable pageable); + List findWalletRewardEntitiesByIdentityId(long identityId, Pageable pageable); double countWalletRewardEntitiesByIdentityId(long identityId); diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/listener/RewardReportUpdateListener.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/listener/RewardReportUpdateListener.java new file mode 100644 index 000000000..35af512c6 --- /dev/null +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/listener/RewardReportUpdateListener.java @@ -0,0 +1,94 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.wallet.reward.listener; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.meeds.wallet.model.RewardPeriod; +import io.meeds.wallet.model.RewardSettings; +import io.meeds.wallet.reward.service.RewardReportService; +import io.meeds.wallet.reward.service.RewardSettingsService; +import io.meeds.wallet.reward.service.WalletRewardReportService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import org.exoplatform.services.listener.Event; +import org.exoplatform.services.listener.Listener; +import org.exoplatform.services.listener.ListenerService; + +import io.meeds.common.ContainerTransactional; + +import jakarta.annotation.PostConstruct; + +import static io.meeds.gamification.utils.Utils.*; +import static io.meeds.wallet.reward.service.WalletRewardSettingsService.REWARD_SETTINGS_UPDATED; +import static io.meeds.wallet.utils.WalletUtils.MODIFY_ADDRESS_ASSOCIATED_EVENT; +import static io.meeds.wallet.utils.WalletUtils.NEW_ADDRESS_ASSOCIATED_EVENT; + +/** + * A listener that is triggered to update estimated reward report for current + * period. + */ +@Component +public class RewardReportUpdateListener extends Listener> { + + private static final List EVENT_NAMES = Arrays.asList(NEW_ADDRESS_ASSOCIATED_EVENT, + MODIFY_ADDRESS_ASSOCIATED_EVENT, + REWARD_SETTINGS_UPDATED, + POST_CREATE_ANNOUNCEMENT_EVENT, + POST_UPDATE_ANNOUNCEMENT_EVENT, + POST_CANCEL_ANNOUNCEMENT_EVENT, + POST_REALIZATION_CREATE_EVENT, + POST_REALIZATION_UPDATE_EVENT, + POST_REALIZATION_CANCEL_EVENT); + + @Autowired + private RewardReportService rewardReportService; + + @Autowired + private RewardSettingsService rewardSettingsService; + + @Autowired + private ListenerService listenerService; + + @PostConstruct + public void init() { + EVENT_NAMES.forEach(name -> listenerService.addListener(name, this)); + } + + @ContainerTransactional + @Override + public void onEvent(Event> event) { + String eventName = event.getEventName(); + Map updatedSettings = rewardReportService.getRewardSettingChanged(); + if (REWARD_SETTINGS_UPDATED.equals(eventName)) { + List rewardPeriods = rewardReportService.getRewardPeriodsNotSent(); + updatedSettings = rewardPeriods.stream().collect(Collectors.toMap(RewardPeriod::getId, rewardPeriod -> true)); + } else { + RewardSettings rewardSettings = rewardSettingsService.getSettings(); + RewardPeriod rewardPeriod = rewardReportService.getRewardPeriod(rewardSettings.getPeriodType(), LocalDate.now()); + updatedSettings.put(rewardPeriod.getId(), true); + } + rewardReportService.setRewardSettingChanged(updatedSettings); + } +} diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/notification/RewardSuccessTemplateBuilder.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/notification/RewardSuccessTemplateBuilder.java index 479bf0bf8..215b36dbd 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/notification/RewardSuccessTemplateBuilder.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/notification/RewardSuccessTemplateBuilder.java @@ -35,22 +35,15 @@ import org.exoplatform.commons.api.notification.model.*; import org.exoplatform.commons.api.notification.service.template.TemplateContext; import org.exoplatform.commons.notification.template.TemplateUtils; -import org.exoplatform.container.ExoContainer; -import org.exoplatform.services.log.ExoLogger; -import org.exoplatform.services.log.Log; import org.exoplatform.social.notification.plugin.SocialNotificationUtils; import org.exoplatform.webui.utils.TimeConvertUtils; public class RewardSuccessTemplateBuilder extends AbstractTemplateBuilder { - private static final Log LOG = ExoLogger.getLogger(RewardSuccessTemplateBuilder.class); - private ChannelKey channelKey; private boolean pushNotification; - private ExoContainer container; - private RewardSettingsService rewardSettingsService; public RewardSuccessTemplateBuilder(RewardSettingsService rewardSettingsService, ChannelKey channelKey) { diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardReportREST.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardReportREST.java index 07e465f64..b9a8f7abd 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardReportREST.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardReportREST.java @@ -49,7 +49,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.bind.DefaultValue; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.web.PagedResourcesAssembler; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.PagedModel; @@ -75,32 +77,23 @@ public class RewardReportREST { @GetMapping(path = "compute") @Secured("rewarding") @Operation( - summary = "Compute rewards of wallets per a chosen period of time", + summary = "Gets rewards report per a chosen period of time", method = "GET", - description = "returns a set of wallet reward object") + description = "returns a list of reward report") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Request fulfilled"), @ApiResponse(responseCode = "400", description = "Invalid query input"), @ApiResponse(responseCode = "401", description = "Unauthorized operation"), @ApiResponse(responseCode = "500", description = "Internal server error") }) - public List computeRewards(@Parameter(description = "Page") - @RequestParam(value = "page", defaultValue = "0", required = false) - int page, - @Parameter(description = "Page size") - @RequestParam(value = "size", defaultValue = "12", required = false) - int size) { + public List getReportsStatus(@Parameter(description = "Page") + @RequestParam(value = "page", defaultValue = "0", required = false) + int page, + @Parameter(description = "Page size") + @RequestParam(value = "size", defaultValue = "12", required = false) + int size) { int skip = page * size; List periods = generatePreviousPeriods(skip + size).subList(skip, skip + size); - List rewardReports = periods.parallelStream() - .map(period -> { - RewardReport rewardReport = - rewardReportService.computeRewards(period.getPeriodMedianDate()); - rewardReport.setPeriod(new RewardPeriodWithFullDate(rewardReport.getPeriod())); - return rewardReport; - }) - .toList(); - rewardReports.forEach(rewardReport -> rewardReport.setPeriod(new RewardPeriodWithFullDate(rewardReport.getPeriod()))); - return rewardReports; + return periods.parallelStream().map(period -> rewardReportService.getReport(period)).toList(); } @PostMapping(path = "period/compute") @@ -121,6 +114,39 @@ public RewardReport computeRewardsByPeriod(@RequestBody return rewardReport; } + @GetMapping(path = "rewards") + @Secured("rewarding") + @Operation(summary = "Gets rewards of wallets per a chosen period of time", method = "GET", description = "returns the list of wallets per a chosen period of time") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + @ApiResponse(responseCode = "400", description = "Invalid query input"), + @ApiResponse(responseCode = "401", description = "Unauthorized operation"), + @ApiResponse(responseCode = "500", description = "Internal server error") }) + public PagedModel> getWalletRewards(Pageable pageable, + PagedResourcesAssembler assembler, + @Parameter(description = "Period id", required = true) + @RequestParam("periodId") + long periodId, + @Parameter(description = "Wallet reward status filtering, possible values: VALId and INVALID. Default value = VALId.") + @RequestParam(value = "status", defaultValue = "VALID") + String status, + @Parameter(description = "Field to sort by. Possible values: 'tokensSent', 'points'. Default is 'tokensSent'.") + @RequestParam(value = "sortField", defaultValue = "tokensSent") String sortField, + @Parameter(description = "Sort direction for tokensToSend field. Possible values: 'asc' (ascending) or 'desc' (descending). Default value = asc.") + @RequestParam(value = "sortDir", defaultValue = "asc") String sortDir) { + + Sort sort = sortDir.equalsIgnoreCase("desc") ? Sort.by(sortField).descending() : Sort.by(sortField).ascending(); + + Pageable sortedPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort); + + Page walletRewards; + walletRewards = rewardReportService.findWalletRewardsByPeriodIdAndStatus(periodId, + status, + rewardSettingsService.getSettings().zoneId(), + sortedPageable); + return assembler.toModel(walletRewards); + } + @PostMapping(path = "forecast") @Secured("rewarding") @Operation( @@ -268,8 +294,8 @@ private List generatePreviousPeriods(int count) { ZonedDateTime currentDateTime = ZonedDateTime.now(zoneId); return IntStream.range(0, count).mapToObj(i -> { - ZonedDateTime start; - ZonedDateTime end; + ZonedDateTime start = null; + ZonedDateTime end = null; switch (periodType) { case WEEK -> { @@ -287,16 +313,14 @@ private List generatePreviousPeriods(int count) { .truncatedTo(ChronoUnit.DAYS); end = start.plusMonths(3); } - default -> throw new UnsupportedOperationException("Unknown period type"); } - RewardPeriod rewardPeriod = new RewardPeriod(); rewardPeriod.setRewardPeriodType(periodType); rewardPeriod.setStartDateInSeconds(timeToSecondsAtDayStart(LocalDate.from(start), currentDateTime.getZone())); rewardPeriod.setEndDateInSeconds(timeToSecondsAtDayStart(LocalDate.from(end), currentDateTime.getZone())); return rewardPeriod; - }).collect(Collectors.toList()); + }).toList(); } } diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardSettingsREST.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardSettingsREST.java index d80b8a94c..11bab0e35 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardSettingsREST.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/rest/RewardSettingsREST.java @@ -100,8 +100,8 @@ public void deleteSettings() { @ApiResponse(responseCode = "401", description = "Unauthorized operation"), @ApiResponse(responseCode = "500", description = "Internal server error") }) public Response getRewardDates(@Parameter(description = "A date with format yyyy-MM-dd", required = true) - @RequestParam("date") - String date) { + @RequestParam("date") + String date) { if (StringUtils.isBlank(date)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Bad request sent to server with empty 'date' parameter"); } diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/scheduling/task/RewardReportUpdateTask.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/scheduling/task/RewardReportUpdateTask.java new file mode 100644 index 000000000..7d18f8ded --- /dev/null +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/scheduling/task/RewardReportUpdateTask.java @@ -0,0 +1,59 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.wallet.reward.scheduling.task; + +import java.util.Map; + +import io.meeds.common.ContainerTransactional; +import org.apache.commons.collections.CollectionUtils; +import org.quartz.DisallowConcurrentExecution; + +import io.meeds.wallet.model.RewardPeriod; +import io.meeds.wallet.reward.service.RewardReportService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +/** + * This job is used to check update for not sent reward periods. + */ +@DisallowConcurrentExecution +@Component +public class RewardReportUpdateTask { + + @Autowired + private RewardReportService rewardReportService; + + @ContainerTransactional + @Scheduled(cron = "${exo.wallet.RewardReportUpdateTask.expression:0 */5 * * * ?}") + public void execute() { + Map rewardSettingChanged = rewardReportService.getRewardSettingChanged(); + if (CollectionUtils.isNotEmpty(rewardSettingChanged.values())) { + for (Map.Entry entry : rewardSettingChanged.entrySet()) { + if (Boolean.TRUE.equals(entry.getValue())) { + Long id = entry.getKey(); + RewardPeriod rewardPeriod = rewardReportService.getRewardPeriodById(id); + if (rewardPeriod != null) { + rewardReportService.getReport(rewardPeriod); + } + } + } + } + } +} diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardReportService.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardReportService.java index f48b8f8ea..3c3229856 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardReportService.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardReportService.java @@ -35,18 +35,14 @@ import java.math.BigInteger; import java.time.LocalDate; import java.time.ZoneId; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import io.meeds.gamification.constant.RealizationStatus; +import io.meeds.wallet.model.*; +import lombok.Getter; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; @@ -62,18 +58,6 @@ import io.meeds.gamification.constant.IdentityType; import io.meeds.gamification.model.filter.RealizationFilter; import io.meeds.gamification.service.RealizationService; -import io.meeds.wallet.model.ContractDetail; -import io.meeds.wallet.model.DistributionForecast; -import io.meeds.wallet.model.RewardBudgetType; -import io.meeds.wallet.model.RewardPeriod; -import io.meeds.wallet.model.RewardPeriodType; -import io.meeds.wallet.model.RewardReport; -import io.meeds.wallet.model.RewardSettings; -import io.meeds.wallet.model.RewardStatus; -import io.meeds.wallet.model.TransactionDetail; -import io.meeds.wallet.model.Wallet; -import io.meeds.wallet.model.WalletReward; -import io.meeds.wallet.model.WalletType; import io.meeds.wallet.reward.storage.WalletRewardReportStorage; import io.meeds.wallet.service.WalletAccountService; import io.meeds.wallet.service.WalletTokenAdminService; @@ -103,6 +87,9 @@ public class WalletRewardReportService implements RewardReportService { @Setter private boolean rewardSendingInProgress; + @Getter + public Map rewardSettingChanged = new ConcurrentHashMap<>(); + public WalletRewardReportService(WalletAccountService walletAccountService, WalletTokenAdminService walletTokenAdminService, RewardSettingsService rewardSettingsService, @@ -132,8 +119,8 @@ public void sendRewards(LocalDate date, String username) throws IllegalAccessExc if (rewardReport.getPendingTransactionCount() > 0) { String startDateFormatted = rewardReport.getPeriod().getStartDateFormatted(Locale.getDefault().getLanguage()); String endDateFormatted = rewardReport.getPeriod().getEndDateFormatted(Locale.getDefault().getLanguage()); - throw new IllegalStateException("There are some pending transactions for rewards of period between " + startDateFormatted + - " and " + endDateFormatted + ", thus no reward sending is allowed until the transactions finishes"); + throw new IllegalStateException("There are some pending transactions for rewards of period between " + startDateFormatted + + " and " + endDateFormatted + ", thus no reward sending is allowed until the transactions finishes"); } String adminWalletAddress = getTokenAdminService().getAdminWalletAddress(); @@ -145,8 +132,7 @@ public void sendRewards(LocalDate date, String username) throws IllegalAccessExc Iterator rewardedWalletsIterator = rewards.iterator(); while (rewardedWalletsIterator.hasNext()) { WalletReward walletReward = rewardedWalletsIterator.next(); - if (walletReward == null || !walletReward.isEnabled() - || walletReward.getAmount() == 0 + if (walletReward == null || !walletReward.isEnabled() || walletReward.getAmount() == 0 || (walletReward.getTransaction() != null && (walletReward.getTransaction().isPending() || walletReward.getTransaction().isSucceeded()))) { rewardedWalletsIterator.remove(); @@ -154,8 +140,8 @@ public void sendRewards(LocalDate date, String username) throws IllegalAccessExc } if (walletReward.getAmount() < 0) { - throw new IllegalStateException("Can't send reward transaction for wallet of " + walletReward.getWallet().getType() + - " " + walletReward.getWallet().getId() + " with a negative amount" + walletReward.getAmount()); + throw new IllegalStateException("Can't send reward transaction for wallet of " + walletReward.getWallet().getType() + " " + + walletReward.getWallet().getId() + " with a negative amount" + walletReward.getAmount()); } // If the tokens are already sent, then ignore sending rewards to user // If the sent transaction is pending, an exception is thrown ate the @@ -211,6 +197,22 @@ public boolean isRewardSendingInProgress() { return rewardSendingInProgress; } + @Override + @ExoTransactional + public RewardReportStatus getReport(RewardPeriod rewardPeriod) { + RewardReport rewardReport = getRewardReport(rewardPeriod.getPeriodMedianDate()); + RewardPeriod storedRewardPeriod = getRewardPeriod(rewardPeriod.getRewardPeriodType(), rewardPeriod.getPeriodMedianDate()); + if (storedRewardPeriod != null && storedRewardPeriod.getId() > 0 && getRewardSettingChanged() != null + && Boolean.TRUE.equals(getRewardSettingChanged().get(storedRewardPeriod.getId()))) { + rewardReport = computeRewards(rewardPeriod.getPeriodMedianDate()); + saveRewardReport(rewardReport); + Map rewardSettingChangedMap = getRewardSettingChanged(); + rewardSettingChangedMap.put(storedRewardPeriod.getId(), false); + setRewardSettingChanged(rewardSettingChangedMap); + } + return buildReportStatus(rewardReport, rewardPeriod); + } + @Override @ExoTransactional public RewardReport computeRewards(LocalDate date) { @@ -236,12 +238,10 @@ public RewardReport computeRewards(LocalDate date) { } RewardReport rewardReport = getRewardReport(date); - if (rewardReport != null) { - rewardReport.setParticipationsCount(realizationService.countRealizationsByFilter(realizationFilter)); - return rewardReport; + if (rewardReport == null) { + rewardReport = new RewardReport(); + rewardReport.setPeriod(getRewardPeriod(date)); } - rewardReport = new RewardReport(); - rewardReport.setPeriod(getRewardPeriod(date)); rewardReport.setParticipationsCount(realizationService.countRealizationsByFilter(realizationFilter)); List participants = realizationService.getParticipantsBetweenDates(start, end); @@ -287,6 +287,11 @@ public RewardReport getRewardReport(LocalDate date) { public RewardPeriod getRewardPeriod(RewardPeriodType periodType, LocalDate date) { RewardSettings rewardSettings = rewardSettingsService.getSettings(); return rewardReportStorage.getRewardPeriod(periodType, date, rewardSettings.zoneId()); + } + + @Override + public RewardPeriod getRewardPeriodById(long rewardPeriodId) { + return rewardReportStorage.getRewardPeriodById(rewardPeriodId); } @Override @@ -385,6 +390,16 @@ public void replaceRewardTransactions(String oldHash, String newHash) { rewardReportStorage.replaceRewardTransactions(oldHash, newHash); } + @Override + public Page findWalletRewardsByPeriodIdAndStatus(long periodId, String status, ZoneId zoneId, Pageable pageable) { + boolean isValid = !status.equals("INVALID"); + return rewardReportStorage.findWalletRewardsByPeriodIdAndStatus(periodId, isValid, zoneId, pageable); + } + + public void setRewardSettingChanged(Map updatedSettings) { + rewardSettingChanged.putAll(updatedSettings); + } + private RewardPeriod getRewardPeriod(LocalDate date) { RewardSettings rewardSettings = rewardSettingsService.getSettings(); RewardPeriodType periodType = rewardSettings.getPeriodType(); @@ -433,7 +448,7 @@ private Set retrieveWalletRewards(RewardReport rewardReport, Set walletRewardList = walletRewards.stream() .filter(wr -> wallet != null && wr.getWallet() != null - && wr.getIdentityId() == wallet.getTechnicalId()) + && wr.getIdentityId() == wallet.getTechnicalId()) .toList(); WalletReward walletReward = walletRewardList.stream() @@ -601,4 +616,42 @@ private WalletTokenAdminService getTokenAdminService() { return walletTokenAdminService; } + private RewardReportStatus buildReportStatus(RewardReport rewardReport, RewardPeriod rewardPeriod) { + Date fromDate = new Date(rewardPeriod.getStartDateInSeconds() * 1000L); + Date toDate = new Date(rewardPeriod.getEndDateInSeconds() * 1000L); + + long participantsCount = realizationService.countParticipantsBetweenDates(fromDate, toDate); + + RealizationFilter realizationFilter = new RealizationFilter(); + realizationFilter.setFromDate(fromDate); + realizationFilter.setToDate(toDate); + realizationFilter.setEarnerType(IdentityType.USER); + realizationFilter.setStatus(RealizationStatus.ACCEPTED); + int achievementsCount = realizationService.countRealizationsByFilter(realizationFilter); + if (rewardReport == null) { + if (participantsCount > 0) { + rewardReport = computeRewards(rewardPeriod.getPeriodMedianDate()); + saveRewardReport(rewardReport); + } else { + rewardReport = new RewardReport(); + rewardReport.setPeriod(rewardPeriod); + } + } + WalletReward succeededTransaction = rewardReport.getRewards() + .stream() + .filter(reward -> reward.getTransaction() != null + && reward.getTransaction().isSucceeded()) + .findFirst() + .orElse(null); + + return new RewardReportStatus(succeededTransaction != null ? succeededTransaction.getTransaction().getSentTimestamp() : 0, + rewardReport.getPeriod(), + participantsCount, + rewardReport.getValidRewardCount(), + achievementsCount, + rewardReport.getTokensSent(), + rewardReport.getTokensToSend(), + CollectionUtils.isNotEmpty(rewardReport.getRewards()) && rewardReport.isCompletelyProceeded()); + } + } diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardSettingsService.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardSettingsService.java index 1f85ff956..8cc071e98 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardSettingsService.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/service/WalletRewardSettingsService.java @@ -26,6 +26,7 @@ import java.util.Objects; +import org.exoplatform.services.listener.ListenerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -40,10 +41,15 @@ @Service public class WalletRewardSettingsService implements RewardSettingsService { + public static final String REWARD_SETTINGS_UPDATED = "reward.settings.updated"; + + @Autowired + private SettingService settingService; + @Autowired - private SettingService settingService; + private ListenerService listenerService; - private RewardSettings configuredRewardSettings; + private RewardSettings configuredRewardSettings; @Override public RewardSettings getSettings() { // NOSONAR @@ -54,8 +60,8 @@ public RewardSettings getSettings() { // NOSONAR SettingValue settingsValue = settingService.get(REWARD_CONTEXT, REWARD_SCOPE, REWARD_SETTINGS_KEY_NAME); - String settingsValueString = settingsValue == null || settingsValue.getValue() == null ? null : - settingsValue.getValue().toString(); + String settingsValueString = settingsValue == null || settingsValue.getValue() == null ? null + : settingsValue.getValue().toString(); RewardSettings rewardSettings; if (settingsValueString == null) { @@ -82,6 +88,7 @@ public void saveSettings(RewardSettings rewardSettingsToStore) { // Purge cached settings this.configuredRewardSettings = null; + listenerService.broadcast(REWARD_SETTINGS_UPDATED, this, null); } @Override @@ -90,5 +97,6 @@ public void deleteSettings() { // Purge cached settings this.configuredRewardSettings = null; + listenerService.broadcast(REWARD_SETTINGS_UPDATED, this, null); } } diff --git a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/storage/WalletRewardReportStorage.java b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/storage/WalletRewardReportStorage.java index fc110dea0..7392586f1 100644 --- a/wallet-reward-services/src/main/java/io/meeds/wallet/reward/storage/WalletRewardReportStorage.java +++ b/wallet-reward-services/src/main/java/io/meeds/wallet/reward/storage/WalletRewardReportStorage.java @@ -100,6 +100,11 @@ public RewardPeriod getRewardPeriod(RewardPeriodType periodType, LocalDate date, return toDTO(rewardPeriodEntity); } + public RewardPeriod getRewardPeriodById(long rewardPeriodId) { + WalletRewardPeriodEntity rewardPeriodEntity = rewardPeriodDAO.findById(rewardPeriodId).orElse(null); + return toDTO(rewardPeriodEntity); + } + private RewardReport getRewardReport(WalletRewardPeriodEntity rewardPeriodEntity, ZoneId zoneId) { if (rewardPeriodEntity == null) { return null; @@ -144,10 +149,6 @@ public void saveRewardReport(RewardReport rewardReport) { // NOSONAR throw new IllegalArgumentException("reward report is null"); } RewardPeriod period = rewardReport.getPeriod(); - LOG.info("Saving reward report for period from {} to {}", - period.getStartDateFormatted("en"), - period.getEndDateFormatted("en")); - WalletRewardPeriodEntity rewardPeriodEntity = rewardPeriodDAO.findRewardPeriodByTypeAndTime(period.getRewardPeriodType(), period.getPeriodMedianDateInSeconds()); @@ -245,6 +246,11 @@ public double countRewards(long identityId) { public void replaceRewardTransactions(String oldHash, String newHash) { rewardDAO.replaceRewardTransactions(oldHash, newHash); + } + + public Page findWalletRewardsByPeriodIdAndStatus(long periodId, boolean isValid, ZoneId zoneId, Pageable pageable) { + Page walletRewardEntities = rewardDAO.findWalletRewardsByPeriodIdAndStatus(periodId, isValid, pageable); + return walletRewardEntities.map(walletRewardEntity -> toDTO(walletRewardEntity, zoneId)); } private RewardPeriod toDTO(WalletRewardPeriodEntity period) { @@ -263,7 +269,7 @@ private RewardPeriod toDTO(WalletRewardPeriodEntity period) { private WalletReward toDTO(WalletRewardEntity rewardEntity, ZoneId zoneId) { WalletReward walletReward = new WalletReward(); - walletReward.setAmount(rewardEntity.getTokensSent()); + walletReward.setAmount(rewardEntity.getTokensToSend()); walletReward.setPoints(rewardEntity.getPoints() == null ? 0d : rewardEntity.getPoints()); retrieveWallet(rewardEntity, walletReward); retrieveTransaction(rewardEntity, walletReward); diff --git a/wallet-reward-services/src/main/resources/db/changelog/reward-rdbms.db.changelog-1.0.0.xml b/wallet-reward-services/src/main/resources/db/changelog/reward-rdbms.db.changelog-1.0.0.xml index 05a1a56a4..3f6494b5c 100644 --- a/wallet-reward-services/src/main/resources/db/changelog/reward-rdbms.db.changelog-1.0.0.xml +++ b/wallet-reward-services/src/main/resources/db/changelog/reward-rdbms.db.changelog-1.0.0.xml @@ -192,16 +192,20 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - + ANY + + + UPDATE ADDONS_WALLET_REWARD - SET POINTS=(SELECT SUM(POINTS) FROM ADDONS_WALLET_REWARD_PLUGIN WHERE ADDONS_WALLET_REWARD.REWARD_ID = ADDONS_WALLET_REWARD_PLUGIN.REWARD_ID) + SET POINTS=(SELECT SUM(POINTS) + FROM ADDONS_WALLET_REWARD_PLUGIN + WHERE ADDONS_WALLET_REWARD.REWARD_ID = ADDONS_WALLET_REWARD_PLUGIN.REWARD_ID) - - - + + diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/listener/RewardReportUpdateListenerTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/listener/RewardReportUpdateListenerTest.java new file mode 100644 index 000000000..3c1c2a494 --- /dev/null +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/listener/RewardReportUpdateListenerTest.java @@ -0,0 +1,123 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.wallet.reward.listener; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +import io.meeds.wallet.model.*; +import io.meeds.wallet.reward.service.RewardSettingsService; +import io.meeds.wallet.reward.service.WalletRewardReportService; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import org.exoplatform.services.listener.Event; +import org.exoplatform.services.listener.ListenerService; + +import static io.meeds.wallet.reward.service.WalletRewardSettingsService.REWARD_SETTINGS_UPDATED; +import static org.mockito.Mockito.*; + +@SpringBootTest(classes = { RewardReportUpdateListener.class, }) +class RewardReportUpdateListenerTest { + + @MockBean + private WalletRewardReportService rewardReportService; + + @MockBean + private RewardSettingsService rewardSettingsService; + + @MockBean + private ListenerService listenerService; + + @MockBean + private Event> event; + + @Autowired + private RewardReportUpdateListener rewardReportUpdateListener; + + @Test + void onEvent() { + when(event.getEventName()).thenReturn(REWARD_SETTINGS_UPDATED); + + RewardPeriod rewardPeriod1 = mock(RewardPeriod.class); + RewardPeriod rewardPeriod2 = mock(RewardPeriod.class); + when(rewardPeriod1.getId()).thenReturn(1L); + when(rewardPeriod2.getId()).thenReturn(2L); + + // Given + List rewardPeriodsNotSent = List.of( + rewardPeriod1, + rewardPeriod2 + ); + when(rewardReportService.getRewardPeriodsNotSent()).thenReturn(rewardPeriodsNotSent); + + Map initialSettings = new HashMap<>(); + when(rewardReportService.getRewardSettingChanged()).thenReturn(initialSettings); + + // When + rewardReportUpdateListener.onEvent(event); + + // Then + Map expectedUpdatedSettings = rewardPeriodsNotSent.stream() + .collect(Collectors.toMap(RewardPeriod::getId, rewardPeriod -> true)); + verify(rewardReportService, times(1)).setRewardSettingChanged(expectedUpdatedSettings); + + + List rewardPeriods = Arrays.asList(rewardPeriod1, rewardPeriod2); + when(rewardReportService.getRewardPeriodsNotSent()).thenReturn(rewardPeriods); + + Map rewardSettingChangedMap = new HashMap<>(); + when(rewardReportService.getRewardSettingChanged()).thenReturn(rewardSettingChangedMap); + + rewardReportUpdateListener.onEvent(event); + + verify(rewardReportService, times(2)).getRewardPeriodsNotSent(); + verify(rewardReportService, times(2)).getRewardSettingChanged(); + + when(event.getEventName()).thenReturn("OTHER_EVENT"); + + RewardSettings rewardSettings = mock(RewardSettings.class); + when(rewardSettingsService.getSettings()).thenReturn(rewardSettings); + RewardPeriod rewardPeriod = mock(RewardPeriod.class); + when(rewardPeriod.getId()).thenReturn(3L); + try (MockedStatic mockedRewardPeriod = mockStatic(RewardPeriod.class)) { + mockedRewardPeriod.when(() -> RewardPeriod.getCurrentPeriod(rewardSettings)).thenReturn(rewardPeriod); + // Given + when(rewardSettingsService.getSettings()).thenReturn(rewardSettings); + when(rewardSettings.getPeriodType()).thenReturn(RewardPeriodType.MONTH); + when(rewardReportService.getRewardPeriod(RewardPeriodType.MONTH, LocalDate.now())).thenReturn(rewardPeriod); + when(rewardPeriod.getId()).thenReturn(1L); + + initialSettings = new HashMap<>(); + when(rewardReportService.getRewardSettingChanged()).thenReturn(initialSettings); + + // When + rewardReportUpdateListener.onEvent(event); + + // Then + expectedUpdatedSettings = new HashMap<>(); + expectedUpdatedSettings.put(1L, true); + verify(rewardReportService, times(1)).setRewardSettingChanged(expectedUpdatedSettings); + } + } +} diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardReportREST.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardReportREST.java index 627ff5617..4f7ff9806 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardReportREST.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardReportREST.java @@ -29,7 +29,9 @@ import java.time.ZoneId; import java.util.List; +import io.meeds.wallet.model.*; import io.meeds.wallet.utils.WalletUtils; +import lombok.EqualsAndHashCode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -61,10 +63,6 @@ import io.meeds.spring.web.security.PortalAuthenticationManager; import io.meeds.spring.web.security.WebSecurityConfiguration; -import io.meeds.wallet.model.RewardPeriod; -import io.meeds.wallet.model.RewardPeriodType; -import io.meeds.wallet.model.RewardReport; -import io.meeds.wallet.model.RewardSettings; import io.meeds.wallet.reward.service.RewardReportService; import io.meeds.wallet.reward.service.RewardSettingsService; @@ -125,7 +123,9 @@ void computeRewardsAnonymously() throws Exception { @Test void computeRewardsSimpleUser() throws Exception { - ResultActions response = mockMvc.perform(get(REST_PATH + "/compute").param("page", "0").param("size", "12").with(testSimpleUser())); + ResultActions response = mockMvc.perform(get(REST_PATH + "/compute").param("page", "0") + .param("size", "12") + .with(testSimpleUser())); response.andExpect(status().isForbidden()); } @@ -157,6 +157,38 @@ void computeRewardsAdmin() throws Exception { } + @Test + void getWalletRewardsAnonymously() throws Exception { + ResultActions response = mockMvc.perform(get(REST_PATH + "/rewards").param("periodId", "1") + .param("status", "VALID") + .param("sortField", "tokensSent") + .param("sortDir", "desc")); + response.andExpect(status().isForbidden()); + } + + @Test + void getWalletRewardsSimpleUser() throws Exception { + ResultActions response = mockMvc.perform(get(REST_PATH + "/rewards").param("periodId", "1") + .param("status", "VALID") + .param("sortField", "tokensSent") + .param("sortDir", "desc") + .with(testSimpleUser())); + response.andExpect(status().isForbidden()); + } + + @Test + void getWalletRewardsAdmin() throws Exception { + when(rewardReportService.findWalletRewardsByPeriodIdAndStatus(anyLong(), anyString(), any(ZoneId.class), any(Pageable.class))).thenReturn(new PageImpl<>(List.of(walletReward()))); + + when(rewardSettingsService.getSettings()).thenReturn(new RewardSettings()); + + ResultActions response = mockMvc.perform(get(REST_PATH + "/rewards").param("periodId", "1") + .param("status", "VALID") + .param("sortField", "tokensSent") + .param("sortDir", "desc").with(testAdminUser())); + response.andExpect(status().isOk()); + } + @Test void computeRewardsByPeriodAnonymously() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/period/compute").content(asJsonString(rewardPeriod())) @@ -191,17 +223,17 @@ void computeRewardsByPeriodAdmin() throws Exception { @Test void computeDistributionForecastAnonymously() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/forecast").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)); response.andExpect(status().isForbidden()); } @Test void computeDistributionForecastSimpleUser() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/forecast").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON) - .with(testSimpleUser())); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(testSimpleUser())); response.andExpect(status().isForbidden()); } @@ -246,32 +278,35 @@ void computeRewardsByUserSimpleUser() throws Exception { @Test void sendRewardsAnonymously() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/send").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON)); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)); response.andExpect(status().isForbidden()); } @Test void sendRewardsSimpleUser() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/send").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON).with(testSimpleUser())); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(testSimpleUser())); response.andExpect(status().isForbidden()); } @Test void sendRewardsAdmin() throws Exception { ResultActions response = mockMvc.perform(post(REST_PATH + "/send").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON).with(testAdminUser())); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(testAdminUser())); verify(rewardReportService, times(1)).sendRewards(any(LocalDate.class), anyString()); response.andExpect(status().isOk()); // When doThrow(new IllegalAccessException()).when(rewardReportService).sendRewards(any(LocalDate.class), anyString()); response = mockMvc.perform(post(REST_PATH + "/send").content(asJsonString(rewardPeriod())) - .contentType(MediaType.APPLICATION_JSON) - .accept(MediaType.APPLICATION_JSON).with(testAdminUser())); + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(testAdminUser())); response.andExpect(status().isUnauthorized()); } @@ -346,6 +381,10 @@ private RewardPeriod rewardPeriod() { return new RewardPeriod(RewardPeriodType.WEEK, ZoneId.systemDefault().getId(), 1725832800, 1726437600); } + private WalletReward walletReward() { + return new WalletReward(new Wallet(), new TransactionDetail(), 1L, 100.0, 40.0, rewardPeriod()); + } + @SneakyThrows public static String asJsonString(final Object obj) { return OBJECT_MAPPER.writeValueAsString(obj); diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardSettingsREST.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardSettingsREST.java index 7588e700c..d414dcb4b 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardSettingsREST.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/rest/TestRewardSettingsREST.java @@ -29,6 +29,7 @@ import java.time.ZoneId; +import io.meeds.wallet.model.RewardPeriod; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardReportUpdateTaskTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardReportUpdateTaskTest.java new file mode 100644 index 000000000..8e7d8fe35 --- /dev/null +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardReportUpdateTaskTest.java @@ -0,0 +1,73 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.wallet.reward.scheduling; + +import java.util.*; + +import io.meeds.wallet.model.*; +import io.meeds.wallet.reward.scheduling.task.RewardReportUpdateTask; +import io.meeds.wallet.reward.service.RewardReportService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import static org.mockito.Mockito.*; + +@SpringBootTest(classes = { RewardReportUpdateTask.class }) +class RewardReportUpdateTaskTest { + + @MockBean + private RewardReportService rewardReportService; + + @Autowired + private RewardReportUpdateTask rewardReportUpdateTask; + + + @Test + void executeWhenRewardSettingChanged() { + + RewardPeriod rewardPeriod = mock(RewardPeriod.class); + Map rewardSettingChanged = new HashMap<>(); + rewardSettingChanged.put(1L, true); + rewardSettingChanged.put(2L, false); + when(rewardReportService.getRewardSettingChanged()).thenReturn(rewardSettingChanged); + + // When + when(rewardReportService.getRewardPeriodById(1L)).thenReturn(rewardPeriod); + + rewardReportUpdateTask.execute(); + + // Then + verify(rewardReportService, times(1)).getReport(rewardPeriod); + verify(rewardReportService, times(1)).getRewardPeriodById(1L); + } + + @Test + void executeWhenRewardSettingNotChanged() { + // When + Map rewardSettingChanged = new HashMap<>(); + rewardSettingChanged.put(1L, false); + when(rewardReportService.getRewardSettingChanged()).thenReturn(rewardSettingChanged); + + rewardReportUpdateTask.execute(); + + // Then + verify(rewardReportService, never()).getReport(any()); + } +} diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardStatusVerifierTaskTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardStatusVerifierTaskTest.java index 320637f79..954700372 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardStatusVerifierTaskTest.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/scheduling/RewardStatusVerifierTaskTest.java @@ -34,7 +34,7 @@ import static org.mockito.Mockito.*; @SpringBootTest(classes = { RewardStatusVerifierTask.class }) -public class RewardStatusVerifierTaskTest { +class RewardStatusVerifierTaskTest { @MockBean private RewardReportService rewardReportService; @@ -49,7 +49,7 @@ public class RewardStatusVerifierTaskTest { private RewardStatusVerifierTask rewardStatusVerifierTask; @Test - public void testSendRewards_NoAdminWallet() { + void testSendRewards_NoAdminWallet() { // No admin wallet or admin wallet has zero balance when(walletAccountService.getAdminWallet()).thenReturn(null); when(walletAccountService.isAdminAccountEnabled()).thenReturn(true); @@ -62,7 +62,7 @@ public void testSendRewards_NoAdminWallet() { } @Test - public void testSendRewards_RewardPeriodsInProgress() { + void testSendRewards_RewardPeriodsInProgress() { // When Wallet adminWallet = new Wallet(); adminWallet.setEtherBalance(100.0); diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardReportServiceTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardReportServiceTest.java index 576c97899..8af98bd34 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardReportServiceTest.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardReportServiceTest.java @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.time.LocalDate; import java.time.YearMonth; +import java.time.ZoneId; import java.util.*; import io.meeds.gamification.model.filter.RealizationFilter; @@ -44,6 +45,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Pageable; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.*; @@ -55,6 +57,8 @@ public class WalletRewardReportServiceTest { // NOSONAR private static final String ADMIN_USER = "root"; + private static final Pageable PAGEABLE = Pageable.ofSize(2); + @MockBean private WalletAccountService walletAccountService; @@ -154,6 +158,53 @@ void testGetRewardReport() { } + @Test + void testGetReportStatus() { + when(rewardSettingsService.getSettings()).thenReturn(new RewardSettings()); + + RewardPeriod rewardPeriod = new RewardPeriod(RewardPeriodType.WEEK, ZoneId.systemDefault().getId(), 1725832800, 1726437600); + // When + rewardReportService.getReport(rewardPeriod); + // Then + verify(realizationService, times(1)).countParticipantsBetweenDates(any(Date.class), any(Date.class)); + verify(realizationService, times(1)).countRealizationsByFilter(any(RealizationFilter.class)); + } + + @Test + void testFindRewardReportPeriods() { + rewardReportService.findRewardReportPeriods(PAGEABLE); + verify(rewardReportStorage, times(1)).findRewardReportPeriods(PAGEABLE); + } + + @Test + void testFindRewardPeriodsBetween() { + rewardReportService.findRewardPeriodsBetween(1725832800, 1726437600, PAGEABLE); + verify(rewardReportStorage, times(1)).findRewardPeriodsBetween(1725832800, 1726437600, PAGEABLE); + } + + @Test + void testGetRewardPeriodsInProgress() { + rewardReportService.getRewardPeriodsInProgress(); + verify(rewardReportStorage, times(1)).findRewardPeriodsByStatus(RewardStatus.PENDING); + } + + @Test + void testFindWalletRewardsByPeriodIdAndStatus() { + RewardSettings rewardSettings = new RewardSettings(); + + rewardReportService.findWalletRewardsByPeriodIdAndStatus(1, "VALID", rewardSettings.zoneId(), PAGEABLE); + verify(rewardReportStorage, times(1)).findWalletRewardsByPeriodIdAndStatus(1, true, rewardSettings.zoneId(), PAGEABLE); + + rewardReportService.findWalletRewardsByPeriodIdAndStatus(1, "INVALID", rewardSettings.zoneId(), PAGEABLE); + verify(rewardReportStorage, times(1)).findWalletRewardsByPeriodIdAndStatus(1, false, rewardSettings.zoneId(), PAGEABLE); + } + + @Test + void testGetRewardPeriodsNotSent() { + rewardReportService.getRewardPeriodsNotSent(); + verify(rewardReportStorage, times(1)).findRewardPeriodsByStatus(RewardStatus.ESTIMATION); + } + @Test void testComputeRewardsByUser() { LocalDate date = YearMonth.of(2022, 12).atEndOfMonth(); diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardSettingsServiceTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardSettingsServiceTest.java index 597b61f23..dd982759b 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardSettingsServiceTest.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/service/WalletRewardSettingsServiceTest.java @@ -26,6 +26,7 @@ import io.meeds.wallet.model.RewardPeriodType; import io.meeds.wallet.model.RewardSettings; +import org.exoplatform.services.listener.ListenerService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -43,6 +44,9 @@ public class WalletRewardSettingsServiceTest { @MockBean private SettingService settingService; + @MockBean + private ListenerService listenerService; + @Autowired private RewardSettingsService rewardSettingsService; diff --git a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/storage/WalletRewardReportStorageTest.java b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/storage/WalletRewardReportStorageTest.java index 1118833d9..ddcd802ee 100644 --- a/wallet-reward-services/src/test/java/io/meeds/wallet/reward/storage/WalletRewardReportStorageTest.java +++ b/wallet-reward-services/src/test/java/io/meeds/wallet/reward/storage/WalletRewardReportStorageTest.java @@ -88,6 +88,7 @@ void setup() { when(rewardDAO.count()).thenReturn(1L); when(rewardDAO.countWalletRewardEntitiesByIdentityId(IDENTITY_ID)).thenReturn(1.0); when(rewardDAO.findRewardsByPeriodId(REWARD_ID)).thenReturn(List.of(rewardEntity)); + when(rewardDAO.findWalletRewardsByPeriodIdAndStatus(REWARD_PERIOD_ID, true, PAGEABLE)).thenReturn(new PageImpl<>(List.of(rewardEntity))); return rewardEntity; }); doAnswer(invocation -> { @@ -122,7 +123,7 @@ void setup() { @Test void countRewards() { // Given - RewardReport rewardReport = createRewardReportInstance(); + RewardReport rewardReport = createRewardReportInstance(true); // When walletRewardReportStorage.saveRewardReport(rewardReport); @@ -134,7 +135,7 @@ void countRewards() { @Test void listRewards() { // Given - RewardReport rewardReport = createRewardReportInstance(); + RewardReport rewardReport = createRewardReportInstance(true); // When walletRewardReportStorage.saveRewardReport(rewardReport); @@ -151,7 +152,17 @@ void getRewardReport() { assertNull(walletRewardReportStorage.getRewardReport(RewardPeriodType.WEEK, LocalDate.now(), ZoneId.systemDefault())); // Given - RewardReport rewardReport = createRewardReportInstance(); + RewardReport rewardReport = createRewardReportInstance(true); + + // When + walletRewardReportStorage.saveRewardReport(rewardReport); + + // Then + assertNotNull(walletRewardReportStorage.getRewardReportByPeriodId(REWARD_PERIOD_ID, ZoneId.systemDefault())); + assertNotNull(walletRewardReportStorage.getRewardPeriod(RewardPeriodType.WEEK, LocalDate.now(), ZoneId.systemDefault())); + + // Given + rewardReport = createRewardReportInstance(false); // When walletRewardReportStorage.saveRewardReport(rewardReport); @@ -164,7 +175,7 @@ void getRewardReport() { @Test void findRewardPeriodsByStatus() { // Given - RewardReport rewardReport = createRewardReportInstance(); + RewardReport rewardReport = createRewardReportInstance(true); // When walletRewardReportStorage.saveRewardReport(rewardReport); @@ -173,7 +184,18 @@ void findRewardPeriodsByStatus() { assertNotNull(walletRewardReportStorage.findRewardPeriodsByStatus(RewardStatus.SUCCESS)); } - protected RewardReport createRewardReportInstance() { + @Test + void findWalletRewardsByPeriodIdAndStatus() { + // Given + RewardReport rewardReport = createRewardReportInstance(false); + + walletRewardReportStorage.saveRewardReport(rewardReport); + + // Then + assertNotNull(walletRewardReportStorage.findWalletRewardsByPeriodIdAndStatus(REWARD_PERIOD_ID, true, ZoneId.systemDefault(), PAGEABLE)); + } + + protected RewardReport createRewardReportInstance(boolean isSucceeded) { Wallet wallet = newWallet(IDENTITY_ID); Wallet wallet4 = newWallet(4L); Wallet wallet5 = newWallet(5L); @@ -184,7 +206,7 @@ protected RewardReport createRewardReportInstance() { rewardReport.setParticipationsCount(10); Set walletRewards = new HashSet<>(); TransactionDetail transactionDetail = new TransactionDetail(); - transactionDetail.setSucceeded(true); + transactionDetail.setSucceeded(isSucceeded); walletRewards.add(new WalletReward(wallet, transactionDetail, 1L, 100, 10, rewardPeriod)); walletRewards.add(new WalletReward(wallet4, transactionDetail, 4L, 200, 50, rewardPeriod)); walletRewards.add(new WalletReward(wallet5, transactionDetail, 5L, 300, 40, rewardPeriod)); diff --git a/wallet-services/src/main/resources/locale/addon/Wallet_en.properties b/wallet-services/src/main/resources/locale/addon/Wallet_en.properties index 7819712ec..937144f29 100644 --- a/wallet-services/src/main/resources/locale/addon/Wallet_en.properties +++ b/wallet-services/src/main/resources/locale/addon/Wallet_en.properties @@ -551,7 +551,6 @@ wallet.administration.rewardDetails.rewardsSent=No contributions to reward yet wallet.administration.rewardDetails.successfullyProceeded=Successfully proceeded wallet.administration.rewardDetails.transactionInProgress=Transaction in progress wallet.administration.rewardDetails.transactionError=Transaction error -wallet.administration.rewardDetails.searchLabel=search in names, wallet address wallet.administration.rewardDetails.sendingProgress=Sending in progress wallet.administration.rewardDetails.sendingError=Rewards not sent. Try again wallet.administration.rewardDetails.label.name=Name diff --git a/wallet-webapps/src/main/webapp/vue-app/wallet-common/js/RewardService.js b/wallet-webapps/src/main/webapp/vue-app/wallet-common/js/RewardService.js index d209a9daa..d81d67e44 100644 --- a/wallet-webapps/src/main/webapp/vue-app/wallet-common/js/RewardService.js +++ b/wallet-webapps/src/main/webapp/vue-app/wallet-common/js/RewardService.js @@ -225,3 +225,28 @@ export function getRewardReportPeriods(paramsObj) { } }); } + +export function getWalletRewards(paramsObj) { + const formData = new FormData(); + if (paramsObj) { + Object.keys(paramsObj).forEach(key => { + const value = paramsObj[key]; + if (window.Array && Array.isArray && Array.isArray(value)) { + value.forEach(val => formData.append(key, val)); + } else { + formData.append(key, value); + } + }); + } + const params = new URLSearchParams(formData).toString(); + return fetch(`/wallet/rest/reward/rewards?${params}`, { + method: 'GET', + credentials: 'include', + }).then((resp) => { + if (resp?.ok) { + return resp.json(); + } else { + throw new Error('Error when getting wallet rewards'); + } + }); +} diff --git a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/RewardApp.vue b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/RewardApp.vue index 7cf83ca34..f9d7e5dbe 100644 --- a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/RewardApp.vue +++ b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/RewardApp.vue @@ -42,7 +42,7 @@ @@ -78,6 +78,7 @@ export default { rewardSettings: {}, loading: false, loadingRewards: false, + loadingRewardSettings: false, rewardReports: [], selectedRewardReport: null, showRewardDetails: false, @@ -87,6 +88,7 @@ export default { contractDetails: null, distributionForecast: null, adminWallet: null, + settingsUpdated: false, }), computed: { rewardReport() { @@ -137,21 +139,18 @@ export default { }); }, refreshRewardSettings() { - this.loading = true; return this.$rewardService.getRewardSettings() .then(settings => { this.rewardSettings = settings || {}; }) .then(() => this.refreshRewards()) - .then(() => this.computeDistributionForecast()) - .finally(() => this.loading = false); + .then(() => this.computeDistributionForecast()); }, refreshRewards(period) { if (period) { this.period = period; } this.loadingRewards = true; - return this.$rewardService.computeRewards(this.rewardsPage, this.rewardsPageSize) .then(rewardReports => { this.rewardReports.push(...rewardReports); @@ -174,8 +173,9 @@ export default { this.refreshRewards(); }, settingUpdated() { + this.settingsUpdated = true; this.rewardReports = []; - this.refreshRewardSettings(); + this.refreshRewardSettings().then(() => this.settingsUpdated = false); }, rewardReportUpdated(rewardReport) { const index = this.rewardReports.findIndex(r => r?.period === rewardReport?.period); diff --git a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/BudgetConfiguration.vue b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/BudgetConfiguration.vue index c2202f44a..a45642178 100644 --- a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/BudgetConfiguration.vue +++ b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/BudgetConfiguration.vue @@ -148,11 +148,14 @@ export default { type: Object, default: null }, - rewardReport: { - type: Object, - default: null + settingsUpdated: { + type: Boolean, + default: false, } }, + data: () => ({ + rewardPeriod: null + }), computed: { periodType() { return this.rewardSettings?.periodType; @@ -160,17 +163,23 @@ export default { periodTypeLabel() { return this.$t(`wallet.administration.periodType.label.${this.periodType?.toLowerCase()}`) || ''; }, - rewardPeriod() { - return this.rewardReport?.period; + startDateInSeconds() { + return this.rewardPeriod?.startDateInSeconds; + }, + endDateInSeconds() { + return this.rewardPeriod?.endDateInSeconds; }, periodDatesDisplay() { if (!this.rewardPeriod) { return ''; } - const startDateFormatted = this.$dateUtil.formatDateObjectToDisplay(new Date(this.rewardPeriod.startDateInSeconds * 1000), this.dateformat, eXo.env.portal.language); - const endDateFormatted = this.$dateUtil.formatDateObjectToDisplay(new Date(this.rewardPeriod.endDateInSeconds * 1000 - 1), this.dateformat, eXo.env.portal.language); + const startDateFormatted = this.$dateUtil.formatDateObjectToDisplay(new Date(this.startDateInSeconds * 1000), this.dateformat, eXo.env.portal.language); + const endDateFormatted = this.$dateUtil.formatDateObjectToDisplay(new Date(this.endDateInSeconds * 1000 - 1), this.dateformat, eXo.env.portal.language); return `${startDateFormatted} ${this.$t('exoplatform.wallet.label.to')} ${endDateFormatted}`; }, + currentDate() { + return this.$dateUtil.getISODate(new Date()); + }, dateformat() { return this.timeZone && { year: 'numeric', @@ -207,6 +216,23 @@ export default { return this.rewardSettings?.storedSetting; }, }, + watch: { + loading() { + if (!this.rewardPeriod) { + this.getRewardDates(); + } + }, + settingsUpdated() { + if (this.settingsUpdated) { + this.getRewardDates(); + } + }, + }, + created() { + if (!this.rewardPeriod) { + this.getRewardDates(); + } + }, methods: { openConfigurationDrawer() { this.$emit('openConfiguration'); @@ -221,6 +247,12 @@ export default { maximumFractionDigits: 0, }).format(max); }, + getRewardDates() { + return this.$rewardService.getRewardDates(this.currentDate) + .then(rewardPeriod => { + this.rewardPeriod = rewardPeriod?.entity; + }); + } }, }; \ No newline at end of file diff --git a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardCard.vue b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardCard.vue index 3dd420b53..528868744 100644 --- a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardCard.vue +++ b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardCard.vue @@ -77,7 +77,7 @@ size="20"> fas fa-users - {{ participants }} + {{ participantsCount }}
fas fa-trophy - {{ participationsCount }} {{ $t('wallet.administration.rewardCard.label.contributions') }} + {{ achievementsCount }} {{ $t('wallet.administration.rewardCard.label.contributions') }}
reward?.transaction?.succeeded); - const sentDate = new Date(reward?.transaction?.timestamp); - return sentDate?.toLocaleString(this.lang, this.dateFormat); + return new window.Intl.DateTimeFormat(this.lang, this.dateFormat).format(new Date(this.sentDate)); }, }, methods: { diff --git a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardDetails.vue b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardDetails.vue index 9cdaa6b2f..df137f658 100644 --- a/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardDetails.vue +++ b/wallet-webapps/src/main/webapp/vue-app/wallet-reward/components/reward/RewardDetails.vue @@ -82,20 +82,22 @@ icon: 'fa-file-excel', text: 'Export', }" - :right-text-filter="{ - minCharacters: 3, - placeholder: this.$t('wallet.administration.rewardDetails.searchLabel'), - tooltip: this.$t('wallet.administration.rewardDetails.searchLabel') + :right-select-box="{ + selected: status, + items: walletRewardStatus, }" - @filter-text-input-end-typing="search = $event" /> + @filter-select-change="status = $event" /> + must-sort> + + + + + {{ $t("realization.label.loadMore") }} + + + + export default { props: { - loading: { - type: Boolean, - default: false, - }, rewardReport: { type: Object, default: null @@ -147,8 +162,8 @@ export default { } }, data: () => ({ - search: null, loadingSending: false, + loading: false, currentTimeInSeconds: Date.now() / 1000, lang: eXo.env.portal.language, dateFormat: { @@ -156,32 +171,53 @@ export default { month: 'short', day: 'numeric', }, + walletRewards: [], + status: 'VALID', + sortBy: 'tokensToSend', + sortDescending: true, + walletRewardsCount: 0, + pageSize: 100, + page: 0, }), computed: { + walletRewardStatus() { + return [{ + text: 'Eligible Members', + value: 'VALID', + },{ + text: 'Non Eligible', + value: 'INVALID', + }]; + }, identitiesHeaders() { return [ { text: this.$t('wallet.administration.rewardDetails.label.name'), + value: 'name', align: 'start', sortable: false, }, { text: this.$t('wallet.administration.rewardDetails.label.points'), + value: 'points', align: 'center', - sortable: false, + sortable: true, }, { text: this.$t('wallet.administration.rewardDetails.label.status'), + value: 'status', align: 'center', sortable: false, }, { text: this.$t('wallet.administration.rewardDetails.label.rewards'), + value: 'tokensToSend', align: 'center', - sortable: false, + sortable: true, }, { text: this.$t('wallet.administration.rewardDetails.label.actions'), + value: 'actions', align: 'center', sortable: false, }, @@ -193,17 +229,12 @@ export default { period() { return this.rewardReport?.period; }, - startDate() { - return new Date(this.period?.startDate); - }, - endDate() { - return new Date(this.period?.endDate); - }, starDateFormat() { - return this.startDate?.toLocaleString(this.lang, this.dateFormat); + return new window.Intl.DateTimeFormat(this.lang, this.dateFormat).format(new Date(this.startDateInSeconds * 1000 - new Date().getTimezoneOffset() * 60 * 1000)); }, toDateFormat() { - return this.endDate?.toLocaleString(this.lang, this.dateFormat); + return new window.Intl.DateTimeFormat(this.lang, this.dateFormat) + .format(new Date(this.endDateInSeconds * 1000 - 86400 * 1000 - new Date().getTimezoneOffset() * 60 * 1000)); }, completelyProceeded() { return this.rewardReport?.completelyProceeded; @@ -220,14 +251,8 @@ export default { isNotPastPeriod() { return !this.period || this.endDateInSeconds > this.currentTimeInSeconds; }, - walletRewards() { - return (this.rewardReport && this.rewardReport.rewards) || []; - }, - validRewards() { - return (this.rewardReport && this.rewardReport.validRewards) || []; - }, filteredIdentitiesList() { - return (this.walletRewards && this.walletRewards.filter((wallet) => (wallet.enabled || wallet.tokensSent || wallet.tokensToSend) && this.filterItemFromList(wallet, this.search))) || []; + return this.walletRewards?.filter((wallet) => (wallet.enabled || wallet.tokensSent || wallet.tokensToSend)) || []; }, tokenSymbol() { return window.walletSettings.contractDetail?.symbol; @@ -276,23 +301,45 @@ export default { }, disabledSendButtonLabel() { return this.isNotPastPeriod ? this.$t('wallet.administration.rewardCard.status.inPeriod') : this.balanceBelowBudgetLabel; - } + }, + walletRewardsFilter() { + return { + page: this.page, + size: this.pageSize, + periodId: this.period.id, + status: this.status, + sortField: this.sortBy, + sortDir: this.sortDescending ? 'desc' : 'asc', + }; + }, + hasMore() { + return this.walletRewards.length < this.walletRewardsCount; + }, }, - methods: { - filterItemFromList(walletReward, searchText) { - if (!searchText || !searchText.length) { - return true; + watch: { + status() { + this.page = 0; + this.walletRewards = []; + this.getWalletRewards(); + }, + sortBy(newVal, oldVal) { + if (newVal !== oldVal) { + if (this.sortDescending){ + this.sortDescending = false; + } + this.sortUpdated(); } - searchText = searchText.trim().toLowerCase(); - const name = walletReward?.wallet?.name?.toLowerCase(); - if (name.indexOf(searchText) > -1) { - return true; - } - const address = walletReward?.wallet?.address?.toLowerCase(); - if (address.indexOf(searchText) > -1) { - return true; + }, + sortDescending(newVal, oldVal) { + if (newVal !== oldVal) { + this.sortUpdated(); } }, + }, + created() { + this.getWalletRewards(); + }, + methods: { valueFormatted(max) { return new Intl.NumberFormat(this.lang, { style: 'decimal', @@ -306,6 +353,10 @@ export default { this.$rewardService.computeRewardsByPeriod(this.period) .then(rewardReport => { this.$emit('reward-report-updated', rewardReport); + }).then(() => { + this.status = 'VALID'; + this.walletRewards = []; + this.getWalletRewards(); }).finally(() => { this.loadingSending = false; }); @@ -313,7 +364,30 @@ export default { }, openContributionDetails(userId) { this.$refs?.profileStatsDrawer?.openByIdentityId(userId, this.rewardPeriodType); - } + }, + sortUpdated() { + if (!this.loading) { + this.loading = true; + this.page = 0; + this.walletRewards = []; + this.getWalletRewards(); + } + }, + getWalletRewards() { + this.loading = true; + return this.$rewardService.getWalletRewards(this.walletRewardsFilter).then(data => { + const newRewards = data?._embedded?.walletRewardList || []; + this.walletRewards = [...this.walletRewards, ...newRewards]; + this.walletRewardsCount = data?.page?.totalElements || 0; + }).finally(() => { + this.loading = false; + this.$root.$applicationLoaded(); + }); + }, + loadMore() { + this.page++; + this.getWalletRewards(); + }, }, }; \ No newline at end of file