Procházet zdrojové kódy

fix(core): Update variants when changing channel defaultCurrencyCode

Fixes #2190
Michael Bromley před 2 roky
rodič
revize
2303328bb8

+ 2 - 0
packages/core/e2e/graphql/fragments.ts

@@ -546,6 +546,8 @@ export const CHANNEL_FRAGMENT = gql`
         code
         token
         currencyCode
+        availableCurrencyCodes
+        defaultCurrencyCode
         defaultLanguageCode
         defaultShippingZone {
             id

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 590 - 571
packages/core/e2e/graphql/generated-e2e-admin-types.ts


+ 7 - 1
packages/core/e2e/graphql/shared-definitions.ts

@@ -763,6 +763,7 @@ export const GET_PRODUCTS_WITH_VARIANT_PRICES = gql`
                     id
                     price
                     priceWithTax
+                    currencyCode
                     sku
                     facetValues {
                         id
@@ -981,7 +982,7 @@ export const TRANSITION_PAYMENT_TO_STATE = gql`
 `;
 
 export const GET_PRODUCT_VARIANT_LIST = gql`
-    query GetProductVariantLIST($options: ProductVariantListOptions, $productId: ID) {
+    query GetProductVariantList($options: ProductVariantListOptions, $productId: ID) {
         productVariants(options: $options, productId: $productId) {
             items {
                 id
@@ -989,6 +990,11 @@ export const GET_PRODUCT_VARIANT_LIST = gql`
                 sku
                 price
                 priceWithTax
+                currencyCode
+                prices {
+                    currencyCode
+                    price
+                }
             }
             totalItems
         }

+ 91 - 1
packages/core/e2e/product-channel.e2e-spec.ts

@@ -1,5 +1,10 @@
 /* eslint-disable @typescript-eslint/no-non-null-assertion */
-import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
+import {
+    createErrorResultGuard,
+    createTestEnvironment,
+    E2E_DEFAULT_CHANNEL_TOKEN,
+    ErrorResultGuard,
+} from '@vendure/testing';
 import path from 'path';
 import { afterAll, beforeAll, describe, expect, it } from 'vitest';
 
@@ -9,6 +14,7 @@ import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-conf
 import {
     AssignProductsToChannelDocument,
     AssignProductVariantsToChannelDocument,
+    ChannelFragment,
     CreateAdministratorDocument,
     CreateChannelDocument,
     CreateProductDocument,
@@ -17,6 +23,8 @@ import {
     CreateRoleDocument,
     CreateRoleMutation,
     CurrencyCode,
+    GetChannelsDocument,
+    GetProductVariantListDocument,
     GetProductWithVariantsDocument,
     GetProductWithVariantsQuery,
     LanguageCode,
@@ -24,7 +32,9 @@ import {
     ProductVariantFragment,
     RemoveProductsFromChannelDocument,
     RemoveProductVariantsFromChannelDocument,
+    UpdateChannelDocument,
     UpdateProductDocument,
+    UpdateProductVariantsDocument,
 } from './graphql/generated-e2e-admin-types';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 
@@ -417,4 +427,84 @@ describe('ChannelAware Products and ProductVariants', () => {
             }, 'No Product with the id "2" could be found'),
         );
     });
