Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
index 304a0d4..36b0ee7 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/util/InvoiceChecker.java
@@ -66,7 +66,7 @@ public class InvoiceChecker {
}
public Invoice checkInvoice(final UUID accountId, final int invoiceOrderingNumber, final CallContext context, final List<ExpectedInvoiceItemCheck> expected) throws InvoiceApiException {
- final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, context);
+ final List<Invoice> invoices = invoiceUserApi.getInvoicesByAccount(accountId, false, false, context);
//Assert.assertEquals(invoices.size(), invoiceOrderingNumber);
final Invoice invoice = invoices.get(invoiceOrderingNumber - 1);
checkInvoice(invoice.getId(), context, expected);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
index d3b422a..43d1eb9 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -47,9 +47,11 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceApiHelper;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.api.InvoicePayment;
import org.killbill.billing.invoice.api.InvoiceStatus;
import org.killbill.billing.invoice.api.InvoiceUserApi;
import org.killbill.billing.invoice.api.WithAccountLock;
+import org.killbill.billing.invoice.calculator.InvoiceCalculatorUtils;
import org.killbill.billing.invoice.dao.InvoiceDao;
import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
import org.killbill.billing.invoice.dao.InvoiceModelDao;
@@ -122,20 +124,20 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
- public List<Invoice> getInvoicesByAccount(final UUID accountId, boolean includesMigrated, final TenantContext context) {
+ public List<Invoice> getInvoicesByAccount(final UUID accountId, boolean includesMigrated, final boolean includeVoidedInvoices, final TenantContext context) {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, context);
final List<InvoiceModelDao> invoicesByAccount = includesMigrated ?
- dao.getAllInvoicesByAccount(internalTenantContext) :
- dao.getInvoicesByAccount(internalTenantContext);
+ dao.getAllInvoicesByAccount(includeVoidedInvoices, internalTenantContext) :
+ dao.getInvoicesByAccount(includeVoidedInvoices, internalTenantContext);
return fromInvoiceModelDao(invoicesByAccount, getCatalogSafelyForPrettyNames(internalTenantContext));
}
@Override
- public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final TenantContext context) {
+ public List<Invoice> getInvoicesByAccount(final UUID accountId, final LocalDate fromDate, final boolean includeVoidedInvoices, final TenantContext context) {
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(accountId, context);
- final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(fromDate, internalTenantContext);
+ final List<InvoiceModelDao> invoicesByAccount = dao.getInvoicesByAccount(includeVoidedInvoices, fromDate, internalTenantContext);
return fromInvoiceModelDao(invoicesByAccount, getCatalogSafelyForPrettyNames(internalTenantContext));
}
@@ -616,4 +618,26 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
return null;
}
}
+
+ @Override
+ public void voidInvoice(final UUID invoiceId, final CallContext context) throws InvoiceApiException {
+ final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(invoiceId, ObjectType.INVOICE, context);
+ Invoice invoice = getInvoice(invoiceId, context);
+
+ if (invoice.getNumberOfPayments() > 0) {
+ canInvoiceBeVoided(invoice);
+ }
+
+ dao.changeInvoiceStatus(invoiceId, InvoiceStatus.VOID, internalCallContext);
+ }
+
+ private void canInvoiceBeVoided(final Invoice invoice) throws InvoiceApiException {
+ final List<InvoicePayment> invoicePayments = invoice.getPayments();
+ final BigDecimal amountPaid = InvoiceCalculatorUtils.computeInvoiceAmountPaid(invoice.getCurrency(), invoicePayments)
+ .add(InvoiceCalculatorUtils.computeInvoiceAmountRefunded(invoice.getCurrency(), invoicePayments));
+
+ if (amountPaid.compareTo(BigDecimal.ZERO) != 0) {
+ throw new InvoiceApiException(ErrorCode.CAN_NOT_VOID_INVOICE_THAT_IS_PAID, invoice.getId().toString());
+ }
+ }
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
index 162c204..a2a4cd6 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/CBADao.java
@@ -154,7 +154,7 @@ public class CBADao {
// PERF: Computing the invoice balance is difficult to do in the DB, so we effectively need to retrieve all invoices on the account and filter the unpaid ones in memory.
// This should be infrequent though because of the account CBA check above.
- final List<InvoiceModelDao> allInvoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+ final List<InvoiceModelDao> allInvoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(false, invoicesTags, entitySqlDaoWrapperFactory, context);
final List<InvoiceModelDao> unpaidInvoices = invoiceDaoHelper.getUnpaidInvoicesByAccountFromTransaction(allInvoices, null);
// We order the same os BillingStateCalculator-- should really share the comparator
final List<InvoiceModelDao> orderedUnpaidInvoices = Ordering.from(new Comparator<InvoiceModelDao>() {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
index 1c50c2b..8720a38 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/DefaultInvoiceDao.java
@@ -156,7 +156,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public List<InvoiceModelDao> getInvoicesByAccount(final InternalTenantContext context) {
+ public List<InvoiceModelDao> getInvoicesByAccount(final Boolean includeVoidedInvoices, final InternalTenantContext context) {
final List<Tag> invoicesTags = getInvoicesTags(context);
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@@ -168,7 +168,8 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
new Predicate<InvoiceModelDao>() {
@Override
public boolean apply(final InvoiceModelDao invoice) {
- return !invoice.isMigrated();
+ return !invoice.isMigrated() &&
+ (includeVoidedInvoices ? true : !InvoiceStatus.VOID.equals(invoice.getStatus()));
}
})));
invoiceDaoHelper.populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
@@ -179,26 +180,26 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public List<InvoiceModelDao> getAllInvoicesByAccount(final InternalTenantContext context) {
+ public List<InvoiceModelDao> getAllInvoicesByAccount(final Boolean includeVoidedInvoices, final InternalTenantContext context) {
final List<Tag> invoicesTags = getInvoicesTags(context);
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
- return invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+ return invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(includeVoidedInvoices, invoicesTags, entitySqlDaoWrapperFactory, context);
}
});
}
@Override
- public List<InvoiceModelDao> getInvoicesByAccount(final LocalDate fromDate, final InternalTenantContext context) {
+ public List<InvoiceModelDao> getInvoicesByAccount(final Boolean includeVoidedInvoices, final LocalDate fromDate, final InternalTenantContext context) {
final List<Tag> invoicesTags = getInvoicesTags(context);
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
@Override
public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
- final List<InvoiceModelDao> invoices = getAllNonMigratedInvoicesByAccountAfterDate(invoiceDao, fromDate, context);
+ final List<InvoiceModelDao> invoices = getAllNonMigratedInvoicesByAccountAfterDate(includeVoidedInvoices, invoiceDao, fromDate, context);
invoiceDaoHelper.populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
@@ -206,12 +207,13 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
});
}
- private List<InvoiceModelDao> getAllNonMigratedInvoicesByAccountAfterDate(final InvoiceSqlDao invoiceSqlDao, final LocalDate fromDate, final InternalTenantContext context) {
+ private List<InvoiceModelDao> getAllNonMigratedInvoicesByAccountAfterDate(final Boolean includeVoidedInvoices, final InvoiceSqlDao invoiceSqlDao, final LocalDate fromDate, final InternalTenantContext context) {
return ImmutableList.<InvoiceModelDao>copyOf(INVOICE_MODEL_DAO_ORDERING.sortedCopy(Iterables.<InvoiceModelDao>filter(invoiceSqlDao.getByAccountRecordId(context),
new Predicate<InvoiceModelDao>() {
@Override
public boolean apply(final InvoiceModelDao invoice) {
- return !invoice.isMigrated() && invoice.getTargetDate().compareTo(fromDate) >= 0;
+ return !invoice.isMigrated() && invoice.getTargetDate().compareTo(fromDate) >= 0 &&
+ (includeVoidedInvoices ? true : !InvoiceStatus.VOID.equals(invoice.getStatus()));
}
})));
}
@@ -455,11 +457,11 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
BigDecimal cba = BigDecimal.ZERO;
BigDecimal accountBalance = BigDecimal.ZERO;
- final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+ final List<InvoiceModelDao> invoices = invoiceDaoHelper.getAllInvoicesByAccountFromTransaction(false, invoicesTags, entitySqlDaoWrapperFactory, context);
for (final InvoiceModelDao cur : invoices) {
- // Skip DRAFT invoices
- if (cur.getStatus().equals(InvoiceStatus.DRAFT)) {
+ // Skip DRAFT OR VOID invoices
+ if (cur.getStatus().equals(InvoiceStatus.DRAFT) || cur.getStatus().equals(InvoiceStatus.VOID)) {
continue;
}
@@ -467,6 +469,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
cur.getParentInvoice() != null &&
(cur.getParentInvoice().isWrittenOff() ||
cur.getParentInvoice().getStatus() == InvoiceStatus.DRAFT ||
+ cur.getParentInvoice().getStatus() == InvoiceStatus.VOID ||
InvoiceModelDaoHelper.getRawBalanceForRegularInvoice(cur.getParentInvoice()).compareTo(BigDecimal.ZERO) == 0);
@@ -930,7 +933,7 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
if (accountCBA.compareTo(cbaItem.getAmount().negate()) < 0) {
throw new IllegalStateException("The account balance can't be lower than the amount adjusted");
}
- final List<InvoiceModelDao> invoicesFollowing = getAllNonMigratedInvoicesByAccountAfterDate(transactional, invoice.getInvoiceDate(), context);
+ final List<InvoiceModelDao> invoicesFollowing = getAllNonMigratedInvoicesByAccountAfterDate(false, transactional, invoice.getInvoiceDate(), context);
invoiceDaoHelper.populateChildren(invoicesFollowing, invoicesTags, entitySqlDaoWrapperFactory, context);
// The remaining amount to adjust (i.e. the amount of credits used on following invoices)
@@ -1103,10 +1106,11 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
throw new InvoiceApiException(ErrorCode.INVOICE_NOT_FOUND, invoiceId);
}
- if (invoice.getStatus().equals(newStatus)) {
+ if (invoice.getStatus().equals(newStatus) || invoice.getStatus().equals(InvoiceStatus.VOID)) {
throw new InvoiceApiException(ErrorCode.INVOICE_INVALID_STATUS, newStatus, invoiceId, invoice.getStatus());
}
+
transactional.updateStatus(invoiceId.toString(), newStatus.toString(), context);
cbaDao.doCBAComplexityFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
index 377b150..205c55f 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDao.java
@@ -50,9 +50,9 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
InvoiceModelDao getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
- List<InvoiceModelDao> getInvoicesByAccount(InternalTenantContext context);
+ List<InvoiceModelDao> getInvoicesByAccount(final Boolean includeVoidedInvoices, InternalTenantContext context);
- List<InvoiceModelDao> getInvoicesByAccount(LocalDate fromDate, InternalTenantContext context);
+ List<InvoiceModelDao> getInvoicesByAccount(final Boolean includeVoidedInvoices, LocalDate fromDate, InternalTenantContext context);
List<InvoiceModelDao> getInvoicesBySubscription(UUID subscriptionId, InternalTenantContext context);
@@ -71,7 +71,7 @@ public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceA
List<InvoiceModelDao> getUnpaidInvoicesByAccountId(UUID accountId, @Nullable LocalDate upToDate, InternalTenantContext context);
// Include migrated invoices
- List<InvoiceModelDao> getAllInvoicesByAccount(InternalTenantContext context);
+ List<InvoiceModelDao> getAllInvoicesByAccount(final Boolean includeVoidedInvoices, InternalTenantContext context);
InvoicePaymentModelDao postChargeback(UUID paymentId, String chargebackTransactionExternalKey, BigDecimal amount, Currency currency, InternalCallContext context) throws InvoiceApiException;
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
index 3343234..0fd4823 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceDaoHelper.java
@@ -165,7 +165,7 @@ public class InvoiceDaoHelper {
}
public List<InvoiceModelDao> getUnpaidInvoicesByAccountFromTransaction(final UUID accountId, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final LocalDate upToDate, final InternalTenantContext context) {
- final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
+ final List<InvoiceModelDao> invoices = getAllInvoicesByAccountFromTransaction(false, invoicesTags, entitySqlDaoWrapperFactory, context);
log.debug("Found invoices={} for accountId={}", invoices, accountId);
return getUnpaidInvoicesByAccountFromTransaction(invoices, upToDate);
}
@@ -250,8 +250,14 @@ public class InvoiceDaoHelper {
}
}
- public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
- final List<InvoiceModelDao> invoices = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getByAccountRecordId(context);
+ public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final Boolean includeVoidedInvoices, final List<Tag> invoicesTags, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
+ final List<InvoiceModelDao> invoices = ImmutableList.<InvoiceModelDao>copyOf(Iterables.<InvoiceModelDao>filter(entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class).getByAccountRecordId(context),
+ new Predicate<InvoiceModelDao>() {
+ @Override
+ public boolean apply(final InvoiceModelDao invoice) {
+ return includeVoidedInvoices ? true : !InvoiceStatus.VOID.equals(invoice.getStatus());
+ }
+ }));
populateChildren(invoices, invoicesTags, entitySqlDaoWrapperFactory, context);
return invoices;
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
index c812bfe..ad28373 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/InvoiceDispatcher.java
@@ -333,7 +333,7 @@ public class InvoiceDispatcher {
// (Note that we can't return right away as we send a NullInvoice event)
final List<Invoice> existingInvoices = billingEvents.isAccountAutoInvoiceOff() ?
ImmutableList.<Invoice>of() :
- ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(context),
+ ImmutableList.<Invoice>copyOf(Collections2.transform(invoiceDao.getInvoicesByAccount(false, context),
new Function<InvoiceModelDao, Invoice>() {
@Override
public Invoice apply(final InvoiceModelDao input) {
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
index bc4ccb0..b56e6ac 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/model/DefaultInvoice.java
@@ -261,6 +261,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
if (isWrittenOff() ||
isMigrationInvoice() ||
getStatus() == InvoiceStatus.DRAFT ||
+ getStatus() == InvoiceStatus.VOID ||
hasZeroParentBalance()) {
return BigDecimal.ZERO;
} else {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index c65a637..29475d4 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -488,7 +488,7 @@ public class AccountResource extends JaxRsResourceBase {
final Callable<List<Invoice>> invoicesCallable = new Callable<List<Invoice>>() {
@Override
public List<Invoice> call() throws Exception {
- return invoiceApi.getInvoicesByAccount(accountId, false, tenantContext);
+ return invoiceApi.getInvoicesByAccount(accountId, false, false, tenantContext);
}
};
final Callable<List<InvoicePayment>> invoicePaymentsCallable = new Callable<List<InvoicePayment>>() {
@@ -699,6 +699,7 @@ public class AccountResource extends JaxRsResourceBase {
@QueryParam(QUERY_INVOICE_WITH_ITEMS) @DefaultValue("false") final boolean withItems,
@QueryParam(QUERY_WITH_MIGRATION_INVOICES) @DefaultValue("false") final boolean withMigrationInvoices,
@QueryParam(QUERY_UNPAID_INVOICES_ONLY) @DefaultValue("false") final boolean unpaidInvoicesOnly,
+ @QueryParam(QUERY_INCLUDE_VOIDED_INVOICES) @DefaultValue("false") final boolean includeVoidedInvoices,
@QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
@javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
@@ -710,7 +711,7 @@ public class AccountResource extends JaxRsResourceBase {
final List<Invoice> invoices = unpaidInvoicesOnly ?
new ArrayList<Invoice>(invoiceApi.getUnpaidInvoicesByAccountId(accountId, null, tenantContext)) :
- invoiceApi.getInvoicesByAccount(accountId, withMigrationInvoices, tenantContext);
+ invoiceApi.getInvoicesByAccount(accountId, withMigrationInvoices, includeVoidedInvoices, tenantContext);
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index f98623f..df9215f 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -1010,6 +1010,27 @@ public class InvoiceResource extends JaxRsResourceBase {
return Response.status(Response.Status.OK).build();
}
+ @TimedResource
+ @PUT
+ @Path("/{invoiceId:" + UUID_PATTERN + "}/" + VOID_INVOICE)
+ @Consumes(APPLICATION_JSON)
+ @Produces(APPLICATION_JSON)
+ @ApiOperation(value = "Perform the action of voiding an invoice")
+ @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid invoice id supplied"),
+ @ApiResponse(code = 404, message = "Invoice not found")})
+ public Response voidInvoice(@PathParam("invoiceId") final String invoiceIdString,
+ @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 HttpServletRequest request,
+ @javax.ws.rs.core.Context final UriInfo uriInfo) throws InvoiceApiException {
+
+ final CallContext callContext = context.createCallContextNoAccountId(createdBy, reason, comment, request);
+ final UUID invoiceId = UUID.fromString(invoiceIdString);
+ invoiceApi.voidInvoice(invoiceId, callContext);
+ return Response.status(Response.Status.OK).build();
+ }
+
@Override
protected ObjectType getObjectType() {
return ObjectType.INVOICE;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 8a27ee3..6264557 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -107,6 +107,7 @@ public interface JaxrsResource {
public static final String QUERY_INVOICE_WITH_ITEMS = "withItems";
public static final String QUERY_WITH_MIGRATION_INVOICES = "withMigrationInvoices";
public static final String QUERY_UNPAID_INVOICES_ONLY = "unpaidInvoicesOnly";
+ public static final String QUERY_INCLUDE_VOIDED_INVOICES = "includeVoidedInvoices";
public static final String QUERY_INVOICE_WITH_CHILDREN_ITEMS = "withChildrenItems";
public static final String QUERY_PAYMENT_EXTERNAL = "externalPayment";
@@ -287,6 +288,7 @@ public interface JaxrsResource {
public static final String INVOICE_TRANSLATION = "translation";
public static final String INVOICE_CATALOG_TRANSLATION = "catalogTranslation";
public static final String COMMIT_INVOICE = "commitInvoice";
+ public static final String VOID_INVOICE = "voidInvoice";
public static final String COMBO = "combo";
public static final String MIGRATION = "migration";