killbill-memoizeit

invoice: See #444 Initial implementation for WRITTEN_OFF

12/9/2015 2:57:06 AM

Changes

Details

diff --git a/api/src/main/java/org/killbill/billing/tag/TagInternalApi.java b/api/src/main/java/org/killbill/billing/tag/TagInternalApi.java
index 7181a4f..90eda82 100644
--- a/api/src/main/java/org/killbill/billing/tag/TagInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/tag/TagInternalApi.java
@@ -40,6 +40,9 @@ public interface TagInternalApi {
      */
     public List<Tag> getTags(UUID objectId, ObjectType objectType, InternalTenantContext context);
 
+
+    public List<Tag> getTagsForAccountType(ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
+
     public void addTag(final UUID objectId, final ObjectType objectType, UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
 
     public void removeTag(final UUID objectId, final ObjectType objectType, final UUID tagDefinitionId, InternalCallContext context) throws TagApiException;
diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
new file mode 100644
index 0000000..f5d854c
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestIntegrationWithWrittenOffTag.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 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.beatrix.integration;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.killbill.billing.ObjectType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.Invoice;
+import org.killbill.billing.invoice.api.InvoiceUserApi;
+import org.killbill.billing.payment.api.Payment;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.TagApiException;
+import org.killbill.billing.util.api.TagDefinitionApiException;
+import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.tag.ControlTagType;
+import org.killbill.billing.util.tag.Tag;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+public class TestIntegrationWithWrittenOffTag extends TestIntegrationBase {
+
+    @Inject
+    private InvoiceUserApi invoiceApi;
+
+    @Inject
+    private TagUserApi tagApi;
+
+    private Account account;
+    private String productName;
+    private BillingPeriod term;
+
+    @Override
+    @BeforeMethod(groups = "slow")
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
+        account = createAccountWithNonOsgiPaymentMethod(getAccountData(25));
+        assertNotNull(account);
+        productName = "Shotgun";
+        term = BillingPeriod.MONTHLY;
+    }
+
+    @Test(groups = "slow")
+    public void testWithWrittenOffInvoice() throws Exception {
+        clock.setTime(new DateTime(2012, 5, 1, 0, 3, 42, 0));
+
+
+        // Put the account in AUTO_PAY_OFF to make sure payment system does not try to pay initial invoices
+        add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
+
+        // set next invoice to fail and create network
+        final DefaultEntitlement bpEntitlement = createBaseEntitlementAndCheckForCompletion(account.getId(), "externalKey", productName, ProductCategory.BASE, term, NextEvent.CREATE, NextEvent.INVOICE);
+        assertNotNull(bpEntitlement);
+
+        List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        assertEquals(invoices.size(), 1);
+
+        busHandler.pushExpectedEvents(NextEvent.PHASE, NextEvent.INVOICE);
+        clock.addDays(31);
+        assertListenerStatus();
+
+        invoices = invoiceApi.getInvoicesByAccount(account.getId(), callContext);
+        assertEquals(invoices.size(), 2);
+
+        // Tag non $0 invoice with WRITTEN_OFF and remove AUTO_PAY_OFF => System should still not pay anything
+        add_WRITTEN_OFF_Tag(invoices.get(1).getId(), ObjectType.INVOICE);
+        remove_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);
+        assertListenerStatus();
+
+        List<Payment> accountPayments = paymentApi.getAccountPayments(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
+        assertEquals(accountPayments.size(), 0);
+
+    }
+
+    private void add_WRITTEN_OFF_Tag(final UUID id, final ObjectType type) throws TagDefinitionApiException, TagApiException {
+        busHandler.pushExpectedEvent(NextEvent.TAG);
+        tagApi.addTag(id, type, ControlTagType.WRITTEN_OFF.getId(), callContext);
+        assertListenerStatus();
+        final List<Tag> tags = tagApi.getTagsForObject(id, type, false, callContext);
+        assertEquals(tags.size(), 1);
+    }
+
+}
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
index a4a7621..80472a9 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/api/svcs/DefaultInvoiceInternalApi.java
@@ -42,8 +42,6 @@ import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.api.WithAccountLock;
 import org.killbill.billing.invoice.dao.InvoiceDao;
-import org.killbill.billing.invoice.dao.InvoiceDaoHelper;
-import org.killbill.billing.invoice.dao.InvoiceItemModelDao;
 import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.dao.InvoicePaymentModelDao;
 import org.killbill.billing.invoice.model.DefaultInvoice;
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 00df900..a5cf5c4 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
@@ -21,6 +21,8 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.UUID;
 
+import javax.inject.Inject;
+
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -34,8 +36,9 @@ public class CBADao {
 
     private final InvoiceDaoHelper invoiceDaoHelper;
 
-    public CBADao() {
-        this.invoiceDaoHelper = new InvoiceDaoHelper();
+    @Inject
+    public CBADao(final InvoiceDaoHelper invoiceDaoHelper) {
+        this.invoiceDaoHelper = invoiceDaoHelper;
     }
 
 
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 8f97ea4..9f86bc9 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
@@ -104,14 +104,16 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
                              final CacheControllerDispatcher cacheControllerDispatcher,
                              final NonEntityDao nonEntityDao,
                              final InvoiceConfig invoiceConfig,
+                             final InvoiceDaoHelper invoiceDaoHelper,
+                             final CBADao cbaDao,
                              final InternalCallContextFactory internalCallContextFactory) {
         super(new EntitySqlDaoTransactionalJdbiWrapper(dbi, clock, cacheControllerDispatcher, nonEntityDao), InvoiceSqlDao.class);
         this.nextBillingDatePoster = nextBillingDatePoster;
         this.eventBus = eventBus;
         this.invoiceConfig = invoiceConfig;
         this.internalCallContextFactory = internalCallContextFactory;
-        this.invoiceDaoHelper = new InvoiceDaoHelper();
-        this.cbaDao = new CBADao();
+        this.invoiceDaoHelper = invoiceDaoHelper;
+        this.cbaDao = cbaDao;
         this.clock = clock;
     }
 
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 407aa6f..f6b6619 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
@@ -28,23 +28,36 @@ import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
+import javax.inject.Inject;
 
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
+import org.killbill.billing.util.tag.ControlTagType;
+import org.killbill.billing.util.tag.Tag;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class InvoiceDaoHelper {
 
+    private final TagInternalApi tagInternalApi;
+
+    @Inject
+    public InvoiceDaoHelper(final TagInternalApi tagInternalApi) {
+        this.tagInternalApi = tagInternalApi;
+    }
+
     /**
      * Find amounts to adjust for individual items, if not specified.
      * The user gives us a list of items to adjust associated with a given amount (how much to refund per invoice item).
@@ -204,11 +217,13 @@ public class InvoiceDaoHelper {
     public void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         getInvoiceItemsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
         getInvoicePaymentsWithinTransaction(ImmutableList.<InvoiceModelDao>of(invoice), entitySqlDaoWrapperFactory, context);
+        setInvoiceWrittenOff(invoice, context);
     }
 
     public void populateChildren(final Iterable<InvoiceModelDao> invoices, final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
         getInvoiceItemsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
         getInvoicePaymentsWithinTransaction(invoices, entitySqlDaoWrapperFactory, context);
+        setInvoicesWrittenOff(invoices, context);
     }
 
     public List<InvoiceModelDao> getAllInvoicesByAccountFromTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalTenantContext context) {
@@ -267,4 +282,37 @@ public class InvoiceDaoHelper {
             }
         }
     }
+
+    private void setInvoicesWrittenOff(final Iterable<InvoiceModelDao> invoices, final InternalTenantContext internalTenantContext) {
+
+        final List<Tag> tags = tagInternalApi.getTagsForAccountType(ObjectType.INVOICE, false, internalTenantContext);
+        final Iterable<Tag> writtenOffTags = filterForWrittenOff(tags);
+        for (final Tag cur : writtenOffTags) {
+            final InvoiceModelDao foundInvoice = Iterables.tryFind(invoices, new Predicate<InvoiceModelDao>() {
+                @Override
+                public boolean apply(final InvoiceModelDao input) {
+                    return input.getId().equals(cur.getObjectId());
+                }
+            }).orNull();
+            if (foundInvoice != null) {
+                foundInvoice.setIsWrittenOff(true);
+            }
+        }
+    }
+
+    private void setInvoiceWrittenOff(final InvoiceModelDao invoice, final InternalTenantContext internalTenantContext) {
+        final List<Tag> tags =  tagInternalApi.getTags(invoice.getId(), ObjectType.INVOICE, internalTenantContext);
+        final Iterable<Tag> writtenOffTags = filterForWrittenOff(tags);
+        invoice.setIsWrittenOff(writtenOffTags.iterator().hasNext());
+    }
+
+    private Iterable<Tag> filterForWrittenOff(final List<Tag> tags) {
+        return Iterables.filter(tags, new Predicate<Tag>() {
+            @Override
+            public boolean apply(final Tag input) {
+                return  input.getTagDefinitionId().equals(ControlTagType.WRITTEN_OFF.getId());
+            }
+        });
+    }
+
 }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
index 5d62bf0..0316d8d 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceModelDao.java
@@ -46,6 +46,8 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
     private List<InvoicePaymentModelDao> invoicePayments = new LinkedList<InvoicePaymentModelDao>();
     private Currency processedCurrency;
 
+    private boolean isWrittenOff;
+
     public InvoiceModelDao() { /* For the DAO mapper */ }
 
     public InvoiceModelDao(final UUID id, @Nullable final DateTime createdDate, final UUID accountId,
@@ -58,6 +60,7 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         this.targetDate = targetDate;
         this.currency = currency;
         this.migrated = migrated;
+        this.isWrittenOff = false;
     }
 
     public InvoiceModelDao(final UUID accountId, final LocalDate invoiceDate, final LocalDate targetDate, final Currency currency, final boolean migrated) {
@@ -157,6 +160,14 @@ public class InvoiceModelDao extends EntityModelDaoBase implements EntityModelDa
         this.invoicePayments = invoicePayments;
     }
 
+    public boolean isWrittenOff() {
+        return isWrittenOff;
+    }
+
+    public void setIsWrittenOff(final boolean isWrittenOff) {
+        this.isWrittenOff = isWrittenOff;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
index 1973ff2..3080c5a 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/glue/DefaultInvoiceModule.java
@@ -36,8 +36,10 @@ import org.killbill.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import org.killbill.billing.invoice.api.migration.DefaultInvoiceMigrationApi;
 import org.killbill.billing.invoice.api.svcs.DefaultInvoiceInternalApi;
 import org.killbill.billing.invoice.api.user.DefaultInvoiceUserApi;
+import org.killbill.billing.invoice.dao.CBADao;
 import org.killbill.billing.invoice.dao.DefaultInvoiceDao;
 import org.killbill.billing.invoice.dao.InvoiceDao;
+import org.killbill.billing.invoice.dao.InvoiceDaoHelper;
 import org.killbill.billing.invoice.generator.DefaultInvoiceGenerator;
 import org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator;
 import org.killbill.billing.invoice.generator.InvoiceGenerator;
@@ -70,6 +72,8 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
 
     protected void installInvoiceDao() {
         bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
+        bind(InvoiceDaoHelper.class).asEagerSingleton();
+        bind(CBADao.class).asEagerSingleton();
     }
 
     @Override
@@ -162,7 +166,6 @@ public class DefaultInvoiceModule extends KillBillModule implements InvoiceModul
         installInvoiceMigrationApi();
         installResourceBundleFactory();
         bind(RawUsageOptimizer.class).asEagerSingleton();
-        ;
         bind(InvoiceApiHelper.class).asEagerSingleton();
     }
 }
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 0394a5a..514e368 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
@@ -51,6 +51,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     private final LocalDate targetDate;
     private final Currency currency;
     private final boolean migrationInvoice;
+    private final boolean isWrittenOff;
 
     private final Currency processedCurrency;
 
@@ -63,7 +64,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
 
     public DefaultInvoice(final UUID invoiceId, final UUID accountId, @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
                           final LocalDate targetDate, final Currency currency, final boolean isMigrationInvoice) {
-        this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice);
+        this(invoiceId, null, accountId, invoiceNumber, invoiceDate, targetDate, currency, currency, isMigrationInvoice, false);
     }
 
 
@@ -71,7 +72,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     public DefaultInvoice(final InvoiceModelDao invoiceModelDao) {
         this(invoiceModelDao.getId(), invoiceModelDao.getCreatedDate(), invoiceModelDao.getAccountId(),
              invoiceModelDao.getInvoiceNumber(), invoiceModelDao.getInvoiceDate(), invoiceModelDao.getTargetDate(),
-             invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated());
+             invoiceModelDao.getCurrency(), invoiceModelDao.getProcessedCurrency(), invoiceModelDao.isMigrated(), invoiceModelDao.isWrittenOff());
         addInvoiceItems(Collections2.transform(invoiceModelDao.getInvoiceItems(), new Function<InvoiceItemModelDao, InvoiceItem>() {
             @Override
             public InvoiceItem apply(final InvoiceItemModelDao input) {
@@ -88,7 +89,8 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
 
     private DefaultInvoice(final UUID invoiceId, @Nullable final DateTime createdDate, final UUID accountId,
                           @Nullable final Integer invoiceNumber, final LocalDate invoiceDate,
-                          final LocalDate targetDate, final Currency currency, final Currency processedCurrency, final boolean isMigrationInvoice) {
+                          final LocalDate targetDate, final Currency currency, final Currency processedCurrency,
+                           final boolean isMigrationInvoice, final boolean isWrittenOff) {
         super(invoiceId, createdDate, createdDate);
         this.accountId = accountId;
         this.invoiceNumber = invoiceNumber;
@@ -97,6 +99,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
         this.currency = currency;
         this.processedCurrency = processedCurrency;
         this.migrationInvoice = isMigrationInvoice;
+        this.isWrittenOff = isWrittenOff;
         this.invoiceItems = new ArrayList<InvoiceItem>();
         this.payments = new ArrayList<InvoicePayment>();
     }
@@ -105,7 +108,7 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
     // Semi deep copy where we copy the lists but not the elements in the lists since they are immutables.
     @Override
     public Object clone() {
-        final Invoice clonedInvoice = new DefaultInvoice(getId(),  getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice());
+        final Invoice clonedInvoice = new DefaultInvoice(getId(),  getCreatedDate(), getAccountId(), getInvoiceNumber(), getInvoiceDate(), getTargetDate(), getCurrency(), getProcessedCurrency(), isMigrationInvoice(), isWrittenOff());
         clonedInvoice.getInvoiceItems().addAll(getInvoiceItems());
         clonedInvoice.getPayments().addAll(getPayments());
         return clonedInvoice;
@@ -228,12 +231,17 @@ public class DefaultInvoice extends EntityBase implements Invoice, Cloneable {
 
     @Override
     public BigDecimal getBalance() {
-        return InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+        return isWrittenOff ? BigDecimal.ZERO : InvoiceCalculatorUtils.computeInvoiceBalance(currency, invoiceItems, payments);
+    }
+
+    public boolean isWrittenOff() {
+        return isWrittenOff;
     }
 
     @Override
     public String toString() {
         return "DefaultInvoice [items=" + invoiceItems + ", payments=" + payments + ", id=" + id + ", accountId=" + accountId + ", invoiceDate=" + invoiceDate + ", targetDate=" + targetDate + ", currency=" + currency + ", amountPaid=" + getPaidAmount() + "]";
     }
+
 }
 
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
index afac77d..edbe79f 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/api/user/TestDefaultInvoiceUserApi.java
@@ -61,6 +61,8 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
         invoiceId = invoiceUtil.generateRegularInvoice(account, clock.getUTCNow(), callContext);
     }
 
+
+
     @Test(groups = "slow")
     public void testPostExternalChargeOnNewInvoice() throws Exception {
         // Initial account balance
@@ -342,14 +344,25 @@ public class TestDefaultInvoiceUserApi extends InvoiceTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testAddRemoveWrittenOffTag() throws InvoiceApiException, TagApiException {
+
+        final Invoice originalInvoice = invoiceUserApi.getInvoice(invoiceId, callContext);
+        assertEquals(originalInvoice.getBalance(), new BigDecimal("0.77"));
+
         invoiceUserApi.tagInvoiceAsWrittenOff(invoiceId, callContext);
 
         List<Tag> tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, false, callContext);
         assertEquals(tags.size(), 1);
         assertEquals(tags.get(0).getTagDefinitionId(), ControlTagType.WRITTEN_OFF.getId());
 
+        final Invoice invoiceWithTag = invoiceUserApi.getInvoice(invoiceId, callContext);
+        assertEquals(invoiceWithTag.getBalance(), BigDecimal.ZERO);
+
+
         invoiceUserApi.tagInvoiceAsNotWrittenOff(invoiceId, callContext);
         tags = tagUserApi.getTagsForObject(invoiceId, ObjectType.INVOICE, false, callContext);
         assertEquals(tags.size(), 0);
+
+        final Invoice invoiceAfterTagRemoval = invoiceUserApi.getInvoice(invoiceId, callContext);
+        assertEquals(invoiceAfterTagRemoval.getBalance(), new BigDecimal("0.77"));
     }
 }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestDefaultInvoiceDaoUnit.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestDefaultInvoiceDaoUnit.java
index 052ac21..4c30673 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestDefaultInvoiceDaoUnit.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestDefaultInvoiceDaoUnit.java
@@ -65,7 +65,6 @@ public class TestDefaultInvoiceDaoUnit extends InvoiceTestSuiteNoDB {
         final InvoicePaymentModelDao invoicePayment = Mockito.mock(InvoicePaymentModelDao.class);
         Mockito.when(invoicePayment.getAmount()).thenReturn(paymentAmount);
 
-        final InvoiceDaoHelper invoiceDaoHelper = new InvoiceDaoHelper();
         final BigDecimal actualRefundAmount = invoiceDaoHelper.computePositiveRefundAmount(invoicePayment, requestedAmount, invoiceItemIdsWithAmounts);
         Assert.assertEquals(actualRefundAmount, expectedRefundAmount);
     }
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
index 2e2692e..6945f02 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/InvoiceTestSuiteNoDB.java
@@ -27,6 +27,7 @@ import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory;
 import org.killbill.billing.invoice.dao.InvoiceDao;
+import org.killbill.billing.invoice.dao.InvoiceDaoHelper;
 import org.killbill.billing.invoice.generator.FixedAndRecurringInvoiceItemGenerator;
 import org.killbill.billing.invoice.generator.InvoiceGenerator;
 import org.killbill.billing.invoice.glue.TestInvoiceModuleNoDB;
@@ -99,6 +100,8 @@ public abstract class InvoiceTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected RawUsageOptimizer rawUsageOptimizer;
     @Inject
+    protected InvoiceDaoHelper invoiceDaoHelper;
+    @Inject
     protected FixedAndRecurringInvoiceItemGenerator fixedAndRecurringInvoiceItemGenerator;
     @Override
     protected KillbillConfigSource getConfigSource() {
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
index 638f017..725d6d7 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/template/formatters/TestDefaultInvoiceFormatter.java
@@ -35,6 +35,7 @@ import org.killbill.billing.invoice.api.InvoiceItemType;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.invoice.api.formatters.InvoiceFormatter;
 import org.killbill.billing.invoice.api.formatters.ResourceBundleFactory.ResourceBundleType;
+import org.killbill.billing.invoice.dao.InvoiceModelDao;
 import org.killbill.billing.invoice.model.CreditAdjInvoiceItem;
 import org.killbill.billing.invoice.model.CreditBalanceAdjInvoiceItem;
 import org.killbill.billing.invoice.model.DefaultInvoice;
@@ -370,7 +371,12 @@ public class TestDefaultInvoiceFormatter extends InvoiceTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testProcessedCurrencyExists() throws Exception {
-        final Invoice invoice = new DefaultInvoice(UUID.randomUUID(), UUID.randomUUID(), new Integer(234), new LocalDate(), new LocalDate(), Currency.BRL, false);
+
+        // Use InvoiceModelDao to build the invoice to be able to set the processedCurrency (No suitable CTOR for DefaultInvoice on purpose)
+        final InvoiceModelDao invoiceModelDao = new InvoiceModelDao(UUID.randomUUID(), new LocalDate(), new LocalDate(), Currency.BRL);
+        invoiceModelDao.setProcessedCurrency(Currency.USD);
+
+        final Invoice invoice = new DefaultInvoice(invoiceModelDao);
 
         checkOutput(invoice,
                     "{{#invoice.processedCurrency}}" +
diff --git a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
index 6426727..44f530c 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/api/DefaultTagUserApi.java
@@ -188,7 +188,7 @@ public class DefaultTagUserApi implements TagUserApi {
 
     @Override
     public List<Tag> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final TenantContext context) {
-        return withModelTransform(tagDao.getTagsForAccountType(accountId, objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(accountId, context)));
+        return withModelTransform(tagDao.getTagsForAccountType(objectType, includedDeleted, internalCallContextFactory.createInternalTenantContext(accountId, context)));
     }
 
     @Override
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
index 0ee5cde..865af9d 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/DefaultTagDao.java
@@ -84,7 +84,7 @@ public class DefaultTagDao extends EntityDaoBase<TagModelDao, Tag, TagApiExcepti
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForAccountType(final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         final List<TagModelDao> allTags = getTagsForAccount(includedDeleted, internalTenantContext);
         return ImmutableList.<TagModelDao>copyOf(Collections2.filter(allTags, new Predicate<TagModelDao>() {
             @Override
diff --git a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
index e8932a6..5dd9135 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/dao/TagDao.java
@@ -35,7 +35,7 @@ public interface TagDao extends EntityDao<TagModelDao, Tag, TagApiException> {
 
     List<TagModelDao> getTagsForObject(UUID objectId, ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
 
-    List<TagModelDao> getTagsForAccountType(UUID accountId, ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
+    List<TagModelDao> getTagsForAccountType(ObjectType objectType, boolean includedDeleted, InternalTenantContext internalTenantContext);
 
     List<TagModelDao> getTagsForAccount(boolean includedDeleted, InternalTenantContext internalTenantContext);
 }
diff --git a/util/src/main/java/org/killbill/billing/util/tag/DefaultTagInternalApi.java b/util/src/main/java/org/killbill/billing/util/tag/DefaultTagInternalApi.java
index e40f7f5..9db3025 100644
--- a/util/src/main/java/org/killbill/billing/util/tag/DefaultTagInternalApi.java
+++ b/util/src/main/java/org/killbill/billing/util/tag/DefaultTagInternalApi.java
@@ -35,6 +35,7 @@ import org.killbill.billing.util.tag.dao.TagModelDaoHelper;
 import com.google.common.base.Function;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class DefaultTagInternalApi implements TagInternalApi {
 
@@ -61,15 +62,12 @@ public class DefaultTagInternalApi implements TagInternalApi {
 
     @Override
     public List<Tag> getTags(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
-        return ImmutableList.<Tag>copyOf(Collections2.transform(tagDao.getTagsForObject(objectId, objectType, false, context),
-                                                                new Function<TagModelDao, Tag>() {
-                                                                    @Override
-                                                                    public Tag apply(final TagModelDao input) {
-                                                                        return TagModelDaoHelper.isControlTag(input.getTagDefinitionId()) ?
-                                                                               new DefaultControlTag(ControlTagType.getTypeFromId(input.getTagDefinitionId()), objectType, objectId, input.getCreatedDate()) :
-                                                                               new DescriptiveTag(input.getTagDefinitionId(), objectType, objectId, input.getCreatedDate());
-                                                                    }
-                                                                }));
+        return toTagList(tagDao.getTagsForObject(objectId, objectType, false, context));
+    }
+
+    @Override
+    public List<Tag> getTagsForAccountType(final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
+        return toTagList(tagDao.getTagsForAccountType(objectType, includedDeleted, internalTenantContext));
     }
 
     @Override
@@ -85,4 +83,17 @@ public class DefaultTagInternalApi implements TagInternalApi {
             throws TagApiException {
         tagDao.deleteTag(objectId, objectType, tagDefinitionId, context);
     }
+
+    private List<Tag> toTagList(final List<TagModelDao> input) {
+        return ImmutableList.<Tag>copyOf(Iterables.transform(input, new Function<TagModelDao, Tag>() {
+            @Override
+            public Tag apply(final TagModelDao input) {
+                return TagModelDaoHelper.isControlTag(input.getTagDefinitionId()) ?
+                       new DefaultControlTag(ControlTagType.getTypeFromId(input.getTagDefinitionId()), input.getObjectType(), input.getObjectId(), input.getCreatedDate()) :
+                       new DescriptiveTag(input.getTagDefinitionId(), input.getObjectType(), input.getObjectId(), input.getCreatedDate());
+            }
+        }));
+    }
+
+
 }
diff --git a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
index dcbe233..a2238e9 100644
--- a/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
+++ b/util/src/test/java/org/killbill/billing/util/tag/dao/MockTagDao.java
@@ -87,7 +87,7 @@ public class MockTagDao extends MockEntityDaoBase<TagModelDao, Tag, TagApiExcept
     }
 
     @Override
-    public List<TagModelDao> getTagsForAccountType(final UUID accountId, final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
+    public List<TagModelDao> getTagsForAccountType(final ObjectType objectType, final boolean includedDeleted, final InternalTenantContext internalTenantContext) {
         throw new UnsupportedOperationException();
     }