{"action":"created","comment":{"url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/comments/74846786","id":74846786,"diff_hunk":"@@ -29,439 +29,597 @@\n */\n /**\n * @author Salesforce.org\n-* @date 2014\n+* @date 2016\n * @group Addresses\n * @group-content ../../ApexDocContent/Addresses.htm\n * @description Trigger Handler on Contact for Address management. \n */\n public class ADDR_Contact_TDTM extends TDTM_Runnable {\n-\n-    public static Boolean contactsUpdatedAfterInsert = false;\n+    \n+    /* @description Flag to prevent recursive call on insert */\n+    public static Boolean alreadyRunAfterInsert = false;\n+    /* @description Flag to prevent recursive call on update */\n+    public static Boolean alreadyRunAfterUpdate = false;\n+    /* @description Flag used to indicate if this class is running as a result of an automatic Account creation caused by a Contact insert/update */\n+    public static Boolean afterAutomaticAccInsert = false;\n+    \n+    /*******************************************************************************************************\n+    * @description Turns class off.\n+    * @return void\n+    ********************************************************************************************************/\n+    public static void turnOff() {\n+        alreadyRunAfterInsert = true;\n+        alreadyRunAfterUpdate = true;\n+    }\n+    \n+    /*******************************************************************************************************\n+    * @description Turns class on.\n+    * @return void\n+    ********************************************************************************************************/\n+    public static void turnOn() {\n+        alreadyRunAfterInsert = false;\n+        alreadyRunAfterUpdate = false;\n+    }\n     \n     /*******************************************************************************************************\n     * @description Trigger Handler on Contact that handles Address Management.\n+    *\n     *   Rules:\n-    *       inserting new contact - make their address a new default address, unless they say it is an override\n-    *       updating an existing contact - make their address a new default address, unless they say it is an override\n+    *       inserting new contact\n+    *           if parent Account is Household --> create new Address as child of Account. If not override, make it default and \n+                                                   propagate to children without override\n+    *           else && Contact addresses active --> create new Address as child of Contact\n+    *\n+    *       updating an existing contact\n+    *           if parent Account is Household --> create new Address as child of Account. If not override, make it default and\n+                                                   propagate to children without override\n+    *           else  && Contact addresses active --> create new Address as child of Contact\n+    *\n     * @param listNew the list of Contacts from trigger new. \n     * @param listOld the list of Contacts from trigger old. \n     * @param triggerAction which trigger event (BeforeInsert, AfterInsert, etc.). \n     * @param objResult the describe for Contacts \n     * @return dmlWrapper.  \n     ********************************************************************************************************/\n     public override DmlWrapper run(List<SObject> listNew, List<SObject> listOld, \n-        TDTM_Runnable.Action triggerAction, Schema.DescribeSObjectResult objResult) {\n-            \n-        DmlWrapper dmlWrapper = new DmlWrapper();        \n-        list<Contact> listConAddrSet = new list<Contact>(); \n-        list<Contact> listConCreateAddr = new list<Contact>();\n-        map<Id, boolean> mapAddrIdIsOverride = new map<Id, boolean>();\n-        list<Contact> listConAddrReset = new list<Contact>();\n-        Map<Id,Account> mapAccountIdAccount = null;\n-        \n-        // bail out if address mgmt turned off.\n-        if (String.isBlank(UTIL_CustomSettingsFacade.getSettings().Accounts_Addresses_Enabled__c))\n-            return dmlWrapper;\n-                \n-        // Rules:\n-        // inserting new contact - make their address a new default address, unless they say it is an override\n-        // updating an existing contact - make their address a new default address, unless they say it is an override\n+    TDTM_Runnable.Action triggerAction, Schema.DescribeSObjectResult objResult) {\n         \n-        boolean isBefore = (triggerAction == TDTM_Runnable.Action.BeforeUpdate || triggerAction == TDTM_Runnable.Action.BeforeInsert);\n-        if (isBefore) { \n-            // need to query account fields we need to look at\n-            Set<Id> setAccountId = new Set<Id>();\n-            for (SObject so : listNew) {\n-                Contact con = (Contact)so;\n-                if (con.AccountId != null)\n-                    setAccountId.add(con.AccountId);\n-            }\n-            mapAccountIdAccount = new Map<Id,Account>([select Id, RecordTypeId from Account where Id IN :setAccountId]);\n-        }        \n-       \n-        integer i = -1;        \n-        for (SObject so : listNew) {\n-            i++;  \n-            Contact con = (Contact)so;\n-\n-            // we only support address management with HH Accounts being used\n-            if (con.AccountId == null)\n-                continue;               \n+        DmlWrapper dmlWrapper = new DmlWrapper();\n+            \n+        if(!alreadyRunAfterInsert || !alreadyRunAfterUpdate) {\n+\t    \n+\t       //Turn off other address triggers\n+            ADDR_Account_TDTM.turnOff();\n+            ADDR_Addresses_TDTM.turnOff();\n \n-            // BEFORE INSERT\n-            if (triggerAction == TDTM_Runnable.Action.BeforeInsert) {\n-                if (con.Current_Address__c != null) {\n-                    // remember which contacts will need their address set\n-                    listConAddrSet.add(con);\n-                    if (con.is_Address_Override__c)\n-                        // track which address's need their dates updated\n-                        mapAddrIdIsOverride.put(con.Current_Address__c, true);\n-                } else if (isContactAddressEmpty(con)) {\n-                    // if the contact has no address specified, pick up the hh default.\n-                    listConAddrReset.add(con);                  \n-                }\n-            }\n+            Map<ID, ID> contactIDToAccRecTypeID;\n             \n-            // BEFORE UPDATE\n-            if (triggerAction == TDTM_Runnable.Action.BeforeUpdate) {\n-                // there can be four different types of changes we need to consider\n-                // A. changes to isAddressOverride\n-                // B. changes to CurrentAddress\n-                // C. changes to MailingAddress fields\n-                // D. changes to Account (ie switching accounts)\n-                //\n-                // that leads to 16 permutations! \n-                // (it's actually more permutations, since we need to take into account what\n-                // the change in isAddressOverride or CurrentAddress is).\n-                //\n-                // A,B,C       if (CurrentAddress) refill to it, else update/new address object     \n-                // A,!B,!C     if !isAddrOverride, refill contact from Default Address\n-                // A,!B,C      if isAddrOverride, update/new addr object and refresh CurrentAddress, else update/new address object\n-                // A,B,!C      if (CurrentAddress) refill to it, else refill from Default Address\n-                // !A,B,C      if (CurrentAddress) refill to it, else update/new address object\n-                // !A,!B,C     update/new address object\n-                // !A,B,!C     if (CurrentAddress) refill to it, else refill from Default Address\n-                // !A,!B,!C    nothing!\n-                // *,*,*,D     if no override, refill from Default Address, else update/new address object \n-                //\n-                // simplifying this to some precedence rules:\n-                //     - if Account changes && no override && no MailingAddress field changes, refill from Default Address\n-                //     - if CurrentAddress changes to a new non-null value, refill to it.\n-                //     - if isAddressOverride changes to false, refill from Default Address\n-                //     - if MailingAddress field changes, update/new address object\n+\t        //List of Contacts from which an Address record needs to be created.\n+\t        List<Contact> contactsCreateAddrFrom = new List<Contact>();\n+\t        \n+\t        //List of Contact IDs that need their address info propagated to the parent and all siblings without override\n+\t        Map<ID, ID> contactIdParentIdAddrPropagate = new Map<ID, ID>();  \n+\t        \n+\t        //List of Contact IDs that need their address info deletion propagated to the parent and all siblings without override\n+\t        List<Contact> contactsAddrPropagateDelete = new List<Contact>();  \n+\t        \n+\t        //Map of Adress IDs to Boolean, with the Boolean representing if the Address needs its Latest Date fields updated.\n+\t        Map<Id, Boolean> addrIdsOverride = new Map<Id, Boolean>();\n+\t        \n+\t        //List of Contacts that need to have their address info pulled from the parent Household (if they unchecked the override field, for example)\n+\t        List<Contact> contactsAddrGetFromHh = new List<Contact>();\n+\t        \n+\t        //List of Contacts that have had their address information cleared. List is not the same as contactsAddrGetFromHh, because in this case, \n+\t        //if it's not an override, address info has to be deleted from the Hosehould.\n+\t        List<Contact> contactsAddrInfoCleared = new List<Contact>();\n+\t        \n+\t        //List of Contacts that need to have address info copied from parent Househod (for example, they have been created without address info)\n+\t        List<Contact> contactsAddrInfoReset = new List<Contact>(); //This different from the contactsAddrGetFromHh list because contacts in \n+\t        //contactsAddrGetFromHh should have a value in their Current_Address__c field\n+\t        \n+\t        Boolean householdAddrsEnabled = UTIL_CustomSettingsFacade.getSettings().Household_Addresses_RecType__c == UTIL_Describe.getHhAccRecTypeID();\n+\t        \n+\t        // AFTER INSERT\n+\t        if (!alreadyRunAfterInsert && triggerAction == TDTM_Runnable.Action.AfterInsert) {\n+\t            alreadyRunAfterInsert = true;\n+\t            \n+\t            contactIDToAccRecTypeID = getContactIdToAccRecTypeId(listNew);\n+\t            \t            \n+\t            for (SObject so : listNew) {\n+\t                Contact contact = (Contact)so;\n+\t                Boolean childOfHousehold = contactIDToAccRecTypeID.get(contact.ID) == UTIL_Describe.getHhAccRecTypeID();\n+\t\t            \n+\t\t            if (childOfHousehold && householdAddrsEnabled) {\n+\t\t                if (ADDR_Addresses_UTIL.isContactAddressEmpty(contact)) {\n+                           // if the contact has no address specified, pick up the HH default.\n+                           contactsAddrInfoReset.add(contact);\n+                        } else { //address info not empty\n+\t                        if (contact.is_Address_Override__c) {\n+\t                            //create non-default address record\n+\t                            contactsCreateAddrFrom.add(contact);\n+\t                        } else {\n+\t                            //Address record needs to be created as child of parent Account (can be done in the Before cuz we already have the ID to populate Parent_Account__c)\n+\t                            contactsCreateAddrFrom.add(contact);\n+\t                            //Address needs to be copied to parent Household, and all siblings that don't have an override.\n+\t                            contactIdParentIdAddrPropagate.put(contact.ID, contact.AccountID);\n+\t                        }\n+\t\t                }\n+                    //For new Contacts that are using address management and are not children of Household, create the address object. We do it in the after, so that the \n+                    //Parent_Contact__c field in Address can point back to the Contact.\n+                    } else if (!childOfHousehold && UTIL_CustomSettingsFacade.getSettings().Contacts_Addresses_Enabled__c == true \n+                    && !ADDR_Addresses_UTIL.isContactAddressEmpty(contact) && !afterAutomaticAccInsert) {\n+\t\t                contactsCreateAddrFrom.add(contact);  \n+\t\t            }\n+\t            }\n+\t        }\n+\t        \n+\t        // AFTER UPDATE\n+\t        if (!alreadyRunAfterUpdate && triggerAction == TDTM_Runnable.Action.AfterUpdate) {\n+                alreadyRunAfterUpdate = true;\n                 \n-                Contact conOld = (Contact)listOld[i];\n-                \n-                // new highest priority! if Account changes... \n-                if (conOld != null && con.AccountId != conOld.AccountId) {\n-                    // if no override, refill from the Default Address\n-                    if (!con.is_Address_Override__c && !isContactAddressChanged(con, conOld)) {\n-                        listConAddrReset.add(con);\n-                    } else { // create a new address on the account\n-                        listConCreateAddr.add(con);    \n-                    }\n-                } else {    \n-\t                // highest priority. if they are changing to a new Current Address, refill from it.\n-\t                if (con.Current_Address__c != conOld.Current_Address__c && con.Current_Address__c != null) {\n-\t                    listConAddrSet.add(con);\n-\t                    if (con.is_Address_Override__c)\n-\t                        // track which address's need their dates updated\n-\t                        mapAddrIdIsOverride.put(con.Current_Address__c, true);                    \n-\t                }\n-\t                \n-\t                // second priority.  if they are clearing isAddressOverride, refill from the Default Address\n-\t                if (con.is_Address_Override__c != conOld.is_Address_Override__c && !con.is_Address_Override__c) {\n-\t                    listConAddrReset.add(con);\n-\t                    if (conOld.Current_Address__c != null)\n-\t                        // track which address's need their dates updated\n-\t                        mapAddrIdIsOverride.put(conOld.Current_Address__c, false);\n-\t                }\n+                //To know if a contact belongs to a household (if the parent account has Household record type) \n+                contactIDToAccRecTypeID = getContactIdToAccRecTypeId(listNew);\n+            \n+\t            Integer i = 0;\n+\t            for (SObject so : listNew) {\n+\t                Contact contact = (Contact)so;\n+\t                Boolean childOfHousehold = contactIDToAccRecTypeID.get(contact.ID) == UTIL_Describe.getHhAccRecTypeID();\n+\t                Contact contactOld = (Contact)listOld[i];\n \t                \n-\t                // third priority.  if MailingAddress fields changed, update the existing address object or create a new one.\n-\t                // this is handled in the next block, since we do it for both new contacts and updated contacts.\n-                }                             \n-            }\n-    \n-            // BEFORE UPDATE & AFTER UPDATE & BEFORE INSERT && AFTER INSERT\n-            // our functionality for dealing with contact address changes requires some trickery!\n-            // if the contact doesn't have an address override, we create (or match) a default address,\n-            // which requires us to do the work in the AfterUpdate (or else we can't propagate the default address).\n-            // but if the contact has an address override, we are supposed to create (or match) a non default address override.\n-            // in order to update the contact with the new address override's Id, we have to do it in the BeforeUpdate.\n-            if (triggerAction == TDTM_Runnable.Action.BeforeUpdate ||\n-               triggerAction == TDTM_Runnable.Action.BeforeInsert ||  \n-               triggerAction == TDTM_Runnable.Action.AfterInsert || \n-               triggerAction == TDTM_Runnable.Action.AfterUpdate\n-               ) {\n-                // note that we detect whether the address change is coming from the address trigger,\n-                // in which case we don't want to create a new address.\n-                if (ADDR_Addresses_TDTM.hasRunAddrTrigger) {\n-                    //UTIL_Debug.debug('DJH**** stop Addr Trigger recursion in ADDR_Contact_TDTM');\n-                    continue;\n-                }\n+\t                Boolean addrInfoChanged = ADDR_Addresses_UTIL.isContactAddressChanged(contact, contactOld);\t                \n+\t                Boolean addrInfoCleared = !ADDR_Addresses_UTIL.iscontactAddressEmpty(contactOld) && ADDR_Addresses_UTIL.iscontactAddressEmpty(contact);\t                \n+\t                Boolean currentAddressChanged = contact.Current_Address__c != contactOld.Current_Address__c && contact.Current_Address__c != null;\n+\t                \t                \n+\t                // if they are changing to a new Current Address, but address info doesn't change, refill from it (both household and not household)\n+                    if (currentAddressChanged) { \n+                        if(!addrInfoChanged) {\n+\t\t                    contactsAddrGetFromHh.add(contact);\n+\t\t                    if(contact.is_Address_Override__c == true && contactOld.is_Address_Override__c == false)\n+\t\t                        addrIdsOverride.put(contact.Current_Address__c, true);\n+                        }  else {\n+                            UTIL_Debug.debug('****Current address changed & address info also changed');\n+                        }\n+                    } else { \n+\t\t\t            //If Contact Addresses are enabled, and the Contact is not the child of a HH \n+\t\t\t            if(!childOfHousehold && UTIL_CustomSettingsFacade.getSettings().Contacts_Addresses_Enabled__c == true) {\n+\t\t\t                //Non-household Contact addr info changed\n+\t\t\t                if(addrInfoChanged && !addrInfoCleared && !currentAddressChanged) {\n+\t\t\t                   contactsCreateAddrFrom.add(contact);\n+\t\t\t                //Non-household Contact addr info cleared\n+\t\t\t                } else if(addrInfoCleared) {\n+\t\t\t                   contactsAddrInfoCleared.add(contact);\n+\t\t\t                }\n+\t\t\t            } else if(childOfHousehold && householdAddrsEnabled) {\n+\t\t\t                \n+\t\t\t                // if the Contact has been changed to a different household\n+\t                        if(contactOld != null && contact.AccountID != contactOld.AccountID && !currentAddressChanged) {\n+\t                            \n+\t                            // if no override, refill from the Default Address\n+\t                            if (!contact.is_Address_Override__c) {\n+\t                                //Refilling address info from parent Household (unless parent account address info is empty), \n+\t                                //and unless we are coming from the automatic parent Account creation.\n+\t                                if(!afterAutomaticAccInsert) {\n+\t                                   contactsAddrInfoReset.add(contact);\n+\t                                } else {\n+\t                                   contactsCreateAddrFrom.add(contact); \n+\t                                }\n+\t                            \n+\t                            //if override, create new Address record in new household\n+\t                            } else if(contact.is_Address_Override__c && !addrInfoChanged) {\n+\t                                contactsCreateAddrFrom.add(contact);\n+\t                            }\n+\t\t\t                //Contact not switching household\n+\t                        } else {\n+\t\t\t\t                // if household Contact addr info changed \n+\t\t\t\t                if (addrInfoChanged && !addrInfoCleared && !currentAddressChanged) {\n+\t\t\t                        //create new Address as child of parent Account\n+\t\t\t                        contactsCreateAddrFrom.add(contact);\n+\t\t\t                        if(!contact.is_Address_Override__c)\n+\t\t\t                            //Address needs to be copied to parent Household, and all siblings that don't have an override.\n+\t\t\t                            contactIdParentIdAddrPropagate.put(contact.ID, contact.AccountID);\n+\t\t\t                    \n+\t\t\t                    // if household Contact addr info cleare\n+\t\t\t                    } else if (addrInfoCleared) {\n+\t\t\t\t                    contactsAddrInfoCleared.add(contact);\n+\t\t\t\t                    //if the contact was not override, but pointing to the default Address instead\n+\t\t\t\t                    if(!contact.is_Address_Override__c)\n+\t\t\t\t                        contactsAddrPropagateDelete.add(contact);\n+\t\t\t                    \n+\t\t\t                    // if they are clearing isAddressOverride, refill from the Default Address\n+\t\t\t\t                } else if (!contact.is_Address_Override__c && contact.is_Address_Override__c != contactOld.is_Address_Override__c) {\n+\t\t                            contactsAddrInfoReset.add(contact);\n+\t\t                            // if tthere is an address to fill contact info from\n+\t\t                            if (contactOld.Current_Address__c != null) {\n+\t\t                                // old current address (before clearing override) needs to have Latest_End_Date updated\n+\t\t                                addrIdsOverride.put(contactOld.Current_Address__c, false);\n+\t\t                            }\n+\t\t                        }\t                        \n+\t\t\t                }\n+\t\t\t            }\n+                    }  \n+\t\t            i++;\n+\t            }\n+\t        }\n+\n+\t        // set the mailing address for contacts who specify an address lookup or override\n+\t        if(contactsAddrGetFromHh.size() > 0)\n+\t            setConAddrFromLookup(contactsAddrGetFromHh, triggerAction, dmlWrapper);\n+\t        \n+\t        // reset the mailing address for contacts who no longer have an address override\n+\t        if(contactsAddrInfoReset.size() > 0)\n+\t            resetConAddrInfo(contactsAddrInfoReset, triggerAction, dmlWrapper);\n \n-                // if the address changed, remember the contact we want to add a new address for    \n-                Contact conOld = (listOld != null ? (Contact)listOld[i] : null);\n-                if (isContactAddressChanged(con, conOld)) \n-                    listConCreateAddr.add(con);\n-            }             \n+\t        // update the latest date fields on any address overrides\n+\t        if(addrIdsOverride.size() > 0) \n+\t            ADDR_Addresses_UTIL.updateAddrIsOverride(addrIdsOverride, dmlWrapper);\n+\t\n+\t        // create any new Address objects\n+\t        if(contactsCreateAddrFrom.size() > 0)\n+\t            createAddrsFromContacts(contactsCreateAddrFrom, contactIDToAccRecTypeID, dmlWrapper);\n+\t        \n+\t        // handle Contacts with address info removed\n+\t        if(contactsAddrInfoCleared.size() > 0)\n+\t            addrInfoDeleted(contactsAddrInfoCleared, dmlWrapper);\n+\t    \n+\t        // propagate address info to parent Household and and siblings without override\n+\t        if(contactIdParentIdAddrPropagate.size() > 0)\n+\t            propagateAddrInfo(contactIdParentIdAddrPropagate);\n+\t            \n+\t        // propagate address info deletion to parent Household and and siblings without override\n+\t        if(contactsAddrPropagateDelete.size() > 0)\n+\t            propagateAddrInfoDelete(contactsAddrPropagateDelete, dmlWrapper);\n         }\n-        \n-        // set the mailing address for contacts who specify an address lookup or override\n-        if (listConAddrSet.size() > 0) {\n-            setConAddrFromLookup(listConAddrSet);\n-            // to avoid pushing these address changes to the HH.\n-            ADDR_Addresses_TDTM.hasRunAddrTrigger = true;\n+        return dmlWrapper;\n+    }\n+    \n+    private Map<ID, ID> getContactIdToAccRecTypeId(List<SObject> listNew) {\n+        Map<ID, ID> contactIDToAccRecTypeID = new Map<ID, ID>();\n+        Map<ID, ID> accIDToAccRecTypeID = new Map<ID, ID>();\n+        List<ID> accIDs = new List<ID>();\n+        for(SObject so : listNew) {\n+            Contact contact = (Contact)so;\n+            ID parentAccID = contact.AccountID;\n+            accIDs.add(parentAccID);\n         }\n         \n-        // reset the mailing address for contacts who no longer have an address override\n-        if (listConAddrReset.size() > 0) {\n-            resetConAddr(listConAddrReset);\n-            // to avoid pushing these address changes to the HH.\n-            ADDR_Addresses_TDTM.hasRunAddrTrigger = true;\n+        List<Account> parentAccs = [select ID, RecordTypeID from Account where ID in :accIDs];\n+        for(Account acc : parentAccs) {\n+            accIDToAccRecTypeID.put(acc.ID, acc.RecordTypeID);\n         }\n         \n-        // update the Latest date fields on any address overrides\n-        if (mapAddrIdIsOverride.size() > 0) \n-            updateAddrIsOverride(mapAddrIdIsOverride, dmlWrapper);\n-            \n-        // create any new Default Address objects\n-        if (listConCreateAddr.size() > 0)\n-            createAddrFromCon(listConCreateAddr, dmlWrapper, triggerAction);\n-            \n-        return dmlWrapper;    \n-    }\n-\n-    /*******************************************************************************************************\n-    * @description for each contact, refresh its mailing address from its Address lookup\n-    * @param listCon the list of Contacts to update\n-    * @return void  \n-    ********************************************************************************************************/\n-    private void setConAddrFromLookup(list<Contact> listCon) {\n-        set<Id> setAddrId = new set<Id>();\n-        for (Contact con : listCon) {\n-            if (con.Current_Address__c != null)\n-              setAddrId.add(con.Current_Address__c);\n+        for(SObject so : listNew) {\n+            Contact contact = (Contact)so;\n+            ID parentAccRecTypeID = accIDToAccRecTypeID.get(contact.AccountID);\n+            contactIDToAccRecTypeID.put(contact.ID, parentAccRecTypeID);\n         }\n+        return contactIDToAccRecTypeID;\n+    }\n     \n-        map<Id, Address__c> mapAddrIdAddr = new map<Id, Address__c>([select Id, Address_Type__c, MailingStreet__c, MailingStreet2__c, MailingCity__c, MailingState__c, \n-            Geolocation__Latitude__s, Geolocation__Longitude__s, MailingPostalCode__c, MailingCountry__c from Address__c where Id in :setAddrId]);\n+    private static void propagateAddrInfo(Map<ID, ID> contactIdParentIdAddrPropagate) {\n             \n-        for (Contact con : listCon) {\n-            Address__c addr = mapAddrIdAddr.get(con.Current_Address__c);\n-            if (addr != null) {\n-                con.Primary_Address_Type__c = addr.Address_Type__c;\n-                ADDR_Addresses_TDTM.copyAddressAddrSObj(addr, con, 'Mailing', 'Primary_Address_Type__c');\n-            }\n-        }       \n-    }\n-\n-    /*******************************************************************************************************\n-    * @description compares two contacts' addresses\n-    * @param con1 a Contact\n-    * @param con2 a Contact\n-    * @return boolean. true if the Mailing Address fields have changed \n-    ********************************************************************************************************/\n-    private boolean isContactAddressChanged(Contact con1, Contact con2) {\n-        UTIL_Debug.debug('****con1: ' + JSON.serializePretty(con1));\n-        UTIL_Debug.debug('****con2: ' + JSON.serializePretty(con2));\n+        //Create map to use when excluding contacts in trigger from propagation, below\n+        Set<ID> originalContactIDs = contactIdParentIdAddrPropagate.keySet();\n         \n-        if(contactsUpdatedAfterInsert) {\n-            UTIL_Debug.debug('****Contacts were updated after insert');\n-            return true;\n+        Set<ID> parentAccIDs = new Set<ID>();\n+        //Gather all parent Acc IDs\n+        for(ID accId : contactIdParentIdAddrPropagate.values()) {\n+            parentAccIDs.add(accId);\n         }\n         \n-        // if both null, no change\n-        if (con1 == null && con2 == null) \n-           return false;\n-\n-        // if one null, make sure other has at least one address field set\n-        if (con1 == null || con2 == null) {\n-            if (con1 == null) \n-               con1 = con2;\n-            return (!isContactAddressEmpty(con1) ||\n-               con1.Primary_Address_Type__c != null);\n-        }\n+        List<SObject> objectsToUpdate = new List<SObject>();\n         \n-        // both provided, so look for at least one change between address fields\n-        boolean isChanged = \n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.MailingStreet, con2.MailingStreet) ||\n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.MailingCity, con2.MailingCity) ||\n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.MailingState, con2.MailingState) ||\n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.MailingPostalCode, con2.MailingPostalCode) ||\n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.MailingCountry, con2.MailingCountry) ||\n-           !ADDR_Addresses_TDTM.equalsCaseSensitive(con1.Primary_Address_Type__c, con2.Primary_Address_Type__c);\n-           \n-        if (!isChanged && ADDR_Addresses_TDTM.isStateCountryPicklistsEnabled) {\n-            isChanged = \n-                !ADDR_Addresses_TDTM.equalsCaseSensitive(string.valueOf(con1.get('MailingStateCode')), string.valueOf(con2.get('MailingStateCode'))) || \n-                !ADDR_Addresses_TDTM.equalsCaseSensitive(string.valueOf(con1.get('MailingCountryCode')), string.valueOf(con2.get('MailingCountryCode')));\n-        }    \n-        UTIL_Debug.debug('****Address changed: ' + isChanged);\n-        return isChanged;   \n+        //Query for parent Acc and sibling Contacts\n+        String query = ADDR_Addresses_UTIL.getParentAccsWithChildrenQuery() + ' :parentAccIDs';\n+        List<Account> parentAccsWithChildren = Database.query(query);\n+        for(Account acc : parentAccsWithChildren) {\n+            List<Contact> children = acc.Contacts;\n+            //Find the original Contact, that is part of the trigger\n+            Contact originalContact;\n+            for(Contact child : children) {\n+                if(originalContactIDs.contains(child.ID)) {\n+                    originalContact = child;\n+                    break;\n+                }\n+            }\n+            //Copy Address info to parent and siblings, if it's different\n+            if(originalContact != null && !ADDR_Addresses_UTIL.isContactAccAddrInfoSame(originalContact, acc)) {\n+\t            \n+\t            //Copy address info to parent Account\n+\t            ADDR_Addresses_UTIL.copyAddressStdSObj(originalContact, 'Mailing', acc, 'Billing');\n+\t            objectsToUpdate.add(acc);\n+\t            \n+\t            //Copy address info to each sibling - we need to exclude the one the trigger is running on!\n+\t            for(Contact contact : acc.Contacts) {\n+\t                if(contact.ID != originalContact.ID && !contact.is_Address_Override__c) {\n+\t                   contact.Primary_Address_Type__c = originalContact.Primary_Address_Type__c;\n+\t                   ADDR_Addresses_UTIL.copyAddressStdSObj(originalContact, 'Mailing', contact, 'Mailing');\n+\t                   objectsToUpdate.add(contact);\n+\t                }\n+\t            }\n+\t        //If the address info is the same, just update the Current_Address__c field in the original contact\n+            } else if(originalContact != null && ADDR_Addresses_UTIL.isContactAccAddrInfoSame(originalContact, acc)) {\n+                originalContact.Current_Address__c = acc.Current_Address__c;\n+                objectsToUpdate.add(originalContact);\n+            }\n+        }\n+        //explicitly doing the update, because if we put the records in DmlWrapper we get a \"duplicate id in list\" error due to\n+        //the method updateAccountPrimaryContact in ACCT_IndividualAccounts_TDTM also putting them there.\n+        update objectsToUpdate;   \n     }\n     \n-    /*******************************************************************************************************\n-    * @description returns whether the contact's mailing address is empty\n-    * @param con1 a Contact\n-    * @return boolean. true if the Mailing Address fields are all empty \n-    ********************************************************************************************************/\n-    private boolean isContactAddressEmpty(Contact con1) {\n-        boolean isEmpty = \n-            con1.MailingStreet == null &&\n-            con1.MailingCity == null &&\n-            con1.MailingState == null &&\n-            con1.MailingPostalCode == null &&\n-            // note that we decided to ignore country so that a default value won't create unnecessary address objects\n-            (con1.MailingCountry == null || ADDR_Addresses_TDTM.isStateCountryPicklistsEnabled) &&\n-            // only test StateCode if picklists enabled.\n-            (!ADDR_Addresses_TDTM.isStateCountryPicklistsEnabled || con1.get('MailingStateCode') == null);\n-        return isEmpty;   \n-           \n+    private void propagateAddrInfoDelete(List<Contact> contactsAddrPropagate, DmlWrapper dmlWrapper) {\n+        //Create map to use when excluding contacts in trigger from propagation, below\n+        Map<ID, Contact> contactsMap = new Map<ID, Contact>();\n+        for(Contact contact : contactsAddrPropagate) {\n+            if(contact.ID != null) {\n+                contactsMap.put(contact.ID, contact);\n+            }\n+        }\n+        \n+        Set<ID> parentAccIDs = new Set<ID>();\n+        //Gather all parent Acc IDs\n+        for(Contact contact : contactsAddrPropagate) {\n+            parentAccIDs.add(Contact.AccountId);\n+        }\n+        //Query for parent Acc and sibling Contacts\n+        List<Account> parentAccsWithChildren = Database.query(ADDR_Addresses_UTIL.getParentAccsWithChildrenQuery() + ' :parentAccIDs');\n+        for(Account acc : parentAccsWithChildren) {\n+            List<Contact> childContacts = acc.Contacts;\n+            //Find the original Contact, that is part of the trigger\n+            Contact originalContact;\n+            for(Contact childContact : childContacts) {\n+                if(contactsMap.containsKey(childContact.ID)) {\n+                    originalContact = childContact;\n+                    break;\n+                }\n+            }\n+            //Clear Address info from parent and siblings\n+            if(originalContact != null) {\n+                //No need to clear current_address field from original contact --> this is done in addrInfoDeleted!\n+                \n+                //Clear address info from parent Account\n+                acc.Current_Address__c = null;\n+                ADDR_Addresses_UTIL.clearAddrInfo(acc);\n+                dmlWrapper.objectsToUpdate.add(acc);\n+                //Clear address info from each sibling - we need to exclude the one the trigger is running on!\n+                for(Contact contact : acc.Contacts) {\n+                    if(contact.ID != originalContact.ID && !contact.is_Address_Override__c) {\n+                       contact.Current_Address__c = null;\n+                       ADDR_Addresses_UTIL.clearAddrInfo(contact);\n+                       DmlWrapper.objectsToUpdate.add(contact);\n+                    }\n+                }\n+            }\n+            //Clear Default_Address__c field from address record that was the default\n+            List<Address__c> childAddrs = acc.Addresses__r;\n+            for(Address__c childAddr : childAddrs) {\n+                if(childAddr.Default_Address__c) {\n+                    childAddr.Default_Address__c = false;\n+                    dmlWrapper.objectsToUpdate.add(childAddr);\n+                }\n+            }\n+        }\n     }\n-       \n+    \n     /*******************************************************************************************************\n-    * @description Creates a new address or matches an existing one, for each Contact.\n-    * This routine is called both by BeforeInsert/Update and AfterInsert/Update.\n-    * when pushing a new default address, we need to do it in the AfterInsert/Update.\n-    * otherwise we want to do it in the BeforeInsert/Update so we can update the contacts' info now.\n-    * @param listConAll the list of Contacts\n-    * @param dmlWrapper to hold the Addresses that need inserting/updating \n-    * @param triggerAction which trigger event (BeforeInsert, AfterInsert, etc.). \n-    * @return void  \n+    * @description for each Contact, create a new default address and add it to dmlWrapper\n+    * @param Listcontact a List of Contacts\n+    * @param dmlWrapper to hold the Addresses that need creating\n+    * @return void\n     ********************************************************************************************************/\n-    private void createAddrFromCon(list<Contact> listConAll, DmlWrapper dmlWrapper,  TDTM_Runnable.Action triggerAction) {\n-        \n-        // Strategy:\n-        // create a new (unsaved) address for each contact.\n-        // see if there is an existing address that matches each new address.\n-        // \n-        // for existing addresses we match\n-        //     process during BeforeInsert/Update\n-        //     update appropriate fields on contact\n-        //\n-        // for new addresses that won't be a default (ie, the contact has override set)\n-        //     process during BeforeInsert/Update\n-        //     create the address now\n-        //     update appropriate fields on contact\n-        //\n-        // for new addresses that will be a default address\n-        //     process during AfterInsert/Update\n-        //     mark the new address as default\n-        //     put it on dmlWrapper\n-        //\n-        \n-        map<Contact, Address__c> mapConAddr = new map<Contact, Address__c>();\n-        list<Contact> listCon = new list<Contact>();\n-        boolean isBefore = (triggerAction == TDTM_Runnable.Action.BeforeInsert || triggerAction == TDTM_Runnable.Action.BeforeUpdate);\n+    private void createAddrsFromContacts(List<Contact> listContact, Map<ID, ID> contactIDToAccRecTypeID, DmlWrapper dmlWrapper) {\n+        Set<ID> originalContactIDs = new Set<ID>();\n+        for(Contact contact : listContact) {\n+            originalContactIDs.add(contact.ID);\n+        }\n         \n-        for (Contact con : listConAll) {\n-            listCon.add(con);\n+        List<Address__c> newAddrs = new List<Address__c>();\n+        for (Contact contact : listContact) {\n             Address__c addr = new Address__c();\n-            addr.Parent_Account__c = con.AccountId;\n-            addr.Default_Address__c = !con.is_Address_Override__c;\n-            addr.Latest_Start_Date__c = system.today();\n-            addr.Latest_End_Date__c = null;         \n-            ADDR_Addresses_TDTM.copyAddressStdSObjAddr(con, 'Mailing', addr, con.Primary_Address_Type__c);\n-            mapConAddr.put(con, addr);\n+            //Creating address as child of Account\n+            if(contactIDToAccRecTypeID.get(contact.ID) == UTIL_Describe.getHhAccRecTypeID()) {","path":"src/classes/ADDR_Contact_TDTM.cls","position":673,"original_position":673,"commit_id":"ad946e8a7f079ebf630acef9dd3a96d4def5216c","original_commit_id":"ad946e8a7f079ebf630acef9dd3a96d4def5216c","user":{"login":"davidhabib","id":908119,"avatar_url":"https://avatars.githubusercontent.com/u/908119?v=3","gravatar_id":"","url":"https://api.github.com/users/davidhabib","html_url":"https://github.com/davidhabib","followers_url":"https://api.github.com/users/davidhabib/followers","following_url":"https://api.github.com/users/davidhabib/following{/other_user}","gists_url":"https://api.github.com/users/davidhabib/gists{/gist_id}","starred_url":"https://api.github.com/users/davidhabib/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidhabib/subscriptions","organizations_url":"https://api.github.com/users/davidhabib/orgs","repos_url":"https://api.github.com/users/davidhabib/repos","events_url":"https://api.github.com/users/davidhabib/events{/privacy}","received_events_url":"https://api.github.com/users/davidhabib/received_events","type":"User","site_admin":false},"body":"you know what I'm going to say!","created_at":"2016-08-15T22:24:10Z","updated_at":"2016-08-15T22:24:10Z","html_url":"https://github.com/SalesforceFoundation/HEDAP/pull/255#discussion_r74846786","pull_request_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255","_links":{"self":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/comments/74846786"},"html":{"href":"https://github.com/SalesforceFoundation/HEDAP/pull/255#discussion_r74846786"},"pull_request":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255"}}},"pull_request":{"url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255","id":71627252,"html_url":"https://github.com/SalesforceFoundation/HEDAP/pull/255","diff_url":"https://github.com/SalesforceFoundation/HEDAP/pull/255.diff","patch_url":"https://github.com/SalesforceFoundation/HEDAP/pull/255.patch","issue_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/255","number":255,"state":"open","locked":false,"title":"Feature/245 contact addresses","user":{"login":"ceiroa","id":1894238,"avatar_url":"https://avatars.githubusercontent.com/u/1894238?v=3","gravatar_id":"","url":"https://api.github.com/users/ceiroa","html_url":"https://github.com/ceiroa","followers_url":"https://api.github.com/users/ceiroa/followers","following_url":"https://api.github.com/users/ceiroa/following{/other_user}","gists_url":"https://api.github.com/users/ceiroa/gists{/gist_id}","starred_url":"https://api.github.com/users/ceiroa/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceiroa/subscriptions","organizations_url":"https://api.github.com/users/ceiroa/orgs","repos_url":"https://api.github.com/users/ceiroa/repos","events_url":"https://api.github.com/users/ceiroa/events{/privacy}","received_events_url":"https://api.github.com/users/ceiroa/received_events","type":"User","site_admin":false},"body":"# Warning\r\nThe post-install script that automatically runs after the push upgrade will reparent all Administrative Address records and make them children of each matching Contact.\r\n\r\n# Info\r\nFor non-Household Accounts in HEDA, **Address functionality is now supported at the Contact level**, which means that you can create addresses directly from the Addresses related list on a Contact. Addresses created in this manner do not propagate up to the Contact's parent Account, and exist independently. Similarly, any mailing address information entered for a Contact does not propagate up to the billing fields of the parent Administrative Account.\r\n\r\nIf you are using the Household Account model, addresses changes made to Contact records *will* propagate up to the Household Account.\r\n\r\n# Issues\r\nFixes #245. \r\nFixes #223. ","created_at":"2016-05-26T21:26:01Z","updated_at":"2016-08-15T22:24:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"367468a014b383539fc3e12a1770504edf867934","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255/commits","review_comments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255/comments","review_comment_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/comments{/number}","comments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/255/comments","statuses_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/statuses/ad946e8a7f079ebf630acef9dd3a96d4def5216c","head":{"label":"SalesforceFoundation:feature/245-contact-addresses","ref":"feature/245-contact-addresses","sha":"ad946e8a7f079ebf630acef9dd3a96d4def5216c","user":{"login":"SalesforceFoundation","id":972305,"avatar_url":"https://avatars.githubusercontent.com/u/972305?v=3","gravatar_id":"","url":"https://api.github.com/users/SalesforceFoundation","html_url":"https://github.com/SalesforceFoundation","followers_url":"https://api.github.com/users/SalesforceFoundation/followers","following_url":"https://api.github.com/users/SalesforceFoundation/following{/other_user}","gists_url":"https://api.github.com/users/SalesforceFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/SalesforceFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SalesforceFoundation/subscriptions","organizations_url":"https://api.github.com/users/SalesforceFoundation/orgs","repos_url":"https://api.github.com/users/SalesforceFoundation/repos","events_url":"https://api.github.com/users/SalesforceFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/SalesforceFoundation/received_events","type":"Organization","site_admin":false},"repo":{"id":31678154,"name":"HEDAP","full_name":"SalesforceFoundation/HEDAP","owner":{"login":"SalesforceFoundation","id":972305,"avatar_url":"https://avatars.githubusercontent.com/u/972305?v=3","gravatar_id":"","url":"https://api.github.com/users/SalesforceFoundation","html_url":"https://github.com/SalesforceFoundation","followers_url":"https://api.github.com/users/SalesforceFoundation/followers","following_url":"https://api.github.com/users/SalesforceFoundation/following{/other_user}","gists_url":"https://api.github.com/users/SalesforceFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/SalesforceFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SalesforceFoundation/subscriptions","organizations_url":"https://api.github.com/users/SalesforceFoundation/orgs","repos_url":"https://api.github.com/users/SalesforceFoundation/repos","events_url":"https://api.github.com/users/SalesforceFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/SalesforceFoundation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SalesforceFoundation/HEDAP","description":"Higher Education Data Architecture Package","fork":false,"url":"https://api.github.com/repos/SalesforceFoundation/HEDAP","forks_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/forks","keys_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/teams","hooks_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/hooks","issue_events_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/events{/number}","events_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/events","assignees_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/assignees{/user}","branches_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/branches{/branch}","tags_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/tags","blobs_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/refs{/sha}","trees_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/statuses/{sha}","languages_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/languages","stargazers_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/stargazers","contributors_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/contributors","subscribers_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/subscribers","subscription_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/subscription","commits_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/commits{/sha}","git_commits_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/commits{/sha}","comments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/comments{/number}","issue_comment_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/comments{/number}","contents_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/contents/{+path}","compare_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/merges","archive_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/downloads","issues_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues{/number}","pulls_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls{/number}","milestones_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/milestones{/number}","notifications_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/labels{/name}","releases_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/releases{/id}","deployments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/deployments","created_at":"2015-03-04T20:24:29Z","updated_at":"2016-07-13T17:57:57Z","pushed_at":"2016-08-15T19:33:01Z","git_url":"git://github.com/SalesforceFoundation/HEDAP.git","ssh_url":"git@github.com:SalesforceFoundation/HEDAP.git","clone_url":"https://github.com/SalesforceFoundation/HEDAP.git","svn_url":"https://github.com/SalesforceFoundation/HEDAP","homepage":"","size":13875,"stargazers_count":24,"watchers_count":24,"language":"Apex","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":28,"mirror_url":null,"open_issues_count":23,"forks":28,"open_issues":23,"watchers":24,"default_branch":"master"}},"base":{"label":"SalesforceFoundation:master","ref":"master","sha":"54780382c0985a098adbbe004e99dba6bf7e3769","user":{"login":"SalesforceFoundation","id":972305,"avatar_url":"https://avatars.githubusercontent.com/u/972305?v=3","gravatar_id":"","url":"https://api.github.com/users/SalesforceFoundation","html_url":"https://github.com/SalesforceFoundation","followers_url":"https://api.github.com/users/SalesforceFoundation/followers","following_url":"https://api.github.com/users/SalesforceFoundation/following{/other_user}","gists_url":"https://api.github.com/users/SalesforceFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/SalesforceFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SalesforceFoundation/subscriptions","organizations_url":"https://api.github.com/users/SalesforceFoundation/orgs","repos_url":"https://api.github.com/users/SalesforceFoundation/repos","events_url":"https://api.github.com/users/SalesforceFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/SalesforceFoundation/received_events","type":"Organization","site_admin":false},"repo":{"id":31678154,"name":"HEDAP","full_name":"SalesforceFoundation/HEDAP","owner":{"login":"SalesforceFoundation","id":972305,"avatar_url":"https://avatars.githubusercontent.com/u/972305?v=3","gravatar_id":"","url":"https://api.github.com/users/SalesforceFoundation","html_url":"https://github.com/SalesforceFoundation","followers_url":"https://api.github.com/users/SalesforceFoundation/followers","following_url":"https://api.github.com/users/SalesforceFoundation/following{/other_user}","gists_url":"https://api.github.com/users/SalesforceFoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/SalesforceFoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SalesforceFoundation/subscriptions","organizations_url":"https://api.github.com/users/SalesforceFoundation/orgs","repos_url":"https://api.github.com/users/SalesforceFoundation/repos","events_url":"https://api.github.com/users/SalesforceFoundation/events{/privacy}","received_events_url":"https://api.github.com/users/SalesforceFoundation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SalesforceFoundation/HEDAP","description":"Higher Education Data Architecture Package","fork":false,"url":"https://api.github.com/repos/SalesforceFoundation/HEDAP","forks_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/forks","keys_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/teams","hooks_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/hooks","issue_events_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/events{/number}","events_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/events","assignees_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/assignees{/user}","branches_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/branches{/branch}","tags_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/tags","blobs_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/refs{/sha}","trees_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/statuses/{sha}","languages_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/languages","stargazers_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/stargazers","contributors_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/contributors","subscribers_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/subscribers","subscription_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/subscription","commits_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/commits{/sha}","git_commits_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/git/commits{/sha}","comments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/comments{/number}","issue_comment_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/comments{/number}","contents_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/contents/{+path}","compare_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/merges","archive_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/downloads","issues_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues{/number}","pulls_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls{/number}","milestones_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/milestones{/number}","notifications_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/labels{/name}","releases_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/releases{/id}","deployments_url":"https://api.github.com/repos/SalesforceFoundation/HEDAP/deployments","created_at":"2015-03-04T20:24:29Z","updated_at":"2016-07-13T17:57:57Z","pushed_at":"2016-08-15T19:33:01Z","git_url":"git://github.com/SalesforceFoundation/HEDAP.git","ssh_url":"git@github.com:SalesforceFoundation/HEDAP.git","clone_url":"https://github.com/SalesforceFoundation/HEDAP.git","svn_url":"https://github.com/SalesforceFoundation/HEDAP","homepage":"","size":13875,"stargazers_count":24,"watchers_count":24,"language":"Apex","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":28,"mirror_url":null,"open_issues_count":23,"forks":28,"open_issues":23,"watchers":24,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255"},"html":{"href":"https://github.com/SalesforceFoundation/HEDAP/pull/255"},"issue":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/255"},"comments":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/issues/255/comments"},"review_comments":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255/comments"},"review_comment":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/pulls/255/commits"},"statuses":{"href":"https://api.github.com/repos/SalesforceFoundation/HEDAP/statuses/ad946e8a7f079ebf630acef9dd3a96d4def5216c"}}}}
{"actor":{"display_login":"davidhabib"}}