+
+    describe('updating channel defaultCurrencyCode', () => {
+        let secondChannelId: string;
+        const channelGuard: ErrorResultGuard<ChannelFragment> = createErrorResultGuard(input => !!input.id);
+
+        beforeAll(async () => {
+            const { channels } = await adminClient.query(GetChannelsDocument);
+            secondChannelId = channels.items.find(c => c.token === SECOND_CHANNEL_TOKEN)!.id;
+        });
+
+        it('updates variant prices from old default to new', async () => {
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { productVariants } = await adminClient.query(GetProductVariantListDocument, {});
+
+            expect(productVariants.items.map(i => i.currencyCode)).toEqual(['GBP']);
+
+            const { updateChannel } = await adminClient.query(UpdateChannelDocument, {
+                input: {
+                    id: secondChannelId,
+                    availableCurrencyCodes: [CurrencyCode.MYR, CurrencyCode.GBP, CurrencyCode.EUR],
+                    defaultCurrencyCode: CurrencyCode.MYR,
+                },
+            });
+
+            channelGuard.assertSuccess(updateChannel);
+            expect(updateChannel.defaultCurrencyCode).toBe(CurrencyCode.MYR);
+
+            const { productVariants: variantsAfter } = await adminClient.query(
+                GetProductVariantListDocument,
+                {},
+            );
+
+            expect(variantsAfter.items.map(i => i.currencyCode)).toEqual(['MYR']);
+        });
+
+        it('does not change prices in other currencies', async () => {
+            adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
+            const { productVariants } = await adminClient.query(GetProductVariantListDocument, {});
+
+            const { updateProductVariants } = await adminClient.query(UpdateProductVariantsDocument, {
+                input: productVariants.items.map(i => ({
+                    id: i.id,
+                    prices: [
+                        { price: 100, currencyCode: CurrencyCode.GBP },
+                        { price: 200, currencyCode: CurrencyCode.MYR },
+                        { price: 300, currencyCode: CurrencyCode.EUR },
+                    ],
+                })),
+            });
+
+            expect(updateProductVariants[0]?.prices.sort((a, b) => a.price - b.price)).toEqual([
+                { currencyCode: 'GBP', price: 100 },
+                { currencyCode: 'MYR', price: 200 },
+                { currencyCode: 'EUR', price: 300 },
+            ]);
+            expect(updateProductVariants[0]?.currencyCode).toBe('MYR');
+
+            await adminClient.query(UpdateChannelDocument, {
+                input: {
+                    id: secondChannelId,
+                    availableCurrencyCodes: [
+                        CurrencyCode.MYR,
+                        CurrencyCode.GBP,
+                        CurrencyCode.EUR,
+                        CurrencyCode.AUD,
+                    ],
+                    defaultCurrencyCode: CurrencyCode.AUD,
+                },
+            });
+
+            const { productVariants: after } = await adminClient.query(GetProductVariantListDocument, {});
+
+            expect(after.items.map(i => i.currencyCode)).toEqual(['AUD']);
+            expect(after.items[0]?.prices.sort((a, b) => a.price - b.price)).toEqual([
+                { currencyCode: 'GBP', price: 100 },
+                { currencyCode: 'AUD', price: 200 },
+                { currencyCode: 'EUR', price: 300 },
+            ]);
+        });
+    });
 });

+ 19 - 0
packages/core/src/service/services/channel.service.ts

@@ -293,6 +293,7 @@ export class ChannelService {
         if (!channel) {
             throw new EntityNotFoundError('Channel', input.id);
         }
+        const originalDefaultCurrencyCode = channel.defaultCurrencyCode;
         const defaultLanguageValidationResult = await this.validateDefaultLanguageCode(ctx, input);
         if (isGraphQlErrorResult(defaultLanguageValidationResult)) {
             return defaultLanguageValidationResult;
@@ -319,6 +320,24 @@ export class ChannelService {
         if (input.currencyCode) {
             updatedChannel.defaultCurrencyCode = input.currencyCode;
         }
+        if (input.currencyCode || input.defaultCurrencyCode) {
+            const newCurrencyCode = input.defaultCurrencyCode || input.currencyCode;
+            if (originalDefaultCurrencyCode !== newCurrencyCode) {
+                // When updating the default currency code for a Channel, we also need to update
+                // and ProductVariantPrices in that channel which use the old currency code.
+                const qb = this.connection
+                    .getRepository(ctx, ProductVariantPrice)
+                    .createQueryBuilder('pvp')
+                    .update()
+                    .where('channelId = :channelId', { channelId: channel.id })
+                    .andWhere('currencyCode = :currencyCode', {
+                        currencyCode: originalDefaultCurrencyCode,
+                    })
+                    .set({ currencyCode: newCurrencyCode });
+
+                await qb.execute();
+            }
+        }
         await this.connection.getRepository(ctx, Channel).save(updatedChannel, { reload: false });
         await this.customFieldRelationService.updateRelations(ctx, Channel, input, updatedChannel);
         await this.allChannels.refresh(ctx);

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů