This repository is the capstone project for the Developer Kickstart curriculum at Cloud Code Academy. You will design and build a Salesforce solution that tracks workers' compensation claims for an HR team — from data model and CSV import through Apex, triggers, and record pages — while applying what you learned across the program.
Throughout this project, you will practice:
- Modeling standard and custom objects and relationships that fit real-world workers' comp scenarios (including many-to-many links between employees and claims).
- Importing the provided CSV data in
data/with the Data Import Wizard or Data Loader — not manual entry. - Implementing bulk-safe Apex and triggers for risk levels, claim duration, rollups, status rules, and high-value flags, as specified by your instructor.
- Delivering record pages and collaboration practices (GitHub, team workflow) that reflect how you would ship in a real org.
Your instructor will share the full requirements, timeline, presentation expectations, and rubric.
- Create or configure a Trailhead Playground or Developer Edition org for this project.
- Install Visual Studio Code.
- Install Salesforce Extension Pack in VS Code (search Extensions for "Salesforce Extension Pack").
- Authorize your org: Cmd/Ctrl + Shift + P →
SFDX: Authorize an Org→ complete login in the browser. - Deploy from your machine using the command palette or SFDX: Deploy Source to Org on folders or files.
- Open
force-app/main/default/and deploy the metadata to your org (right-click the folder → SFDX: Deploy Source to Org). - Review the CSV files in
data/and align your schema before importing. - Build to your instructor's requirements, deploy iteratively, and keep metadata in this repository.
- Run Apex tests from VS Code if you add tests (SFDX: Run Apex Tests).
- Push your work to GitHub and submit the repository link where your cohort asks (for example, the Slack submission form).
Apex is Salesforce's strongly-typed, Java-like programming language. Everything you write runs on Salesforce's servers, not your machine. This section walks you through what Apex classes and triggers you will likely need for this project, how to think about each one, and the patterns that keep them production-ready.
- In VS Code open the Command Palette (
Cmd/Ctrl + Shift + P). - Type
SFDX: Create Apex Classand press Enter. - Give the class a name (e.g.,
ClaimTriggerHandler) and chooseforce-app/main/default/classesas the folder. - Two files are created:
ClassName.clsandClassName.cls-meta.xml. Both must be committed to Git. - Write your code, save, then deploy: right-click the
.clsfile → SFDX: Deploy This Source to Org.
- Open the Command Palette →
SFDX: Create Apex Trigger. - Name it after the object it listens on (e.g.,
EmployeeClaimTrigger). - Choose
force-app/main/default/triggersas the folder. - Two files are created:
TriggerName.triggerandTriggerName.trigger-meta.xml. Commit both.
The #1 best practice in Salesforce development is the one trigger per object, handler class pattern. A trigger should contain almost no logic — it just calls a handler class that does the work. This makes your code testable, readable, and easy to maintain.
// EmployeeClaimTrigger.trigger
trigger EmployeeClaimTrigger on Employee_Claim__c (
after insert, after update, after delete, after undelete
) {
EmployeeClaimTriggerHandler.handle(Trigger.new, Trigger.old, Trigger.operationType);
}// EmployeeClaimTriggerHandler.cls
public class EmployeeClaimTriggerHandler {
public static void handle(
List<Employee_Claim__c> newRecords,
List<Employee_Claim__c> oldRecords,
System.TriggerOperation operationType
) {
// Route to different methods based on the event
if (operationType == System.TriggerOperation.AFTER_INSERT) {
// call your method
} else if (operationType == System.TriggerOperation.AFTER_DELETE) {
// call your method
}
// etc.
}
}Below is a breakdown of the classes and triggers this project calls for. Read each section carefully — the goal is to understand what each piece does and why, not to copy code.
What it does: Fires whenever an Employee_Claim__c junction record is created, updated, deleted, or undeleted. It collects the affected employee IDs, queries their claims, and recalculates the risk level on each Employee__c record.
Key concepts to understand before writing it:
Trigger.newvsTrigger.old—Trigger.newholds the records after the change;Trigger.oldholds the before-state. On delete, onlyTrigger.oldexists. You need to handle both to correctly collect affected employee IDs.- Bulkification — Triggers fire once for a batch of records, not once per record. Never put a SOQL query or DML statement inside a
forloop. Collect all the IDs you need first, run one SOQL query, process the results in memory, then run one DML update. - Maps for grouping — The requirement asks you to group claim data by insurance provider per employee. A
Map<Id, Map<Id, Decimal>>(employee → insurer → total amount) is the right tool. Think about how you would build that structure as you loop through query results.
Skeleton to think about:
1. Collect a Set<Id> of all employee IDs touched by this operation
(from Trigger.new for insert/update, from Trigger.old for delete/undelete)
2. Query Employee_Claim__c records for those employees, joining to the Claim
to get Settlement_Amount__c and Insurance_Provider__c
3. Loop through query results and build a Map grouping totals by
(Employee Id → Insurance Provider Id → running total)
4. Loop through your map, apply your risk thresholds, and build a
List<Employee__c> with the updated Risk_Level__c
5. Update that list in a single DML call outside the loop
What it does: Fires on Claim__c insert and update. It handles three separate requirements:
- Claim Status Automation — sets
Settlement_Date__cwhen status becomes Settled; clearsSettlement_Amount__cwhen status becomes Denied; validates date logic. - High-Value Claim Flag — sets or clears the
Is_High_Value__ccheckbox based onSettlement_Amount__c. - Days Open Calculation — calculates the number of days the claim has been open and stores it in
Days_Open__c.
Key concepts:
Trigger.oldMap— aMap<Id, SObject>of the old record values. Use it to detect field changes:if (newClaim.Status__c != Trigger.oldMap.get(newClaim.Id).Status__c).addError()— call this on a record to block the save and display a message to the user. It is the correct way to enforce validation in Apex (as opposed to declarative Validation Rules). Example:newClaim.addError('Settlement Amount must be greater than zero when marking a claim as Settled.').Datearithmetic —Date.today() - someDatereturns anIntegerrepresenting calendar days. TheDateclass also hasdaysBetween(). Think about which date you should subtract from and whether the claim being settled should change your calculation.- Inserting vs updating — on insert
Trigger.oldMapis null, so guard any old-value check:if (!Trigger.isInsert && ...).
Skeleton:
For each claim in Trigger.new:
// Status automation
if status changed to 'Settled':
validate Settlement Amount > 0, else addError()
if Settlement Date is blank, set it to today
if status changed to 'Denied':
clear Settlement Amount
// Date validation (on insert and update)
validate Incident Date is not in the future
validate Incident Date is not after Date Filed
// Days open
if status is Settled or Denied:
Days Open = Date Filed - Incident Date
else:
Days Open = Date.today() - Incident Date
// High-value flag
if Settlement Amount > YOUR_THRESHOLD:
Is High Value = true
else:
Is High Value = false
What it does: Keeps Total_Claims__c and Total_Settlement_Amount__c on Employee__c accurate. Any time an Employee_Claim__c is inserted, updated, or deleted these numbers need to recalculate.
Key concepts:
- Aggregate SOQL — you can write a SOQL query that sums and counts in the database rather than loading every record into memory. Look into
AggregateResultand theSUM()andCOUNT()aggregate functions in SOQL. - Alternative: re-query and loop — if aggregate SOQL feels too advanced, you can query all
Employee_Claim__crecords for the affected employees (joining to theClaim__cforSettlement_Amount__c), loop through them, and accumulate totals in aMap<Id, Decimal>. - This logic fits naturally inside
EmployeeClaimTriggerHandlersince you are already collecting the affected employee IDs there.
Skeleton:
Given a Set<Id> of employee IDs that were affected:
Query all related Employee_Claim__c records for those employees
(include the Claim's Settlement_Amount__c in your query)
Build a Map<Id, Integer> for claim counts
Build a Map<Id, Decimal> for settlement totals
Loop through query results and accumulate into those maps
Loop through the employee IDs and build a List<Employee__c>
with Total_Claims__c and Total_Settlement_Amount__c set from the maps
DML update that list
Before submitting, review each class and trigger against this list:
- No SOQL inside a loop. Collect all IDs first, query once.
- No DML inside a loop. Build a list, update once.
- One trigger per object. All logic for
Employee_Claim__clives in one trigger + one handler. - Meaningful names.
employeeIdSetis better thaneIds;updateEmployeeRiskLevel()is better thandoStuff(). - Comments explain why, not what.
// Exclude denied claims from payout total per business ruleis a good comment.// loop through listis not. -
addError()messages are user-friendly. Write them as if a real HR person will read them. - Test coverage exists (optional but encouraged). At minimum, run your trigger scenarios manually in the org to verify they behave correctly in bulk.
| Mistake | Why It's a Problem | Fix |
|---|---|---|
| SOQL in a loop | Hits governor limits (max 100 SOQL queries per transaction) | Collect IDs → query once → process in memory |
| Separate triggers for the same object | Execution order is not guaranteed; logic splits across files | One trigger → one handler class |
| Hard-coding record IDs | IDs differ between orgs; code breaks on deploy | Query by Name or use Custom Settings/Custom Metadata for thresholds |
Not handling Trigger.old on delete |
Delete events only have Trigger.old, not Trigger.new |
Always check which context you are in |
Forgetting .cls-meta.xml |
File won't deploy without its metadata companion | Always commit both files |
force-app/main/default/
├── classes/
│ ├── ClaimTriggerHandler.cls
│ ├── ClaimTriggerHandler.cls-meta.xml
│ ├── EmployeeClaimTriggerHandler.cls
│ └── EmployeeClaimTriggerHandler.cls-meta.xml
└── triggers/
├── ClaimTrigger.trigger
├── ClaimTrigger.trigger-meta.xml
├── EmployeeClaimTrigger.trigger
└── EmployeeClaimTrigger.trigger-meta.xml
If you get stuck, these may help:
- Apex Developer Guide
- Apex Trigger Best Practices
- SOQL and SOSL Reference
- Salesforce Stack Exchange
- Visual Studio Code Documentation
- Salesforce Extensions for Visual Studio Code
Good luck with your capstone.