Business wants to automatically create an Invoice record whenever an Order record’s status becomes “Completed,” but only if a related payment record exists. How would you achieve this?

A Salesforce environment (Apex trigger) since you’re talking about records, status change, and related records. If you're using another CRM (like Dynamics), tell me and I’ll adapt it.
🎯 Requirement Breakdown
The business rule:
Automatically create an Invoice record when:
- Order status becomes "Completed"
- AND a related Payment record exists
So the logic must:
- Detect when Order status changes to "Completed"
- Check if at least one related Payment exists
- Create an Invoice record
- Prevent duplicate invoices
- Work in bulk (multiple records)
- Follow Salesforce best practices
🧠 Design Considerations (Important)
Before writing code, think like an architect:
1️⃣ Where should logic live?
We’ll use:
- Trigger → detects change
- Handler Class → contains business logic
Why?
- Clean structure
- Testable
- Maintainable
- Enterprise best practice
2️⃣ Data Model Assumption
Let’s assume:
Order__cStatus__c(Picklist)
Payment__cOrder__c(Lookup to Order)
Invoice__cOrder__c(Lookup to Order)Amount__cInvoice_Date__c
Adjust field names as needed.
🏗️ Step 1: Apex Trigger
Trigger should:
- Run after update
- Check if status changed to Completed
trigger OrderTrigger on Order__c (after update) {
if(Trigger.isAfter && Trigger.isUpdate) {
OrderHandler.createInvoiceWhenCompleted(Trigger.new, Trigger.oldMap);
}
}
JavaScriptVery clean. All logic goes to handler.
🧩 Step 2: Handler Class
Now we implement the real logic.
public class OrderHandler {
public static void createInvoiceWhenCompleted(
List<Order__c> newOrders,
Map<Id, Order__c> oldOrderMap
) {
Set<Id> completedOrderIds = new Set<Id>();
// Step 1: Identify orders whose status changed to Completed
for(Order__c ord : newOrders) {
Order__c oldOrder = oldOrderMap.get(ord.Id);
if(ord.Status__c == 'Completed' &&
oldOrder.Status__c != 'Completed') {
completedOrderIds.add(ord.Id);
}
}
if(completedOrderIds.isEmpty()) {
return;
}
// Step 2: Check if related Payment records exist
Map<Id, Payment__c> paymentMap = new Map<Id, Payment__c>();
for(Payment__c pay : [
SELECT Id, Order__c
FROM Payment__c
WHERE Order__c IN :completedOrderIds
]) {
paymentMap.put(pay.Order__c, pay);
}
if(paymentMap.isEmpty()) {
return;
}
// Step 3: Prevent duplicate invoices
Set<Id> alreadyInvoicedOrders = new Set<Id>();
for(Invoice__c inv : [
SELECT Id, Order__c
FROM Invoice__c
WHERE Order__c IN :completedOrderIds
]) {
alreadyInvoicedOrders.add(inv.Order__c);
}
// Step 4: Create invoices
List<Invoice__c> invoicesToInsert = new List<Invoice__c>();
for(Id orderId : completedOrderIds) {
if(paymentMap.containsKey(orderId) &&
!alreadyInvoicedOrders.contains(orderId)) {
Invoice__c invoice = new Invoice__c();
invoice.Order__c = orderId;
invoice.Invoice_Date__c = Date.today();
// Example: setting amount from payment
invoice.Amount__c = 1000; // Replace with actual logic
invoicesToInsert.add(invoice);
}
}
if(!invoicesToInsert.isEmpty()) {
insert invoicesToInsert;
}
}
}
JavaScript🧪 Step 3: Test Class (MANDATORY in Salesforce)
You cannot deploy without test coverage.
@isTest
public class OrderHandlerTest {
@isTest
static void testInvoiceCreation() {
// Step 1: Create Order
Order__c ord = new Order__c(
Name = 'Test Order',
Status__c = 'Pending'
);
insert ord;
// Step 2: Create Payment
Payment__c pay = new Payment__c(
Order__c = ord.Id
);
insert pay;
// Step 3: Update order to Completed
ord.Status__c = 'Completed';
update ord;
// Step 4: Verify invoice created
List<Invoice__c> invoices = [
SELECT Id, Order__c
FROM Invoice__c
WHERE Order__c = :ord.Id
];
System.assertEquals(1, invoices.size());
}
}
JavaScript🔎 Why This Is Production-Ready
Let’s analyze why this solution is strong:
✅ Bulk Safe
Works for 1 or 200 records.
✅ No SOQL in loops
Governor limit safe.
✅ Duplicate prevention
Doesn't create multiple invoices.
✅ Clean separation
Trigger → Handler
✅ Status change detection
Only fires when status transitions.
🚀 Alternative Approaches (If Code Is Not Allowed)
If business prefers no-code / low-code, you could:
Option 1: Record-Triggered Flow
- Trigger on Order
- Entry condition: Status = Completed
- Get Records (Payment)
- Decision: Payment exists?
- Create Invoice
But:
- Flow can become complex in enterprise systems
- Harder to bulk optimize
For large orgs → Apex is safer.
🛡️ Edge Cases You Should Consider
Real architect thinking now:
1️⃣ What if payment is created AFTER order is completed?
Current solution won’t create invoice.
You’d need:
- Either Payment trigger
- Or Scheduled job
- Or Platform Event
2️⃣ What if multiple payments exist?
Currently we:
- Just check existence
- Not summing amounts
If invoice should sum payments:
AggregateResult[] results = [
SELECT Order__c, SUM(Amount__c) totalAmount
FROM Payment__c
WHERE Order__c IN :completedOrderIds
GROUP BY Order__c
];
JavaScriptThen assign dynamic amount.
3️⃣ What if invoice creation fails?
You could add:
Database.insert(invoicesToInsert, false);
JavaScriptFor partial success handling.
🏆 Enterprise-Grade Version (With Aggregate Logic)
Here’s an improved amount logic version:
Map<Id, Decimal> orderPaymentTotals = new Map<Id, Decimal>();
for(AggregateResult ar : [
SELECT Order__c ordId, SUM(Amount__c) totalAmt
FROM Payment__c
WHERE Order__c IN :completedOrderIds
GROUP BY Order__c
]) {
orderPaymentTotals.put(
(Id) ar.get('ordId'),
(Decimal) ar.get('totalAmt')
);
}
JavaScriptThen:
invoice.Amount__c = orderPaymentTotals.get(orderId);
JavaScriptThat’s real-world logic.
Related Posts

How to Automatically create a follow-up Task when a Lead is converted

How You need to update a related child record whenever a parent record’s status changes, but only if the status is “Closed Won.” How would you design this in Apex?
