diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..7342c09 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,56 @@ +# Unique name for this workflow +name: Run Unit Tests + +# Workflow starts when receiving custom event sent by CI workflow +on: [push, pull_request] + +# Jobs to be executed +jobs: + deploy-and-test: + runs-on: ubuntu-latest + + steps: + # Setup Node (for LWC tests) + - name: Setup NPM + uses: actions/setup-node@v1 + with: + node-version: 16 + + # Install Salesforce CLI + - name: "Install Salesforce CLI" + run: npm install sfdx-cli --global + + # Checkout the source code + - name: "Checkout source code" + uses: actions/checkout@v2 + + # Store secret for dev hub + - name: "Populate auth file with DEVHUB_SFDX_URL secret" + shell: bash + run: echo ${{ secrets.DEVHUB_SFDX_URL}} > ./DEVHUB_SFDX_URL.txt + + # Authenticate dev hub + - name: "Authenticate Dev Hub" + run: sfdx auth:sfdxurl:store -f ./DEVHUB_SFDX_URL.txt -a devhub -d + #node_modules/sfdx-cli/bin/run force:auth:jwt:grant --clientid ${{ secrets.SALESFORCE_CONSUMER_KEY }} --jwtkeyfile server/server.key --username ${{ secrets.SALESFORCE_DEVHUB_USERNAME}} --setdefaultdevhubusername --setalias devhub + + # Remove auth file + - name: "Remove auth file" + run: rm -f ./DEVHUB_SFDX_URL.txt + + # Create scratch org + - name: "Create scratch org" + run: sfdx force:org:create -f config/project-scratch-def.json -a scratch-org -s -d 1 + + # Push source to scratch org + - name: "Push source to scratch org" + run: sfdx force:source:push + + # Run Apex tests + - name: "Run apex tests" + run: sfdx force:apex:test:run -w 5 + + # Housekeeping + - name: "Delete scratch org" + if: always() + run: sfdx force:org:delete -p -u scratch-org \ No newline at end of file diff --git a/config/project-scratch-def.json b/config/project-scratch-def.json new file mode 100644 index 0000000..62156e0 --- /dev/null +++ b/config/project-scratch-def.json @@ -0,0 +1,20 @@ +{ + "orgName": "salonitinker", + "edition": "Developer", + "features": [], + "settings": { + "lightningExperienceSettings": { + "enableS1DesktopEnabled": true + }, + "mobileSettings": { + "enableS1EncryptedStoragePref2": false + }, + "userManagementSettings": { + "enableNewProfileUI": true + }, + "contentSettings": { + "skipContentAssetTriggers": true, + "skipContentAssetTriggersOnDeploy": true + } + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/AccountTrigger.trigger b/force-app/main/default/classes/AccountTrigger.trigger new file mode 100644 index 0000000..35df7a3 --- /dev/null +++ b/force-app/main/default/classes/AccountTrigger.trigger @@ -0,0 +1,17 @@ +trigger AccountTrigger on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete) { + + if (Trigger.isAfter) { + + if (Trigger.isInsert) {} + + if (Trigger.isUpdate) { + //AccountTriggerEmailTesting.method(Trigger.new, Trigger.Old, Trigger.newMap, Trigger.oldMap); + AccountTriggerHandler.onAfterUpdate (Trigger.new, Trigger.Old, Trigger.newMap, Trigger.oldMap); + } + + if (Trigger.isDelete) {} + + if (Trigger.isUndelete) {} + + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/AccountTrigger.trigger-meta.xml b/force-app/main/default/classes/AccountTrigger.trigger-meta.xml new file mode 100644 index 0000000..7acf013 --- /dev/null +++ b/force-app/main/default/classes/AccountTrigger.trigger-meta.xml @@ -0,0 +1,5 @@ + + + 50.0 + Active + diff --git a/force-app/main/default/classes/AccountTriggerHandler.cls b/force-app/main/default/classes/AccountTriggerHandler.cls new file mode 100644 index 0000000..5d2ab00 --- /dev/null +++ b/force-app/main/default/classes/AccountTriggerHandler.cls @@ -0,0 +1,38 @@ +public with sharing class AccountTriggerHandler { + public static void onAfterUpdate (List lstNewAccount, List lstOldAccount, Map mapNewAccount, Map mapOldAccount) { + sendEmailToOldOwnerOnAccountOwnerChange (lstNewAccount, mapOldAccount); + } + + public static void sendEmailToOldOwnerOnAccountOwnerChange (List lstNewAccount, Map mapOldAccount){ + + try { + Map > mapOldOwnerIdWithAccountNames = new Map > (); + + for (Account objAccount : [ SELECT Id, Name, OwnerId FROM Account WHERE ID IN : lstNewAccount ]) { // => Storing account Names and Old Owner Id if Account Owner has been changed + Account objOldAccount = (Account) mapOldAccount.get(objAccount.ID); + if (objAccount.OwnerId != objOldAccount.OwnerId) { + if (!mapOldOwnerIdWithAccountNames.containsKey(objOldAccount.OwnerId)) + mapOldOwnerIdWithAccountNames.put(objOldAccount.OwnerId, new List ()); + mapOldOwnerIdWithAccountNames.get(objOldAccount.OwnerId).add(objAccount.Name); + } + } + + if (mapOldOwnerIdWithAccountNames.size() > 0) { // => Sending Email to the Old Owner if Account Owner has been changed + for (User objUsers : [ SELECT ID, Name, Email FROM User WHERE ID IN : mapOldOwnerIdWithAccountNames.keySet() ]) { + Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); + mail.setSubject('Account Owner Changed!'); + String [] toAddresses = new String [] { objUsers.Email }; + mail.setToAddresses(toAddresses); + List lstChangedOwnersAccountNames = new List (); + lstChangedOwnersAccountNames.addAll(mapOldOwnerIdWithAccountNames.get(objUsers.Id)); + mail.setPlainTextBody('Dear '+ objUsers.Name + ',\n\nOwner of the following Account(s) has been changed on ' + System.now().format('dd/MM/yyyy HH:mm:ss') + ':\n' + lstChangedOwnersAccountNames + '\n\nThank you!');//mapOldOwnerIdWithAccountName.values() + Messaging.sendEmail(new Messaging.Email[] { mail }); + } + } + } + + catch (Exception e) { + System.debug ('--- Exception in sendEmailToOldOwnerOnAccountOwnerChange --- ' + e); + } + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/AccountTriggerHandler.cls-meta.xml b/force-app/main/default/classes/AccountTriggerHandler.cls-meta.xml new file mode 100644 index 0000000..541584f --- /dev/null +++ b/force-app/main/default/classes/AccountTriggerHandler.cls-meta.xml @@ -0,0 +1,5 @@ + + + 50.0 + Active + diff --git a/force-app/main/default/classes/AccountTriggerHandlerTest.cls b/force-app/main/default/classes/AccountTriggerHandlerTest.cls new file mode 100644 index 0000000..86abe66 --- /dev/null +++ b/force-app/main/default/classes/AccountTriggerHandlerTest.cls @@ -0,0 +1,55 @@ +@isTest +public with sharing class AccountTriggerHandlerTest { + @TestSetup + static void makeData() { + Profile objProfile = [SELECT Id FROM Profile WHERE Name='Standard User']; + + User objUser1 = new User( + Alias = 'Test', + LastName='User 1', + Email='TestUser1@gmail.com', + EmailEncodingKey='UTF-8', + LanguageLocaleKey='en_US', + LocaleSidKey='en_US', + ProfileId = objProfile.Id, + TimeZoneSidKey='America/Los_Angeles', + UserName='testuserforownerchange1@gmail.com' + ); + insert objUser1; + + User objUser2 = new User( + Alias = 'Test', + LastName='User 2', + Email='testuser2@gmail.com', + EmailEncodingKey='UTF-8', + LanguageLocaleKey='en_US', + LocaleSidKey='en_US', + ProfileId = objProfile.Id, + TimeZoneSidKey='America/Los_Angeles', + UserName='testuserforownerchange2@gmail.com' + ); + insert objUser2; + } + @isTest + static void changeAccountOwner() { + + Test.startTest(); + + List lstUser = new List (); + + for (User objUser : [ SELECT Id FROM User LIMIT 2 ]) + lstUser.add(objUser); + + Account objAccount = new Account( + Name = 'Test Account', + OwnerId = lstUser[0].Id + ); + insert objAccount; + + objAccount.OwnerId = lstUser[1].Id; + + update objAccount; + + Test.stopTest(); + } +} \ No newline at end of file diff --git a/force-app/main/default/classes/AccountTriggerHandlerTest.cls-meta.xml b/force-app/main/default/classes/AccountTriggerHandlerTest.cls-meta.xml new file mode 100644 index 0000000..541584f --- /dev/null +++ b/force-app/main/default/classes/AccountTriggerHandlerTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 50.0 + Active + diff --git a/server/server.key b/server/server.key new file mode 100644 index 0000000..2854b07 --- /dev/null +++ b/server/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA8HD1wvg8/p9LKngDcfssb8+bJIpQPpIpWpnvfkeQUzTn/HT7 +pkFPqwQRclxyAp9lkchW+cdp/PXTPlwSVBMezL2glyrCFK3SKnXPvk2OVnhcKpF/ +9rKB6u4PWRQ1yXpCP7b+RawUILRm7Kq+feaiXnwJSRLG4e5hBuqSLJii/ahuWnjt +NfMKIrmyrIddOqxW3mZbwIpaqaZ1h/n2wJ2VFWt+pk59TngcJXeRIQOBmxjPzPTD +RLui1RiVzLBhVOHITg+Ba7bWeKUNxDkeClcnf7Um9mkgdA3urQYQdhghMomnrfZi +VFTHkemx8/rooYO+79WA0x43JwwH/UbDyGsoCwIDAQABAoIBAQCN+YWEzOPnTdQq +jZcrNK82jpYDa5Cagd/dWcwq+sLj/veFAOafcy1BxDMxHhlMJybogC2ra+7PqbBE +wEUqU+f56XbsDBlx8HmLDOnQaydDM+roT+YJm7pPKKbzaJWiFIkN6lYWedegwCWO +VfwJh6gUtpUdCvNY8VNlrBkIMFD+CdOwXf2G6Q+Ax3j/kzT9iX7lHqaiADKN+ATe +HlAAA5NyvNkGkKXori1/va7CXM4VLbjtyHvMvvTAqZSLzTvFF/Zf/TXH7w4IY4bg +OQO2CKdOWfOiHU03QyFf0zxM8TAHhEv4+qafL5CujoJdmiOmkr4ZcoKPZdTDPoqb +J4sZzn7BAoGBAPhsA8IavQigN8LwI3aAPaP6Zsv9BErGbWsJu1Cco0iszJZJfYdW +S6cAihovwrwPlxgxxDBg+wJ0GgvyekcWZ5kOnIxaGZWktSUKuM1akEGa7b05mzta +GwDOSeVlCYbp6jekeDYjZ2S+UFv60e58dUftqk3oTAnvD+cNV0syc1exAoGBAPfG +n08I6LkPTUDixWGLJJCGypMNy4u5tuYRd7mqtVSi1WtnI2lAqfL+q4Gor4yMIT0R +L0RHNy0unBgqWdQAFVFKaCLaQG1aTCGyjkzosVzlk+SY0u1QWeLNi1wIOOvObBsn +ap7PNFRngoUjzJRDk7g4m98CUOl73N2YX7tCZ+Z7AoGALCf+7wSTrzHTV4oZs5mN +21TJ6t5ZYfUtQ8ZiDi0ZdPSKFPiRIHo9TjzxT0BIC+d1gu5toe/LDh+W9aKvC223 +EmSffO8S9x9kt2XVfWLom1Cvi1/u8LjObfE8/1JbRCjV4fgRCgZ2/Vg6rm7B2/Mx +YYykJd7xjR56JCah6pLYH7ECgYBC5z7c+GrejWMuwgzJ6ehVng/U5USw1dStCspp +N87+q3k1JwRIx00cb+efewzrxmqzMpCwvwoHCS7c15Kpy9v1t0GnJ4d9/4PPFYeW +/L3nd5YXg6xwp+GdfCLFFvNHuNU3/8yqVQisQcbcbG4xGo4okCCGdvmiEe/xZdNz +nNqxQQKBgBozQM9V77lokVyisEC58VMyKttn8mhOuFeHQBNQ2v6LISJSQnSMRrm+ +I0SVX0TKf/WC0cf/HC7zfJOLVNKLzggmWU4DtGyyp33kDPIrW/izFxoDFHBOP4T8 +XO2gFkFQI2CWjEj1t/62cHhSACXStInB18orK3qYX5ju2+Ph/WNE +-----END RSA PRIVATE KEY----- diff --git a/sfdx-project.json b/sfdx-project.json new file mode 100644 index 0000000..1ac36e6 --- /dev/null +++ b/sfdx-project.json @@ -0,0 +1,11 @@ +{ + "packageDirectories": [ + { + "path": "force-app", + "default": true + } + ], + "namespace": "", + "sfdcLoginUrl": "https://login.salesforce.com", + "sourceApiVersion": "51.0" +}