@Test void fromBlockedAmount() { final BlockedAmount a = new BlockedAmount(1, BigDecimal.ONE); final Blocked b = new Blocked(a, Rating.D); assertSoftly(softly -> { softly.assertThat(b.getAmount()).isEqualTo(a.getAmount()); softly.assertThat(b.getRating()).isEqualTo(Rating.D); softly.assertThat(b.getId()).isEqualTo(a.getLoanId()); }); }
static Map<Integer, Blocked> readBlockedAmounts(final Tenant tenant, final Statistics stats) { final long portfolioSize = stats.getCurrentOverview().getPrincipalLeft(); final Divisor divisor = new Divisor(portfolioSize); return tenant.call(Zonky::getBlockedAmounts) .parallel() .peek(ba -> LOGGER.debug("Found: {}.", ba)) .filter(ba -> ba.getLoanId() > 0) .flatMap(ba -> getLoan(tenant, ba, divisor) .map(l -> Stream.of(new Blocked(ba, l.getRating()))) .orElse(Stream.empty())) .collect(Collectors.toMap(Blocked::getId, b -> b, Util::merge)); }
static Map<Integer, Blocked> readBlockedAmounts(final Tenant tenant, final Statistics stats) { final long portfolioSize = stats.getCurrentOverview().getPrincipalLeft(); final Divisor divisor = new Divisor(portfolioSize); return tenant.call(Zonky::getBlockedAmounts) .parallel() .peek(ba -> LOGGER.debug("Found: {}.", ba)) .filter(ba -> ba.getLoanId() > 0) .flatMap(ba -> getLoan(tenant, ba, divisor) .map(l -> Stream.of(new Blocked(ba, l.getRating()))) .orElse(Stream.empty())) .collect(Collectors.toMap(Blocked::getId, b -> b, Util::merge)); }
static Optional<Loan> getLoan(final Tenant tenant, final BlockedAmount ba, final Divisor divisor) { final int loanId = ba.getLoanId(); try { return Optional.of(tenant.getLoan(loanId)); } catch (final NotFoundException ex) { /* * Zonky has an intermittent caching problem and a failure here would prevent the robot from * ever finishing the portfolio update. As a result, the robot would not be able to do * anything. Comparatively, being wrong by 0,5 % is not so bad. */ LOGGER.warn("Zonky API mistakenly reports loan #{} as non-existent. " + "Consider reporting this to Zonky so that they can fix their cache.", loanId, ex); final BigDecimal amount = ba.getAmount(); divisor.add(amount.longValue()); final long shareThatIsWrongPerMille = divisor.getSharePerMille(); LOGGER.debug("Share per mille: {}.", shareThatIsWrongPerMille); if (shareThatIsWrongPerMille >= 5) { throw new IllegalStateException("RoboZonky portfolio structure is too far off.", ex); } else { // let this slide as the portfolio is only a little bit off return Optional.empty(); } } } }
static Optional<Loan> getLoan(final Tenant tenant, final BlockedAmount ba, final Divisor divisor) { final int loanId = ba.getLoanId(); try { return Optional.of(LoanCache.get().getLoan(loanId, tenant)); } catch (final NotFoundException ex) { /* * Zonky has an intermittent caching problem and a failure here would prevent the robot from * ever finishing the portfolio update. As a result, the robot would not be able to do * anything. Comparatively, being wrong by 0,5 % is not so bad. */ LOGGER.warn("Zonky API mistakenly reports loan #{} as non-existent. " + "Consider reporting this to Zonky so that they can fix their cache.", loanId, ex); final BigDecimal amount = ba.getAmount(); divisor.add(amount.longValue()); final long shareThatIsWrongPerMille = divisor.getSharePerMille(); LOGGER.debug("Share per mille: {}.", shareThatIsWrongPerMille); if (shareThatIsWrongPerMille >= 5) { throw new IllegalStateException("RoboZonky portfolio structure is too far off.", ex); } else { // let this slide as the portfolio is only a little bit off return Optional.empty(); } } } }