private SaleTier createTier(int tierNo, String startDate, String endDate, BigDecimal discount, BigInteger tomicsMax, boolean hasDynamicDuration, boolean hasDynamicMax) { return new SaleTier( tierNo, "test tier " + tierNo, Date.valueOf(startDate), Date.valueOf(endDate), discount, BigInteger.ZERO, tomicsMax, hasDynamicDuration, hasDynamicMax); } }
protected SaleTierResponse fromEntityToResponse(SaleTier tier) { SaleTierResponse response = new SaleTierResponse(); response.setAmount(tier.getTomicsSold()); response.setDiscount(tier.getDiscount()); response.setEndDate(tier.getEndDate()); response.setStartDate(tier.getStartDate()); response.setMaxAmount(tier.getTomicsMax()); response.setName(tier.getDescription()); response.setTierNo(tier.getTierNo()); response.setType(tier.getStatusAtDate(Date.from(Instant.now()))); return response; } }
BigDecimal tomicsDecimal = convertUsdToTomics(usd, tier.getDiscount()); BigInteger tomicsInteger = tomicsDecimal.toBigInteger(); if (tier.isAmountOverflowingTier(tomicsInteger)) { BigInteger remainingTomicsOnTier = tier.getRemainingTomics(); BigDecimal overflowOverTier = tomicsDecimal.subtract(new BigDecimal(remainingTomicsOnTier)); BigDecimal overflowInUsd = convertTomicsToUsd(overflowOverTier, tier.getDiscount()); if (isOverflowingTotalMax(remainingTomicsOnTier)) { LOG.debug("Distributing {} USD to tier {} lead to overflow over the total " + "available amount of tokens.", usd, tier.getTierNo()); return handleTotalMaxOverflow(tier, remainingTomicsOnTier).addToOverflow(overflowInUsd); } else { tier.setTomicsSold(tier.getTomicsMax()); tier.setTomicsSold(tier.getTomicsMax()); tier = saleTierRepository.save(tier); if (tier.hasDynamicDuration()) shiftDates(tier, blockTime); LOG.debug("Distributing {} USD to tier {}. Distributing Overflow of {} USD to " + "next tier.", usd, tier.getTierNo(), overflowInUsd); return distributeToNextTier(overflowInUsd, saleTierService.getSubsequentTier(tier), blockTime) .addToAllocatedTomics(remainingTomicsOnTier); if (isOverflowingTotalMax(tomicsInteger)) { LOG.debug("Distributing {} USD to tier {} lead to overflow over the total " + "available amount of tokens.", usd, tier.getTierNo()); return handleTotalMaxOverflow(tier, tomicsInteger); } else { tier.setTomicsSold(tier.getTomicsSold().add(tomicsInteger)); if (tier.isFull()) { if (tier.hasDynamicDuration()) shiftDates(tier, blockTime); saleTierService.getSubsequentTier(tier).ifPresent(this::handleDynamicMax);
private void handleDynamicMax(SaleTier tier) { if (tier.hasDynamicMax() && (tier.getTomicsMax().compareTo(BigInteger.ZERO) == 0 || tier.getTomicsMax() == null)) { tier.setTomicsMax(getTotalRemainingTomics()); saleTierRepository.save(tier); } }
private void shiftDates(SaleTier tier, Date blockTime) { long dateShift; if (blockTime.getTime() < tier.getStartDate().getTime()) { dateShift = tier.getEndDate().getTime() - tier.getStartDate().getTime(); tier.setEndDate(tier.getStartDate()); } else { dateShift = tier.getEndDate().getTime() - blockTime.getTime(); tier.setEndDate(blockTime); } tier = saleTierRepository.save(tier); saleTierService.getAllSubsequentTiers(tier).forEach(t -> { t.setStartDate(new Date(t.getStartDate().getTime() - dateShift)); t.setEndDate(new Date(t.getEndDate().getTime() - dateShift)); saleTierRepository.save(t); }); }
/** * Calculates the status of the sale tier according to his current properties. * See {@link StatusType} for how which status is determined. * * @param date The date at which to determine the tiers status. Usually one will want to use the * current date. * @return the status of the sale tier at the given date. */ public StatusType getStatusAtDate(Date date) { StatusType status; if (this.getEndDate().compareTo(date) <= 0) { status = StatusType.CLOSED; } else if (this.getStartDate().compareTo(date) <= 0 && this.getEndDate().compareTo(date) > 0) { if (this.getTomicsSold().compareTo(this.getTomicsMax()) >= 0) { status = StatusType.CLOSED; } else { status = StatusType.ACTIVE; } } else { status = StatusType.INCOMING; } return status; } }
Date startDateS2 = endDateS1; Date endDateS2 = Date.from(startDateS2.toInstant().plus(1, ChronoUnit.HOURS)); SaleTier s1 = new SaleTier( 1, "Development Tier 1", false ); SaleTier s2 = new SaleTier( 2, "Development Tier 2", saleTierService.saveTransactionless(s2); LOG.info("Development tiers successfully set-up."); LOG.info("Tier {}: startDate={} endDate={}", s1.getTierNo(), startDateS1, endDateS1); LOG.info("Tier {}: startDate={} endDate={}", s2.getTierNo(), startDateS2, endDateS2);
BigInteger tomicsFromTier1 = tiers.get(1).getTier().getRemainingTomics(); BigDecimal paymentToTier1 = monitorService.convertTomicsToUsd(tomicsFromTier1, tiers.get(1).getDiscount()); tiers.get(1).mustBeFull(); long dateShift = tiers.get(1).getTier().getEndDate().getTime() - blockTime.getTime(); tiers.get(1).newEndDateMustBe(blockTime); tiers.get(2).datesMustBeShiftedBy(dateShift); tiers.get(5).datesMustBeShiftedBy(dateShift); tiers.get(2).tomicsMaxMustBe(totalTomicsAmount() .subtract(tiers.get(0).getTier().getTomicsSold()) .subtract(tiers.get(1).getTomicsMax())); tiers.get(4).tomicsMaxMustBe(overallRemainingTomics); tiers.get(4).mustBeFull(); dateShift = tiers.get(4).getTier().getEndDate().getTime() - blockTime.getTime(); tiers.get(4).newEndDateMustBe(blockTime); tiers.get(5).datesMustBeShiftedBy(dateShift);
public void datesMustBeShiftedBy(long dateShift) { newStartDateMustBe(new Date(getTier().getStartDate().getTime() - dateShift)); newEndDateMustBe(new Date(getTier().getEndDate().getTime() - dateShift)); }
/** * @param tier The sale tier for which to get all subsequent tiers. * @return all tiers that have a tier number greater than the given sale tier. Ordered by * ascending tier number. */ public List<SaleTier> getAllSubsequentTiers(SaleTier tier) { return saleTierRepository.findTierByTierNoGreaterThanOrderByTierNoAsc(tier.getTierNo()); }
private TokenAllocationResult distributeTotalRemainingTokensToTier(SaleTier tier) { BigInteger totalRemainingTokens = getTotalRemainingTomics(); tier.setTomicsSold(tier.getTomicsSold().add(totalRemainingTokens)); saleTierRepository.save(tier); return new TokenAllocationResult(totalRemainingTokens, BigDecimal.ZERO); }
private TokenAllocationResult handleTotalMaxOverflow(SaleTier tier, BigInteger tomicsForTier) { TokenAllocationResult result = distributeTotalRemainingTokensToTier(tier); BigInteger totalMaxOverflow = tomicsForTier.subtract(result.getAllocatedTomics()); return result.addToOverflow(convertTomicsToUsd(totalMaxOverflow, tier.getDiscount())); }
@Test @Transactional(propagation = Propagation.NOT_SUPPORTED) public void testOverflowTotalTokenAmountInSecondTier() throws Throwable { final BigInteger tomicsOverflowOverTier1 = tomicsFactor(); final Date blockTime = Date.valueOf("1970-01-02"); final TestTier tt1 = new TestTier(1, "1970-01-01", "1970-01-03", new BigDecimal("0.25"), totalTomicsAmount().subtract(tomicsOverflowOverTier1), true, false); tt1.mustBeFull(); tt1.newEndDateMustBe(blockTime); final TestTier tt2 = new TestTier(2, "1970-01-03", "1970-01-05", new BigDecimal("0.10"), totalTomicsAmount(), true, false); tt2.tomicsSoldMustBe(tomicsOverflowOverTier1); tt2.datesMustBeShiftedBy(tt1.getTier().getEndDate().getTime() - blockTime.getTime()); // payment setup final BigInteger tomicsToTier2 = tomicsOverflowOverTier1.multiply(BigInteger.valueOf(2)); final BigDecimal paymentToTier1 = monitorService.convertTomicsToUsd(tt1.getTomicsMax(), tt1.getDiscount()); final BigDecimal paymentToTier2 = monitorService.convertTomicsToUsd(tomicsToTier2, tt2.getDiscount()); final BigDecimal payment = paymentToTier1.add(paymentToTier2); final BigDecimal overflow = paymentToTier2.divide(new BigDecimal(2), new MathContext(6, RoundingMode.HALF_EVEN)); // test PaymentLog log = createPaymentLog(payment, blockTime); log = monitorService.allocateTokens(log); if (log.getEligibleForRefund() == null) fail(); assertEquals(0, log.getAllocatedTomics().compareTo(totalTomicsAmount())); assertEquals(0, log.getEligibleForRefund().getUsdAmount().round(new MathContext(6, RoundingMode.HALF_EVEN)).compareTo(overflow)); tt1.assertTier(); tt2.assertTier(); }
@Test public void testFindAllOrderByBeginDate() { tierRepository.save(createTier(1, "2018-01-01", "2018-01-10", new BigDecimal("0.5"), 1000L)); tierRepository.save(createTier(2, "2018-01-11", "2018-01-20", new BigDecimal("0.2"), 2000L)); tierRepository.save(createTier(3, "2018-01-21", "2018-01-30", new BigDecimal("0.1"), 3000L)); List<SaleTier> tiers = tierRepository.findAllByOrderByStartDateAsc(); ListIterator<SaleTier> it = tiers.listIterator(); SaleTier t1 = it.next(); while (it.hasNext()) { SaleTier t2 = it.next(); assertTrue(t1.getStartDate().before(t2.getStartDate())); t1 = t2; } }
public BigInteger getTomicsMax() { return getTier().getTomicsMax(); }
@Test public void testGetStatusAtDate() { // Tier with preset max token amount and not all sold SaleTier t = createTier(0, "1970-01-03", "1970-01-06", BigDecimal.ZERO, BigInteger.TEN, true, false); Date date = Date.valueOf("1970-01-02"); assertEquals(t.getStatusAtDate(date), StatusType.INCOMING); date = Date.valueOf("1970-01-03"); assertEquals(t.getStatusAtDate(date), StatusType.ACTIVE); date = Date.valueOf("1970-01-04"); assertEquals(t.getStatusAtDate(date), StatusType.ACTIVE); date = Date.valueOf("1970-01-06"); assertEquals(t.getStatusAtDate(date), StatusType.CLOSED); date = Date.valueOf("1970-01-07"); assertEquals(t.getStatusAtDate(date), StatusType.CLOSED); t.setTomicsSold(BigInteger.TEN); date = Date.valueOf("1970-01-04"); assertEquals(t.getStatusAtDate(date), StatusType.CLOSED); t.setTomicsSold(BigInteger.TEN.add(BigInteger.TEN)); date = Date.valueOf("1970-01-04"); assertEquals(t.getStatusAtDate(date), StatusType.CLOSED); // Tier without max token amount set (i.e. set to zero) t = createTier(0, "1970-01-03", "1970-01-06", BigDecimal.ZERO, BigInteger.ZERO, false, true); t.setTomicsSold(BigInteger.TEN); date = Date.valueOf("1970-01-02"); assertEquals(t.getStatusAtDate(date), StatusType.INCOMING); date = Date.valueOf("1970-01-03"); assertEquals(t.getStatusAtDate(date), StatusType.CLOSED); }
public void assertTier() { LOG.info("Asserting Tier {}", tierNo); if (newStartDate != null) assertEquals(0, newStartDate.compareTo(getTier().getStartDate())); else assertEquals(0, initialStartDate.compareTo(getTier().getStartDate())); if (newEndDate != null) assertEquals(0, newEndDate.compareTo(getTier().getEndDate())); else assertEquals(0, initialEndDate.compareTo(getTier().getEndDate())); LOG.info("{} tomics sold on Tier {}", getTier().getTomicsSold(), tierNo); LOG.info("{} tomics should have been sold on Tier {}", tomicsSold, tierNo); if (tomicsSold != null) assertEquals(0, tomicsSold.compareTo(getTier().getTomicsSold())); if (mustBeFull) assertTrue(getTier().isFull()); else assertFalse(getTier().isFull()); if (newTomicsMax != null) assertEquals(0, newTomicsMax.compareTo(getTier().getTomicsMax())); else assertEquals(0, initialTomicsMax.compareTo(getTier().getTomicsMax())); }
/** * @param tier The sale tier for which to get the subsequent tier. * @return the sale tier that has tier number equals to the tier number of the given tier * plus one. */ public Optional<SaleTier> getSubsequentTier(SaleTier tier) { return saleTierRepository.findByTierNo(tier.getTierNo() + 1); }
public BigDecimal getDiscount() { return getTier().getDiscount(); }
tt2.datesMustBeShiftedBy(tt1.getTier().getEndDate().getTime() - blockTime.getTime()); tt2.tomicsSoldMustBe(tomicsToSellFromTier2);