sf-intelligence 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/LICENSE +20 -13
  2. package/demo-source/main/default/approvalProcesses/Project__c.Discount_Approval.approvalProcess-meta.xml +31 -0
  3. package/demo-source/main/default/classes/IncentiveBatch.cls +54 -0
  4. package/demo-source/main/default/classes/IncentiveBatch.cls-meta.xml +5 -0
  5. package/demo-source/main/default/classes/PaymentService.cls +44 -0
  6. package/demo-source/main/default/classes/PaymentService.cls-meta.xml +5 -0
  7. package/demo-source/main/default/classes/PaymentServiceTest.cls +57 -0
  8. package/demo-source/main/default/classes/PaymentServiceTest.cls-meta.xml +5 -0
  9. package/demo-source/main/default/classes/ProjectTriggerHandler.cls +60 -0
  10. package/demo-source/main/default/classes/ProjectTriggerHandler.cls-meta.xml +5 -0
  11. package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls +80 -0
  12. package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls-meta.xml +5 -0
  13. package/demo-source/main/default/flows/Installation_On_Complete.flow-meta.xml +72 -0
  14. package/demo-source/main/default/flows/Project_On_Approve.flow-meta.xml +48 -0
  15. package/demo-source/main/default/groups/Finance_Group.group-meta.xml +7 -0
  16. package/demo-source/main/default/groups/Ops_Group.group-meta.xml +7 -0
  17. package/demo-source/main/default/layouts/Installation__c-Installation Layout.layout-meta.xml +43 -0
  18. package/demo-source/main/default/layouts/Project__c-Residential Layout.layout-meta.xml +78 -0
  19. package/demo-source/main/default/objects/Battery__c/Battery__c.object-meta.xml +12 -0
  20. package/demo-source/main/default/objects/Battery__c/fields/Capacity_kWh__c.field-meta.xml +9 -0
  21. package/demo-source/main/default/objects/Battery__c/fields/Manufacturer__c.field-meta.xml +21 -0
  22. package/demo-source/main/default/objects/Battery__c/fields/Unit_Cost__c.field-meta.xml +9 -0
  23. package/demo-source/main/default/objects/Equipment_Allocation__c/Equipment_Allocation__c.object-meta.xml +13 -0
  24. package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Battery__c.field-meta.xml +10 -0
  25. package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Line_Total__c.field-meta.xml +10 -0
  26. package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Project__c.field-meta.xml +14 -0
  27. package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Quantity__c.field-meta.xml +9 -0
  28. package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Solar_Panel__c.field-meta.xml +10 -0
  29. package/demo-source/main/default/objects/Incentive__c/Incentive__c.object-meta.xml +12 -0
  30. package/demo-source/main/default/objects/Incentive__c/fields/Amount__c.field-meta.xml +9 -0
  31. package/demo-source/main/default/objects/Incentive__c/fields/Approved__c.field-meta.xml +7 -0
  32. package/demo-source/main/default/objects/Incentive__c/fields/Project__c.field-meta.xml +10 -0
  33. package/demo-source/main/default/objects/Incentive__c/fields/Type__c.field-meta.xml +26 -0
  34. package/demo-source/main/default/objects/Installation__c/Installation__c.object-meta.xml +12 -0
  35. package/demo-source/main/default/objects/Installation__c/fields/Crew_Lead__c.field-meta.xml +10 -0
  36. package/demo-source/main/default/objects/Installation__c/fields/Install_Date__c.field-meta.xml +7 -0
  37. package/demo-source/main/default/objects/Installation__c/fields/Panels_Installed__c.field-meta.xml +9 -0
  38. package/demo-source/main/default/objects/Installation__c/fields/Project__c.field-meta.xml +10 -0
  39. package/demo-source/main/default/objects/Installation__c/fields/Status__c.field-meta.xml +31 -0
  40. package/demo-source/main/default/objects/Invoice__c/Invoice__c.object-meta.xml +17 -0
  41. package/demo-source/main/default/objects/Invoice__c/fields/Amount__c.field-meta.xml +12 -0
  42. package/demo-source/main/default/objects/Invoice__c/fields/Balance__c.field-meta.xml +14 -0
  43. package/demo-source/main/default/objects/Invoice__c/fields/Due_Date__c.field-meta.xml +10 -0
  44. package/demo-source/main/default/objects/Invoice__c/fields/Project__c.field-meta.xml +14 -0
  45. package/demo-source/main/default/objects/Invoice__c/fields/Status__c.field-meta.xml +36 -0
  46. package/demo-source/main/default/objects/Invoice__c/fields/Total_Paid__c.field-meta.xml +12 -0
  47. package/demo-source/main/default/objects/Payment__c/Payment__c.object-meta.xml +17 -0
  48. package/demo-source/main/default/objects/Payment__c/fields/Amount__c.field-meta.xml +12 -0
  49. package/demo-source/main/default/objects/Payment__c/fields/Invoice__c.field-meta.xml +14 -0
  50. package/demo-source/main/default/objects/Payment__c/fields/Method__c.field-meta.xml +36 -0
  51. package/demo-source/main/default/objects/Payment__c/fields/Payment_Date__c.field-meta.xml +10 -0
  52. package/demo-source/main/default/objects/Payment__c/validationRules/Amount_Positive.validationRule-meta.xml +9 -0
  53. package/demo-source/main/default/objects/Permit__c/Permit__c.object-meta.xml +12 -0
  54. package/demo-source/main/default/objects/Permit__c/fields/Approved_Date__c.field-meta.xml +7 -0
  55. package/demo-source/main/default/objects/Permit__c/fields/Jurisdiction__c.field-meta.xml +8 -0
  56. package/demo-source/main/default/objects/Permit__c/fields/Project__c.field-meta.xml +10 -0
  57. package/demo-source/main/default/objects/Permit__c/fields/Status__c.field-meta.xml +26 -0
  58. package/demo-source/main/default/objects/Permit__c/fields/Submitted_Date__c.field-meta.xml +7 -0
  59. package/demo-source/main/default/objects/Permit__c/validationRules/Approved_Needs_Date.validationRule-meta.xml +9 -0
  60. package/demo-source/main/default/objects/Project__c/Project__c.object-meta.xml +12 -0
  61. package/demo-source/main/default/objects/Project__c/fields/Account__c.field-meta.xml +10 -0
  62. package/demo-source/main/default/objects/Project__c/fields/Contract_Value__c.field-meta.xml +9 -0
  63. package/demo-source/main/default/objects/Project__c/fields/Expected_Completion__c.field-meta.xml +7 -0
  64. package/demo-source/main/default/objects/Project__c/fields/Is_Complete__c.field-meta.xml +8 -0
  65. package/demo-source/main/default/objects/Project__c/fields/Margin_Percent__c.field-meta.xml +10 -0
  66. package/demo-source/main/default/objects/Project__c/fields/Opportunity__c.field-meta.xml +10 -0
  67. package/demo-source/main/default/objects/Project__c/fields/Permit_Approved__c.field-meta.xml +7 -0
  68. package/demo-source/main/default/objects/Project__c/fields/Risk_Score__c.field-meta.xml +9 -0
  69. package/demo-source/main/default/objects/Project__c/fields/Status__c.field-meta.xml +41 -0
  70. package/demo-source/main/default/objects/Project__c/fields/System_Size_kW__c.field-meta.xml +9 -0
  71. package/demo-source/main/default/objects/Project__c/fields/Total_Invoiced__c.field-meta.xml +9 -0
  72. package/demo-source/main/default/objects/Project__c/recordTypes/Commercial.recordType-meta.xml +6 -0
  73. package/demo-source/main/default/objects/Project__c/recordTypes/Residential.recordType-meta.xml +6 -0
  74. package/demo-source/main/default/objects/Project__c/validationRules/Complete_Requires_Permit.validationRule-meta.xml +9 -0
  75. package/demo-source/main/default/objects/SBQQ__ProductRule__c/SBQQ__ProductRule__c.object-meta.xml +16 -0
  76. package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__ErrorMessage__c.field-meta.xml +12 -0
  77. package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__Type__c.field-meta.xml +12 -0
  78. package/demo-source/main/default/objects/Service_Visit__c/Service_Visit__c.object-meta.xml +17 -0
  79. package/demo-source/main/default/objects/Service_Visit__c/fields/Installation__c.field-meta.xml +13 -0
  80. package/demo-source/main/default/objects/Service_Visit__c/fields/Reason__c.field-meta.xml +36 -0
  81. package/demo-source/main/default/objects/Service_Visit__c/fields/Resolved__c.field-meta.xml +10 -0
  82. package/demo-source/main/default/objects/Service_Visit__c/fields/Technician__c.field-meta.xml +13 -0
  83. package/demo-source/main/default/objects/Service_Visit__c/fields/Visit_Date__c.field-meta.xml +10 -0
  84. package/demo-source/main/default/objects/Site_Survey__c/Site_Survey__c.object-meta.xml +12 -0
  85. package/demo-source/main/default/objects/Site_Survey__c/fields/Project__c.field-meta.xml +10 -0
  86. package/demo-source/main/default/objects/Site_Survey__c/fields/Roof_Type__c.field-meta.xml +31 -0
  87. package/demo-source/main/default/objects/Site_Survey__c/fields/Shading_Factor__c.field-meta.xml +9 -0
  88. package/demo-source/main/default/objects/Site_Survey__c/fields/Survey_Date__c.field-meta.xml +7 -0
  89. package/demo-source/main/default/objects/Site_Survey__c/fields/Surveyor__c.field-meta.xml +10 -0
  90. package/demo-source/main/default/objects/Solar_Panel__c/Solar_Panel__c.object-meta.xml +12 -0
  91. package/demo-source/main/default/objects/Solar_Panel__c/fields/Active__c.field-meta.xml +7 -0
  92. package/demo-source/main/default/objects/Solar_Panel__c/fields/Manufacturer__c.field-meta.xml +26 -0
  93. package/demo-source/main/default/objects/Solar_Panel__c/fields/Unit_Cost__c.field-meta.xml +9 -0
  94. package/demo-source/main/default/objects/Solar_Panel__c/fields/Wattage__c.field-meta.xml +9 -0
  95. package/demo-source/main/default/objects/Warranty_Claim__c/Warranty_Claim__c.object-meta.xml +17 -0
  96. package/demo-source/main/default/objects/Warranty_Claim__c/fields/Claim_Date__c.field-meta.xml +10 -0
  97. package/demo-source/main/default/objects/Warranty_Claim__c/fields/Installation__c.field-meta.xml +13 -0
  98. package/demo-source/main/default/objects/Warranty_Claim__c/fields/Issue__c.field-meta.xml +12 -0
  99. package/demo-source/main/default/objects/Warranty_Claim__c/fields/Status__c.field-meta.xml +36 -0
  100. package/demo-source/main/default/omniDataTransforms/Quote_To_Project_Map_1.rpt-meta.xml +83 -0
  101. package/demo-source/main/default/omniIntegrationProcedures/Project_Provision_1.oip-meta.xml +87 -0
  102. package/demo-source/main/default/omniScripts/Customer_Intake_English_1.os-meta.xml +69 -0
  103. package/demo-source/main/default/permissionsets/Finance_Team.permissionset-meta.xml +77 -0
  104. package/demo-source/main/default/permissionsets/Project_Manager.permissionset-meta.xml +88 -0
  105. package/demo-source/main/default/profiles/Verdant_Installer.profile-meta.xml +193 -0
  106. package/demo-source/main/default/profiles/Verdant_Read_Only.profile-meta.xml +263 -0
  107. package/demo-source/main/default/profiles/Verdant_Sales_Rep.profile-meta.xml +194 -0
  108. package/demo-source/main/default/queues/Permit_Review_Queue.queue-meta.xml +9 -0
  109. package/demo-source/main/default/queues/Warranty_Queue.queue-meta.xml +9 -0
  110. package/demo-source/main/default/roles/Installer.role-meta.xml +10 -0
  111. package/demo-source/main/default/roles/Ops_Director.role-meta.xml +9 -0
  112. package/demo-source/main/default/roles/Ops_Manager.role-meta.xml +10 -0
  113. package/demo-source/main/default/roles/Sales_Manager.role-meta.xml +10 -0
  114. package/demo-source/main/default/roles/Sales_Rep.role-meta.xml +10 -0
  115. package/demo-source/main/default/roles/Sales_VP.role-meta.xml +9 -0
  116. package/demo-source/main/default/sharingRules/Invoice__c.sharingRules-meta.xml +17 -0
  117. package/demo-source/main/default/sharingRules/Project__c.sharingRules-meta.xml +17 -0
  118. package/demo-source/main/default/triggers/ProjectTrigger.trigger +8 -0
  119. package/demo-source/main/default/triggers/ProjectTrigger.trigger-meta.xml +5 -0
  120. package/demo-source/main/default/workflows/Opportunity.workflow-meta.xml +24 -0
  121. package/dist/index.js +537 -441
  122. package/package.json +38 -20
  123. package/server.json +32 -0
