public InvestmentRejectedEventImpl(final RecommendedLoan recommendation, final String confirmationProviderId) { this.loan = recommendation.descriptor().item(); this.recommendation = recommendation.amount(); this.confirmationProviderId = confirmationProviderId; }
/** * Convert the descriptor into an actual investment recommendation. This will be executed by the * {@link InvestmentStrategy}. * @param amount The amount recommended to invest. * @param confirmationRequired Whether or not {@link ConfirmationProvider} is required to confirm the investment. * @return Empty if amount is out of bounds. */ public Optional<RecommendedLoan> recommend(final int amount, final boolean confirmationRequired) { final int remaining = loan.getNonReservedRemainingInvestment(); if (amount <= remaining) { return Optional.of(new RecommendedLoan(this, amount, confirmationRequired)); } else { LOGGER.warn("Can not recommend {} CZK with {} CZK remaining in loan #{}.", amount, remaining, loan.getId()); return Optional.empty(); } }
@Test void constructor() { final LoanDescriptor ld = RecommendedLoanTest.mockLoanDescriptor(); final int amount = 200; final RecommendedLoan r = new RecommendedLoan(ld, amount, true); SoftAssertions.assertSoftly(softly -> { softly.assertThat(r.descriptor()).isSameAs(ld); softly.assertThat(r.amount()).isEqualTo(BigDecimal.valueOf(amount)); softly.assertThat(r.isConfirmationRequired()).isTrue(); }); }
@Test void recommendAmount() { final Loan mockedLoan = LoanDescriptorTest.mockLoan(); final LoanDescriptor ld = new LoanDescriptor(mockedLoan); final Optional<RecommendedLoan> r = ld.recommend(200); assertThat(r).isPresent(); final RecommendedLoan recommendation = r.get(); assertSoftly(softly -> { softly.assertThat(recommendation.descriptor()).isSameAs(ld); softly.assertThat(recommendation.amount()).isEqualTo(BigDecimal.valueOf(200)); softly.assertThat(recommendation.isConfirmationRequired()).isFalse(); }); }
public Either<InvestmentFailure, BigDecimal> invest(final RecommendedLoan r, final boolean alreadySeenBefore) { final boolean confirmationRequired = r.isConfirmationRequired(); if (alreadySeenBefore) { LOGGER.debug("Loan seen before."); final boolean protectedByCaptcha = r.descriptor().getLoanCaptchaProtectionEndDateTime() .map(date -> DateUtil.offsetNow().isBefore(date)) .orElse(false); if (!protectedByCaptcha && !confirmationRequired) { /* * investment is no longer protected by CAPTCHA and no confirmation is required. therefore we invest. */ return this.investLocallyFailingOnCaptcha(r); } else { /* * protected by captcha or confirmation required. yet already seen from a previous investment session. * this must mean that the previous response was DELEGATED and the user did not respond in the * meantime. we therefore keep the investment as delegated. */ return Either.left(InvestmentFailure.SEEN_BEFORE); } } else if (confirmationRequired) { if (this.provider == null) { throw new IllegalStateException("Confirmation required but no confirmation provider specified."); } else { return this.delegateOrReject(r); } } else { return this.investOrDelegateOnCaptcha(r); } }
private Either<InvestmentFailure, BigDecimal> investOrDelegateOnCaptcha(final RecommendedLoan r) { final Optional<OffsetDateTime> captchaEndDateTime = r.descriptor().getLoanCaptchaProtectionEndDateTime(); final boolean isCaptchaProtected = captchaEndDateTime.isPresent() && captchaEndDateTime.get().isAfter(DateUtil.offsetNow()); final boolean confirmationSupported = this.provider != null; if (!isCaptchaProtected) { return this.investLocallyFailingOnCaptcha(r); } else if (confirmationSupported) { return this.delegateOrReject(r); } LOGGER.warn("CAPTCHA protected, no support for delegation. Not investing: {}.", r); return Either.left(InvestmentFailure.REJECTED); }
@Override public BigDecimal getRecommendation() { return recommendation.amount(); }
@Test void notEqualsDifferentJavaType() { final RecommendedLoan r1 = new RecommendedLoan(RecommendedLoanTest.mockLoanDescriptor(), 200, true); assertThat(r1).isNotEqualTo(r1.toString()); } }
public Either<InvestmentFailure, BigDecimal> invest(final RecommendedLoan r, final boolean alreadySeenBefore) { final boolean confirmationRequired = r.isConfirmationRequired(); if (alreadySeenBefore) { LOGGER.debug("Loan seen before."); final boolean protectedByCaptcha = r.descriptor().getLoanCaptchaProtectionEndDateTime() .map(date -> DateUtil.offsetNow().isBefore(date)) .orElse(false); if (!protectedByCaptcha && !confirmationRequired) { /* * investment is no longer protected by CAPTCHA and no confirmation is required. therefore we invest. */ return this.investLocallyFailingOnCaptcha(r); } else { /* * protected by captcha or confirmation required. yet already seen from a previous investment session. * this must mean that the previous response was DELEGATED and the user did not respond in the * meantime. we therefore keep the investment as delegated. */ return Either.left(InvestmentFailure.SEEN_BEFORE); } } else if (confirmationRequired) { if (this.provider == null) { throw new IllegalStateException("Confirmation required but no confirmation provider specified."); } else { return this.delegateOrReject(r); } } else { return this.investOrDelegateOnCaptcha(r); } }
private Either<InvestmentFailure, BigDecimal> investOrDelegateOnCaptcha(final RecommendedLoan r) { final Optional<OffsetDateTime> captchaEndDateTime = r.descriptor().getLoanCaptchaProtectionEndDateTime(); final boolean isCaptchaProtected = captchaEndDateTime.isPresent() && captchaEndDateTime.get().isAfter(DateUtil.offsetNow()); final boolean confirmationSupported = this.provider != null; if (!isCaptchaProtected) { return this.investLocallyFailingOnCaptcha(r); } else if (confirmationSupported) { return this.delegateOrReject(r); } LOGGER.warn("CAPTCHA protected, no support for delegation. Not investing: {}.", r); return Either.left(InvestmentFailure.REJECTED); }
@Override public BigDecimal getRecommendation() { return recommendation.amount(); }
public InvestmentSkippedEventImpl(final RecommendedLoan recommendation) { this.loan = recommendation.descriptor().item(); this.recommendation = recommendation.amount(); }
private boolean unsuccessfulInvestment(final RecommendedLoan recommendation, final InvestmentFailure reason) { final String providerId = investor.getConfirmationProvider().map(ConfirmationProvider::getId).orElse("-"); final LoanDescriptor loan = recommendation.descriptor(); switch (reason) { case FAILED: case DELEGATED: tenant.fire(investmentDelegated(recommendation, providerId)); if (recommendation.isConfirmationRequired()) {
private boolean successfulInvestment(final RecommendedLoan recommendation, final BigDecimal amount) { final int confirmedAmount = amount.intValue(); final MarketplaceLoan l = recommendation.descriptor().item(); final Investment i = Investment.fresh(l, confirmedAmount); markSuccessfulInvestment(i); discard(recommendation.descriptor()); // never show again tenant.fire(investmentMadeLazy(() -> investmentMade(i, l, tenant.getPortfolio().getOverview()))); return true; }
@Override public BigDecimal getRecommendation() { return recommendation.amount(); }
/** * Convert the descriptor into an actual investment recommendation. This will be executed by the * {@link InvestmentStrategy}. * @param amount The amount recommended to invest. * @param confirmationRequired Whether or not {@link ConfirmationProvider} is required to confirm the investment. * @return Empty if amount is out of bounds. */ public Optional<RecommendedLoan> recommend(final int amount, final boolean confirmationRequired) { final int remaining = loan.getNonReservedRemainingInvestment(); if (amount <= remaining) { return Optional.of(new RecommendedLoan(this, amount, confirmationRequired)); } else { LOGGER.warn("Can not recommend {} CZK with {} CZK remaining in loan #{}.", amount, remaining, loan.getId()); return Optional.empty(); } }
public InvestmentDelegatedEventImpl(final RecommendedLoan recommendation, final String confirmationProviderId) { this.loan = recommendation.descriptor().item(); this.recommendation = recommendation.amount(); this.confirmationProviderId = confirmationProviderId; }
private boolean unsuccessfulInvestment(final RecommendedLoan recommendation, final InvestmentFailure reason) { final String providerId = investor.getConfirmationProvider().map(ConfirmationProvider::getId).orElse("-"); final LoanDescriptor loan = recommendation.descriptor(); switch (reason) { case FAILED: case DELEGATED: tenant.fire(investmentDelegated(recommendation, providerId)); if (recommendation.isConfirmationRequired()) {
private boolean successfulInvestment(final RecommendedLoan recommendation, final BigDecimal amount) { final int confirmedAmount = amount.intValue(); final MarketplaceLoan l = recommendation.descriptor().item(); final Investment i = Investment.fresh(l, confirmedAmount); markSuccessfulInvestment(i); discard(recommendation.descriptor()); // never show again tenant.fire(investmentMadeLazy(() -> investmentMade(i, l, tenant.getPortfolio().getOverview()))); return true; }
private static Either<InvestmentFailure, BigDecimal> invest(final Tenant auth, final RecommendedLoan recommendedLoan) { LOGGER.debug("Executing investment: {}.", recommendedLoan); final Investment i = convertToInvestment(recommendedLoan); try { auth.run(zonky -> zonky.invest(i)); LOGGER.info("Invested {} CZK into loan #{}.", recommendedLoan.amount(), i.getLoanId()); return Either.right(recommendedLoan.amount()); } catch (final Exception ex) { LOGGER.debug("Failed investing {} CZK into loan #{}. Likely already full in the meantime.", recommendedLoan.amount(), i.getLoanId(), ex); return Either.left(InvestmentFailure.FAILED); } }