Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invoice: fix NPE in DefaultInvoiceDao #1233

Merged
merged 1 commit into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -530,28 +530,48 @@ public Void apply(@Nullable final Void dontcare) {
}, events);
}

protected Payment createPaymentAndCheckForCompletion(final Account account, final Invoice invoice, final BigDecimal amount, final Currency currency, final NextEvent... events) {
return doCallAndCheckForCompletion(new Function<Void, Payment>() {
protected Payment createPaymentAndCheckForCompletion(final Account account,
final Invoice invoice,
final BigDecimal amount,
final Currency currency,
final NextEvent... events) {
try {
return createPaymentAndCheckForCompletion(account,
invoice,
amount,
currency,
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
events);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
}

protected Payment createPaymentAndCheckForCompletion(final Account account,
final Invoice invoice,
final BigDecimal amount,
final Currency currency,
final String paymentExternalKey,
final String transactionExternalKey,
final NextEvent... events) throws PaymentApiException {
return doCallAndCheckForCompletionWithException(new FunctionWithException<Void, Payment, PaymentApiException>() {
@Override
public Payment apply(@Nullable final Void input) {
try {
final InvoicePayment invoicePayment = invoicePaymentApi.createPurchaseForInvoicePayment(account,
invoice.getId(),
account.getPaymentMethodId(),
null,
amount,
currency,
null,
UUID.randomUUID().toString(),
UUID.randomUUID().toString(),
ImmutableList.<PluginProperty>of(),
PAYMENT_OPTIONS,
callContext);
return paymentApi.getPayment(invoicePayment.getPaymentId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
} catch (final PaymentApiException e) {
fail(e.toString());
return null;
}
public Payment apply(@Nullable final Void input) throws PaymentApiException {
final InvoicePayment invoicePayment = invoicePaymentApi.createPurchaseForInvoicePayment(account,
invoice.getId(),
account.getPaymentMethodId(),
null,
amount,
currency,
null,
paymentExternalKey,
transactionExternalKey,
ImmutableList.<PluginProperty>of(),
PAYMENT_OPTIONS,
callContext);
return paymentApi.getPayment(invoicePayment.getPaymentId(), false, true, ImmutableList.<PluginProperty>of(), callContext);
}
}, events);
}
Expand Down Expand Up @@ -921,6 +941,22 @@ private void remove_account_Tag(final UUID id, final ControlTagType controlTagTy
}

private <T> T doCallAndCheckForCompletion(final Function<Void, T> f, final NextEvent... events) {
try {
return doCallAndCheckForCompletionWithException(new FunctionWithException<Void, T, RuntimeException>() {
@Override
public T apply(final Void input) throws RuntimeException {
return f.apply(input);
}
},
events);
} catch (final RuntimeException e) {
fail(e.getMessage());
return null;
}

}

private <T, E extends Throwable> T doCallAndCheckForCompletionWithException(final FunctionWithException<Void, T, E> f, final NextEvent... events) throws E {
final Joiner joiner = Joiner.on(", ");
log.debug(" ************ STARTING BUS HANDLER CHECK : {} ********************", joiner.join(events));

Expand All @@ -933,6 +969,11 @@ private <T> T doCallAndCheckForCompletion(final Function<Void, T> f, final NextE
return result;
}

public interface FunctionWithException<F, T, E extends Throwable> {

T apply(F input) throws E;
}

protected void recordUsageData(final UUID subscriptionId, final String trackingId, final String unitType, final LocalDate startDate, final Long amount, final CallContext context) throws UsageApiException {
final List<UsageRecord> usageRecords = new ArrayList<UsageRecord>();
usageRecords.add(new UsageRecord(startDate, amount));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,52 @@ public void testPartialPaymentByPaymentPlugin() throws Exception {
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(BigDecimal.ZERO), 0);
}

@Test(groups = "slow", description = "https://github.com/killbill/killbill/issues/1230")
public void testMultiplePartialPaymentsWithSameExternalKeys() throws Exception {
// 2012-05-01T00:03:42.000Z
clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));

final AccountData accountData = getAccountData(0);
final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
accountChecker.checkAccount(account.getId(), accountData, callContext);

final DefaultEntitlement baseEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", "Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.BLOCK, NextEvent.INVOICE);
invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 5, 1), callContext);

// Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay the initial invoice
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);

// 2012-05-31 => DAY 30 have to get out of trial {I0, P0}
addDaysAndCheckForCompletion(30, NextEvent.PHASE, NextEvent.INVOICE);

Invoice invoice2 = invoiceChecker.checkInvoice(account.getId(), 2, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 31), new LocalDate(2012, 6, 30), InvoiceItemType.RECURRING, new BigDecimal("249.95")));
invoiceChecker.checkChargedThroughDate(baseEntitlement.getId(), new LocalDate(2012, 6, 30), callContext);

// Invoice is not paid
Assert.assertEquals(paymentApi.getAccountPayments(account.getId(), false, false, ImmutableList.<PluginProperty>of(), callContext).size(), 0);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("249.95")), 0);
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);

// Trigger partial payment
final Payment payment1 = createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
paymentChecker.checkPayment(account.getId(), 1, callContext, new ExpectedPaymentCheck(new LocalDate(2012, 5, 31), BigDecimal.TEN, TransactionStatus.SUCCESS, invoice2.getId(), Currency.USD));
Assert.assertEquals(payment1.getTransactions().size(), 1);
Assert.assertEquals(payment1.getPurchasedAmount().compareTo(BigDecimal.TEN), 0);
Assert.assertEquals(payment1.getTransactions().get(0).getProcessedAmount().compareTo(BigDecimal.TEN), 0);
invoice2 = invoiceUserApi.getInvoice(invoice2.getId(), callContext);
Assert.assertEquals(invoice2.getBalance().compareTo(new BigDecimal("239.95")), 0);
Assert.assertEquals(invoiceUserApi.getAccountBalance(account.getId(), callContext).compareTo(invoice2.getBalance()), 0);

// Trigger another partial payment with same external keys
try {
createPaymentAndCheckForCompletion(account, invoice2, BigDecimal.TEN, account.getCurrency(), payment1.getExternalKey(), payment1.getTransactions().get(0).getExternalKey(), NextEvent.INVOICE_PAYMENT_ERROR);
Assert.fail("Shouldn't have been able to create a payment");
} catch (final PaymentApiException e) {
Assert.assertEquals(e.getCode(), (int) ErrorCode.PAYMENT_ACTIVE_TRANSACTION_KEY_EXISTS.getCode());
}
}

@Test(groups = "slow")
public void testPartialRefunds() throws Exception {
// 2012-05-01T00:03:42.000Z
Expand Down
Loading