package/LICENSE CHANGED
@@ -12,23 +12,30 @@ furnished to do so, subject to the following conditions:
12
12
  1. The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
- 2. COMMONS CLAUSE (Restriction on Commercial Use)
16
- The licensor hereby grants you permission to use the Software for
17
- non-commercial purposes only.
15
+ 2. COMMONS CLAUSE LICENSE CONDITION v1.0
18
16
 
19
- "Commercial use" means any use of the Software where the primary purpose is
20
- to generate revenue or commercial advantage, including but not limited to:
17
+ The Software is provided to you by the Licensor under the MIT License above,
18
+ subject to the following condition.
21
19
 
22
- - Offering the Software or any derivative works as a service or product
23
- - Bundling the Software with a commercial offering
24
- - Using the Software to create a competing intelligence platform
25
- - Reselling access to the Software or its output
26
- - Using the Software as part of a paid consulting engagement
20
+ Without limiting other conditions in the License, the grant of rights under
21
+ the License will not include, and the License does not grant to you, the
22
+ right to Sell the Software.
27
23
 
28
- If you wish to use this Software for commercial purposes, you must obtain a
29
- commercial license. Contact: pranav.sfintelligence@gmail.com
24
+ For purposes of the foregoing, "Sell" means practicing any or all of the
25
+ rights granted to you under the License to provide to third parties, for a
26
+ fee or other consideration (including without limitation fees for hosting or
27
+ consulting/support services related to the Software), a product or service
28
+ whose value derives, entirely or substantially, from the functionality of
29
+ the Software. Any license notice or attribution required by the License must
30
+ also include this Commons Clause License Condition notice.
30
31
 
