@Override public InvoiceItem insertInvoiceItemAdjustment(final UUID accountId, final UUID invoiceId, final UUID invoiceItemId, final LocalDate effectiveDate, final CallContext context) throws InvoiceApiException { return insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, null, null, context); }
@Override public InvoiceItem insertCredit(final UUID accountId, final BigDecimal amount, final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException { return insertCreditForInvoice(accountId, null, amount, effectiveDate, currency, context); }
@Override public InvoiceItem insertExternalCharge(final UUID accountId, final BigDecimal amount, @Nullable final String description, final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException { return insertExternalChargeForInvoiceAndBundle(accountId, null, null, amount, description, effectiveDate, currency, context); }
@Test(groups = "fast") public void testInvoiceCreationEvent() throws Exception { final InvoiceCreationInternalEvent e = new DefaultInvoiceCreationEvent(UUID.randomUUID(), UUID.randomUUID(), new BigDecimal(12.0), Currency.USD, 1L, 2L, null); final String json = mapper.writeValueAsString(e); final Object obj = mapper.readValue(json, DefaultInvoiceCreationEvent.class); Assert.assertEquals(obj, e); }
@Test(groups = "fast") public void testEmptyInvoiceEvent() throws Exception { final NullInvoiceInternalEvent e = new DefaultNullInvoiceEvent(UUID.randomUUID(), new LocalDate(), 1L, 2L, null); final String json = mapper.writeValueAsString(e); final Object obj = mapper.readValue(json, DefaultNullInvoiceEvent.class); Assert.assertEquals(obj, e); } }
private void notifyBusOfInvoiceAdjustment(final UUID invoiceId, final UUID accountId, final InternalCallContext context) { try { eventBus.post(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken())); } catch (EventBusException e) { log.warn("Failed to post adjustment event for invoice " + invoiceId, e); } } }
@Test(groups = "slow") public void testPostExternalChargeForBundleOnNewInvoice() throws Exception { // Initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); // Post an external charge final BigDecimal externalChargeAmount = BigDecimal.TEN; final UUID bundleId = UUID.randomUUID(); final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalChargeForBundle(accountId, bundleId, externalChargeAmount, UUID.randomUUID().toString(), clock.getUTCToday(), accountCurrency, callContext); verifyExternalChargeOnNewInvoice(accountBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem); }
@Override public void tagInvoiceAsNotWrittenOff(final UUID invoiceId, final CallContext context) throws TagApiException, InvoiceApiException { // Note: the tagApi is audited final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, context); tagApi.removeTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.getId(), internalContext); // Retrieve the invoice for the account id final Invoice invoice = new DefaultInvoice(dao.getById(invoiceId, internalContext)); // This is for overdue notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), internalContext); }
@Test(groups = "slow") public void testPostExternalChargeOnExistingInvoice() throws Exception { // Verify the initial invoice balance final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1); // Verify the initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(accountBalance, invoiceBalance); // Post an external charge final BigDecimal externalChargeAmount = BigDecimal.TEN; final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalChargeForInvoice(accountId, invoiceId, externalChargeAmount, UUID.randomUUID().toString(), clock.getUTCToday(), accountCurrency, callContext); verifyExternalChargeOnExistingInvoice(invoiceBalance, null, externalChargeAmount, externalChargeInvoiceItem); }
@Test(groups = "slow") public void testAdjustPartialInvoice() throws Exception { // Verify the initial invoice balance final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1); // Verify the initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(accountBalance, invoiceBalance); // Adjust the invoice for a fraction of the balance final BigDecimal creditAmount = invoiceBalance.divide(BigDecimal.TEN); final InvoiceItem creditInvoiceItem = invoiceUserApi.insertCreditForInvoice(accountId, invoiceId, creditAmount, clock.getUTCToday(), accountCurrency, callContext); Assert.assertEquals(creditInvoiceItem.getInvoiceId(), invoiceId); Assert.assertEquals(creditInvoiceItem.getInvoiceItemType(), InvoiceItemType.CREDIT_ADJ); Assert.assertEquals(creditInvoiceItem.getAccountId(), accountId); Assert.assertEquals(creditInvoiceItem.getAmount(), creditAmount.negate()); Assert.assertEquals(creditInvoiceItem.getCurrency(), accountCurrency); Assert.assertNull(creditInvoiceItem.getLinkedItemId()); // Verify the adjusted invoice balance final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); verifyAdjustedInvoiceBalance(invoiceBalance, creditAmount, adjustedInvoiceBalance); // Verify the adjusted account balance final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance); }
@Override public String getInvoiceAsHTML(final UUID invoiceId, final TenantContext context) throws AccountApiException, IOException, InvoiceApiException { final Invoice invoice = getInvoice(invoiceId, context); if (invoice == null) { throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId); } final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(invoiceId, ObjectType.INVOICE, context); final Account account = accountUserApi.getAccountById(invoice.getAccountId(), internalContext); // Check if this account has the MANUAL_PAY system tag boolean manualPay = false; final List<Tag> accountTags = tagApi.getTags(account.getId(), ObjectType.ACCOUNT, internalContext); for (final Tag tag : accountTags) { if (ControlTagType.MANUAL_PAY.getId().equals(tag.getTagDefinitionId())) { manualPay = true; break; } } return generator.generateInvoice(account, invoice, manualPay); }
private void createInvoiceAndPaymentCreationEvents(final Account account) { final DefaultInvoice invoice = new DefaultInvoice(account.getId(), clock.getUTCToday(), clock.getUTCToday(), ACCOUNT_CURRENCY); final FixedPriceInvoiceItem invoiceItem = new FixedPriceInvoiceItem(invoice.getId(), account.getId(), bundleId, subscriptionId, "somePlan", "somePhase", clock.getUTCToday(), INVOICE_AMOUNT, ACCOUNT_CURRENCY); invoice.addInvoiceItem(invoiceItem); Mockito.when(invoiceInternalApi.getInvoicesByAccountId(Mockito.eq(account.getId()), Mockito.<InternalCallContext>any())).thenReturn(ImmutableList.<Invoice>of(invoice)); // It doesn't really matter what the events contain - the listener will go back to the db invoiceCreationNotification = new DefaultInvoiceCreationEvent(invoice.getId(), account.getId(), INVOICE_AMOUNT, ACCOUNT_CURRENCY, null, 1L, 1L); paymentInfoNotification = new DefaultPaymentInfoEvent(account.getId(), invoice.getId(), null, INVOICE_AMOUNT, -1, PaymentStatus.UNKNOWN, null, clock.getUTCNow(), 1L, 1L); }
@Override public InvoiceItem insertExternalChargeForBundle(final UUID accountId, final UUID bundleId, final BigDecimal amount, @Nullable final String description, final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException { return insertExternalChargeForInvoiceAndBundle(accountId, null, bundleId, amount, description, effectiveDate, currency, context); }
private void notifyBusOfInvoiceAdjustment(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final UUID invoiceId, final UUID accountId, final UUID userToken, final InternalCallContext context) { try { eventBus.postFromTransaction(new DefaultInvoiceAdjustmentEvent(invoiceId, accountId, context.getAccountRecordId(), context.getTenantRecordId(), userToken), entitySqlDaoWrapperFactory.getSqlDao()); } catch (EventBusException e) { log.warn("Failed to post adjustment event for invoice " + invoiceId, e); } } }
@Test(groups = "slow") public void testPostExternalChargeOnNewInvoice() throws Exception { // Initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); // Post an external charge final BigDecimal externalChargeAmount = BigDecimal.TEN; final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalCharge(accountId, externalChargeAmount, UUID.randomUUID().toString(), clock.getUTCToday(), accountCurrency, callContext); verifyExternalChargeOnNewInvoice(accountBalance, null, externalChargeAmount, externalChargeInvoiceItem); }
@Override public void tagInvoiceAsWrittenOff(final UUID invoiceId, final CallContext context) throws TagApiException, InvoiceApiException { // Note: the tagApi is audited final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, context); tagApi.addTag(invoiceId, ObjectType.INVOICE, ControlTagType.WRITTEN_OFF.getId(), internalContext); // Retrieve the invoice for the account id final Invoice invoice = new DefaultInvoice(dao.getById(invoiceId, internalContext)); // This is for overdue notifyBusOfInvoiceAdjustment(invoiceId, invoice.getAccountId(), internalContext); }
@Test(groups = "slow") public void testPostExternalChargeForBundleOnExistingInvoice() throws Exception { // Verify the initial invoice balance final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1); // Verify the initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(accountBalance, invoiceBalance); // Post an external charge final BigDecimal externalChargeAmount = BigDecimal.TEN; final UUID bundleId = UUID.randomUUID(); final InvoiceItem externalChargeInvoiceItem = invoiceUserApi.insertExternalChargeForInvoiceAndBundle(accountId, invoiceId, bundleId, externalChargeAmount, UUID.randomUUID().toString(), clock.getUTCToday(), accountCurrency, callContext); verifyExternalChargeOnExistingInvoice(invoiceBalance, bundleId, externalChargeAmount, externalChargeInvoiceItem); }
@Test(groups = "slow") public void testAdjustFullInvoiceItem() throws Exception { final InvoiceItem invoiceItem = invoiceUserApi.getInvoice(invoiceId, callContext).getInvoiceItems().get(0); // Verify we picked a non zero item Assert.assertEquals(invoiceItem.getAmount().compareTo(BigDecimal.ZERO), 1); // Verify the initial invoice balance final BigDecimal invoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); Assert.assertEquals(invoiceBalance.compareTo(BigDecimal.ZERO), 1); // Verify the initial account balance final BigDecimal accountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(accountBalance, invoiceBalance); // Adjust the invoice for the full amount final InvoiceItem adjInvoiceItem = invoiceUserApi.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItem.getId(), clock.getUTCToday(), callContext); Assert.assertEquals(adjInvoiceItem.getInvoiceId(), invoiceId); Assert.assertEquals(adjInvoiceItem.getInvoiceItemType(), InvoiceItemType.ITEM_ADJ); Assert.assertEquals(adjInvoiceItem.getAccountId(), accountId); Assert.assertEquals(adjInvoiceItem.getAmount(), invoiceItem.getAmount().negate()); Assert.assertEquals(adjInvoiceItem.getCurrency(), accountCurrency); Assert.assertEquals(adjInvoiceItem.getLinkedItemId(), invoiceItem.getId()); // Verify the adjusted invoice balance final BigDecimal adjustedInvoiceBalance = invoiceUserApi.getInvoice(invoiceId, callContext).getBalance(); verifyAdjustedInvoiceBalance(invoiceBalance, invoiceItem.getAmount(), adjustedInvoiceBalance); // Verify the adjusted account balance final BigDecimal adjustedAccountBalance = invoiceUserApi.getAccountBalance(accountId, callContext); Assert.assertEquals(adjustedAccountBalance, adjustedInvoiceBalance); }
@Override public void createInvoice(final InvoiceModelDao invoice, final List<InvoiceItemModelDao> invoiceItems, final List<InvoicePaymentModelDao> invoicePayments, final boolean isRealInvoice, final Map<UUID, DateTime> callbackDateTimePerSubscriptions, final InternalCallContext context) { synchronized (monitor) { invoices.put(invoice.getId(), invoice); for (final InvoiceItemModelDao invoiceItemModelDao : invoiceItems) { items.put(invoiceItemModelDao.getId(), invoiceItemModelDao); } for (final InvoicePaymentModelDao paymentModelDao : invoicePayments) { payments.put(paymentModelDao.getId(), paymentModelDao); } accountRecordIds.put(invoice.getAccountId(), context.getAccountRecordId()); } try { eventBus.post(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(), InvoiceModelDaoHelper.getBalance(invoice), invoice.getCurrency(), context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken())); } catch (PersistentBus.EventBusException ex) { throw new RuntimeException(ex); } }
@Override public InvoiceItem insertExternalChargeForInvoice(final UUID accountId, final UUID invoiceId, final BigDecimal amount, @Nullable final String description, final LocalDate effectiveDate, final Currency currency, final CallContext context) throws InvoiceApiException { return insertExternalChargeForInvoiceAndBundle(accountId, invoiceId, null, amount, description, effectiveDate, currency, context); }