Details
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index aff1bd1..c21c935 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -194,7 +194,7 @@ public enum ErrorCode {
INVOICE_NOTHING_TO_DO(4007, "No invoice to generate for account %s and date %s"),
INVOICE_NO_SUCH_CREDIT(4008, "Credit item for id %s does not exist"),
CREDIT_AMOUNT_INVALID(4009, "Credit amount %s should be strictly positive"),
- INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID(4010, "Invoice adjustment amount %s should be strictly positive"),
+ INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE(4010, "Invoice adjustment amount %s should be strictly positive"),
INVOICE_ITEM_NOT_FOUND(4011, "No invoice item could be found for id %s."),
INVOICE_INVALID_FOR_INVOICE_ITEM_ADJUSTMENT(4012, "Invoice item %s doesn't belong to invoice %s."),
INVOICE_NO_SUCH_EXTERNAL_CHARGE(4014, "External charge item for id %s does not exist"),
@@ -203,6 +203,7 @@ public enum ErrorCode {
INVOICE_ALREADY_EXISTS(4017, "The invoice already exists %s"),
INVOICE_NUMBER_NOT_FOUND(4018, "No invoice could be found for number %s."),
INVOICE_INVALID_NUMBER(4019, "Invalid invoice number %s."),
+ INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID(4020, "Invoice adjustment amount %s should be lower than %s"),
/*
*
@@ -216,7 +217,7 @@ public enum ErrorCode {
CHARGE_BACK_DOES_NOT_EXIST(4004, "Could not find chargeback for id %s."),
INVOICE_PAYMENT_BY_ATTEMPT_NOT_FOUND(4905, "No invoice payment could be found for paymentAttempt id %s."),
REFUND_AMOUNT_TOO_HIGH(4906, "Tried to refund %s of a %s payment."),
- REFUND_AMOUNT_DONT_MATCH_ITEMS_TO_ADJUST(4907, "You can't specify a refund amount of %s that doesn't match the invoice items amount of %s."),
+ REFUND_AMOUNT_DONT_MATCH_ITEMS_TO_ADJUST(4907, "You can't specify a refund amount of %s that is less than the sum of the invoice items amount of %s."),
/*
*
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index e0fcc2f..fa3e420 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -270,7 +270,7 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
final LocalDate effectiveDate, @Nullable final BigDecimal amount,
@Nullable final Currency currency, final CallContext context) throws InvoiceApiException {
if (amount != null && amount.compareTo(BigDecimal.ZERO) <= 0) {
- throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID, amount);
+ throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE, amount);
}
final InvoiceItemModelDao adjustment = dao.insertInvoiceItemAdjustment(accountId, invoiceId, invoiceItemId, effectiveDate, amount, currency, internalCallContextFactory.createInternalCallContext(accountId, context));
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
index 641d4f3..61797f6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
@@ -78,18 +78,28 @@ public class InvoiceDaoHelper {
throw new IllegalStateException("Invoice shouldn't be null for id " + invoiceId);
}
+ //
+ // If we have an item amount, we 'd like to use it, but we need to check first that it is lesser or equal than maximum allowed
+ //If, not we compute maximum value we can adjust per item
for (final UUID invoiceItemId : invoiceItemIdsWithNullAmounts.keySet()) {
- final BigDecimal adjAmount = Objects.firstNonNull(invoiceItemIdsWithNullAmounts.get(invoiceItemId),
- getInvoiceItemAmountForId(invoice, invoiceItemId));
- final BigDecimal adjAmountRemainingAfterRepair = computeItemAdjustmentAmount(invoiceItemId, adjAmount, invoice.getInvoiceItems());
- if (adjAmountRemainingAfterRepair.compareTo(BigDecimal.ZERO) > 0) {
- invoiceItemIdsWithAmountsBuilder.put(invoiceItemId, adjAmountRemainingAfterRepair);
+ final BigDecimal originalItemAmount = getInvoiceItemAmountForId(invoice, invoiceItemId);
+ final BigDecimal maxAdjAmount = computeItemAdjustmentAmount(invoiceItemId, originalItemAmount, invoice.getInvoiceItems());
+
+ final BigDecimal proposedItemAmount = invoiceItemIdsWithNullAmounts.get(invoiceItemId);
+ if (proposedItemAmount != null && proposedItemAmount.compareTo(maxAdjAmount) > 0) {
+ throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID, proposedItemAmount, maxAdjAmount);
+ }
+
+ final BigDecimal itemAmountToAdjust = Objects.firstNonNull(proposedItemAmount, maxAdjAmount);
+ if (itemAmountToAdjust.compareTo(BigDecimal.ZERO) > 0) {
+ invoiceItemIdsWithAmountsBuilder.put(invoiceItemId, itemAmountToAdjust);
}
}
return invoiceItemIdsWithAmountsBuilder.build();
}
+
/**
* @param invoiceItem item we are adjusting
* @param requestedPositiveAmountToAdjust
@@ -124,9 +134,9 @@ public class InvoiceDaoHelper {
throw new InvoiceApiException(ErrorCode.INVOICE_ITEM_NOT_FOUND, invoiceItemId);
}
- public BigDecimal computePositiveRefundAmount(final InvoicePaymentModelDao payment, final BigDecimal requestedAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) throws InvoiceApiException {
+ public BigDecimal computePositiveRefundAmount(final InvoicePaymentModelDao payment, final BigDecimal requestedRefundAmount, final Map<UUID, BigDecimal> invoiceItemIdsWithAmounts) throws InvoiceApiException {
final BigDecimal maxRefundAmount = payment.getAmount() == null ? BigDecimal.ZERO : payment.getAmount();
- final BigDecimal requestedPositiveAmount = requestedAmount == null ? maxRefundAmount : requestedAmount;
+ final BigDecimal requestedPositiveAmount = requestedRefundAmount == null ? maxRefundAmount : requestedRefundAmount;
// This check is good but not enough, we need to also take into account previous refunds
// (But that should have been checked in the payment call already)
if (requestedPositiveAmount.compareTo(maxRefundAmount) > 0) {
@@ -140,7 +150,7 @@ public class InvoiceDaoHelper {
}
// Sanity check: if some items were specified, then the sum should be equal to specified refund amount, if specified
- if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && requestedPositiveAmount.compareTo(amountFromItems) != 0) {
+ if (amountFromItems.compareTo(BigDecimal.ZERO) != 0 && requestedPositiveAmount.compareTo(amountFromItems) < 0) {
throw new InvoiceApiException(ErrorCode.REFUND_AMOUNT_DONT_MATCH_ITEMS_TO_ADJUST, requestedPositiveAmount, amountFromItems);
}
return requestedPositiveAmount;
diff --git a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index 364a4b6..5145f8f 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -332,7 +332,7 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
BigDecimal.TEN.negate(), accountCurrency, callContext);
Assert.fail("Should not have been able to adjust an item with a negative amount");
} catch (InvoiceApiException e) {
- Assert.assertEquals(e.getCode(), ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID.getCode());
+ Assert.assertEquals(e.getCode(), ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE.getCode());
}
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
index a612e00..977f427 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
@@ -60,7 +60,7 @@ public class InvoiceApiExceptionMapper extends ExceptionMapperBase implements Ex
return buildBadRequestResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.CREDIT_AMOUNT_INVALID.getCode()) {
return buildBadRequestResponse(exception, uriInfo);
- } else if (exception.getCode() == ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_INVALID.getCode()) {
+ } else if (exception.getCode() == ErrorCode.INVOICE_ITEM_ADJUSTMENT_AMOUNT_SHOULD_BE_POSITIVE.getCode()) {
return buildBadRequestResponse(exception, uriInfo);
} else if (exception.getCode() == ErrorCode.INVOICE_NO_SUCH_EXTERNAL_CHARGE.getCode()) {
return buildBadRequestResponse(exception, uriInfo);
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index 106499a..f1e918a 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -16,6 +16,23 @@
package com.ning.billing.payment.provider;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.payment.api.PaymentMethodKVInfo;
+import com.ning.billing.payment.api.PaymentMethodPlugin;
+import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentMethodInfoPlugin;
+import com.ning.billing.payment.plugin.api.PaymentPluginApi;
+import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
+import com.ning.billing.payment.plugin.api.PaymentPluginStatus;
+import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
+import com.ning.billing.payment.plugin.api.RefundPluginStatus;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.clock.Clock;
import com.google.inject.Inject;
@@ -25,14 +42,61 @@ import com.google.inject.Inject;
* <p/>
* The implementation is very similar to the no-op plugin, which it extends. This can potentially be an issue
* if Killbill is processing a lot of external payments as they are all kept in memory.
- * TODO: do something about it
*/
-public class ExternalPaymentProviderPlugin extends DefaultNoOpPaymentProviderPlugin {
+public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
public static final String PLUGIN_NAME = "__EXTERNAL_PAYMENT__";
+ private final Clock clock;
+
@Inject
public ExternalPaymentProviderPlugin(final Clock clock) {
- super(clock);
+ this.clock = clock;
+ }
+
+ @Override
+ public PaymentInfoPlugin processPayment(final UUID kbAccountId, final UUID kbPaymentId, final UUID kbPaymentMethodId, final BigDecimal amount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
+ return new DefaultNoOpPaymentInfoPlugin(amount, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+ }
+
+ @Override
+ public PaymentInfoPlugin getPaymentInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+ return new DefaultNoOpPaymentInfoPlugin(BigDecimal.ZERO, clock.getUTCNow(), clock.getUTCNow(), PaymentPluginStatus.PROCESSED, null);
+ }
+
+ @Override
+ public RefundInfoPlugin processRefund(final UUID kbAccountId, final UUID kbPaymentId, final BigDecimal refundAmount, final Currency currency, final CallContext context) throws PaymentPluginApiException {
+ return new DefaultNoOpRefundInfoPlugin(BigDecimal.ZERO, clock.getUTCNow(), clock.getUTCNow(), RefundPluginStatus.PROCESSED, null);
+ }
+
+ @Override
+ public List<RefundInfoPlugin> getRefundInfo(final UUID kbAccountId, final UUID kbPaymentId, final TenantContext context) throws PaymentPluginApiException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void addPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final PaymentMethodPlugin paymentMethodProps, final boolean setDefault, final CallContext context) throws PaymentPluginApiException {
+ }
+
+ @Override
+ public void deletePaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+ }
+
+ @Override
+ public PaymentMethodPlugin getPaymentMethodDetail(final UUID kbAccountId, final UUID kbPaymentMethodId, final TenantContext context) throws PaymentPluginApiException {
+ return new DefaultNoOpPaymentMethodPlugin("unknow", false, Collections.<PaymentMethodKVInfo>emptyList());
+ }
+
+ @Override
+ public void setDefaultPaymentMethod(final UUID kbAccountId, final UUID kbPaymentMethodId, final CallContext context) throws PaymentPluginApiException {
+ }
+
+ @Override
+ public List<PaymentMethodInfoPlugin> getPaymentMethods(final UUID kbAccountId, final boolean refreshFromGateway, final CallContext context) throws PaymentPluginApiException {
+ return null;
+ }
+
+ @Override
+ public void resetPaymentMethods(final UUID kbAccountId, final List<PaymentMethodInfoPlugin> paymentMethods) throws PaymentPluginApiException {
}
}