Skip to content

Conversation

@aelhor
Copy link

@aelhor aelhor commented Dec 8, 2025

Description

Added new mutation to update order currency and trigger price recalculations
Added e2e tests to cover the new added mutation

Fixes issue #3897

Breaking changes

No

Screenshots

You can add screenshots here if applicable.

Checklist

📌 Always:

  • I have set a clear title
  • My PR is small and contains a single feature
  • I have checked my own PR

👍 Most of the time:

  • I have added or updated test cases
  • I have updated the README if needed

Summary by CodeRabbit

  • New Features

    • Add mutation to set an order's currency; active order pricing is recalculated in the new currency.
    • Order history gains a new entry type for currency changes (ORDER_CURRENCY_UPDATED).
    • Currency changes are validated against the current channel; invalid requests return a clear error.
  • Tests

    • Added end-to-end tests for updating order currency, channel currency validation, and resulting order/line updates.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 8, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs Ready Ready Preview Dec 22, 2025 2:03pm
vendure-storybook Ready Ready Preview, Comment Dec 22, 2025 2:03pm

@github-actions
Copy link
Contributor

github-actions bot commented Dec 8, 2025

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 8, 2025

Walkthrough

Adds a Shop API mutation setCurrencyCodeForOrder to re-price an active order in a different currency, implements OrderService.updateOrderCurrency and resolver wiring, records a new ORDER_CURRENCY_UPDATED history entry, updates schema/types and generated GraphQL artifacts, and adds e2e tests exercising the flow.

Changes

Cohort / File(s) Change Summary
Schema & API
packages/core/src/api/schema/common/history-entry.type.graphql, packages/core/src/api/schema/shop-api/shop.api.graphql
Added ORDER_CURRENCY_UPDATED to HistoryEntryType and new mutation setCurrencyCodeForOrder(currencyCode: CurrencyCode!): UpdateOrderItemsResult!.
Order Service & History
packages/core/src/service/services/order.service.ts, packages/core/src/service/services/history.service.ts
Added updateOrderCurrency implementation (validate mutability & channel currency availability, short-circuit if unchanged, create RequestContext with target currency, update order.currencyCode, re-price, persist, emit event) and extended OrderHistoryEntryData to include previous/new currency for ORDER_CURRENCY_UPDATED.
Resolver
packages/core/src/api/resolvers/shop/shop-order.resolver.ts
Added setCurrencyCodeForOrder() resolver method that enforces permissions and delegates to OrderService.
Shop GraphQL Definitions & Tests
packages/core/e2e/graphql/shop-definitions.ts, packages/core/e2e/order.e2e-spec.ts
Added SET_CURRENCY_CODE_FOR_ORDER GraphQL mutation definition and e2e tests asserting channel currency availability, error handling, and successful currency update; added supporting channel/variant ops used by tests.
Generated Types & Documents
Generated files across packages
packages/common/src/generated-types.ts, packages/common/src/generated-shop-types.ts, packages/core/e2e/graphql/*, packages/*/e2e/graphql/generated-*-types.ts, packages/admin-ui/src/lib/core/src/common/generated-types.ts, packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts, ...
Propagated ORDER_CURRENCY_UPDATED enum and added MutationSetCurrencyCodeForOrderArgs, mutation types, and SetCurrencyCodeForOrderDocument/related GraphQL documents across generated code and plugin artifacts.
Misc. Generated Artifacts
many packages/**/generated-*-types.ts, admin UI fragments/docs
Auto-generated updates to many fragments/operations/documents to reflect the new enum/mutation across packages.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant ShopOrderResolver as Resolver
    participant OrderService as Service
    participant RequestContextService as RCService
    participant HistoryService as History
    participant Database as DB
    participant EventBus as Events

    Client->>Resolver: setCurrencyCodeForOrder(currencyCode)
    Resolver->>Resolver: permission checks
    Resolver->>Service: updateOrderCurrency(ctx, orderId, currencyCode)
    Service->>DB: load active order (strict)
    Service->>Service: validate mutability & channel currency availability
    alt currency not available
        Service-->>Resolver: throw UserInputError
        Resolver-->>Client: Error
    else no change
        Service-->>Resolver: return existing Order
        Resolver-->>Client: Success
    else currency change required
        Service->>RCService: create context(with currency)
        RCService-->>Service: new context
        Service->>DB: update order.currencyCode & persist
        Service->>History: record ORDER_CURRENCY_UPDATED(previous,new)
        History->>DB: persist history entry
        Service->>Service: apply price adjustments under new context
        Service->>DB: persist updated prices
        Service->>Events: emit Order updated event
        Events-->>Service: dispatched
        Service-->>Resolver: return updated Order
        Resolver-->>Client: Success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus areas:
    • Currency availability validation and UserInputError handling in order.service.ts.
    • Construction and use of the new RequestContext for re-pricing.
    • Correct history entry shape and persistence for ORDER_CURRENCY_UPDATED.
    • Resolver permission wiring and error propagation.
    • e2e test coverage and setup for channel currency availability and variant/channel assignments.

Possibly related issues

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title contains a typo ('Feet' instead of likely 'Feat') and lacks proper clarity about the main change, making it unclear to developers scanning history. Correct the typo and revise to: 'feat(core): add shop API mutation to update order currency code' for clarity and consistency with conventional commit standards.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description includes all required sections from the template with substantive content: summary of changes, breaking changes acknowledgment, and a completed checklist confirming self-review and test coverage.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/core/src/service/services/history.service.ts (1)

4-10: ORDER_CURRENCY_UPDATED history payload should use CurrencyCode for newCurrency

OrderService.updateOrderCurrency() records history with data: { previousCurrency: previousCurrencyCode, newCurrency: currencyCode }, where both values are CurrencyCode. Typing newCurrency as ID here is inconsistent and can confuse consumers of OrderHistoryEntryData, even though it’s assignable at runtime.

Recommend aligning the type:

-    [HistoryEntryType.ORDER_CURRENCY_UPDATED]: {
-        previousCurrency?: CurrencyCode;
-        newCurrency?: ID;
-    };
+    [HistoryEntryType.ORDER_CURRENCY_UPDATED]: {
+        previousCurrency?: CurrencyCode;
+        newCurrency?: CurrencyCode;
+    };

Also applies to: 120-123

packages/core/e2e/order.e2e-spec.ts (1)

609-615: Optional chaining on order?.history.items doesn’t guard against order being undefined

The expression

expect(order?.history.items.map(pick(['type', 'data']))).toEqual([...]);

still throws if order were ever undefined, because order?.history would yield undefined and .items would then be accessed on undefined. Since this test expects a real order, the non‑null assertion was safer and clearer.

Suggest either reverting to the original non‑null form:

- expect(order?.history.items.map(pick(['type', 'data']))).toEqual([
+ expect(order!.history.items.map(pick(['type', 'data']))).toEqual([

or fully guarding the chain if you truly expect order to sometimes be missing.

packages/core/src/service/services/order.service.ts (1)

3-12: Currency update flow is solid overall, but state validation is currently ignored

The new currency‑change support is nicely integrated (channel validation, new RequestContext, full repricing via applyPriceAdjustments, history entry, and event emission), but there are a couple of issues and small improvements:

  • assertAddingItemsState(order) is called in updateOrderCurrency() but its return value is ignored. Everywhere else this helper is used, its result gates the operation (e.g. addItemsToOrder, removeItemsFromOrder). As written, currency changes will still proceed even if the order is not in AddingItems or Draft, which is inconsistent with other “modification” operations.

    Suggest handling the validation result as you do elsewhere:

    async updateOrderCurrency(
        ctx: RequestContext,
        orderId: ID,
        currencyCode: CurrencyCode,
        relations?: RelationPaths<Order>,
    ): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
        const order = await this.getOrderOrThrow(ctx, orderId);
  • //  Check if order can be modified
    
  • this.assertAddingItemsState(order);
    
  • //  Check if order can be modified
    
  • const validationError = this.assertAddingItemsState(order);
    
  • if (validationError) {
    
  •     return validationError;
    
  • }
    
    
    
  • For the history payload you currently use previousCurrency: ctx.currencyCode. Using order.currencyCode before you mutate it is slightly more robust if a caller ever invokes this with a context whose currency differs from the order:

  •    const previousCurrencyCode = ctx.currencyCode;
    
  •    const previousCurrencyCode = order.currencyCode;
    
    
    
  • Per the static analysis hint, requestContextService (and other injected services) can be marked readonly:

  •    private requestContextService: RequestContextService,
    
  •    private readonly requestContextService: RequestContextService,
    
    
    

These changes keep the new behavior consistent with existing order‑modification invariants and address the Sonar warning.

Also applies to: 47-50, 82-83, 119-120, 145-171, 589-641

🧹 Nitpick comments (4)
packages/core/e2e/graphql/generated-e2e-shop-types.ts (1)

3696-3733: Align new setCurrencyCodeForOrder operation types with existing generated patterns

These two aliases look hand-edited in an otherwise generated file and differ from nearby conventions:

  • All other operation types use PascalCase (e.g. AddItemToOrderMutationVariables, GetActiveOrderQueryVariables), whereas these are lower camelCase. Consider renaming to SetCurrencyCodeForOrderMutationVariables / SetCurrencyCodeForOrderMutation for consistency.
  • Existing mutation result types model the GraphQL union as a discriminated union of concrete shapes (different field sets per member) and generally don’t expose __typename. Here you collapse everything into a single object with many optional fields plus a __typename union, which weakens type‑safety in tests.
  • The __typename union only lists 'Order' | 'OrderModificationError' | 'InsufficientStockError', but UpdateOrderItemsResult elsewhere in this file also includes other members (e.g. OrderInterceptorError, OrderLimitError, NegativeQuantityError). If any of those can ever be returned by setCurrencyCodeForOrder, they’ll be effectively untyped here.

If possible, I’d recommend:

  1. Adding the setCurrencyCodeForOrder GraphQL operation to whatever documents feed your codegen (e.g. shop-definitions.ts) and re‑running codegen so this file stays purely generated.
  2. Letting codegen produce the usual discriminated union result type, or manually shaping this alias to mirror the union members you truly expect for this mutation and documenting that it’s a strict subset of UpdateOrderItemsResult.
packages/core/src/api/schema/shop-api/shop.api.graphql (1)

73-73: New setCurrencyCodeForOrder mutation shape looks consistent with existing API

Signature and return type align with other order‑mutating operations (UpdateOrderItemsResult!). Consider adding a short description above the field to clarify that it re-prices the active order in the new channel currency and is only valid when that currency is available on the current channel.

packages/core/e2e/graphql/shop-definitions.ts (1)

133-148: ASSIGN_PRODUCT_VARIANTS_TO_CHANNEL mutation is fine; selection set could be trimmed if unused

The mutation definition matches the schema (AssignProductVariantsToChannelInput!) and returns enough fields to inspect assigned variants. If the test code never reads most of these fields (e.g., name, sku, channels), you can safely trim the selection set to just what’s asserted to keep generated types slightly leaner, but it’s not required. Based on learnings, these gql constants are correctly used as inputs to codegen and should not be treated as dead code.

packages/core/e2e/order.e2e-spec.ts (1)

97-97: Remove unused SECOND_CHANNEL_TOKEN and stale commented channels config

SECOND_CHANNEL_TOKEN is declared but never used, and the commented‑out channels array in initialData is superseded by the runtime UPDATE_CHANNEL mutation in the new tests. Both add noise without value; consider deleting them to keep the fixture setup focused.

Also applies to: 155-165

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2eab9a8 and e5742fc.

📒 Files selected for processing (17)
  • packages/admin-ui/src/lib/core/src/common/generated-types.ts (1 hunks)
  • packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts (1 hunks)
  • packages/common/src/generated-shop-types.ts (3 hunks)
  • packages/common/src/generated-types.ts (1 hunks)
  • packages/core/e2e/graphql/generated-e2e-admin-types.ts (1 hunks)
  • packages/core/e2e/graphql/generated-e2e-shop-types.ts (1 hunks)
  • packages/core/e2e/graphql/shop-definitions.ts (1 hunks)
  • packages/core/e2e/order.e2e-spec.ts (5 hunks)
  • packages/core/src/api/resolvers/shop/shop-order.resolver.ts (4 hunks)
  • packages/core/src/api/schema/common/history-entry.type.graphql (1 hunks)
  • packages/core/src/api/schema/shop-api/shop.api.graphql (1 hunks)
  • packages/core/src/service/services/history.service.ts (2 hunks)
  • packages/core/src/service/services/order.service.ts (4 hunks)
  • packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts (1 hunks)
  • packages/payments-plugin/e2e/graphql/generated-admin-types.ts (1 hunks)
  • packages/payments-plugin/e2e/graphql/generated-shop-types.ts (3 hunks)
  • packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-21T18:29:30.413Z
Learnt from: DanielBiegler
Repo: vendure-ecommerce/vendure PR: 3815
File: packages/core/e2e/api-key.e2e-spec.ts:160-224
Timestamp: 2025-11-21T18:29:30.413Z
Learning: In the vendure-ecommerce/vendure repository, hand-written `gql` template literals (e.g., `CREATE_API_KEY`, `API_KEY`, etc.) in test files like `packages/core/e2e/api-key.e2e-spec.ts` are the source documents for GraphQL code generation. The code generator processes these to produce typed `*Document` exports (e.g., `CreateApiKeyDocument`, `ApiKeyDocument`) that are then imported and used in the tests. These hand-written gql constants should not be considered unused or dead code—they are essential inputs to the code generation process.

Applied to files:

  • packages/core/e2e/graphql/shop-definitions.ts
📚 Learning: 2025-11-26T14:08:43.342Z
Learnt from: PavanendraBaahubali
Repo: vendure-ecommerce/vendure PR: 3999
File: packages/dashboard/src/app/routes/_authenticated/_products/components/create-product-variants-dialog.tsx:133-138
Timestamp: 2025-11-26T14:08:43.342Z
Learning: The Vendure dashboard codebase uses awesome-graphql-client (not Apollo Client) for GraphQL operations. The api wrapper is located at `packages/dashboard/src/vdb/graphql/api.ts`. When mutations throw errors, the error object contains a `fieldErrors` property (an array of GraphQL error objects), not `graphQLErrors`. Access error messages via `error.fieldErrors[0].message`.

Applied to files:

  • packages/core/src/api/resolvers/shop/shop-order.resolver.ts
🧬 Code graph analysis (5)
packages/common/src/generated-shop-types.ts (1)
packages/common/src/generated-types.ts (1)
  • UpdateOrderItemsResult (6739-6739)
packages/core/e2e/order.e2e-spec.ts (2)
packages/common/src/generated-types.ts (1)
  • AssignProductVariantsToChannelInput (270-274)
packages/core/e2e/graphql/shop-definitions.ts (2)
  • ASSIGN_PRODUCT_VARIANTS_TO_CHANNEL (133-148)
  • SET_CURRENCY_CODE_FOR_ORDER (166-185)
packages/core/src/service/services/history.service.ts (1)
packages/common/src/shared-types.ts (1)
  • ID (79-79)
packages/core/e2e/graphql/generated-e2e-shop-types.ts (1)
packages/common/src/generated-types.ts (1)
  • Exact (4-4)
packages/core/src/api/resolvers/shop/shop-order.resolver.ts (2)
packages/core/e2e/graphql/generated-e2e-shop-types.ts (2)
  • Order (1989-2044)
  • Mutation (1703-1804)
packages/core/src/config/order/active-order-strategy.ts (1)
  • ACTIVE_ORDER_INPUT_FIELD_NAME (7-7)
🪛 GitHub Check: SonarCloud Code Analysis
packages/core/src/service/services/order.service.ts

[warning] 170-170: Member 'requestContextService: RequestContextService' is never reassigned; mark it as readonly.

See more on https://sonarcloud.io/project/issues?id=vendure-ecommerce_vendure&issues=AZsAHLqX0SV6MOQ7lMhv&open=AZsAHLqX0SV6MOQ7lMhv&pullRequest=4033

🔇 Additional comments (18)
packages/core/src/api/schema/common/history-entry.type.graphql (1)

9-36: New ORDER_CURRENCY_UPDATED history type looks consistent; ensure downstream mappings are updated

Enum naming/placement align with existing ORDER_* history types, so the schema change itself is fine. Please double-check any switch/if mappings (e.g. admin UI labels, icons, filters, history serializers) that branch on HistoryEntryType so this new value doesn’t fall through to an “unknown”/default case at runtime.

packages/payments-plugin/e2e/graphql/generated-admin-types.ts (1)

2007-2034: Generated HistoryEntryType.ORDER_CURRENCY_UPDATED enum entry matches schema

The added enum member is correctly named and consistent with the schema change; for a generated file this is exactly what we want. Just make sure all other generated type bundles (admin/shop, plugins, e2e fixtures) were regenerated from the same updated schema so tests and tooling stay in sync.

packages/common/src/generated-types.ts (1)

2059-2067: New ORDER_CURRENCY_UPDATED history type looks consistent

The new ORDER_CURRENCY_UPDATED entry fits the existing ORDER_* naming pattern and enum grouping; no issues here. Just ensure any exhaustive handling of HistoryEntryType (e.g. switch statements, filters) in non‑generated code has been updated to include this new value.

packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts (1)

2020-2020: LGTM! Generated enum member for currency update tracking.

The addition of ORDER_CURRENCY_UPDATED to the HistoryEntryType enum correctly reflects the new history entry type for tracking order currency changes. The generated code follows the established naming convention and integrates properly with the existing enum members.

packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts (1)

2002-2029: HistoryEntryType.ORDER_CURRENCY_UPDATED addition is consistent

The new enum member is named and valued consistently with the other ORDER_* history types and is a safe, backward-compatible extension of the union. No issues from this file’s perspective.

packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts (3)

1262-1289: HistoryEntryType addition is consistent

ORDER_CURRENCY_UPDATED follows existing naming and string-literal conventions and cleanly extends the history enum set.


1845-1948: New mutation field wiring looks correct

setCurrencyCodeForOrder: UpdateOrderItemsResult aligns with other order-edit mutations and uses the same union result type, which should keep client handling uniform.


2025-2027: Args type matches schema expectations

MutationSetCurrencyCodeForOrderArgs with currencyCode: CurrencyCode matches the core arg shape and enum usage; no issues spotted.

packages/common/src/generated-shop-types.ts (3)

1258-1285: HistoryEntryType extension is well-formed

ORDER_CURRENCY_UPDATED integrates cleanly into the existing order-related history enum values; no typing or naming concerns.


1763-1866: setCurrencyCodeForOrder mutation field is consistent

The new setCurrencyCodeForOrder: UpdateOrderItemsResult mirrors other order mutations’ result type and fits the existing Mutation API surface.


1939-1942: Args type matches core definition

MutationSetCurrencyCodeForOrderArgs with currencyCode: CurrencyCode matches the core packages/common/src/generated-types.ts definition, so shared consumers should remain in sync.

packages/payments-plugin/e2e/graphql/generated-shop-types.ts (2)

1215-1242: Enum extension for ORDER_CURRENCY_UPDATED looks consistent

Adding ORDER_CURRENCY_UPDATED to HistoryEntryType is an additive, backwards-compatible change and fits the existing naming pattern for order-related history events. No issues from the typings side.


1780-1883: New setCurrencyCodeForOrder mutation and args type align with existing order APIs

The setCurrencyCodeForOrder: UpdateOrderItemsResult field and MutationSetCurrencyCodeForOrderArgs { currencyCode: CurrencyCode } mirror the existing order mutations’ patterns (single-arg input, shared union return type). The typings here are coherent and should integrate cleanly with the rest of the generated schema.

Also applies to: 1960-1962

packages/core/e2e/graphql/generated-e2e-admin-types.ts (1)

2017-2023: LGTM!

The new ORDER_CURRENCY_UPDATED enum member is correctly positioned in alphabetical order and follows the existing naming convention for history entry types. As this is a generated file, ensure it was regenerated from the GraphQL schema rather than manually edited.

packages/admin-ui/src/lib/core/src/common/generated-types.ts (1)

2068-2074: LGTM!

The new ORDER_CURRENCY_UPDATED enum member follows the established naming conventions and is correctly positioned in alphabetical order. This aligns with the new currency update mutation feature.

As this is a generated file, ensure it was regenerated from the source GraphQL schema (history-entry.type.graphql) rather than manually edited.

packages/core/e2e/graphql/shop-definitions.ts (2)

156-160: Formatting‑only change in ADD_MULTIPLE_ITEMS_TO_ORDER

This hunk only adjusts indentation around the ... on ErrorResult fragment and has no behavioral impact; nothing to change here.


166-185: SET_CURRENCY_CODE_FOR_ORDER operation matches the new mutation and union contract

The ChangeOrderCurrency mutation correctly calls setCurrencyCodeForOrder and handles the UpdateOrderItemsResult union via __typename, ... on Order { ...UpdatedOrder }, and explicit error fragments. This is in line with the other order‑item mutations and will generate the expected operation types for the e2e clients.

packages/core/src/api/resolvers/shop/shop-order.resolver.ts (1)

12-18: setCurrencyCodeForOrder resolver is wired correctly; verify if type cast is unnecessary

The new mutation:

  • Uses the same active‑order lookup pattern as addItemToOrder/adjustOrderLine.
  • Is guarded by @Allow(Permission.UpdateOrder, Permission.Owner) and wrapped in @Transaction().
  • Delegates cleanly to orderService.setCurrencyCodeForOrder with the provided relations.

The as CurrencyCode cast on args.currencyCode is likely redundant since GraphQL code generation should already type MutationSetCurrencyCodeForOrderArgs['currencyCode'] as CurrencyCode. Confirm the generated type shape and remove the cast if the field is already properly typed.

Also applies to: 31-31, 356-376, 378-398

@aelhor
Copy link
Author

aelhor commented Dec 9, 2025

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Dec 9, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/core/src/service/services/order.service.ts (3)

589-598: Clarify the need for this wrapper method.

setCurrencyCodeForOrder is a direct pass-through to updateOrderCurrency with no additional logic. Having two public methods that perform identical operations creates API confusion and maintenance overhead.

Consider one of the following:

  • Option 1 (Recommended): Remove setCurrencyCodeForOrder and use updateOrderCurrency directly in resolvers.
  • Option 2: If setCurrencyCodeForOrder is the intended public API, make updateOrderCurrency private and rename it (e.g., applyOrderCurrencyUpdate).
  • Option 3: Document why both methods exist if there's a specific architectural reason (e.g., one for shop API, one for admin API).

Apply this diff if choosing Option 1:

-    async setCurrencyCodeForOrder(
-        ctx: RequestContext,
-        orderId: ID,
-        currencyCode: CurrencyCode,
-        relations?: RelationPaths<Order>,
-    ): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
-        const result = await this.updateOrderCurrency(ctx, orderId, currencyCode, relations);
-
-        return result;
-    }
-

617-621: Optimize channel lookup and standardize error handling.

Two concerns here:

  1. Performance: Line 618 fetches the channel from the database, but ctx.channel should already be available. If availableCurrencyCodes is not eagerly loaded, consider optimizing the channel service to include it or checking if it's already present.

  2. Error handling inconsistency: Line 620 throws UserInputError instead of returning a typed GraphQL error result. This deviates from the pattern used elsewhere in this service (e.g., OrderModificationError on line 609-611).

Apply this diff to return a typed error:

+import { ..., CurrencyNotAvailableError } from '../../common/error/generated-graphql-shop-errors';

     // Verify the new currency is available in the channel
     const channel = await this.channelService.getChannelFromToken(ctx.channel.token);
     if (!channel.availableCurrencyCodes.includes(currencyCode)) {
-        throw new UserInputError('error.currency-not-available', { currencyCode });
+        return new CurrencyNotAvailableError({ currencyCode });
     }

Note: You may need to define CurrencyNotAvailableError if it doesn't already exist in the generated shop errors.


600-647: Add JSDoc documentation for the public API method.

Public methods in this service are typically documented with JSDoc comments describing their purpose, parameters, and behavior. This helps API consumers understand when and how to use the method.

Add documentation like:

+    /**
+     * @description
+     * Updates the currency code for an Order and recalculates all prices.
+     * This operation is only allowed when the Order is in the 'AddingItems' or 'Draft' state.
+     * The new currency must be available in the current Channel.
+     *
+     * @param ctx - The RequestContext
+     * @param orderId - The ID of the Order to update
+     * @param currencyCode - The new currency code to apply
+     * @param relations - Optional relations to load on the returned Order
+     * @returns The updated Order or an error result if validation fails
+     */
     async updateOrderCurrency(
         ctx: RequestContext,
         orderId: ID,
         currencyCode: CurrencyCode,
         relations?: RelationPaths<Order>,
     ): Promise<ErrorResultUnion<UpdateOrderItemsResult, Order>> {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5742fc and 6e2b6c9.

📒 Files selected for processing (6)
  • packages/core/e2e/graphql/generated-e2e-shop-types.ts (5 hunks)
  • packages/core/e2e/graphql/shop-definitions.ts (1 hunks)
  • packages/core/e2e/order.e2e-spec.ts (7 hunks)
  • packages/core/src/api/schema/shop-api/shop.api.graphql (1 hunks)
  • packages/core/src/service/services/history.service.ts (2 hunks)
  • packages/core/src/service/services/order.service.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/core/src/api/schema/shop-api/shop.api.graphql
  • packages/core/e2e/graphql/shop-definitions.ts
  • packages/core/src/service/services/history.service.ts
  • packages/core/e2e/order.e2e-spec.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/core/src/service/services/order.service.ts (3)
packages/core/src/api/common/request-context.ts (1)
  • RequestContext (179-447)
packages/common/src/shared-types.ts (1)
  • ID (79-79)
packages/core/src/api/resolvers/entity/channel-entity.resolver.ts (1)
  • currencyCode (21-23)
packages/core/e2e/graphql/generated-e2e-shop-types.ts (1)
packages/common/src/generated-shop-types.ts (2)
  • UpdateOrderItemsResult (3566-3572)
  • MutationSetCurrencyCodeForOrderArgs (1939-1941)
🔇 Additional comments (6)
packages/core/src/service/services/order.service.ts (1)

600-647: The existing assertAddingItemsState check already prevents currency changes on orders with payments. Orders in AddingItems or Draft states cannot have payments because canAddPaymentToOrder only allows payments on ArrangingPayment state or states that can transition to PaymentAuthorized/PaymentSettled. Since AddingItems must transition to ArrangingPayment before reaching payment states, no explicit payment check is needed.

Likely an incorrect or invalid review comment.

packages/core/e2e/graphql/generated-e2e-shop-types.ts (5)

1211-1238: HistoryEntryType: ORDER_CURRENCY_UPDATED addition looks consistent.
Just ensure the server schema and all generated type packages include the same enum value to avoid runtime GraphQL validation errors in e2e.


1704-1806: Mutation.setCurrencyCodeForOrder added appropriately in Shop e2e types.
Return type UpdateOrderItemsResult matches the “order mutation-style” unions used elsewhere in this file.


1879-1882: Args shape for setCurrencyCodeForOrder is correct (required currencyCode).
Matches the expected GraphQL signature pattern (field args modeled via a dedicated Mutation*Args type).


3702-3783: SetCurrencyCodeForOrderMutation result union is coherent with existing UpdateOrderItemsResult patterns.
Including OrderInterceptorError + generic ErrorResult fragments matches how other mutations are modeled here.


6080-6259: SetCurrencyCodeForOrderDocument: variable + selections look correct and consistent with other documents.
Note: the repeated UpdatedOrder fragment definition inside the document is typical of codegen output—avoid manual edits; rely on regeneration.

Comment on lines 154 to 163
// channels: [
// {
// token: 'default',
// defaultLanguageCode: LanguageCode.en,
// // Set the base currency ()
// currencyCode: CurrencyCode.USD,
// availableLanguageCodes: [LanguageCode.en],
// availableCurrencyCodes: [CurrencyCode.USD, CurrencyCode.EGP],
// },
// ],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lil leftover thingy? :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, sorry for this

Comment on lines 387 to 397
const order = await this.activeOrderService.getActiveOrder(
ctx,
args[ACTIVE_ORDER_INPUT_FIELD_NAME],
true,
);
return this.orderService.setCurrencyCodeForOrder(
ctx,
order.id,
args.currencyCode as CurrencyCode,
relations,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware that you're following the current pattern of passing in the orderId instead of the order object because thats how the other methods work, and it's good to follow conventions, but I want to pose a general question to the vendure team here that we could get rid of database queries by replacing the IDs with a real order objects. Food for thought.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain how the order object will be represented without hitting the DB? Does it exist in the context?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And will this postpone this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain how the order object will be represented without hitting the DB?

Sorry I dont mean to get rid off all the requests, just some. The .getActiveOrder function may hit the DB but then we only pass the ID to .setCurrencyCodeForOrder which fetches the order again. This holds true for other methods which also re-fetch orders. We could change function signatures to accept the order object directly, getting rid off this re-fetching.

Like I said, its correct of you to follow this convention, I'm posing this new shift as a general question, no worries

And will this postpone this PR?

No probably not

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DanielBiegler it's a good point and one we can revisit in some future performance work I think.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6e2b6c9 and 0162222.

📒 Files selected for processing (1)
  • packages/core/e2e/order.e2e-spec.ts (7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-21T18:29:30.413Z
Learnt from: DanielBiegler
Repo: vendure-ecommerce/vendure PR: 3815
File: packages/core/e2e/api-key.e2e-spec.ts:160-224
Timestamp: 2025-11-21T18:29:30.413Z
Learning: In the vendure-ecommerce/vendure repository, hand-written `gql` template literals (e.g., `CREATE_API_KEY`, `API_KEY`, etc.) in test files like `packages/core/e2e/api-key.e2e-spec.ts` are the source documents for GraphQL code generation. The code generator processes these to produce typed `*Document` exports (e.g., `CreateApiKeyDocument`, `ApiKeyDocument`) that are then imported and used in the tests. These hand-written gql constants should not be considered unused or dead code—they are essential inputs to the code generation process.

Applied to files:

  • packages/core/e2e/order.e2e-spec.ts
🧬 Code graph analysis (1)
packages/core/e2e/order.e2e-spec.ts (6)
packages/core/e2e/graphql/generated-e2e-admin-types.ts (1)
  • AssignProductVariantsToChannelInput (277-281)
packages/common/src/generated-types.ts (1)
  • AssignProductVariantsToChannelInput (270-274)
packages/core/e2e/graphql/shared-definitions.ts (1)
  • ASSIGN_PRODUCTVARIANT_TO_CHANNEL (412-419)
packages/core/e2e/graphql/generated-e2e-shop-types.ts (2)
  • SetCurrencyCodeForOrderMutation (3706-3783)
  • SetCurrencyCodeForOrderMutationVariables (3702-3704)
packages/core/e2e/graphql/shop-definitions.ts (2)
  • SET_CURRENCY_CODE_FOR_ORDER (150-170)
  • ADD_MULTIPLE_ITEMS_TO_ORDER (133-148)
packages/core/src/service/services/order.service.ts (2)
  • addItemsToOrder (660-772)
  • setCurrencyCodeForOrder (589-598)
🔇 Additional comments (5)
packages/core/e2e/order.e2e-spec.ts (5)

5-5: LGTM: Imports are correctly added for the new currency test suite.

The new imports (CurrencyCode, ASSIGN_PRODUCTVARIANT_TO_CHANNEL, UPDATE_CHANNEL, SET_CURRENCY_CODE_FOR_ORDER) are all utilized in the test suite added below and sourced from the appropriate modules.

Also applies to: 62-62, 77-77, 89-89


601-601: LGTM: Non-null assertion is appropriate here.

The non-null assertion on order! is consistent with patterns used throughout this test file and is safe in this controlled test context.


2943-2951: LGTM: Good negative test case.

This test properly verifies that attempting to set a currency not available in the channel results in an error. The use of rejects.toThrow is appropriate for testing async errors.


2953-2980: LGTM: Well-structured positive test case.

This test properly validates the happy path:

  1. Creates an order with items in the default currency (USD)
  2. Verifies the initial currency
  3. Updates the currency to EGP
  4. Confirms the currency change and order integrity

The type assertions and test structure are consistent with patterns used throughout this file.


2908-2981: Well-structured test suite for currency update feature.

The test suite provides appropriate coverage with both error and success scenarios. The structure follows established patterns in this test file, and the use of beforeAll to set up channel currencies and product variants is consistent with the approach used throughout.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/core/e2e/order.e2e-spec.ts (1)

2908-2981: update order currency tests look solid; a couple of small cleanups

The setup and both test cases correctly exercise the new mutation (invalid currency vs successful change) and use the generated types as expected. A few minor polish points you might consider:

  • The comment above the ASSIGN_PRODUCTVARIANT_TO_CHANNEL call says // Add 1st variant of product 1 to the channel, but the code assigns all variants: product1.variants.map(v => v.id). Either change the comment to match the behavior or, if you really only care about the first variant, restrict the list to [product1.variants[0].id].
  • For consistency with the rest of the file, you could use the generated variables type instead of an inline object type in the AssignProductVariantsToChannel call, e.g.:
    await adminClient.query<
        Codegen.AssignProductVariantsToChannelMutation,
        Codegen.AssignProductVariantsToChannelMutationVariables
    >(ASSIGN_PRODUCTVARIANT_TO_CHANNEL, {
        input: {
            channelId: 'T_1',
            productVariantIds: product1.variants.map(v => v.id),
            priceFactor: 1,
        },
    });
    if AssignProductVariantsToChannelMutationVariables is available from codegen.

These are all non-blocking—tests are clear and should behave correctly as written.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0162222 and f7c464d.

📒 Files selected for processing (1)
  • packages/core/e2e/order.e2e-spec.ts (7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-21T18:29:30.413Z
Learnt from: DanielBiegler
Repo: vendure-ecommerce/vendure PR: 3815
File: packages/core/e2e/api-key.e2e-spec.ts:160-224
Timestamp: 2025-11-21T18:29:30.413Z
Learning: In the vendure-ecommerce/vendure repository, hand-written `gql` template literals (e.g., `CREATE_API_KEY`, `API_KEY`, etc.) in test files like `packages/core/e2e/api-key.e2e-spec.ts` are the source documents for GraphQL code generation. The code generator processes these to produce typed `*Document` exports (e.g., `CreateApiKeyDocument`, `ApiKeyDocument`) that are then imported and used in the tests. These hand-written gql constants should not be considered unused or dead code—they are essential inputs to the code generation process.

Applied to files:

  • packages/core/e2e/order.e2e-spec.ts
🔇 Additional comments (2)
packages/core/e2e/order.e2e-spec.ts (2)

4-10: New currency & GraphQL imports are consistent and well-scoped

The added CurrencyCode import and the new document imports (ASSIGN_PRODUCTVARIANT_TO_CHANNEL, UPDATE_CHANNEL, SET_CURRENCY_CODE_FOR_ORDER) line up cleanly with their usages in the new tests and follow the existing typing patterns in this file. No issues from my side here.

Also applies to: 62-79, 81-92


596-647: History expectation remains precise and readable

The updated assertion on order!.history.items.map(pick(['type', 'data'])) still fully specifies the expected sequence of state and payment transitions for the payment flow on T_2. This keeps the test robust against unintended history changes and matches the surrounding style.

Copy link
Member

@michaelbromley michaelbromley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this!

I left one bit of feedback about the service, and more generally since this is a new feature it should be made against the minor branch.

The minor branch also features a re-worked way of handling the codegen for the e2e tests. We are now using a library called gql.tada. Check the other examples to see how it works.

setCurrencyCodeForOrder was a no-op wrapper; this simplifies the service by removing it.
@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/core/src/service/services/order.service.ts (1)

590-637: LGTM: Currency update implementation is robust and well-structured.

The updateOrderCurrency method correctly handles the currency change workflow:

  • Validates order modification state and currency availability
  • Creates a properly scoped RequestContext with the new currency for accurate price recalculation
  • Records history entry for audit trail
  • Applies price adjustments to all order lines
  • All operations are transactional (via the @Transaction decorator in the resolver)

The implementation follows established patterns and handles edge cases appropriately.

💡 Optional optimization consideration

At line 608, the channel is fetched from the database to access availableCurrencyCodes:

const channel = await this.channelService.getChannelFromToken(ctx.channel.token);

If ctx.channel already has the availableCurrencyCodes property loaded, this fetch could be avoided. However, fetching ensures fresh data and is the safer approach. This is a minor optimization opportunity that could be explored if this code path becomes a performance bottleneck.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7c464d and 86c2029.

📒 Files selected for processing (2)
  • packages/core/src/api/resolvers/shop/shop-order.resolver.ts
  • packages/core/src/service/services/order.service.ts
🔇 Additional comments (4)
packages/core/src/api/resolvers/shop/shop-order.resolver.ts (2)

12-12: LGTM: Import added for new mutation.

The MutationSetCurrencyCodeForOrderArgs import is correctly added to support the new setCurrencyCodeForOrder mutation method.


378-393: LGTM: New mutation follows established patterns correctly.

The setCurrencyCodeForOrder mutation is well-implemented:

  • Appropriate permissions (UpdateOrder and Owner)
  • Consistent with existing mutation patterns in this resolver
  • Proper active order retrieval with strict mode
  • Clean delegation to service layer
packages/core/src/service/services/order.service.ts (2)

5-5: LGTM: Imports added to support currency update functionality.

The CurrencyCode type and RequestContextService imports are correctly added for the new updateOrderCurrency method.

Also applies to: 119-119


170-170: LGTM: RequestContextService properly injected.

The service is correctly injected to support creating currency-aware request contexts.

@aelhor
Copy link
Author

aelhor commented Dec 22, 2025

@michaelbromley
I'm not sure I fully understand the last comment
Does this mean I have to change the base branch from master to a minor branch?

Regarding the e2e test and codegen replacement, could you please clarify what examples you are referring to? And does this require a change in the PR?

@dlhck
Copy link
Collaborator

dlhck commented Dec 29, 2025

@aelhor yes, please rebase to minor and apply the new way how GraphQL codegen works there. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants