A plug-in must block deletion of any account that still has open invoices, returning a clear message to the user. The check should run before the database transaction begins so that a rejected request never starts one. Which stage meets this requirement?
Select an answer to reveal the explanation.
Short Explanation
PreValidation is the bouncer at the front door, checking IDs before anyone gets inside the transaction. Throw InvalidPluginExecutionException there, and the delete never starts. PreOperation is already inside the club, so it's a later checkpoint than you'd think.
Full Explanation
The event pipeline runs registered steps at PreValidation, PreOperation, the core MainOperation and PostOperation. For the initial operation, PreValidation runs before the database transaction begins, which makes it the place for checks that should reject a request cheaply and without opening a transaction. Throwing InvalidPluginExecutionException with a meaningful message cancels the operation and returns that message to the caller. PreOperation is not the earliest stage and runs inside the transaction, so a rejection there happens after the transaction has started and is rolled back. Throwing in a synchronous PostOperation step does cancel the delete by rolling back the transaction, but only after the core operation has executed, which contradicts the requirement. An asynchronous PostOperation step runs after the delete has committed, so it cannot cancel it; the account is already gone. Exam caveat: when an operation is triggered from within another transaction, such as a cascade from a parent delete, PreValidation steps for that nested operation run inside the existing transaction. Operational check: register the step on Delete of account at PreValidation, try to delete an account with an open invoice, confirm the message appears, and confirm the account remains.