use of org.killbill.billing.invoice.model.ItemAdjInvoiceItem in project killbill by killbill.
the class TestFixedAndRecurringInvoiceItemGenerator method testItemPartiallyRepairedAndPartiallyAdjustedV2.
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/664")
public void testItemPartiallyRepairedAndPartiallyAdjustedV2() throws InvoiceApiException {
final LocalDate startDate = new LocalDate("2016-01-01");
final BillingEventSet events = new MockBillingEventSet();
final BigDecimal amount = BigDecimal.TEN;
final MockInternationalPrice price = new MockInternationalPrice(new DefaultPrice(amount, account.getCurrency()));
final Plan plan = new MockPlan("my-plan");
final PlanPhase planPhase = new MockPlanPhase(price, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
final BillingEvent event1 = invoiceUtil.createMockBillingEvent(account, subscription, startDate.toDateTimeAtStartOfDay(), plan, planPhase, null, amount, account.getCurrency(), BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 1L, SubscriptionBaseTransitionType.CREATE);
events.add(event1);
final BillingEvent event2 = invoiceUtil.createMockBillingEvent(account, subscription, startDate.plusDays(1).toDateTimeAtStartOfDay(), plan, planPhase, null, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 2L, SubscriptionBaseTransitionType.CANCEL);
events.add(event2);
// Subscription incorrectly invoiced
final List<Invoice> existingInvoices = new LinkedList<Invoice>();
final Invoice invoice = new DefaultInvoice(account.getId(), clock.getUTCToday(), startDate, account.getCurrency());
invoice.addInvoiceItem(new RecurringInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), subscription.getBundleId(), subscription.getId(), null, plan.getName(), planPhase.getName(), null, startDate, startDate.plusMonths(1), amount, amount, account.getCurrency()));
// Item adjust the remaining
invoice.addInvoiceItem(new ItemAdjInvoiceItem(invoice.getInvoiceItems().get(0), startDate, BigDecimal.ONE.negate(), account.getCurrency()));
// Repaired by the system (the system would have consumed all the remaining amount available)
invoice.addInvoiceItem(new RepairAdjInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), startDate.plusDays(1), startDate.plusMonths(1), new BigDecimal("9").negate(), account.getCurrency(), invoice.getInvoiceItems().get(0).getId()));
existingInvoices.add(invoice);
final List<InvoiceItem> generatedItems = fixedAndRecurringInvoiceItemGenerator.generateItems(account, UUID.randomUUID(), events, new AccountInvoices(null, null, existingInvoices), startDate, account.getCurrency(), new HashMap<UUID, SubscriptionFutureNotificationDates>(), null, internalCallContext).getItems();
assertTrue(generatedItems.isEmpty());
}
use of org.killbill.billing.invoice.model.ItemAdjInvoiceItem in project killbill by killbill.
the class TestFixedAndRecurringInvoiceItemGenerator method testInvalidAdjustment.
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/664")
public void testInvalidAdjustment() throws InvoiceApiException {
final LocalDate startDate = new LocalDate("2016-01-01");
final BillingEventSet events = new MockBillingEventSet();
final List<Invoice> existingInvoices = new LinkedList<Invoice>();
final Invoice invoice = new DefaultInvoice(account.getId(), clock.getUTCToday(), startDate, account.getCurrency());
// Dangling adjustment
invoice.addInvoiceItem(new ItemAdjInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), startDate, "Dangling adjustment", BigDecimal.ONE.negate(), account.getCurrency(), UUID.randomUUID(), null));
existingInvoices.add(invoice);
try {
final List<InvoiceItem> generatedItems = fixedAndRecurringInvoiceItemGenerator.generateItems(account, UUID.randomUUID(), events, new AccountInvoices(null, null, existingInvoices), startDate, account.getCurrency(), new HashMap<UUID, SubscriptionFutureNotificationDates>(), null, internalCallContext).getItems();
fail();
} catch (final InvoiceApiException e) {
assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
assertTrue(e.getCause().getMessage().startsWith("Missing subscription id"));
}
}
use of org.killbill.billing.invoice.model.ItemAdjInvoiceItem in project killbill by killbill.
the class TestFixedAndRecurringInvoiceItemGenerator method testItemFullyRepairedAndFullyAdjusted.
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/664")
public void testItemFullyRepairedAndFullyAdjusted() throws InvoiceApiException {
final LocalDate startDate = new LocalDate("2016-01-01");
final BillingEventSet events = new MockBillingEventSet();
final BigDecimal amount = BigDecimal.TEN;
// Subscription incorrectly invoiced
final List<Invoice> existingInvoices = new LinkedList<Invoice>();
final Invoice invoice = new DefaultInvoice(account.getId(), clock.getUTCToday(), startDate, account.getCurrency());
invoice.addInvoiceItem(new RecurringInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), subscription.getBundleId(), subscription.getId(), null, "my-plan", "my-plan-monthly", null, startDate, startDate.plusMonths(1), amount, amount, account.getCurrency()));
// Repaired by the system
invoice.addInvoiceItem(new RepairAdjInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), startDate, startDate.plusMonths(1), amount.negate(), account.getCurrency(), invoice.getInvoiceItems().get(0).getId()));
invoice.addInvoiceItem(new ItemAdjInvoiceItem(invoice.getInvoiceItems().get(0), startDate, // Note! The amount will matter
BigDecimal.ONE.negate(), account.getCurrency()));
existingInvoices.add(invoice);
try {
fixedAndRecurringInvoiceItemGenerator.generateItems(account, UUID.randomUUID(), events, new AccountInvoices(null, null, existingInvoices), startDate, account.getCurrency(), new HashMap<UUID, SubscriptionFutureNotificationDates>(), null, internalCallContext).getItems();
fail();
} catch (final InvoiceApiException e) {
assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
assertTrue(e.getCause().getMessage().startsWith("Too many repairs"));
}
}
use of org.killbill.billing.invoice.model.ItemAdjInvoiceItem in project killbill by killbill.
the class TestFixedAndRecurringInvoiceItemGenerator method testItemPartiallyRepairedAndInvalidAdjustment.
@Test(groups = "fast", description = "https://github.com/killbill/killbill/issues/664")
public void testItemPartiallyRepairedAndInvalidAdjustment() throws InvoiceApiException {
final LocalDate startDate = new LocalDate("2016-01-01");
final BillingEventSet events = new MockBillingEventSet();
final BigDecimal amount = BigDecimal.TEN;
final MockInternationalPrice price = new MockInternationalPrice(new DefaultPrice(amount, account.getCurrency()));
final Plan plan = new MockPlan("my-plan");
final PlanPhase planPhase = new MockPlanPhase(price, null, BillingPeriod.MONTHLY, PhaseType.EVERGREEN);
final BillingEvent event1 = invoiceUtil.createMockBillingEvent(account, subscription, startDate.toDateTimeAtStartOfDay(), plan, planPhase, null, amount, account.getCurrency(), BillingPeriod.MONTHLY, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 1L, SubscriptionBaseTransitionType.CREATE);
events.add(event1);
final BillingEvent event2 = invoiceUtil.createMockBillingEvent(account, subscription, startDate.plusDays(1).toDateTimeAtStartOfDay(), plan, planPhase, null, null, Currency.USD, BillingPeriod.NO_BILLING_PERIOD, 1, BillingMode.IN_ADVANCE, "Billing Event Desc", 2L, SubscriptionBaseTransitionType.CANCEL);
events.add(event2);
// Subscription incorrectly invoiced
final List<Invoice> existingInvoices = new LinkedList<Invoice>();
final Invoice invoice = new DefaultInvoice(account.getId(), clock.getUTCToday(), startDate, account.getCurrency());
invoice.addInvoiceItem(new RecurringInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), subscription.getBundleId(), subscription.getId(), null, plan.getName(), planPhase.getName(), null, startDate, startDate.plusMonths(1), amount, amount, account.getCurrency()));
// Repaired by the system
invoice.addInvoiceItem(new RepairAdjInvoiceItem(UUID.randomUUID(), startDate.toDateTimeAtStartOfDay(), invoice.getId(), account.getId(), startDate.plusDays(1), startDate.plusMonths(1), new BigDecimal("9.68").negate(), account.getCurrency(), invoice.getInvoiceItems().get(0).getId()));
// Invalid adjustment (too much)
invoice.addInvoiceItem(new ItemAdjInvoiceItem(invoice.getInvoiceItems().get(0), startDate, new BigDecimal("9.68").negate(), account.getCurrency()));
existingInvoices.add(invoice);
try {
fixedAndRecurringInvoiceItemGenerator.generateItems(account, UUID.randomUUID(), events, new AccountInvoices(null, null, existingInvoices), startDate, account.getCurrency(), new HashMap<UUID, SubscriptionFutureNotificationDates>(), null, internalCallContext).getItems();
fail();
} catch (final InvoiceApiException e) {
assertEquals(e.getCode(), ErrorCode.UNEXPECTED_ERROR.getCode());
assertTrue(e.getCause().getMessage().startsWith("Too many repairs"));
}
}
use of org.killbill.billing.invoice.model.ItemAdjInvoiceItem in project killbill by killbill.
the class InvoiceDispatcher method processParentInvoiceForAdjustmentsWithLock.
public void processParentInvoiceForAdjustmentsWithLock(final Account account, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException {
final InvoiceModelDao childInvoiceModelDao = invoiceDao.getById(childInvoiceId, context);
final InvoiceModelDao parentInvoiceModelDao = childInvoiceModelDao.getParentInvoice();
if (parentInvoiceModelDao == null) {
throw new InvoiceApiException(ErrorCode.INVOICE_MISSING_PARENT_INVOICE, childInvoiceModelDao.getId());
} else if (InvoiceModelDaoHelper.getRawBalanceForRegularInvoice(parentInvoiceModelDao).compareTo(BigDecimal.ZERO) == 0) {
// ignore item adjustments for paid invoices.
return;
}
final Long parentAccountRecordId = internalCallContextFactory.getRecordIdFromObject(account.getParentAccountId(), ObjectType.ACCOUNT, buildTenantContext(context));
final InternalCallContext parentContext = internalCallContextFactory.createInternalCallContext(parentAccountRecordId, context);
final String description = "Adjustment for account ".concat(account.getExternalKey());
// find PARENT_SUMMARY invoice item for this child account
final InvoiceItemModelDao parentSummaryInvoiceItem = Iterables.find(parentInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
@Override
public boolean apply(final InvoiceItemModelDao input) {
return input.getType().equals(InvoiceItemType.PARENT_SUMMARY) && input.getChildAccountId().equals(childInvoiceModelDao.getAccountId());
}
});
final Iterable<InvoiceItemModelDao> childAdjustments = Iterables.filter(childInvoiceModelDao.getInvoiceItems(), new Predicate<InvoiceItemModelDao>() {
@Override
public boolean apply(final InvoiceItemModelDao input) {
return input.getType().equals(InvoiceItemType.ITEM_ADJ);
}
});
// childAdjustments can be empty if event was a result of a CBA_ADJ
if (Iterables.isEmpty(childAdjustments)) {
return;
}
// find last ITEM_ADJ invoice added in child invoice
final InvoiceItemModelDao lastChildInvoiceItemAdjustment = Collections.max(Lists.newArrayList(childAdjustments), new Comparator<InvoiceItemModelDao>() {
@Override
public int compare(final InvoiceItemModelDao o1, final InvoiceItemModelDao o2) {
return o1.getCreatedDate().compareTo(o2.getCreatedDate());
}
});
final BigDecimal childInvoiceAdjustmentAmount = lastChildInvoiceItemAdjustment.getAmount();
if (parentInvoiceModelDao.getStatus().equals(InvoiceStatus.COMMITTED)) {
final ItemAdjInvoiceItem adj = new ItemAdjInvoiceItem(UUIDs.randomUUID(), lastChildInvoiceItemAdjustment.getCreatedDate(), parentSummaryInvoiceItem.getInvoiceId(), parentSummaryInvoiceItem.getAccountId(), lastChildInvoiceItemAdjustment.getStartDate(), description, childInvoiceAdjustmentAmount, parentInvoiceModelDao.getCurrency(), parentSummaryInvoiceItem.getId(), null);
parentInvoiceModelDao.addInvoiceItem(new InvoiceItemModelDao(adj));
invoiceDao.createInvoices(ImmutableList.<InvoiceModelDao>of(parentInvoiceModelDao), null, ImmutableSet.of(), parentContext);
return;
}
// update item amount
final BigDecimal newParentInvoiceItemAmount = childInvoiceAdjustmentAmount.add(parentSummaryInvoiceItem.getAmount());
invoiceDao.updateInvoiceItemAmount(parentSummaryInvoiceItem.getId(), newParentInvoiceItemAmount, parentContext);
}
Aggregations