sf-intelligence 0.1.12 → 0.1.14
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.
- package/LICENSE +20 -13
- package/demo-source/main/default/approvalProcesses/Project__c.Discount_Approval.approvalProcess-meta.xml +31 -0
- package/demo-source/main/default/classes/IncentiveBatch.cls +54 -0
- package/demo-source/main/default/classes/IncentiveBatch.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/PaymentService.cls +44 -0
- package/demo-source/main/default/classes/PaymentService.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/PaymentServiceTest.cls +57 -0
- package/demo-source/main/default/classes/PaymentServiceTest.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/ProjectTriggerHandler.cls +60 -0
- package/demo-source/main/default/classes/ProjectTriggerHandler.cls-meta.xml +5 -0
- package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls +80 -0
- package/demo-source/main/default/classes/ProjectTriggerHandlerTest.cls-meta.xml +5 -0
- package/demo-source/main/default/flows/Installation_On_Complete.flow-meta.xml +72 -0
- package/demo-source/main/default/flows/Project_On_Approve.flow-meta.xml +48 -0
- package/demo-source/main/default/groups/Finance_Group.group-meta.xml +7 -0
- package/demo-source/main/default/groups/Ops_Group.group-meta.xml +7 -0
- package/demo-source/main/default/layouts/Installation__c-Installation Layout.layout-meta.xml +43 -0
- package/demo-source/main/default/layouts/Project__c-Residential Layout.layout-meta.xml +78 -0
- package/demo-source/main/default/objects/Battery__c/Battery__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Battery__c/fields/Capacity_kWh__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Battery__c/fields/Manufacturer__c.field-meta.xml +21 -0
- package/demo-source/main/default/objects/Battery__c/fields/Unit_Cost__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/Equipment_Allocation__c.object-meta.xml +13 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Battery__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Line_Total__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Project__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Quantity__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Equipment_Allocation__c/fields/Solar_Panel__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Incentive__c/Incentive__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Amount__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Approved__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Incentive__c/fields/Type__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Installation__c/Installation__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Installation__c/fields/Crew_Lead__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Installation__c/fields/Install_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Installation__c/fields/Panels_Installed__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Installation__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Installation__c/fields/Status__c.field-meta.xml +31 -0
- package/demo-source/main/default/objects/Invoice__c/Invoice__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Amount__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Balance__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Due_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Project__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Status__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Invoice__c/fields/Total_Paid__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Payment__c/Payment__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Payment__c/fields/Amount__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Payment__c/fields/Invoice__c.field-meta.xml +14 -0
- package/demo-source/main/default/objects/Payment__c/fields/Method__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Payment__c/fields/Payment_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Payment__c/validationRules/Amount_Positive.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/Permit__c/Permit__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Permit__c/fields/Approved_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Permit__c/fields/Jurisdiction__c.field-meta.xml +8 -0
- package/demo-source/main/default/objects/Permit__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Permit__c/fields/Status__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Permit__c/fields/Submitted_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Permit__c/validationRules/Approved_Needs_Date.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/Project__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Project__c/fields/Account__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Contract_Value__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Expected_Completion__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Project__c/fields/Is_Complete__c.field-meta.xml +8 -0
- package/demo-source/main/default/objects/Project__c/fields/Margin_Percent__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Opportunity__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Project__c/fields/Permit_Approved__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Project__c/fields/Risk_Score__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Status__c.field-meta.xml +41 -0
- package/demo-source/main/default/objects/Project__c/fields/System_Size_kW__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/fields/Total_Invoiced__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Project__c/recordTypes/Commercial.recordType-meta.xml +6 -0
- package/demo-source/main/default/objects/Project__c/recordTypes/Residential.recordType-meta.xml +6 -0
- package/demo-source/main/default/objects/Project__c/validationRules/Complete_Requires_Permit.validationRule-meta.xml +9 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/SBQQ__ProductRule__c.object-meta.xml +16 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__ErrorMessage__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/SBQQ__ProductRule__c/fields/SBQQ__Type__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Service_Visit__c/Service_Visit__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Installation__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Reason__c.field-meta.xml +36 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Resolved__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Technician__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Service_Visit__c/fields/Visit_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Site_Survey__c/Site_Survey__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Project__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Roof_Type__c.field-meta.xml +31 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Shading_Factor__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Survey_Date__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Site_Survey__c/fields/Surveyor__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Solar_Panel__c/Solar_Panel__c.object-meta.xml +12 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Active__c.field-meta.xml +7 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Manufacturer__c.field-meta.xml +26 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Unit_Cost__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Solar_Panel__c/fields/Wattage__c.field-meta.xml +9 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/Warranty_Claim__c.object-meta.xml +17 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Claim_Date__c.field-meta.xml +10 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Installation__c.field-meta.xml +13 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Issue__c.field-meta.xml +12 -0
- package/demo-source/main/default/objects/Warranty_Claim__c/fields/Status__c.field-meta.xml +36 -0
- package/demo-source/main/default/omniDataTransforms/Quote_To_Project_Map_1.rpt-meta.xml +83 -0
- package/demo-source/main/default/omniIntegrationProcedures/Project_Provision_1.oip-meta.xml +87 -0
- package/demo-source/main/default/omniScripts/Customer_Intake_English_1.os-meta.xml +69 -0
- package/demo-source/main/default/permissionsets/Finance_Team.permissionset-meta.xml +77 -0
- package/demo-source/main/default/permissionsets/Project_Manager.permissionset-meta.xml +88 -0
- package/demo-source/main/default/profiles/Verdant_Installer.profile-meta.xml +193 -0
- package/demo-source/main/default/profiles/Verdant_Read_Only.profile-meta.xml +263 -0
- package/demo-source/main/default/profiles/Verdant_Sales_Rep.profile-meta.xml +194 -0
- package/demo-source/main/default/queues/Permit_Review_Queue.queue-meta.xml +9 -0
- package/demo-source/main/default/queues/Warranty_Queue.queue-meta.xml +9 -0
- package/demo-source/main/default/roles/Installer.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Ops_Director.role-meta.xml +9 -0
- package/demo-source/main/default/roles/Ops_Manager.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_Manager.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_Rep.role-meta.xml +10 -0
- package/demo-source/main/default/roles/Sales_VP.role-meta.xml +9 -0
- package/demo-source/main/default/sharingRules/Invoice__c.sharingRules-meta.xml +17 -0
- package/demo-source/main/default/sharingRules/Project__c.sharingRules-meta.xml +17 -0
- package/demo-source/main/default/triggers/ProjectTrigger.trigger +8 -0
- package/demo-source/main/default/triggers/ProjectTrigger.trigger-meta.xml +5 -0
- package/demo-source/main/default/workflows/Opportunity.workflow-meta.xml +24 -0
- package/dist/index.js +537 -441
- package/package.json +38 -20
- 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
|
|
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
|
-
|
|
20
|
-
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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,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,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,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,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,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>
|