Details
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
index 265a608..21bc8c5 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/InvoiceResource.java
@@ -16,9 +16,6 @@
package com.ning.billing.jaxrs.resources;
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-import static javax.ws.rs.core.MediaType.TEXT_HTML;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -46,11 +43,11 @@ import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.inject.Inject;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.InvoiceItem;
@@ -74,6 +71,12 @@ import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.clock.Clock;
import com.ning.billing.util.dao.ObjectType;
+import com.google.common.base.Objects;
+import com.google.inject.Inject;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.TEXT_HTML;
+
@Path(JaxrsResource.INVOICES_PATH)
public class InvoiceResource extends JaxRsResourceBase {
@@ -222,6 +225,68 @@ public class InvoiceResource extends JaxRsResourceBase {
return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", adjustmentItem.getInvoiceId());
}
+ @POST
+ @Produces(APPLICATION_JSON)
+ @Consumes(APPLICATION_JSON)
+ @Path(CHARGES)
+ public Response createExternalCharge(final InvoiceItemJsonSimple externalChargeJson,
+ @QueryParam(QUERY_REQUESTED_DT) final String requestedDateTimeString,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
+ final Account account = accountApi.getAccountById(UUID.fromString(externalChargeJson.getAccountId()));
+ final CallContext callContext = context.createContext(createdBy, reason, comment);
+
+ // Get the effective date of the external charge, in the account timezone
+ final LocalDate requestedDate;
+ if (requestedDateTimeString == null) {
+ requestedDate = clock.getUTCToday();
+ } else {
+ final DateTime requestedDateTime = DATE_TIME_FORMATTER.parseDateTime(requestedDateTimeString);
+ requestedDate = requestedDateTime.toDateTime(account.getTimeZone()).toLocalDate();
+ }
+
+ final Currency currency = Objects.firstNonNull(externalChargeJson.getCurrency(), account.getCurrency());
+ final InvoiceItem externalCharge = invoiceApi.insertExternalCharge(account.getId(), externalChargeJson.getAmount(),
+ externalChargeJson.getDescription(), requestedDate,
+ currency, callContext);
+
+ return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", externalCharge.getInvoiceId(), uriInfo.getBaseUri().toString());
+ }
+
+ @POST
+ @Produces(APPLICATION_JSON)
+ @Consumes(APPLICATION_JSON)
+ @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CHARGES)
+ public Response createExternalChargeForInvoice(final InvoiceItemJsonSimple externalChargeJson,
+ @PathParam("invoiceId") final String invoiceIdString,
+ @QueryParam(QUERY_REQUESTED_DT) final String requestedDateTimeString,
+ @HeaderParam(HDR_CREATED_BY) final String createdBy,
+ @HeaderParam(HDR_REASON) final String reason,
+ @HeaderParam(HDR_COMMENT) final String comment,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws AccountApiException, InvoiceApiException {
+ final Account account = accountApi.getAccountById(UUID.fromString(externalChargeJson.getAccountId()));
+ final CallContext callContext = context.createContext(createdBy, reason, comment);
+
+ // Get the effective date of the external charge, in the account timezone
+ final LocalDate requestedDate;
+ if (requestedDateTimeString == null) {
+ requestedDate = clock.getUTCToday();
+ } else {
+ final DateTime requestedDateTime = DATE_TIME_FORMATTER.parseDateTime(requestedDateTimeString);
+ requestedDate = requestedDateTime.toDateTime(account.getTimeZone()).toLocalDate();
+ }
+
+ final UUID invoiceId = UUID.fromString(invoiceIdString);
+ final Currency currency = Objects.firstNonNull(externalChargeJson.getCurrency(), account.getCurrency());
+ final InvoiceItem externalCharge = invoiceApi.insertExternalChargeForInvoice(account.getId(), invoiceId,
+ externalChargeJson.getAmount(), externalChargeJson.getDescription(),
+ requestedDate, currency, callContext);
+
+ return uriBuilder.buildResponse(InvoiceResource.class, "getInvoice", externalCharge.getInvoiceId(), uriInfo.getBaseUri().toString());
+ }
+
@GET
@Path("/{invoiceId:" + UUID_PATTERN + "}/" + PAYMENTS)
@Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 567a72b..790ade4 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -13,16 +13,17 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
+
package com.ning.billing.jaxrs.resources;
public interface JaxrsResource {
+
public static final String API_PREFIX = "";
public static final String API_VERSION = "/1.0";
public static final String API_POSTFIX = "/kb";
public static final String PREFIX = API_PREFIX + API_VERSION + API_POSTFIX;
-
public static final String TIMELINE = "timeline";
/*
@@ -81,6 +82,9 @@ public interface JaxrsResource {
public static final String INVOICES = "invoices";
public static final String INVOICES_PATH = PREFIX + "/" + INVOICES;
+ public static final String CHARGES = "charges";
+ public static final String CHARGES_PATH = PREFIX + "/" + INVOICES + "/" + CHARGES;
+
public static final String PAYMENTS = "payments";
public static final String PAYMENTS_PATH = PREFIX + "/" + PAYMENTS;
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
index 675159a..66cfb7c 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestInvoice.java
@@ -186,4 +186,44 @@ public class TestInvoice extends TestJaxrsBase {
final BigDecimal adjustedInvoiceBalance = invoice.getBalance().add(adjustedAmount.negate().setScale(2, RoundingMode.HALF_UP));
assertEquals(adjustedInvoice.getBalance().compareTo(adjustedInvoiceBalance), 0);
}
+
+ @Test(groups = "slow")
+ public void testExternalChargeOnNewInvoice() throws Exception {
+ final AccountJson accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+ // Get the invoices
+ assertEquals(getInvoicesForAccount(accountJson.getAccountId()).size(), 2);
+
+ // Post an external charge
+ final BigDecimal chargeAmount = BigDecimal.TEN;
+ final InvoiceJsonWithItems invoiceWithItems = createExternalCharge(accountJson.getAccountId(), chargeAmount, null, null);
+ assertEquals(invoiceWithItems.getBalance().compareTo(chargeAmount), 0);
+ assertEquals(invoiceWithItems.getItems().size(), 1);
+
+ // Verify the total number of invoices
+ assertEquals(getInvoicesForAccount(accountJson.getAccountId()).size(), 3);
+ }
+
+ @Test(groups = "slow")
+ public void testExternalChargeOnExistingInvoice() throws Exception {
+ final AccountJson accountJson = createAccountNoPMBundleAndSubscriptionAndWaitForFirstInvoice();
+
+ // Get the invoices
+ final List<InvoiceJsonWithItems> invoices = getInvoicesWithItemsForAccount(accountJson.getAccountId());
+ // 2 invoices but look for the non zero dollar one
+ assertEquals(invoices.size(), 2);
+ final String invoiceId = invoices.get(1).getInvoiceId();
+ final BigDecimal originalInvoiceAmount = invoices.get(1).getAmount();
+ final int originalNumberOfItemsForInvoice = invoices.get(1).getItems().size();
+
+ // Post an external charge
+ final BigDecimal chargeAmount = BigDecimal.TEN;
+ final InvoiceJsonWithItems invoiceWithItems = createExternalChargeForInvoice(accountJson.getAccountId(), invoiceId, chargeAmount, null, null);
+ assertEquals(invoiceWithItems.getItems().size(), originalNumberOfItemsForInvoice + 1);
+
+ // Verify the new invoice balance
+ final InvoiceJsonSimple adjustedInvoice = getInvoice(invoiceId);
+ final BigDecimal adjustedInvoiceBalance = originalInvoiceAmount.add(chargeAmount.setScale(2, RoundingMode.HALF_UP));
+ assertEquals(adjustedInvoice.getBalance().compareTo(adjustedInvoiceBalance), 0);
+ }
}
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
index 6708ddf..9585672 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestJaxrsBase.java
@@ -565,6 +565,46 @@ public class TestJaxrsBase extends ServerTestSuiteWithEmbeddedDB {
Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
}
+ protected InvoiceJsonWithItems createExternalCharge(final String accountId, final BigDecimal amount, @Nullable final Currency currency,
+ @Nullable final DateTime requestedDate) throws Exception {
+ return doCreateExternalCharge(accountId, null, amount, currency, requestedDate, JaxrsResource.CHARGES_PATH);
+ }
+
+ protected InvoiceJsonWithItems createExternalChargeForInvoice(final String accountId, final String invoiceId, final BigDecimal amount,
+ @Nullable final Currency currency, @Nullable final DateTime requestedDate) throws Exception {
+ final String uri = JaxrsResource.INVOICES_PATH + "/" + invoiceId + "/" + JaxrsResource.CHARGES;
+ return doCreateExternalCharge(accountId, invoiceId, amount, currency, requestedDate, uri);
+ }
+
+ private InvoiceJsonWithItems doCreateExternalCharge(final String accountId, @Nullable final String invoiceId, @Nullable final BigDecimal amount,
+ @Nullable final Currency currency, final DateTime requestedDate, final String uri) throws IOException {
+ final Map<String, String> queryParams = new HashMap<String, String>();
+ if (requestedDate != null) {
+ queryParams.put(JaxrsResource.QUERY_REQUESTED_DT, requestedDate.toDateTimeISO().toString());
+ }
+
+ final InvoiceItemJsonSimple externalCharge = new InvoiceItemJsonSimple(null, invoiceId, accountId, null, null, null, null,
+ null, null, null, amount, currency, null);
+ final String externalChargeJson = mapper.writeValueAsString(externalCharge);
+ final Response response = doPost(uri, externalChargeJson, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
+ Assert.assertEquals(response.getStatusCode(), Status.CREATED.getStatusCode());
+
+ final String location = response.getHeader("Location");
+ Assert.assertNotNull(location);
+
+ final Map<String, String> queryParamsForInvoice = new HashMap<String, String>();
+ queryParamsForInvoice.put(JaxrsResource.QUERY_ACCOUNT_ID, accountId);
+ queryParamsForInvoice.put(JaxrsResource.QUERY_INVOICE_WITH_ITEMS, "true");
+ final Response invoiceResponse = doGetWithUrl(location, queryParamsForInvoice, DEFAULT_HTTP_TIMEOUT_SEC);
+ assertEquals(invoiceResponse.getStatusCode(), Status.OK.getStatusCode());
+
+ final String invoicesBaseJson = invoiceResponse.getResponseBody();
+ final InvoiceJsonWithItems invoice = mapper.readValue(invoicesBaseJson, new TypeReference<InvoiceJsonWithItems>(){});
+ assertNotNull(invoice);
+
+ return invoice;
+ }
+
//
// PAYMENT UTILITIES
//