Runtime & troubleshooting
Why this approval?
When an approval is pending on a quote, the configurator’s Approvals card lists every firing rule, each with the approver’s name and a hover-revealed Why this approval? tooltip. The tooltip shows:
- Every condition on the rule, with a check / X glyph reflecting that condition’s truth value.
- The combine setting (All / Any / Custom) and the rendered logic expression.
- The live observed value of each tested field, so reps can see exactly which field tipped them past the threshold.
This is the same diagnostic surface the engine writes to
ApprovalEvaluationLog__c.ConditionTrace__c — one log row per rule per
evaluation, with a JSON trace per condition. Open the log records when
investigating “why didn’t this rule fire on this deal?” The note field
on each trace entry surfaces the skip reason verbatim:
skipped: TestedObject Quote incompatible with tenant TargetObject Opportunityskipped: ScopeProduct <id> not present on cartunresolved variable: <Name>tested value unavailableno numeric values for <Field>aggregator field missing
The submission-time toast in the configurator summarises per-chain results — which chain fired, how many of its paths are required, and whether the match policy means “all must approve” or “any will advance.”
Which requests decide a configuration’s status
A configuration collects approval requests across every time it was submitted. Only its latest submission decides whether it is Pending, Approved, or back in Draft. Requests from an earlier submission that the rep recalled, or that was rejected and then resubmitted, stay on the record as history (they still show in the trace; an approver’s My History lists the ones they approved or rejected, not recalled ones) but no longer count. So a configuration that was recalled, resubmitted, and approved is Approved and can be synced.
Within one submission: while a request is waiting for an approver’s decision the configuration is Pending, a rejection by any rule or a recall sends it back to Draft, and it is Approved only once every rule in that submission has approved. A request created by an SLA escalation or a reroute belongs to the submission it replaced.
The one exception is a chain whose Match Policy is Any (Fallback). A step’s rejection there is not the end of the chain: the next matching step’s approver is asked instead, and the configuration stays Pending. If that fallback approver approves, the chain has approved, and the earlier rejection no longer counts against the configuration: it is Approved (once every other rule in the submission has approved too) and can be synced. The rejection stays on the record as history. If every matching step in the chain rejects, nothing is left to fall back to, and the configuration returns to Draft. A recall always returns it to Draft, on any chain.
A submission is over once no request in it is waiting for a decision. A rejection that ends its chain skips every request still waiting beside it (later chain steps and other rules alike), and a recall under the Only this step scope leaves the chain’s later steps Skipped. A skipped step that nothing can reach any more does not hold the configuration in Pending: it goes back to Draft, the rep can resubmit straight away, and the resubmission asks every firing rule again rather than reopening the skipped rows. A submission that skipped a step is never Approved. If a chain step expires with no escalation approver, the configuration likewise returns to Draft instead of waiting forever.
A rep’s recall acts only on configurations whose latest submission is still waiting for a decision, and only on that submission. Another configuration on the deal that is already approved (synced or not), and the history of earlier submissions, are never recalled. A rule’s Recall Scope applies within the waiting submission: Whole chain recalls that submission’s approvals too, but never another configuration’s. A No recall rule refuses the recall only when one of its requests is part of a waiting submission; an approval it gave on another configuration, or on an earlier submission, does not block it.
An Approved configuration also returns to Draft when it is saved with different contents before it is synced. Every configuration save (Quick Save and Save & Sync) compares the incoming snapshot with the stored one, ignoring only the hidden-feature list; any other difference (products, quantities, prices, discounts) demotes it. The demotion is unconditional; whether it then needs a new approval is decided the usual way, by re-evaluating the approval rules against the new contents. If a rule still fires, the rep has to submit again before syncing. If none does, Save & Sync goes ahead as it would for any configuration that doesn’t need approval. An unchanged re-save, a rename, or a sync keeps the approval. In the configurator, an Approved configuration that has been edited and still needs approval shows a disabled Re-approval required button. Separately, the server refuses a Save & Sync of an Approved configuration whose snapshot no longer matches the one that was approved (“This configuration was changed after it was approved. Re-submit the current configuration for approval before syncing.”).
When a save recalculates a pending deal
The field lock stops reps editing a pending deal, but a line-item sync can
still recalculate it (rollups and repricing that run as the sync settles).
When the sync updates the Opportunity or Quote, Pinion re-runs the approval
rules against the final deal at the end of the sync and reconciles every
Pending request, not only rules with RerouteOnFields__c:
- Same approver — stays Pending; its
SnapshotJson__cis refreshed to the recalculated values. No notification. - Different approver — rerouted: the original is marked Skipped and
a new Pending request (linked by
EscalatedFrom__c) goes to the new approver, who is emailed as for a new submission. - Rule no longer fires — auto-recalled, with the comment “Auto-recalled:
the deal changed and this approval is no longer required”. The approver
is emailed that no action is needed. This fires even when the rule’s
recall scope is No Recall (
RecallScope__c = None), which governs only rep recalls. Same-rule downstream steps are recalled per the rule’s recall scope.
A request whose rule was deactivated, or whose approver no longer resolves, is left as it is.
Outside a sync, a direct edit to the Opportunity, Quote, or Account takes
the narrower path: only Pending requests whose rule lists
RerouteOnFields__c are re-resolved, and only when a listed field changed.
The same rule decides the deal-level readings of approval state. The Opportunity or Quote Approval Status field reads Pending only while some configuration’s latest submission (or a deal-level request) is still waiting for a decision, and it reads Approved only when the configuration just approved is Approved. A skipped step left over from an earlier submission no longer holds it at Pending. When a step that nothing can reach any more is all that stands between a configuration and approval (for example its rule was deactivated while it waited), the configuration is Draft but the field keeps Pending, because it has no value for that state. The Pinion: Get approval status AI tool reports each configuration exactly as the configurator does: Pending, Approved, or, for a Draft configuration, Rejected, Recalled, or Not Submitted, depending on why it is not approved.
Disabling approvals
The kill switch lives on the singleton ConfiguratorSettings__mdt
record (the same CMDT that drives the product / pricing rule kill
switches). Check Disable Approval Rules (DisableApprovalRules__c)
to disable the entire engine: no rules fire on submit, no preview
gating happens on the Product Display tab, and any existing pending
requests are preserved but not enforced. Use this if approval rules
cause issues in production — your existing approvals don’t disappear,
they just stop blocking new work.
Worked example — a full discount-escalation rule
A finance team asks: “Any deal with > 20% discount on our Enterprise Tier SKU should escalate. Manager approves up to 25%; VP between 25% and 35%; CFO above 35%.” Here’s how that lands as a Pinion approval chain.
- Open the wizard. Pinion Admin → Approvals → New approval flow. Name: “Enterprise discount escalation.” Target: Opportunity.
- Pick the chain mode and match policy. Mode: Sequential. Match Policy: Any (First Match) — the higher tier shadows the lower, so only one step fires per deal.
- Add the rule-level scope. Set the rule’s Scope Product to “Enterprise Tier.” Every step inherits this gate.
- Author the variable (optional but useful — discount threshold
moves over time). Variables tab → New → Name:
DiscountFloor, Formula:Account.Discount_Threshold__c, Return Type: Percent. (Or skip this and use literals.) - Add Step 1 — Manager. Approver Type:
Manager. No Approver record needed. Firing condition:Opportunity.Discount__c Greater_Than 20. Single condition; ConditionsMet defaults to All. - Add Step 2 — VP. Approver Type:
Approver. Pick an Approver record wrapping “VP, Sales.” Firing condition:Opportunity.Discount__c Greater_Than 25. Because Match Policy is Any (First Match), this step shadows step 1 for any deal > 25%. - Add Step 3 — CFO. Approver Type:
Approver. Pick “CFO.” Firing condition:Opportunity.Discount__c Greater_Than 35. - Step 4 — Policy. SLA Hours: 48. Reroute On Fields:
Opportunity.OwnerId. Editable After Approval: off. Smart Approval: on (if the rep recalls and resubmits unchanged, don’t ping the approver again). - Step 5 — Review. Wizard validates: every step has an approver, every condition has a value, every variable resolves. Save.
What the rep sees: configures Enterprise Tier with a 30% discount, hits
Submit for Approval. The Approvals card shows one pending request
(“VP, Sales”). Hover Why this approval? — the tooltip explains:
Opportunity.Discount > 25 (observed: 30) ✓. The rep can see exactly
which threshold they tripped. The VP approves; the configuration is
locked (Editable After Approval = off); the rep can sync to the QLE.
If the rep then ups the discount to 40% and resubmits, step 2 stops matching, step 3 matches, and the request routes to the CFO instead — because the match policy is First Match, only one step ever fires per submission. Smart Approval doesn’t help here: the snapshot changed.
If the rep is later moved to a different team mid-approval, the
Reroute On Fields setting catches the OwnerId change and re-resolves
the approver chain against the new owner.
Related
- Authoring a Rule — the wizard this example drives.
- Conditions — the firing criteria and the trace notes.
- Approvers — the resolution paths (Manager, Approver, Formula).