Search in sources :

Example 1 with Account

use of org.folio.circulation.domain.Account in project mod-circulation by folio-org.

the class AgedToLostScheduledNoticesProcessingTests method patronNoticeForAdjustmentOfFullyPaidLostItemFeeIsCreatedAndProcessed.

@Test
void patronNoticeForAdjustmentOfFullyPaidLostItemFeeIsCreatedAndProcessed() {
    LostItemFeePolicyBuilder lostItemFeePolicyBuilder = lostItemFeePoliciesFixture.ageToLostAfterOneMinutePolicy().withSetCost(LOST_ITEM_FEE_AMOUNT).withLostItemProcessingFee(PROCESSING_FEE_AMOUNT);
    AgeToLostResult agedToLostLoan = ageToLostFixture.createLoanAgeToLostAndChargeFees(lostItemFeePolicyBuilder, new NoticePolicyBuilder().active().withName("Aged to lost notice policy").withFeeFineNotices(List.of(new NoticeConfigurationBuilder().withAgedToLostReturnedEvent().withTemplateId(UPON_AT_TEMPLATE_ID).withUponAtTiming().create())));
    final UUID loanId = agedToLostLoan.getLoanId();
    final UUID userId = agedToLostLoan.getUser().getId();
    final List<JsonObject> existingAccounts = accountsClient.getAll();
    assertThat(existingAccounts, allOf(iterableWithSize(2), hasItems(isAccount(LOST_ITEM_FEE_AMOUNT, LOST_ITEM_FEE_AMOUNT, ACCOUNT_STATUS_OPEN, PAYMENT_STATUS_OUTSTANDING, LOST_ITEM_FEE, agedToLostLoan.getUser().getId()), isAccount(PROCESSING_FEE_AMOUNT, PROCESSING_FEE_AMOUNT, ACCOUNT_STATUS_OPEN, PAYMENT_STATUS_OUTSTANDING, LOST_ITEM_PROCESSING_FEE, agedToLostLoan.getUser().getId()))));
    // one "charge" action per account
    assertThat(feeFineActionsClient.getAll(), hasSize(2));
    final List<Account> accounts = existingAccounts.stream().map(Account::from).collect(toList());
    assertThat(accounts, hasSize(2));
    final Account firstAccount = accounts.get(0);
    final Account secondAccount = accounts.get(1);
    feeFineAccountFixture.pay(firstAccount.getId(), firstAccount.getAmount().toDouble());
    feeFineAccountFixture.transfer(secondAccount.getId(), secondAccount.getAmount().toDouble());
    // 2 charges + 1 payment + 1 transfer
    assertThat(feeFineActionsClient.getAll(), hasSize(4));
    // closing a loan-related fee/fine should close the loan
    eventSubscribersFixture.publishLoanRelatedFeeFineClosedEvent(loanId);
    assertThat(loansFixture.getLoanById(agedToLostLoan.getLoanId()).getJson(), isClosed());
    // check-in should refund both fees, no cancellations since both were paid/transferred fully
    checkInFixture.checkInByBarcode(agedToLostLoan.getItem());
    assertThat(itemsFixture.getById(agedToLostLoan.getItemId()).getJson(), isAvailable());
    assertThat(loansFixture.getLoanById(agedToLostLoan.getLoanId()).getJson(), isClosed());
    // 2 charges + 1 payment + 1 transfer + 2 credits + 2 refunds
    assertThat(feeFineActionsClient.getAll(), hasSize(8));
    final JsonObject lostItemFeeRefundAction = findFeeFineAction(ACTION_TYPE_REFUNDED_FULLY, LOST_ITEM_FEE_AMOUNT);
    final JsonObject processingFeeRefundAction = findFeeFineAction(ACTION_TYPE_REFUNDED_FULLY, PROCESSING_FEE_AMOUNT);
    final UUID refundLostItemFeeActionId = getId(lostItemFeeRefundAction);
    final UUID refundProcessingFeeActionId = getId(processingFeeRefundAction);
    final ZonedDateTime refundLostItemFeeActionDate = getActionDate(lostItemFeeRefundAction);
    final ZonedDateTime refundProcessingFeeActionDate = getActionDate(processingFeeRefundAction);
    verifyNumberOfSentNotices(0);
    assertThat(scheduledNoticesClient.getAll(), allOf(iterableWithSize(2), hasItems(hasScheduledFeeFineNotice(refundLostItemFeeActionId, loanId, userId, UPON_AT_TEMPLATE_ID, AGED_TO_LOST_RETURNED, refundLostItemFeeActionDate, UPON_AT, null, true), hasScheduledFeeFineNotice(refundProcessingFeeActionId, loanId, userId, UPON_AT_TEMPLATE_ID, AGED_TO_LOST_RETURNED, refundProcessingFeeActionDate, UPON_AT, null, true))));
    ZonedDateTime maxActionDate = Stream.of(refundLostItemFeeActionDate, refundProcessingFeeActionDate).max(ZonedDateTime::compareTo).orElseThrow();
    scheduledNoticeProcessingClient.runFeeFineNoticesProcessing(maxActionDate.plusSeconds(1));
    checkSentFeeFineNotices(agedToLostLoan, Map.of(lostItemFeeRefundAction, UPON_AT_TEMPLATE_ID, processingFeeRefundAction, UPON_AT_TEMPLATE_ID));
    verifyNumberOfScheduledNotices(0);
    verifyNumberOfPublishedEvents(NOTICE, 2);
    verifyNumberOfPublishedEvents(NOTICE_ERROR, 0);
}
Also used : AccountMatchers.isAccount(api.support.matchers.AccountMatchers.isAccount) Account(org.folio.circulation.domain.Account) LostItemFeePolicyBuilder(api.support.builders.LostItemFeePolicyBuilder) ZonedDateTime(java.time.ZonedDateTime) AgeToLostResult(api.support.fixtures.AgeToLostFixture.AgeToLostResult) NoticePolicyBuilder(api.support.builders.NoticePolicyBuilder) NoticeConfigurationBuilder(api.support.builders.NoticeConfigurationBuilder) JsonObject(io.vertx.core.json.JsonObject) UUID(java.util.UUID) Test(org.junit.jupiter.api.Test)

Example 2 with Account

use of org.folio.circulation.domain.Account in project mod-circulation by folio-org.

the class FeeFineFacade method refundAccountIfNeeded.

CompletableFuture<Result<AccountActionResponse>> refundAccountIfNeeded(RefundAndCancelAccountCommand command, User user) {
    final Account account = command.getAccount();
    if (!account.hasPaidOrTransferredAmount()) {
        log.info("Account {} is not processed yet, no refunds have to be issued", account.getId());
        return ofAsync(() -> null);
    }
    log.info("Initiating refund for account {}", account.getId());
    final RefundAccountCommand refundCommand = RefundAccountCommand.builder().account(account).currentServicePointId(command.getServicePointId()).refundReason(command.getRefundReason()).userName(user.getPersonalName()).build();
    return feeFineService.refundAccount(refundCommand);
}
Also used : StoredAccount(org.folio.circulation.domain.representations.StoredAccount) Account(org.folio.circulation.domain.Account) RefundAccountCommand(org.folio.circulation.services.feefine.RefundAccountCommand)

Example 3 with Account

use of org.folio.circulation.domain.Account in project mod-circulation by folio-org.

the class FeeFineFacade method cancelAccountIfNeeded.

CompletableFuture<Result<AccountActionResponse>> cancelAccountIfNeeded(RefundAndCancelAccountCommand command, User user) {
    final Account account = command.getAccount();
    if (!account.getRemaining().hasAmount()) {
        log.info("Nothing to cancel for account {}", account.getId());
        return ofAsync(() -> null);
    }
    log.info("Initiating cancel for account {}", account.getId());
    final CancelAccountCommand cancelCommand = CancelAccountCommand.builder().accountId(account.getId()).currentServicePointId(command.getServicePointId()).cancellationReason(command.getCancelReason()).userName(user.getPersonalName()).build();
    return feeFineService.cancelAccount(cancelCommand);
}
Also used : StoredAccount(org.folio.circulation.domain.representations.StoredAccount) Account(org.folio.circulation.domain.Account) CancelAccountCommand(org.folio.circulation.services.feefine.CancelAccountCommand) RefundAndCancelAccountCommand(org.folio.circulation.services.support.RefundAndCancelAccountCommand)

Example 4 with Account

use of org.folio.circulation.domain.Account in project mod-circulation by folio-org.

the class LostItemFeeRefundService method findRefundableAccounts.

private List<Account> findRefundableAccounts(Account latestAccount, Collection<Account> accounts) {
    List<Account> filteredList = new ArrayList<>();
    filteredList.add(latestAccount);
    ZonedDateTime creationDate = latestAccount.getCreationDate();
    List<String> feeFineTypeForSearch = List.of(LOST_ITEM_FEE_TYPE.equals(latestAccount.getFeeFineType()) ? LOST_ITEM_PROCESSING_FEE_TYPE : LOST_ITEM_FEE_TYPE);
    getLatestAccount(accounts, feeFineTypeForSearch).filter(this::isAccountEligibleForRefund).filter(associatedAccount -> isDifferenceOneMinuteOrLess(creationDate, associatedAccount.getCreationDate())).map(filteredList::add);
    return filteredList;
}
Also used : UserRepository(org.folio.circulation.infrastructure.storage.users.UserRepository) Clients(org.folio.circulation.support.Clients) RenewalContext(org.folio.circulation.resources.context.RenewalContext) LoanRepository(org.folio.circulation.infrastructure.storage.loans.LoanRepository) CompletableFuture.completedFuture(java.util.concurrent.CompletableFuture.completedFuture) ZonedDateTime(java.time.ZonedDateTime) CompletableFuture(java.util.concurrent.CompletableFuture) ValidationErrorFailure.singleValidationError(org.folio.circulation.support.ValidationErrorFailure.singleValidationError) LOST_ITEM_PROCESSING_FEE_TYPE(org.folio.circulation.domain.FeeFine.LOST_ITEM_PROCESSING_FEE_TYPE) CheckInContext(org.folio.circulation.domain.CheckInContext) RefundAndCancelAccountCommand(org.folio.circulation.services.support.RefundAndCancelAccountCommand) ArrayList(java.util.ArrayList) AsyncCoordinationUtil.allOf(org.folio.circulation.support.AsyncCoordinationUtil.allOf) FeeFineAction(org.folio.circulation.domain.FeeFineAction) DateTimeUtil(org.folio.circulation.support.utils.DateTimeUtil) CqlQuery.exactMatch(org.folio.circulation.support.http.client.CqlQuery.exactMatch) AccountRepository(org.folio.circulation.infrastructure.storage.feesandfines.AccountRepository) CANCELLED_ITEM_RETURNED(org.folio.circulation.domain.AccountCancelReason.CANCELLED_ITEM_RETURNED) LostItemPolicyRepository(org.folio.circulation.infrastructure.storage.loans.LostItemPolicyRepository) AccountActionResponse(org.folio.circulation.services.feefine.AccountActionResponse) CqlQuery.exactMatchAny(org.folio.circulation.support.http.client.CqlQuery.exactMatchAny) Result.failed(org.folio.circulation.support.results.Result.failed) ItemRepository(org.folio.circulation.infrastructure.storage.inventory.ItemRepository) CollectionUtils.isNotEmpty(org.apache.commons.collections.CollectionUtils.isNotEmpty) Account(org.folio.circulation.domain.Account) CommonFailures(org.folio.circulation.support.results.CommonFailures) LostItemPolicy(org.folio.circulation.domain.policy.lostitem.LostItemPolicy) LOST_ITEM_FEE_TYPE(org.folio.circulation.domain.FeeFine.LOST_ITEM_FEE_TYPE) LostItemFeeRefundContext.forCheckIn(org.folio.circulation.services.LostItemFeeRefundContext.forCheckIn) Result.succeeded(org.folio.circulation.support.results.Result.succeeded) Collection(java.util.Collection) Loan(org.folio.circulation.domain.Loan) Result(org.folio.circulation.support.results.Result) Result.ofAsync(org.folio.circulation.support.results.Result.ofAsync) LostItemFeeRefundContext.forRenewal(org.folio.circulation.services.LostItemFeeRefundContext.forRenewal) User(org.folio.circulation.domain.User) FeeFineScheduledNoticeService(org.folio.circulation.domain.notice.schedule.FeeFineScheduledNoticeService) List(java.util.List) Logger(org.apache.logging.log4j.Logger) Optional(java.util.Optional) CqlQuery(org.folio.circulation.support.http.client.CqlQuery) Predicate.not(java.util.function.Predicate.not) Comparator(java.util.Comparator) Collections(java.util.Collections) LogManager(org.apache.logging.log4j.LogManager) Account(org.folio.circulation.domain.Account) ZonedDateTime(java.time.ZonedDateTime) ArrayList(java.util.ArrayList)

Aggregations

Account (org.folio.circulation.domain.Account)4 ZonedDateTime (java.time.ZonedDateTime)2 StoredAccount (org.folio.circulation.domain.representations.StoredAccount)2 RefundAndCancelAccountCommand (org.folio.circulation.services.support.RefundAndCancelAccountCommand)2 LostItemFeePolicyBuilder (api.support.builders.LostItemFeePolicyBuilder)1 NoticeConfigurationBuilder (api.support.builders.NoticeConfigurationBuilder)1 NoticePolicyBuilder (api.support.builders.NoticePolicyBuilder)1 AgeToLostResult (api.support.fixtures.AgeToLostFixture.AgeToLostResult)1 AccountMatchers.isAccount (api.support.matchers.AccountMatchers.isAccount)1 JsonObject (io.vertx.core.json.JsonObject)1 ArrayList (java.util.ArrayList)1 Collection (java.util.Collection)1 Collections (java.util.Collections)1 Comparator (java.util.Comparator)1 List (java.util.List)1 Optional (java.util.Optional)1 UUID (java.util.UUID)1 CompletableFuture (java.util.concurrent.CompletableFuture)1 CompletableFuture.completedFuture (java.util.concurrent.CompletableFuture.completedFuture)1 Predicate.not (java.util.function.Predicate.not)1