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);
}
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);
}
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);
}
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;
}
Aggregations