feat: create an order from a quote - #421
Merged
Merged
Conversation
POST /{projectKey}/orders/quotes and POST /{projectKey}/me/orders/quotes had no
route, so the last step of the B2B quote flow could not be exercised at all.
Consumers could unit-test their own orderability guards, but not the conversion
itself, the resulting order, or the quote's transition.
The guards are the interesting part and are modelled: the quote must be Pending
and not past its validTo, both InvalidOperation otherwise, and the draft version
is checked against the stored quote so a stale version raises
ConcurrentModification. quoteStateToAccepted moves the quote to Accepted as part
of creating the order.
The order is built from the quote's own line items and prices rather than
recalculated, since not re-pricing is the point of converting instead of
rebuilding a cart. It references the quote and has origin "Quote".
The draft is validated in the repository rather than through a generated zod
schema; generating one needs the commercetools-api-reference checkout that
scripts/generate-schemas.ts expects.
Fixes #412
🦋 Changeset detectedLatest commit: 0c6462b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The route validated its body by hand, which meant the /orders/quotes draft was the only create endpoint not checked against a zod schema in strict mode. OrderFromQuoteDraft and MyOrderFromQuoteDraft are now generated like every other draft, and both routes validate through them the way AbstractService.post does. Running the generator also picks up the optional fields the spec gained since it last ran; that drift is additive and is included here rather than left to surprise the next person who regenerates. AGENTS.md now describes the workflow, since hand-rolling the validation was the avoidable mistake.
Collaborator
Author
|
Addressed the note on validation — you were right that this should go through the generator rather than be hand-rolled.
Two things worth a look when reviewing:
|
mvantellingen
approved these changes
Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #412.
Endpoints
POST /{projectKey}/orders/quotesOrderFromQuoteDraftPOST /{projectKey}/me/orders/quotesMyOrderFromQuoteDraftBoth land on a new
OrderRepository.createFromQuote; the/meroute maps{ id, version, quoteStateToAccepted }onto the full draft.Behaviour
As the issue notes, the guards are the part worth having:
Pendingstate →InvalidOperation(400) otherwise.validTo→InvalidOperation(400) otherwise.versionis checked against the stored quote →ConcurrentModification(409) on a stale version.ReferencedResourceNotFound(400), which is what resolving a reference in a draft returns everywhere else in the mock.quoteStateToAccepted: truemoves the quote toAcceptedand bumps its version; without it the quote is left alone.The order carries the quote's line items, custom line items, prices and taxed price as-is — nothing is re-priced, which is the point of converting rather than rebuilding a cart — plus
quoteas a reference andorigin: "Quote".Note on validation
The draft is validated in the repository rather than through a generated zod schema:
scripts/generate-schemas.tsneeds thecommercetools-api-referencecheckout, which I do not have here, soOrderFromQuoteDraftis not insrc/schemas/generated. Happy to add it toDRAFT_SCHEMASand regenerate if you'd prefer that.Coverage
order-from-quote.test.ts: create,quoteStateToAcceptedtransitioning the quote, the quote left untouched without it, non-pending quote, expired quote, stale version, unknown quote, and the/mevariant.Full suite: 798 passing.