CalculationStatus Carve-Out for Repricing Locked QLIs
When Quote Line Items (QLIs) are locked by a validation rule to prevent unauthorized edits, the Revenue Cloud pricing engine's "Reprice All" action can fail — even though it's a legitimate system operation. This article explains why it happens and the correct pattern to allow repricing through without weakening your lock.
The Problem
A common pattern in Salesforce CPQ and Revenue Cloud implementations is locking QLIs after they reach a certain stage — for example, after integration with an external procurement or ERP system. A validation rule like this prevents edits to locked lines:
AND(
Locked_Integration__c = TRUE,
NOT(ISNEW()),
// ... other carve-outs
)
This works as intended for manual edits. But when a user clicks Reprice All on a Quote, the Revenue Cloud pricing engine (PST — Pricing Service Transaction) performs a DML update on every QLI to recalculate pricing. The validation rule fires on each locked QLI and blocks the entire operation.
In a Quote with many locked lines, this produces a wall of VALIDATION_FAIL entries in the debug log — one per locked QLI.
Why It Happens
During a reprice operation, Revenue Cloud sets the Quote's CalculationStatus field to "Saving" before performing its DML update on the QLIs. This is a system-managed state — the pricing engine controls the value throughout its lifecycle:
| CalculationStatus Value | Meaning |
|---|---|
Queued | Calculation requested, waiting to start |
Calculating | Pricing engine actively computing |
Saving | Engine is writing updated values to QLIs |
Completed | Calculation finished successfully |
CompletedWithErrors | Calculation finished with issues |
The key insight: when CalculationStatus = "Saving", the DML on QLIs is coming from the pricing engine, not from a user. Your validation rule doesn't distinguish between the two — it just sees a field update on a locked record and blocks it.
The Solution
Add a carve-out to your validation rule that checks the parent Quote's CalculationStatus. When the pricing engine is in its "Saving" state, allow the update through:
AND(
Locked_Integration__c = TRUE,
NOT(ISNEW()),
NOT(ISPICKVAL(Quote.CalculationStatus, "Saving")),
// ... other existing carve-outs
)
The NOT(ISPICKVAL(Quote.CalculationStatus, "Saving")) clause evaluates to FALSE during repricing, which short-circuits the AND() and allows the update.
Why This Is Safe
CalculationStatusis system-managed. Users cannot manually set it to"Saving"through the UI or standard APIs. Only the Revenue Cloud pricing engine controls this field during its calculation lifecycle.- It's more maintainable than field-level carve-outs. The pricing engine touches many fields on a QLI (unit price, net total, discount, tax amounts, etc.). Adding
NOT(ISCHANGED(...))for every pricing field is brittle — you'll inevitably miss one when new pricing fields are added. TheCalculationStatusapproach covers all pricing field updates in a single clause. - Locked QLIs remain protected. Manual edits still fail validation because
CalculationStatuswon't be"Saving"during a normal user edit.
Gotcha: Picklist Fields in Formulas
CalculationStatus is a standard picklist field on the Quote object. Salesforce formulas do not allow direct equality comparison (=) on picklist fields. This will fail on deploy:
// WRONG — will not compile
Quote.CalculationStatus = "Saving"
You must use ISPICKVAL() or TEXT():
// CORRECT
ISPICKVAL(Quote.CalculationStatus, "Saving")
// ALSO CORRECT (but more verbose)
TEXT(Quote.CalculationStatus) = "Saving"
This is an easy mistake to make, especially if you're copying patterns from text or checkbox field carve-outs in the same formula.
Verification
After deploying the updated validation rule:
- Reprice All succeeds — Click "Reprice All" on a Quote with locked QLIs. The operation should complete without validation errors.
- Manual edits still blocked — Try editing a field (e.g., Description) on a locked QLI. The validation rule should still fire and block the edit.
- Unlocked QLIs unaffected — Verify that unlocked QLIs behave normally for both repricing and manual edits.
Key Takeaways
- Revenue Cloud sets
Quote.CalculationStatus = "Saving"during its DML update on QLIs — use this to identify pricing engine operations. - A single
ISPICKVAL()carve-out is safer and more maintainable than individual field-level carve-outs for pricing fields. CalculationStatusis system-managed, so the carve-out cannot be exploited by users to bypass the lock.- Always use
ISPICKVAL()orTEXT()when referencing picklist fields in validation rule formulas.