killbill-uncached

invoice: Disable invoice tracking ids associated with a Void

1/21/2019 10:10:53 PM

Details

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 570d678..0ce1364 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
@@ -1170,11 +1170,14 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
 
                 cbaDao.doCBAComplexityFromTransaction(invoicesTags, entitySqlDaoWrapperFactory, context);
 
+                // Invoice creation event sent on COMMITTED
                 if (InvoiceStatus.COMMITTED.equals(newStatus)) {
-                    // notify invoice creation event
                     notifyBusOfInvoiceCreation(entitySqlDaoWrapperFactory, invoice, context);
+                // Deactivate any usage trackingIds if necessary
+                } else if (InvoiceStatus.VOID.equals(newStatus)) {
+                    final InvoiceTrackingSqlDao trackingSqlDao = entitySqlDaoWrapperFactory.become(InvoiceTrackingSqlDao.class);
+                    trackingSqlDao.deactivateForInvoice(invoiceId.toString(), context);
                 }
-
                 return null;
             }
         });
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
index 4991040..178b464 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingModelDao.java
@@ -38,6 +38,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
     private UUID subscriptionId;
     private String unitType;
     private LocalDate recordDate;
+    private boolean isActive;
 
     public InvoiceTrackingModelDao() { /* For the DAO mapper */ }
 
@@ -54,6 +55,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
         this.subscriptionId = subscriptionId;
         this.unitType = unitType;
         this.recordDate = recordDate;
+        this.isActive = true;
     }
 
     public String getTrackingId() {
@@ -88,6 +90,19 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
         this.unitType = unitType;
     }
 
+    // TODO required for jdbi binder
+    public boolean getIsActive() {
+        return isActive;
+    }
+
+    public boolean isActive() {
+        return isActive;
+    }
+
+    public void setIsActive(final boolean isActive) {
+        this.isActive = isActive;
+    }
+
     public LocalDate getRecordDate() {
         return recordDate;
     }
@@ -110,6 +125,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
         final InvoiceTrackingModelDao that = (InvoiceTrackingModelDao) o;
         return Objects.equal(trackingId, that.trackingId) &&
                Objects.equal(invoiceId, that.invoiceId) &&
+               Objects.equal(isActive, that.isActive) &&
                Objects.equal(subscriptionId, that.subscriptionId) &&
                Objects.equal(unitType, that.unitType) &&
                Objects.equal(recordDate, that.recordDate);
@@ -117,7 +133,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(super.hashCode(), trackingId, invoiceId, subscriptionId, unitType, recordDate);
+        return Objects.hashCode(super.hashCode(), trackingId, invoiceId, subscriptionId, unitType, recordDate, isActive);
     }
 
     @Override
@@ -127,6 +143,7 @@ public class InvoiceTrackingModelDao extends EntityModelDaoBase implements Entit
                ", invoiceId=" + invoiceId +
                ", subscriptionId=" + subscriptionId +
                ", unitType='" + unitType + '\'' +
+               ", isActive='" + isActive + '\'' +
                ", recordDate=" + recordDate +
                '}';
     }
diff --git a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
index 00979f8..57d1b2c 100644
--- a/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
+++ b/invoice/src/main/java/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.java
@@ -22,21 +22,30 @@ import java.util.List;
 
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.util.audit.ChangeType;
 import org.killbill.billing.util.callcontext.InternalTenantContextBinder;
 import org.killbill.billing.util.entity.Entity;
+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.skife.jdbi.v2.sqlobject.SqlBatch;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
 
 @KillBillSqlDaoStringTemplate
 public interface InvoiceTrackingSqlDao extends EntitySqlDao<InvoiceTrackingModelDao, Entity> {
 
+    @SqlUpdate
+    @Audited(ChangeType.UPDATE)
+    public void deactivateForInvoice(@Bind("invoiceId") String invoiceId,
+                              @SmartBindBean final InternalCallContext context);
+
+
     @SqlBatch
     void create(@SmartBindBean Iterable<InvoiceTrackingModelDao> trackings,
-                @InternalTenantContextBinder final InternalCallContext context);
+                @SmartBindBean final InternalCallContext context);
 
     @SqlQuery
     List<InvoiceTrackingModelDao> getTrackingsByDateRange(@Bind("startDate") final Date startDate,
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
index faf56bb..08102cd 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/dao/InvoiceTrackingSqlDao.sql.stg
@@ -8,8 +8,11 @@ tableFields(prefix) ::= <<
 , <prefix>subscription_id
 , <prefix>unit_type
 , <prefix>record_date
+, <prefix> is_active
 , <prefix>created_by
 , <prefix>created_date
+, <prefix>updated_by
+, <prefix>updated_date
 >>
 
 tableValues() ::= <<
@@ -18,8 +21,23 @@ tableValues() ::= <<
 , :subscriptionId
 , :unitType
 , :recordDate
-, :userName
+, :isActive
+, :createdBy
 , :createdDate
+, :updatedBy
+, :updatedDate
+>>
+
+deactivateForInvoice() ::= <<
+update <tableName()>
+set
+is_active = false
+, updated_by = :createdBy
+, updated_date = :updatedDate
+where
+invoice_id = :invoiceId
+<AND_CHECK_TENANT("")>
+;
 >>
 
 getTrackingsByDateRange() ::= <<
@@ -30,6 +48,7 @@ where
 record_date >= :startDate
 and record_date \< :endDate
 and <accountRecordIdField("")> = :accountRecordId
+and is_active = true
 <AND_CHECK_TENANT("")>
 <defaultOrderBy("")>
 ;
@@ -42,6 +61,7 @@ from <tableName()>
 where
 invoice_id = :invoiceId
 and <accountRecordIdField("")> = :accountRecordId
+and is_active = true
 <AND_CHECK_TENANT("")>
 <defaultOrderBy("")>
 ;
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 4fc7087..abb6c76 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -9,13 +9,17 @@ CREATE TABLE invoice_tracking_ids (
     subscription_id varchar(36),
     unit_type varchar(255) NOT NULL,
     record_date date NOT NULL,
+    is_active boolean default true,
     created_by varchar(50) NOT NULL,
     created_date datetime NOT NULL,
+    updated_by varchar(50) NOT NULL,
+    updated_date datetime NOT NULL,
     account_record_id bigint /*! unsigned */ not null,
     tenant_record_id bigint /*! unsigned */ not null default 0,
     PRIMARY KEY(record_id)
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
 CREATE INDEX invoice_tracking_tenant_account_date_idx ON invoice_tracking_ids(tenant_record_id, account_record_id, record_date);
+CREATE INDEX invoice_tracking_invoice_id_idx ON invoice_tracking_ids(tenant_record_id, invoice_id);
 
 
 DROP TABLE IF EXISTS invoice_items;
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20190121141325__tracking_ids_is_active.sql b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20190121141325__tracking_ids_is_active.sql
new file mode 100644
index 0000000..217fa69
--- /dev/null
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/migration/V20190121141325__tracking_ids_is_active.sql
@@ -0,0 +1,4 @@
+alter table invoice_tracking_ids add column is_active boolean default true after record_date;
+alter table invoice_tracking_ids add column updated_by varchar(50) NOT NULL after created_date;
+alter table invoice_tracking_ids add column updated_date datetime NOT NULL after updated_by;
+create index invoice_tracking_invoice_id_idx on invoice_tracking_ids(tenant_record_id, invoice_id);
diff --git a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
index dbcef7e..0072f3d 100644
--- a/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
+++ b/invoice/src/test/java/org/killbill/billing/invoice/dao/TestInvoiceTrackingSqlDao.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.LocalDate;
+import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.invoice.InvoiceTestSuiteWithEmbeddedDB;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -29,7 +30,7 @@ import org.testng.annotations.Test;
 public class TestInvoiceTrackingSqlDao extends InvoiceTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
-    public void testBasicTrackingIds()  {
+    public void testBasicTrackingIds() {
         final InvoiceTrackingSqlDao dao = dbi.onDemand(InvoiceTrackingSqlDao.class);
 
         LocalDate startRange = new LocalDate(2018, 8, 1);
@@ -77,4 +78,55 @@ public class TestInvoiceTrackingSqlDao extends InvoiceTestSuiteWithEmbeddedDB {
         Assert.assertEquals(result.get(2).getSubscriptionId(), subscriptionId);
 
     }
+
+    @Test(groups = "slow")
+    public void testInvalidation() {
+        final InvoiceTrackingSqlDao dao = dbi.onDemand(InvoiceTrackingSqlDao.class);
+
+        LocalDate startRange = new LocalDate(2019, 1, 1);
+        LocalDate endRange = new LocalDate(2019, 1, 31);
+
+        final UUID invoiceId1 = UUID.randomUUID();
+        final UUID invoiceId2 = UUID.randomUUID();
+        final UUID subscriptionId = UUID.randomUUID();
+
+        // invoiceId1
+        final InvoiceTrackingModelDao input1 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId1", invoiceId1, subscriptionId, "unit", new LocalDate(2019, 1, 1));
+        final InvoiceTrackingModelDao input2 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId2", invoiceId1, subscriptionId, "unit", new LocalDate(2019, 1, 2));
+        final InvoiceTrackingModelDao input3 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId3", invoiceId1, subscriptionId, "unit", new LocalDate(2019, 1, 3));
+
+        // invoiceId2
+        final InvoiceTrackingModelDao input4 = new InvoiceTrackingModelDao(UUID.randomUUID(), clock.getUTCNow(), "trackingId4", invoiceId2, subscriptionId, "unit", new LocalDate(2019, 1, 5));
+
+        final List<InvoiceTrackingModelDao> inputs = new ArrayList<>();
+        inputs.add(input1);
+        inputs.add(input2);
+        inputs.add(input3);
+        inputs.add(input4);
+
+        dao.create(inputs, internalCallContext);
+
+        final List<InvoiceTrackingModelDao> result = dao.getTrackingsByDateRange(startRange.toDate(), endRange.toDate(), internalCallContext);
+        Assert.assertEquals(result.size(), 4);
+
+        clock.addDays(1);
+        final InternalCallContext updatedContext = new InternalCallContext(internalCallContext.getTenantRecordId(),
+                                                                           internalCallContext.getAccountRecordId(),
+                                                                           internalCallContext.getFixedOffsetTimeZone(),
+                                                                           clock.getUTCNow(),
+                                                                           internalCallContext.getUserToken(),
+                                                                           "invalidation-user",
+                                                                           internalCallContext.getCallOrigin(),
+                                                                           internalCallContext.getContextUserType(),
+                                                                           internalCallContext.getReasonCode(),
+                                                                           internalCallContext.getComments(),
+                                                                           internalCallContext.getCreatedDate(),
+                                                                           clock.getUTCNow());
+
+        dao.deactivateForInvoice(invoiceId1.toString(), updatedContext);
+
+        final List<InvoiceTrackingModelDao> result2 = dao.getTrackingsByDateRange(startRange.toDate(), endRange.toDate(), internalCallContext);
+        Assert.assertEquals(result2.size(), 1);
+
+    }
 }