31
- 3. For non-commercial use, attribution is appreciated but not required.
32
+ Software: sf-intelligence
33
+ License: MIT
34
+ Licensor: sf-intelligence contributors
35
+
36
+ For a license to Sell the Software, contact: pranav.sfintelligence@gmail.com
37
+
38
+ 3. Attribution is appreciated but not required.
32
39
 
33
40
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34
41
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApprovalProcess xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <active>true</active>
4
+ <allowRecall>true</allowRecall>
5
+ <allowedSubmitters>
6
+ <type>owner</type>
7
+ </allowedSubmitters>
8
+ <approvalStep>
9
+ <name>step_1_manager</name>
10
+ <label>Manager Approval</label>
11
+ <assignedApprover>
12
+ <approver>
13
+ <type>userHierarchyField</type>
14
+ </approver>
15
+ <whenMultipleApprovers>FirstResponse</whenMultipleApprovers>
16
+ </assignedApprover>
17
+ <allowDelegate>false</allowDelegate>
18
+ </approvalStep>
19
+ <description>High-value Projects (Contract Value over 50,000) require manager approval before proceeding.</description>
20
+ <enableMobileDeviceAccess>false</enableMobileDeviceAccess>
21
+ <entryCriteria>
22
+ <criteriaItems>
23
+ <field>Project__c.Contract_Value__c</field>
24
+ <operation>greaterThan</operation>
25
+ <value>50000</value>
26
+ </criteriaItems>
27
+ </entryCriteria>
28
+ <label>Discount Approval</label>
29
+ <recordEditability>AdminOnly</recordEditability>
30
+ <showApprovalHistory>true</showApprovalHistory>
31
+ </ApprovalProcess>
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Batch job that reconciles incentives against their parent projects.
3
+ *
4
+ * NOTE: This class intentionally contains anti-patterns for the
5
+ * sf-intelligence demo (governor_limit_risks + find_hardcoded_values):
6
+ * - a SOQL query INSIDE a for-loop, and
7
+ * - a hardcoded 15-char Salesforce Id literal.
8
+ * Do not copy this style into real code.
9
+ */
10
+ public with sharing class IncentiveBatch implements Database.Batchable<SObject> {
11
+
12
+ // Hardcoded record Id literal (intentional anti-pattern for the demo).
13
+ private static final Id FALLBACK_PROJECT_ID = 'a01000000000001';
14
+
15
+ public Database.QueryLocator start(Database.BatchableContext bc) {
16
+ return Database.getQueryLocator([
17
+ SELECT Id, Project__c, Amount__c, Approved__c, Type__c
18
+ FROM Incentive__c
19
+ WHERE Approved__c = true
20
+ ]);
21
+ }
22
+
23
+ public void execute(Database.BatchableContext bc, List<Incentive__c> scope) {
24
+ List<Project__c> toUpdate = new List<Project__c>();
25
+
26
+ for (Incentive__c inc : scope) {
27
+ // INTENTIONAL: SOQL query inside a for-loop (governor-limit risk).
28
+ List<Project__c> projects = [
29
+ SELECT Id, Contract_Value__c, Status__c
30
+ FROM Project__c
31
+ WHERE Id = :inc.Project__c
32
+ LIMIT 1
33
+ ];
34
+
35
+ Project__c proj;
36
+ if (projects.isEmpty()) {
37
+ // Fall back to the hardcoded Id (intentional anti-pattern).
38
+ proj = new Project__c(Id = FALLBACK_PROJECT_ID);
39
+ } else {
40
+ proj = projects[0];
41
+ }
42
+
43
+ toUpdate.add(proj);
44
+ }
45
+
46
+ if (!toUpdate.isEmpty()) {
47
+ update toUpdate;
48
+ }
49
+ }
50
+
51
+ public void finish(Database.BatchableContext bc) {
52
+ System.debug(LoggingLevel.INFO, 'IncentiveBatch finished.');
53
+ }
54
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Applies payments against invoices. Updates the invoice's running paid total
3
+ * (Invoice__c.Total_Paid__c is a roll-up of Payment__c.Amount__c) and marks the
4
+ * invoice Paid when fully covered.
5
+ */
6
+ public with sharing class PaymentService {
7
+
8
+ /**
9
+ * Apply a payment to an invoice.
10
+ *
11
+ * @param invoiceId the invoice receiving the payment
12
+ * @param amount the payment amount (Payment__c.Amount__c)
13
+ * @param method the payment method picklist value
14
+ * @return the inserted Payment__c record
15
+ */
16
+ public static Payment__c applyPayment(Id invoiceId, Decimal amount, String method) {
17
+ Invoice__c invoice = [
18
+ SELECT Id, Amount__c, Total_Paid__c, Status__c
19
+ FROM Invoice__c
20
+ WHERE Id = :invoiceId
21
+ LIMIT 1
22
+ ];
23
+
24
+ Payment__c payment = new Payment__c(
25
+ Invoice__c = invoiceId,
26
+ Amount__c = amount,
27
+ Payment_Date__c = Date.today(),
28
+ Method__c = method
29
+ );
30
+ insert payment;
31
+
32
+ // Total_Paid__c is maintained by the roll-up, but we read it here to
33
+ // decide whether the invoice is now fully paid.
34
+ Decimal priorPaid = invoice.Total_Paid__c == null ? 0 : invoice.Total_Paid__c;
35
+ Decimal newPaid = priorPaid + amount;
36
+
37
+ if (invoice.Amount__c != null && newPaid >= invoice.Amount__c) {
38
+ invoice.Status__c = 'Paid';
39
+ update invoice;
40
+ }
41
+
42
+ return payment;
43
+ }
44
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Tests for PaymentService.
3
+ *
4
+ * NOTE: This test INTENTIONALLY contains NO assertions (no System.assert*)
5
+ * for the sf-intelligence meaningful_test_audit demo. It runs the code path
6
+ * and "passes" without verifying any outcome. Do not copy this style into
7
+ * real code.
8
+ */
9
+ @isTest
10
+ private class PaymentServiceTest {
11
+
12
+ @isTest
13
+ static void applyPaymentRuns() {
14
+ Project__c proj = new Project__c(
15
+ Status__c = 'Installing',
16
+ Contract_Value__c = 30000
17
+ );
18
+ insert proj;
19
+
20
+ Invoice__c invoice = new Invoice__c(
21
+ Project__c = proj.Id,
22
+ Amount__c = 1000,
23
+ Due_Date__c = Date.today().addDays(30),
24
+ Status__c = 'Sent'
25
+ );
26
+ insert invoice;
27
+
28
+ Test.startTest();
29
+ PaymentService.applyPayment(invoice.Id, 1000, 'ACH');
30
+ Test.stopTest();
31
+
32
+ // No assertions here on purpose (meaningful_test_audit demo).
33
+ }
34
+
35
+ @isTest
36
+ static void applyPartialPaymentRuns() {
37
+ Project__c proj = new Project__c(
38
+ Status__c = 'Installing',
39
+ Contract_Value__c = 30000
40
+ );
41
+ insert proj;
42
+
43
+ Invoice__c invoice = new Invoice__c(
44
+ Project__c = proj.Id,
45
+ Amount__c = 2000,
46
+ Due_Date__c = Date.today().addDays(30),
47
+ Status__c = 'Sent'
48
+ );
49
+ insert invoice;
50
+
51
+ Test.startTest();
52
+ PaymentService.applyPayment(invoice.Id, 500, 'Card');
53
+ Test.stopTest();
54
+
55
+ // Still no assertions (deliberate).
56
+ }
57
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Handler for ProjectTrigger. Derives Risk_Score__c on Project__c from the
3
+ * project's Status__c and Contract_Value__c, and propagates status changes.
4
+ */
5
+ public with sharing class ProjectTriggerHandler {
6
+
7
+ /**
8
+ * Before insert/update: derive a 0-100 risk score for each project.
9
+ * Higher contract values and unstarted/cancelled statuses raise risk.
10
+ */
11
+ public static void handleBeforeSave(List<Project__c> projects) {
12
+ for (Project__c proj : projects) {
13
+ proj.Risk_Score__c = computeRiskScore(proj);
14
+ }
15
+ }
16
+
17
+ /**
18
+ * After update: when a project is moved to Cancelled, log the change.
19
+ * Kept simple for the demo; no DML on related records here.
20
+ */
21
+ public static void handleAfterUpdate(List<Project__c> projects, Map<Id, Project__c> oldMap) {
22
+ for (Project__c proj : projects) {
23
+ Project__c prior = oldMap.get(proj.Id);
24
+ if (prior != null && prior.Status__c != proj.Status__c && proj.Status__c == 'Cancelled') {
25
+ System.debug(LoggingLevel.INFO, 'Project ' + proj.Id + ' moved to Cancelled');
26
+ }
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Risk model: base on status, then scale by contract value.
32
+ */
33
+ private static Decimal computeRiskScore(Project__c proj) {
34
+ Decimal score = 0;
35
+ String status = proj.Status__c;
36
+ if (status == 'Draft') {
37
+ score = 40;
38
+ } else if (status == 'Approved' || status == 'Permitting') {
39
+ score = 25;
40
+ } else if (status == 'Installing') {
41
+ score = 15;
42
+ } else if (status == 'Complete') {
43
+ score = 5;
44
+ } else if (status == 'Cancelled') {
45
+ score = 80;
46
+ }
47
+
48
+ Decimal value = proj.Contract_Value__c == null ? 0 : proj.Contract_Value__c;
49
+ if (value > 100000) {
50
+ score += 20;
51
+ } else if (value > 50000) {
52
+ score += 10;
53
+ }
54
+
55
+ if (score > 100) {
56
+ score = 100;
57
+ }
58
+ return score;
59
+ }
60
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Tests for ProjectTriggerHandler. This is a REAL test: it exercises the
3
+ * risk-score derivation and asserts the expected outcomes (System.assert*).
4
+ */
5
+ @isTest
6
+ private class ProjectTriggerHandlerTest {
7
+
8
+ @isTest
9
+ static void draftHighValueProjectGetsElevatedRisk() {
10
+ Project__c proj = new Project__c(
11
+ Status__c = 'Draft',
12
+ Contract_Value__c = 120000
13
+ );
14
+
15
+ Test.startTest();
16
+ insert proj;
17
+ Test.stopTest();
18
+
19
+ Project__c reloaded = [
20
+ SELECT Id, Risk_Score__c
21
+ FROM Project__c
22
+ WHERE Id = :proj.Id
23
+ LIMIT 1
24
+ ];
25
+
26
+ // Draft (40) + value > 100000 (20) = 60.
27
+ System.assertEquals(60, reloaded.Risk_Score__c,
28
+ 'Draft high-value project should score 60');
29
+ }
30
+
31
+ @isTest
32
+ static void completeLowValueProjectGetsLowRisk() {
33
+ Project__c proj = new Project__c(
34
+ Status__c = 'Complete',
35
+ Contract_Value__c = 10000,
36
+ Permit_Approved__c = true
37
+ );
38
+
39
+ Test.startTest();
40
+ insert proj;
41
+ Test.stopTest();
42
+
43
+ Project__c reloaded = [
44
+ SELECT Id, Risk_Score__c, Status__c
45
+ FROM Project__c
46
+ WHERE Id = :proj.Id
47
+ LIMIT 1
48
+ ];
49
+
50
+ System.assertEquals('Complete', reloaded.Status__c,
51
+ 'Status should persist as Complete');
52
+ System.assert(reloaded.Risk_Score__c < 20,
53
+ 'Complete low-value project should score under 20');
54
+ }
55
+
56
+ @isTest
57
+ static void cancelledProjectGetsHighRisk() {
58
+ Project__c proj = new Project__c(
59
+ Status__c = 'Draft',
60
+ Contract_Value__c = 5000
61
+ );
62
+ insert proj;
63
+
64
+ proj.Status__c = 'Cancelled';
65
+
66
+ Test.startTest();
67
+ update proj;
68
+ Test.stopTest();
69
+
70
+ Project__c reloaded = [
71
+ SELECT Id, Risk_Score__c
72
+ FROM Project__c
73
+ WHERE Id = :proj.Id
74
+ LIMIT 1
75
+ ];
76
+
77
+ System.assert(reloaded.Risk_Score__c >= 80,
78
+ 'Cancelled project should score at least 80');
79
+ }
80
+ }
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <status>Active</status>
5
+ </ApexClass>
@@ -0,0 +1,72 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Flow xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <description>When an Installation is Completed, log an Inspection Service Visit and mark the parent Project Complete.</description>
5
+ <label>Installation On Complete</label>
6
+ <processType>AutoLaunchedFlow</processType>
7
+ <status>Active</status>
8
+ <interviewLabel>Installation On Complete {!$Flow.CurrentDateTime}</interviewLabel>
9
+ <environments>Default</environments>
10
+ <start>
11
+ <locationX>176</locationX>
12
+ <locationY>0</locationY>
13
+ <connector>
14
+ <targetReference>Create_Service_Visit</targetReference>
15
+ </connector>
16
+ <object>Installation__c</object>
17
+ <recordTriggerType>CreateAndUpdate</recordTriggerType>
18
+ <triggerType>RecordAfterSave</triggerType>
19
+ <filterLogic>and</filterLogic>
20
+ <filters>
21
+ <field>Status__c</field>
22
+ <operator>EqualTo</operator>
23
+ <value>
24
+ <stringValue>Completed</stringValue>
25
+ </value>
26
+ </filters>
27
+ </start>
28
+ <recordCreates>
29
+ <name>Create_Service_Visit</name>
30
+ <label>Create Service Visit</label>
31
+ <locationX>176</locationX>
32
+ <locationY>134</locationY>
33
+ <connector>
34
+ <targetReference>Update_Parent_Project</targetReference>
35
+ </connector>
36
+ <inputAssignments>
37
+ <field>Installation__c</field>
38
+ <value>
39
+ <elementReference>$Record.Id</elementReference>
40
+ </value>
41
+ </inputAssignments>
42
+ <inputAssignments>
43
+ <field>Reason__c</field>
44
+ <value>
45
+ <stringValue>Inspection</stringValue>
46
+ </value>
47
+ </inputAssignments>
48
+ <object>Service_Visit__c</object>
49
+ <storeOutputAutomatically>true</storeOutputAutomatically>
50
+ </recordCreates>
51
+ <recordUpdates>
52
+ <name>Update_Parent_Project</name>
53
+ <label>Update Parent Project</label>
54
+ <locationX>176</locationX>
55
+ <locationY>254</locationY>
56
+ <filterLogic>and</filterLogic>
57
+ <filters>
58
+ <field>Id</field>
59
+ <operator>EqualTo</operator>
60
+ <value>
61
+ <elementReference>$Record.Project__c</elementReference>
62
+ </value>
63
+ </filters>
64
+ <inputAssignments>
65
+ <field>Status__c</field>
66
+ <value>
67
+ <stringValue>Complete</stringValue>
68
+ </value>
69
+ </inputAssignments>
70
+ <object>Project__c</object>
71
+ </recordUpdates>
72
+ </Flow>
@@ -0,0 +1,48 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Flow xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <apiVersion>61.0</apiVersion>
4
+ <description>When a Project is approved, create a Permit record (Submitted) linked back to the Project.</description>
5
+ <label>Project On Approve</label>
6
+ <processType>AutoLaunchedFlow</processType>
7
+ <status>Active</status>
8
+ <interviewLabel>Project On Approve {!$Flow.CurrentDateTime}</interviewLabel>
9
+ <environments>Default</environments>
10
+ <start>
11
+ <locationX>176</locationX>
12
+ <locationY>0</locationY>
13
+ <connector>
14
+ <targetReference>Create_Permit</targetReference>
15
+ </connector>
16
+ <object>Project__c</object>
17
+ <recordTriggerType>CreateAndUpdate</recordTriggerType>
18
+ <triggerType>RecordAfterSave</triggerType>
19
+ <filterLogic>and</filterLogic>
20
+ <filters>
21
+ <field>Status__c</field>
22
+ <operator>EqualTo</operator>
23
+ <value>
24
+ <stringValue>Approved</stringValue>
25
+ </value>
26
+ </filters>
27
+ </start>
28
+ <recordCreates>
29
+ <name>Create_Permit</name>
30
+ <label>Create Permit</label>
31
+ <locationX>176</locationX>
32
+ <locationY>134</locationY>
33
+ <inputAssignments>
34
+ <field>Project__c</field>
35
+ <value>
36
+ <elementReference>$Record.Id</elementReference>
37
+ </value>
38
+ </inputAssignments>
39
+ <inputAssignments>
40
+ <field>Status__c</field>
41
+ <value>
42
+ <stringValue>Submitted</stringValue>
43
+ </value>
44
+ </inputAssignments>
45
+ <object>Permit__c</object>
46
+ <storeOutputAutomatically>true</storeOutputAutomatically>
47
+ </recordCreates>
48
+ </Flow>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Group xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <name>Finance Group</name>
4
+ <doesIncludeBosses>false</doesIncludeBosses>
5
+ <doesSendEmailToMembers>false</doesSendEmailToMembers>
6
+ <description>Finance team members who handle invoices and payments.</description>
7
+ </Group>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Group xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <name>Ops Group</name>
4
+ <doesIncludeBosses>false</doesIncludeBosses>
5
+ <doesSendEmailToMembers>false</doesSendEmailToMembers>
6
+ <description>Operations team members who manage projects and installations.</description>
7
+ </Group>
@@ -0,0 +1,43 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Layout xmlns="http://soap.sforce.com/2006/04/metadata">
3
+ <layoutSections>
4
+ <customLabel>false</customLabel>
5
+ <detailHeading>false</detailHeading>
6
+ <editHeading>true</editHeading>
7
+ <label>Information</label>
8
+ <layoutColumns>
9
+ <layoutItems>
10
+ <behavior>Required</behavior>
11
+ <field>Name</field>
12
+ </layoutItems>
13
+ <layoutItems>
14
+ <behavior>Edit</behavior>
15
+ <field>Project__c</field>
16
+ </layoutItems>
17
+ <layoutItems>
18
+ <behavior>Edit</behavior>
19
+ <field>Status__c</field>
20
+ </layoutItems>
21
+ </layoutColumns>
22
+ <layoutColumns>
23
+ <layoutItems>
24
+ <behavior>Edit</behavior>
25
+ <field>Install_Date__c</field>
26
+ </layoutItems>
27
+ <layoutItems>
28
+ <behavior>Edit</behavior>
29
+ <field>Crew_Lead__c</field>
30
+ </layoutItems>
31
+ <layoutItems>
32
+ <behavior>Edit</behavior>
33
+ <field>Panels_Installed__c</field>
34
+ </layoutItems>
35
+ </layoutColumns>
36
+ <style>TwoColumnsTopToBottom</style>
37
+ </layoutSections>
38
+ <showEmailCheckbox>false</showEmailCheckbox>
39
+ <showHighlightsPanel>true</showHighlightsPanel>
40
+ <showInteractionLogPanel>false</showInteractionLogPanel>
41
+ <showRunAssignmentRulesCheckbox>false</showRunAssignmentRulesCheckbox>
42
+ <showSubmitAndAttachButton>false</showSubmitAndAttachButton>
43
+ </Layout>