Details
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithInvoicePlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithInvoicePlugin.java
index db8c664..e1e9460 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithInvoicePlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithInvoicePlugin.java
@@ -190,6 +190,48 @@ public class TestWithInvoicePlugin extends TestIntegrationBase {
assertEquals(externalCharge.getId(), pluginInvoiceItemId);
// verify the ID is the one passed by the plugin #887
assertEquals(externalCharge.getLinkedItemId(), pluginLinkedItemId);
+
+
+ // On next invoice we will update the amount and the description of the previously inserted EXTERNAL_CHARGE item
+ testInvoicePluginApi.additionalInvoiceItem = new ExternalChargeInvoiceItem(pluginInvoiceItemId,
+ clock.getUTCNow(),
+ invoices.get(0).getId(),
+ account.getId(),
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ "Update Description",
+ clock.getUTCToday(),
+ null,
+ BigDecimal.ONE,
+ null,
+ Currency.USD,
+ pluginLinkedItemId,
+ null);
+
+ busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE, NextEvent.INVOICE_PAYMENT, NextEvent.PAYMENT);
+ clock.addDays(30);
+ assertListenerStatus();
+ invoiceChecker.checkInvoice(account.getId(), 2, callContext,
+ new ExpectedInvoiceItemCheck(new LocalDate(2012, 5, 1), new LocalDate(2012, 6, 1), InvoiceItemType.RECURRING, new BigDecimal("29.95")));
+
+
+ final List<Invoice> invoices2 = invoiceUserApi.getInvoicesByAccount(account.getId(), false, false, callContext);
+ final List<InvoiceItem> invoiceItems2 = invoices2.get(0).getInvoiceItems();
+ final InvoiceItem externalCharge2 = Iterables.tryFind(invoiceItems2, new Predicate<InvoiceItem>() {
+ @Override
+ public boolean apply(final InvoiceItem input) {
+ return input.getInvoiceItemType() == InvoiceItemType.EXTERNAL_CHARGE;
+ }
+ }).orNull();
+ assertNotNull(externalCharge2);
+
+ assertEquals(externalCharge2.getAmount().compareTo(BigDecimal.ONE), 0);
}
@Test(groups = "slow")
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 5cd89af..f928ba5 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
@@ -84,6 +84,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -357,7 +358,12 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
(invoiceItemModelDao.getAmount().compareTo(existingInvoiceItem.getAmount()) != 0)) {
checkAgainstExistingInvoiceItemState(existingInvoiceItem, invoiceItemModelDao);
- transInvoiceItemSqlDao.updateAmount(invoiceItemModelDao.getId().toString(), invoiceItemModelDao.getAmount(), context);
+ // We allow plugins to override these 3 fields
+ final BigDecimal updatedAmount = invoiceItemModelDao.getAmount() != null ? invoiceItemModelDao.getAmount() : existingInvoiceItem.getAmount();
+ final String updatedDescription = invoiceItemModelDao.getDescription() != null ? invoiceItemModelDao.getDescription() : existingInvoiceItem.getDescription();
+ final String updatedItemDetails = invoiceItemModelDao.getItemDetails() != null ? invoiceItemModelDao.getItemDetails() : existingInvoiceItem.getItemDetails();
+
+ transInvoiceItemSqlDao.updateItemFields(invoiceItemModelDao.getId().toString(), updatedAmount, updatedDescription, updatedItemDetails, context);
}
}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
index 7a864d9..cb7c841 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -27,9 +27,9 @@ import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.util.audit.ChangeType;
import org.killbill.billing.util.entity.dao.Audited;
import org.killbill.billing.util.entity.dao.EntitySqlDao;
+import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.killbill.commons.jdbi.template.KillBillSqlDaoStringTemplate;
import org.skife.jdbi.v2.sqlobject.Bind;
-import org.killbill.commons.jdbi.binder.SmartBindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -39,7 +39,6 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
@SqlQuery
List<InvoiceItemModelDao> getInvoiceItemsByInvoice(@Bind("invoiceId") final String invoiceId,
@SmartBindBean final InternalTenantContext context);
-
@SqlQuery
List<InvoiceItemModelDao> getInvoiceItemsBySubscription(@Bind("subscriptionId") final String subscriptionId,
@SmartBindBean final InternalTenantContext context);
@@ -55,6 +54,14 @@ public interface InvoiceItemSqlDao extends EntitySqlDao<InvoiceItemModelDao, Inv
@Bind("amount")BigDecimal amount,
@SmartBindBean final InternalCallContext context);
+ @SqlUpdate
+ @Audited(ChangeType.UPDATE)
+ void updateItemFields(@Bind("id") String invoiceItemId,
+ @Bind("amount") BigDecimal amount,
+ @Bind("description") String description,
+ @Bind("itemDetails") String itemDetails,
+ @SmartBindBean final InternalCallContext context);
+
@SqlQuery
List<InvoiceItemModelDao> getInvoiceItemsByParentInvoice(@Bind("parentInvoiceId") final String parentInvoiceId,
@SmartBindBean final InternalTenantContext context);
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 72c9c9e..b4fe07f 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -83,6 +83,17 @@ updateAmount() ::= <<
<AND_CHECK_TENANT("")>;
>>
+
+updateItemFields() ::= <<
+ UPDATE <tableName()>
+ SET amount = coalesce(:amount, amount),
+ description = coalesce(:description, description),
+ item_details = coalesce(:itemDetails, item_details)
+ WHERE id = :id
+ <AND_CHECK_TENANT("")>;
+>>
+
+
getInvoiceItemsByParentInvoice() ::= <<
SELECT <allTableFields(("items."))>
FROM <tableName()> items
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemSqlDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemSqlDao.java
new file mode 100644
index 0000000..e39f7df
--- /dev/null
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceItemSqlDao.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.invoice.dao;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.catalog.api.Currency;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.util.UtilTestSuiteWithEmbeddedDB;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestInvoiceItemSqlDao extends UtilTestSuiteWithEmbeddedDB {
+
+ @Test(groups = "slow")
+ public void testUpdateItemFields() throws Exception {
+ final InvoiceItemSqlDao dao = dbi.onDemand(InvoiceItemSqlDao.class);
+
+ final UUID invoiceItemId = UUID.randomUUID();
+
+ dao.create(new InvoiceItemModelDao(invoiceItemId, null, InvoiceItemType.FIXED, UUID.randomUUID(), UUID.randomUUID(), null, null, null, "description",
+ null, null, null, null, new LocalDate(), null, BigDecimal.ONE, null, Currency.USD, null), internalCallContext);
+
+ // Update all fields
+ dao.updateItemFields(invoiceItemId.toString(), new BigDecimal("2.00"), "new description", "new items", internalCallContext);
+
+ InvoiceItemModelDao UpdatedItem = dao.getById(invoiceItemId.toString(), internalCallContext);
+ Assert.assertTrue(UpdatedItem.getAmount().compareTo(new BigDecimal("2.00")) == 0);
+ Assert.assertEquals(UpdatedItem.getDescription(), "new description");
+ Assert.assertEquals(UpdatedItem.getItemDetails(), "new items");
+
+ // Update just amount
+ dao.updateItemFields(invoiceItemId.toString(), new BigDecimal("3.00"), null, null, internalCallContext);
+ UpdatedItem = dao.getById(invoiceItemId.toString(), internalCallContext);
+ Assert.assertTrue(UpdatedItem.getAmount().compareTo(new BigDecimal("3.00")) == 0);
+ Assert.assertEquals(UpdatedItem.getDescription(), "new description");
+ Assert.assertEquals(UpdatedItem.getItemDetails(), "new items");
+
+
+ // Update just description
+ dao.updateItemFields(invoiceItemId.toString(), null, "newer description", null, internalCallContext);
+ UpdatedItem = dao.getById(invoiceItemId.toString(), internalCallContext);
+ Assert.assertTrue(UpdatedItem.getAmount().compareTo(new BigDecimal("3.00")) == 0);
+ Assert.assertEquals(UpdatedItem.getDescription(), "newer description");
+ Assert.assertEquals(UpdatedItem.getItemDetails(), "new items");
+
+
+
+ }
+}