From 00bfc37a2544eefa902e2e15811cac3212447eb9 Mon Sep 17 00:00:00 2001 From: Tomasz Stalka Date: Mon, 13 Mar 2017 14:53:27 +0100 Subject: [PATCH 1/2] Added Domain Snapshot Job --- src/classes/DomainMonthlySnapshotBatch.cls | 96 +++++++++++++++++++ .../DomainMonthlySnapshotBatch.cls-meta.xml | 5 + src/classes/HqIntegrationTests.cls | 96 +++++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 src/classes/DomainMonthlySnapshotBatch.cls create mode 100644 src/classes/DomainMonthlySnapshotBatch.cls-meta.xml diff --git a/src/classes/DomainMonthlySnapshotBatch.cls b/src/classes/DomainMonthlySnapshotBatch.cls new file mode 100644 index 00000000..11b4b71c --- /dev/null +++ b/src/classes/DomainMonthlySnapshotBatch.cls @@ -0,0 +1,96 @@ +public class DomainMonthlySnapshotBatch implements Database.Batchable, Database.Stateful { + + private Map wamMap; + private Map formsMap; + private Map mobileUsersMap; + private Map recordCountMap; + private Date currentDate; + private Boolean error; + private BatchDefaultSettings__c settings; + + public DomainMonthlySnapshotBatch() { + this(Date.today()); + } + + public DomainMonthlySnapshotBatch(Date currentDate) { + this.currentDate = currentDate; + this.wamMap = new Map{ 'Empty' => 0, 'Advanced' => 0, 'Community' => 0, 'Enterprise' => 0, 'Standard' => 0, 'Pro' => 0}; + this.formsMap = new Map{ 'Empty' => 0, 'Advanced' => 0, 'Community' => 0, 'Enterprise' => 0, 'Standard' => 0, 'Pro' => 0}; + this.mobileUsersMap = new Map{ 'Empty' => 0, 'Advanced' => 0, 'Community' => 0, 'Enterprise' => 0, 'Standard' => 0, 'Pro' => 0}; + this.recordCountMap = new Map{ 'Empty' => 0, 'Advanced' => 0, 'Community' => 0, 'Enterprise' => 0, 'Standard' => 0, 'Pro' => 0}; + this.error = false; + this.settings = BatchDefaultSettings__c.getOrgDefaults(); + } + + public List start(Database.BatchableContext context) { // We cannot group by formula field + return [SELECT Id, Software_Plan_Edition__c, Accounting_subscriber_domain__r.Wam__c, Accounting_subscriber_domain__r.cpActiveMobileUsers__c, + Accounting_subscriber_domain__r.cpAllForms__c FROM Accounting_Subscription__c + WHERE is_trial__c = false AND is_active__c = true AND Accounting_subscriber_domain__c != null AND + Accounting_subscriber_domain__r.Likely_Real__c = true AND Accounting_subscriber_domain__r.is_test__c != 'true' AND + Accounting_subscriber_domain__r.cpIsActive__c = true]; + } + + public void execute(Database.BatchableContext context, List scope) { + try { + for (Accounting_Subscription__c sub : scope) { + String softwarePlan = sub.Software_Plan_Edition__c; + if (softwarePlan == '' || softwarePlan == null || softwarePlan == '-') { + softwarePlan = 'Empty'; + } + + wamMap.put(softwarePlan, wamMap.get(softwarePlan) + sub.Accounting_subscriber_domain__r.Wam__c); + formsMap.put(softwarePlan, formsMap.get(softwarePlan) + sub.Accounting_subscriber_domain__r.cpAllForms__c); + mobileUsersMap.put(softwarePlan, mobileUsersMap.get(softwarePlan) + sub.Accounting_subscriber_domain__r.cpActiveMobileUsers__c); + } + } catch (Exception e) { + System.debug('Error: ' + e); + if (!Test.isRunningTest()) { + EmailHelper.sendEmailFromException(this.settings.Error_Emails__c.split(','), 'Cannot calculate monthy Domain Snapshots', + 'Error : ', e); + } + throw e; // we want stop calculations and fix problems + } + } + + public void finish(Database.BatchableContext context) { + try { + if (error == false) { + SObject sobj = (SObject) new Domain_Monthly_Snapshot__c(); + sobj = setValues(sobj, wamMap, '_Wam__c'); + sobj = setValues(sobj, formsMap, '_Forms__c'); + sobj = setValues(sobj, mobileUsersMap, '_Active_Mobile_Users__c'); + Domain_Monthly_Snapshot__c domainSnapshot = (Domain_Monthly_Snapshot__c) sobj; + domainSnapshot.Snapshot_Date__c = this.currentDate; + insert domainSnapshot; + } + } catch (Exception e) { + System.debug('Error: ' + e); + if (!Test.isRunningTest()) { + EmailHelper.sendEmailFromException(this.settings.Error_Emails__c.split(','), 'Cannot calculate monthy Domain Snapshots', + 'Error : ', e); + } + } + } + + private SObject setValues(SObject obj, Map values, String suffix) { + + for (String key : values.keySet()) { + String name = (key + suffix).toLowerCase(); + Decimal value = values.get(key); + if (hasSObjectField(name, obj)) { + obj.put(name, value); + } else { + System.debug('Cannot find field with name : ' + name); + if (!Test.isRunningTest()) { + EmailHelper.sendEmail(this.settings.Error_Emails__c.split(','), 'Cannot find sObject field for Domain Monthly Snapshot', + 'Cannot find fields: ' + name); + } + } + } + return obj; + } + + private Boolean hasSObjectField(String fieldName, SObject so) { + return so.getSobjectType().getDescribe().fields.getMap().keySet().contains(fieldName); + } +} \ No newline at end of file diff --git a/src/classes/DomainMonthlySnapshotBatch.cls-meta.xml b/src/classes/DomainMonthlySnapshotBatch.cls-meta.xml new file mode 100644 index 00000000..38aa015d --- /dev/null +++ b/src/classes/DomainMonthlySnapshotBatch.cls-meta.xml @@ -0,0 +1,5 @@ + + + 36.0 + Active + diff --git a/src/classes/HqIntegrationTests.cls b/src/classes/HqIntegrationTests.cls index 58d20d7e..fac47629 100644 --- a/src/classes/HqIntegrationTests.cls +++ b/src/classes/HqIntegrationTests.cls @@ -16,6 +16,101 @@ public class HqIntegrationTests { insert testCustomSetting; } + @isTest + static void shouldCreateDomainMonthlySnapshot() { + Accounting_SoftwareProductRate__c productRate1 = new Accounting_SoftwareProductRate__c(is_active__c = true, monthly_fee__c = 150, Server_Name__c = 'CommCare HQ'); + Accounting_SoftwareProductRate__c productRate2 = new Accounting_SoftwareProductRate__c(is_active__c = true, monthly_fee__c = 75, Server_Name__c = 'CommCare HQ'); + Accounting_SoftwareProductRate__c productRate3 = new Accounting_SoftwareProductRate__c(is_active__c = true, monthly_fee__c = 25, Server_Name__c = 'CommCare HQ'); + + insert productRate1; + insert productRate2; + insert productRate3; + + Accounting_Softwareplan__c softwarePlan1 = new Accounting_Softwareplan__c(Name = 'softwarePlan1', edition__c = 'Community', Server_Name__c = 'CommCare HQ'); + Accounting_Softwareplan__c softwarePlan2 = new Accounting_Softwareplan__c(Name = 'softwarePlan2', edition__c = 'Standard', Server_Name__c = 'CommCare HQ'); + Accounting_Softwareplan__c softwarePlan3 = new Accounting_Softwareplan__c(Name = 'softwarePlan3', edition__c = 'Pro', Server_Name__c = 'CommCare HQ'); + + insert softwarePlan1; + insert softwarePlan2; + insert softwarePlan3; + + Accounting_SoftwarePlanVersion__c planVersion1 = new Accounting_SoftwarePlanVersion__c(is_active__c = true, Software_Product_Rate__c = productRate1.Id, + Accounting_SoftwarePlan__c = softwarePlan1.Id, Server_Name__c = 'CommCare HQ'); + Accounting_SoftwarePlanVersion__c planVersion2 = new Accounting_SoftwarePlanVersion__c(is_active__c = true, Software_Product_Rate__c = productRate2.Id, + Accounting_SoftwarePlan__c = softwarePlan2.Id, Server_Name__c = 'CommCare HQ'); + Accounting_SoftwarePlanVersion__c planVersion3 = new Accounting_SoftwarePlanVersion__c(is_active__c = true, Software_Product_Rate__c = productRate3.Id, + Accounting_SoftwarePlan__c = softwarePlan3.Id, Server_Name__c = 'CommCare HQ'); + + insert planVersion1; + insert planVersion2; + insert planVersion3; + + Domain__c domain1 = new Domain__c(Name = 'Sample 1', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, + Wam__c = 15, cpActiveMobileUsers__c = 20, cpAllForms__c = 325); + Domain__c domain2 = new Domain__c(Name = 'Sample 2', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, + Wam__c = 30, cpActiveMobileUsers__c = 35, cpAllForms__c = 340); + Domain__c domain3 = new Domain__c(Name = 'Sample 3', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, + Wam__c = 45, cpActiveMobileUsers__c = 50, cpAllForms__c = 355); + Domain__c domain4 = new Domain__c(Name = 'Sample 4', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, + Wam__c = 60, cpActiveMobileUsers__c = 65, cpAllForms__c = 370); + Domain__c domain5 = new Domain__c(Name = 'Sample 5', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, + Wam__c = 75, cpActiveMobileUsers__c = 80, cpAllForms__c = 385); + + insert domain1; + insert domain2; + insert domain3; + insert domain4; + insert domain5; + + Accounting_Subscription__c subscription1 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2015, 11, 2), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, + Accounting_subscriber_domain__c = domain1.Id, plan_version__c = planVersion1.Id, Server_Name__c = 'CommCare HQ', + is_trial__c = false); + insert subscription1; + Accounting_Subscription__c subscription2 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, + Accounting_subscriber_domain__c = domain2.Id, plan_version__c = planVersion2.Id, Server_Name__c = 'CommCare HQ', + is_trial__c = false); + insert subscription2; + Accounting_Subscription__c subscription3 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2014, 1, 1), date_end__c = Date.newInstance(2014, 3 ,4), is_active__c = true, + Accounting_subscriber_domain__c = domain3.Id, plan_version__c = planVersion3.Id, Server_Name__c = 'CommCare HQ'); + insert subscription3; + Accounting_Subscription__c subscription4 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, + Accounting_subscriber_domain__c = domain4.Id, plan_version__c = planVersion1.Id, Server_Name__c = 'CommCare HQ', + is_trial__c = false); + insert subscription4; + Accounting_Subscription__c subscription5 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, + Accounting_subscriber_domain__c = domain5.Id, plan_version__c = planVersion2.Id, Server_Name__c = 'CommCare HQ', + is_trial__c = false); + insert subscription5; + + Test.startTest(); + Database.executeBatch(new DomainMonthlySnapshotBatch()); + Test.stopTest(); + + Domain_Monthly_Snapshot__c domainSn = [SELECT Advanced_Active_Mobile_Users__c, Advanced_Forms__c, Advanced_Wam__c, + Community_Active_Mobile_Users__c, Community_Forms__c, Community_Wam__c, + Empty_Active_Mobile_Users__c, Empty_Forms__c, Empty_Wam__c, + Enterprise_Active_Mobile_Users__c, Enterprise_Forms__c, Enterprise_Wam__c, + Pro_Active_Mobile_Users__c, Pro_Forms__c, Pro_Wam__c, + Standard_Active_Mobile_Users__c, Standard_Forms__c, Standard_Wam__c, Snapshot_Date__c + FROM Domain_Monthly_Snapshot__c LIMIT 1]; + + // Community => subscription1 AND subscription4 => Wam = 15 + 60 = 75, Mobile Users = 20 + 65 = 85, Forms = 325 + 370 = 695 + // Standard => subscription1 AND subscription4 => Wam = 30 + 75 = 105, Mobile Users = 35 + 80 = 115, Forms = 340 + 385 = 725 + // Pro => subscription1 AND subscription4 => Wam = 45, Mobile Users = 50, Forms = 355 + + System.assertEquals(75, domainSn.Community_Wam__c); + System.assertEquals(85, domainSn.Community_Active_Mobile_Users__c); + System.assertEquals(695, domainSn.Community_Forms__c); + + System.assertEquals(105, domainSn.Standard_Wam__c); + System.assertEquals(115, domainSn.Standard_Active_Mobile_Users__c); + System.assertEquals(725, domainSn.Standard_Forms__c); + + System.assertEquals(45, domainSn.Pro_Wam__c); + System.assertEquals(50, domainSn.Pro_Active_Mobile_Users__c); + System.assertEquals(355, domainSn.Pro_Forms__c); + } + @isTest static void shouldCalculateWAM() { hqInt_Model_DomainClass d1 = new hqInt_Model_DomainClass(); @@ -942,6 +1037,7 @@ public class HqIntegrationTests { cAdj = [SELECT id__c, amount__c FROM Accounting_CreditAdjustment__c WHERE id__c = '3']; System.assertEquals(-500, cAdj.amount__c); } + /** @isTest static void hqIntShouldUpdateMissingRelationsInSubscriberCustomObject() { From e32967fb17e99761986cdb0166162d1a3a84b8e0 Mon Sep 17 00:00:00 2001 From: Tomasz Stalka Date: Tue, 14 Mar 2017 11:15:07 +0100 Subject: [PATCH 2/2] Added record count calculations --- src/classes/DomainMonthlySnapshotBatch.cls | 16 +++++--- src/classes/HqIntegrationTests.cls | 39 ++++++++++++++------ src/classes/PROContractSnapshotScheduled.cls | 6 +++ 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/classes/DomainMonthlySnapshotBatch.cls b/src/classes/DomainMonthlySnapshotBatch.cls index 11b4b71c..312bcdec 100644 --- a/src/classes/DomainMonthlySnapshotBatch.cls +++ b/src/classes/DomainMonthlySnapshotBatch.cls @@ -23,8 +23,8 @@ public class DomainMonthlySnapshotBatch implements Database.Batchable start(Database.BatchableContext context) { // We cannot group by formula field - return [SELECT Id, Software_Plan_Edition__c, Accounting_subscriber_domain__r.Wam__c, Accounting_subscriber_domain__r.cpActiveMobileUsers__c, - Accounting_subscriber_domain__r.cpAllForms__c FROM Accounting_Subscription__c + return [SELECT Id, Software_Plan_Edition__c, Accounting_subscriber_domain__r.Wam__c, Accounting_subscriber_domain__r.cpActiveMobileUsers__c, service_type__c, + Accounting_subscriber_domain__r.cpAllForms__c, Accounting_subscriber_domain__r.InternalProp_self_started__c FROM Accounting_Subscription__c WHERE is_trial__c = false AND is_active__c = true AND Accounting_subscriber_domain__c != null AND Accounting_subscriber_domain__r.Likely_Real__c = true AND Accounting_subscriber_domain__r.is_test__c != 'true' AND Accounting_subscriber_domain__r.cpIsActive__c = true]; @@ -38,9 +38,12 @@ public class DomainMonthlySnapshotBatch implements Database.Batchable values, String suffix) { for (String key : values.keySet()) { diff --git a/src/classes/HqIntegrationTests.cls b/src/classes/HqIntegrationTests.cls index fac47629..80e8a0e0 100644 --- a/src/classes/HqIntegrationTests.cls +++ b/src/classes/HqIntegrationTests.cls @@ -45,16 +45,28 @@ public class HqIntegrationTests { insert planVersion2; insert planVersion3; + Internal_Properties__c InternalPropRec1 = new Internal_Properties__c(self_started__c = true, Server_Name__c = 'CommCare HQ'); + Internal_Properties__c InternalPropRec2 = new Internal_Properties__c(self_started__c = true, Server_Name__c = 'CommCare HQ'); + Internal_Properties__c InternalPropRec3 = new Internal_Properties__c(self_started__c = true, Server_Name__c = 'CommCare HQ'); + Internal_Properties__c InternalPropRec4 = new Internal_Properties__c(self_started__c = true, Server_Name__c = 'CommCare HQ'); + Internal_Properties__c InternalPropRec5 = new Internal_Properties__c(self_started__c = true, Server_Name__c = 'CommCare HQ'); + + insert InternalPropRec1; + insert InternalPropRec2; + insert InternalPropRec3; + insert InternalPropRec4; + insert InternalPropRec5; + Domain__c domain1 = new Domain__c(Name = 'Sample 1', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, - Wam__c = 15, cpActiveMobileUsers__c = 20, cpAllForms__c = 325); + Wam__c = 15, cpActiveMobileUsers__c = 20, cpAllForms__c = 325, Internal_Properties__c = InternalPropRec1.Id); Domain__c domain2 = new Domain__c(Name = 'Sample 2', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, - Wam__c = 30, cpActiveMobileUsers__c = 35, cpAllForms__c = 340); + Wam__c = 30, cpActiveMobileUsers__c = 35, cpAllForms__c = 340, Internal_Properties__c = InternalPropRec2.Id); Domain__c domain3 = new Domain__c(Name = 'Sample 3', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, - Wam__c = 45, cpActiveMobileUsers__c = 50, cpAllForms__c = 355); + Wam__c = 45, cpActiveMobileUsers__c = 50, cpAllForms__c = 355, Internal_Properties__c = InternalPropRec3.Id); Domain__c domain4 = new Domain__c(Name = 'Sample 4', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, - Wam__c = 60, cpActiveMobileUsers__c = 65, cpAllForms__c = 370); + Wam__c = 60, cpActiveMobileUsers__c = 65, cpAllForms__c = 370, Internal_Properties__c = InternalPropRec4.Id); Domain__c domain5 = new Domain__c(Name = 'Sample 5', Server_Name__c = 'CommCare HQ', is_test__c = 'false', cpIsActive__c = true, - Wam__c = 75, cpActiveMobileUsers__c = 80, cpAllForms__c = 385); + Wam__c = 75, cpActiveMobileUsers__c = 80, cpAllForms__c = 385, Internal_Properties__c = InternalPropRec5.Id); insert domain1; insert domain2; @@ -64,22 +76,23 @@ public class HqIntegrationTests { Accounting_Subscription__c subscription1 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2015, 11, 2), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, Accounting_subscriber_domain__c = domain1.Id, plan_version__c = planVersion1.Id, Server_Name__c = 'CommCare HQ', - is_trial__c = false); + is_trial__c = false, service_type__c = 'Self_service'); insert subscription1; Accounting_Subscription__c subscription2 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, Accounting_subscriber_domain__c = domain2.Id, plan_version__c = planVersion2.Id, Server_Name__c = 'CommCare HQ', - is_trial__c = false); + is_trial__c = false, service_type__c = 'Product'); insert subscription2; Accounting_Subscription__c subscription3 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2014, 1, 1), date_end__c = Date.newInstance(2014, 3 ,4), is_active__c = true, - Accounting_subscriber_domain__c = domain3.Id, plan_version__c = planVersion3.Id, Server_Name__c = 'CommCare HQ'); + Accounting_subscriber_domain__c = domain3.Id, plan_version__c = planVersion3.Id, Server_Name__c = 'CommCare HQ', + is_trial__c = false, service_type__c = 'Self_service'); insert subscription3; Accounting_Subscription__c subscription4 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, Accounting_subscriber_domain__c = domain4.Id, plan_version__c = planVersion1.Id, Server_Name__c = 'CommCare HQ', - is_trial__c = false); + is_trial__c = false, service_type__c = 'Self_service'); insert subscription4; Accounting_Subscription__c subscription5 = new Accounting_Subscription__c(date_start__c = Date.newInstance(2016, 1, 1), date_end__c = Date.newInstance(2016, 2 ,1), is_active__c = true, Accounting_subscriber_domain__c = domain5.Id, plan_version__c = planVersion2.Id, Server_Name__c = 'CommCare HQ', - is_trial__c = false); + is_trial__c = false, service_type__c = 'Product'); insert subscription5; Test.startTest(); @@ -91,7 +104,8 @@ public class HqIntegrationTests { Empty_Active_Mobile_Users__c, Empty_Forms__c, Empty_Wam__c, Enterprise_Active_Mobile_Users__c, Enterprise_Forms__c, Enterprise_Wam__c, Pro_Active_Mobile_Users__c, Pro_Forms__c, Pro_Wam__c, - Standard_Active_Mobile_Users__c, Standard_Forms__c, Standard_Wam__c, Snapshot_Date__c + Standard_Active_Mobile_Users__c, Standard_Forms__c, Standard_Wam__c, Snapshot_Date__c, + Pro_Scale_of_Programs__c, Community_Scale_of_Programs__c, Standard_Scale_of_Programs__c FROM Domain_Monthly_Snapshot__c LIMIT 1]; // Community => subscription1 AND subscription4 => Wam = 15 + 60 = 75, Mobile Users = 20 + 65 = 85, Forms = 325 + 370 = 695 @@ -101,14 +115,17 @@ public class HqIntegrationTests { System.assertEquals(75, domainSn.Community_Wam__c); System.assertEquals(85, domainSn.Community_Active_Mobile_Users__c); System.assertEquals(695, domainSn.Community_Forms__c); + System.assertEquals(2, domainSn.Community_Scale_of_Programs__c); System.assertEquals(105, domainSn.Standard_Wam__c); System.assertEquals(115, domainSn.Standard_Active_Mobile_Users__c); System.assertEquals(725, domainSn.Standard_Forms__c); + System.assertEquals(2, domainSn.Standard_Scale_of_Programs__c); System.assertEquals(45, domainSn.Pro_Wam__c); System.assertEquals(50, domainSn.Pro_Active_Mobile_Users__c); System.assertEquals(355, domainSn.Pro_Forms__c); + System.assertEquals(1, domainSn.Pro_Scale_of_Programs__c); } @isTest diff --git a/src/classes/PROContractSnapshotScheduled.cls b/src/classes/PROContractSnapshotScheduled.cls index c20185ba..b8ae32df 100644 --- a/src/classes/PROContractSnapshotScheduled.cls +++ b/src/classes/PROContractSnapshotScheduled.cls @@ -13,5 +13,11 @@ global class PROContractSnapshotScheduled implements Schedulable { PROContractSnapshotBatch proContractSnapshotBatch = new PROContractSnapshotBatch(); database.executeBatch(proContractSnapshotBatch, 50); } + + Integer numberOfDays = Date.daysInMonth(currentDate.year(), currentDate.month()); + Date lastDayOfMonth = Date.newInstance(currentDate.year(), currentDate.month(), numberOfDays); + if (currentDate.isSameDay(lastDayOfMonth)) { + Database.executeBatch(new DomainMonthlySnapshotBatch()); + } } } \ No newline at end of file