PL-200 study materials
PL-200 study resources

Duplicate approvals in Northwind's quote process

PL-200 · Put your knowledge to work in context.

All case studies

Duplicate approvals in Northwind's quote process

Northwind requires manager approval before a quote can be sent, but only when the quote Total exceeds $50,000. A cloud flow triggers on Create and Modify of Quote, checks Total > 50000, starts an approval, and on approval updates the quote Status to Approved via an Update Row action. Sales reps report two problems. First, editing any field on an already-approved quote re-triggers a fresh approval and flips Status back to Pending. Second, when a rep saves repeatedly while typing, three or four identical approval requests arrive. The flow's own Update Row action also appears to fire the trigger. Quotes under $50,000 must never enter approval, and a rejected quote must stay rejected until a manager deliberately reopens it.

1. Diagnose the retrigger loop and the duplicate approvals, and describe the control you add inside the flow.

Read the worked answer

Both defects come from having no state machine - the trigger fires on every write.

  • The flow's own Update Row changes the Quote, which satisfies Create/Modify, which starts a new run. That run sees Total still above $50,000 and starts another approval. That is the loop.
  • Rapid saves while typing each produce a Modify event; with concurrency allowed, several runs start in parallel and each creates an approval.

Fix - gate on status, not on the trigger:

  1. Introduce a status value such as Draft → In Review → Approved / Rejected (a choice column or the existing Status Reason).
  2. As the first action in the flow, Update Row the quote to In Review before starting the approval. Every subsequent trigger sees In Review and exits.
  3. Trigger condition (Settings → Trigger conditions) so the run only starts for drafts:
@and(
  greater(triggerOutputs()?['body/totalamount'], 50000),
  equals(triggerOutputs()?['body/statuscode'], 1)
)

Filtering on the trigger is cheaper and cleaner than an early-exit Condition. 4. The approval and reject branches set the terminal status (Approved / Rejected). Because the trigger only accepts Draft, those writes never re-enter the flow. 5. Turn on Concurrency control and set degree of parallelism to 1 on the loop/trigger so two saves cannot race.

Rejection semantics: rejected stays rejected because the trigger ignores it. A manager editing Status back to Draft is an explicit, auditable re-open that legitimately starts a new approval.

2. Where should the Total > $50,000 rule and the status transitions live - business rule, classic workflow, or cloud flow? Justify each.

Read the worked answer

Split by what each tool can actually guarantee.

Business rule (entity scope) - field-level enforcement:

  • Make Resolution/Quote Total required and validate the number before save, and set Status to Draft on create. Runs in the client and on the server for entity-scoped rules, so users see the error immediately.
  • Cannot call approvals, wait for humans, or call external services - so it cannot own the approval itself.

Cloud flow - the human-in-the-loop part:

  • Only Power Automate can run an approval, wait days, and resume. This is exactly the long-running, human-paced work the flow should own.
  • Keep it thin: gate on status, start approval, write result back.

Avoid the classic workflow here:

  • Legacy workflows are synchronous/asynchronous background processes with no modern approval connector; you would be rebuilding the approval plumbing by hand.

Server-side guarantee: if the business ever needs "no quote can be set to Approved by any client, including the Web API", an entity-scoped business rule is not sufficient against bulk edit - that case requires a synchronous plug-in or a real-time workflow. Flag that as a known gap rather than silently assuming the rule covers it.

3. Approvals are currently sent to the rep's manager using 'Assigned to' on the Quote owner. Some reps have no manager populated. What do you change, and how do you stop unapproved quotes from being sent?

Read the worked answer

Make the approver resolution explicit and fail closed.

  1. Resolve the approver with a deliberate chain: Assigned to manager from the User record → fall back to a shared Approvals owner team or a configured escalation mailbox → if neither resolves, the flow must terminate with a failure notification, never start an approval with a blank approver (Power Automate otherwise assigns it to the flow owner, which is how approvals silently land in the wrong inbox).
  2. Populate the Manager field on all User records as a data-quality task; without it the hierarchy-based security in the model-driven app is also wrong.
  3. Prevent sending at the data layer, not in the UI: make the "send quote" command check Status = Approved, and make the business rule block the status change to Sent while Status is not Approved. Hiding a button does not stop the Web API.
  4. Set the approval action's timeout (e.g., 5 business days) with a parallel branch that marks the quote as Escalated and notifies the manager's manager - otherwise quotes sit in limbo forever.
  5. Add a guard for the '$50,000' boundary: the condition must be > 50000, decided once, in one place. If both the flow and a business rule check it, they will drift.