Răsfoiți Sursa

fix(core): Add constraints to Channel currencyCode settings

Relates to #GHSA-wm63-7627-ch33
Michael Bromley 2 ani în urmă
părinte
comite
0ebf0fb892

+ 2 - 1
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -3988,6 +3988,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];
@@ -4731,7 +4732,7 @@ export type ProductVariantListOptions = {
 export type ProductVariantPrice = {
   __typename?: 'ProductVariantPrice';
   currencyCode: CurrencyCode;
-  price: Scalars['Int']['output'];
+  price: Scalars['Money']['output'];
 };
 
 /**

+ 2 - 1
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -3823,6 +3823,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];
@@ -4536,7 +4537,7 @@ export type ProductVariantListOptions = {
 
 export type ProductVariantPrice = {
   currencyCode: CurrencyCode;
-  price: Scalars['Int']['output'];
+  price: Scalars['Money']['output'];
 };
 
 /**

+ 1 - 0
packages/common/src/generated-shop-types.ts

@@ -2086,6 +2086,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];

+ 2 - 1
packages/common/src/generated-types.ts

@@ -3914,6 +3914,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];
@@ -4656,7 +4657,7 @@ export type ProductVariantListOptions = {
 export type ProductVariantPrice = {
   __typename?: 'ProductVariantPrice';
   currencyCode: CurrencyCode;
-  price: Scalars['Int']['output'];
+  price: Scalars['Money']['output'];
 };
 
 /**

+ 98 - 13
packages/core/e2e/channel.e2e-spec.ts

@@ -124,19 +124,19 @@ describe('Channels', () => {
         });
     });
 
-    it('update currencyCode', async () => {
-        const { updateChannel } = await adminClient.query<
-            Codegen.UpdateChannelMutation,
-            Codegen.UpdateChannelMutationVariables
-        >(UPDATE_CHANNEL, {
-            input: {
-                id: 'T_1',
-                currencyCode: CurrencyCode.MYR,
-            },
-        });
-        channelGuard.assertSuccess(updateChannel);
-        expect(updateChannel.currencyCode).toBe('MYR');
-    });
+    // it('update currencyCode', async () => {
+    //     const { updateChannel } = await adminClient.query<
+    //         Codegen.UpdateChannelMutation,
+    //         Codegen.UpdateChannelMutationVariables
+    //     >(UPDATE_CHANNEL, {
+    //         input: {
+    //             id: 'T_1',
+    //             currencyCode: CurrencyCode.MYR,
+    //         },
+    //     });
+    //     channelGuard.assertSuccess(updateChannel);
+    //     expect(updateChannel.currencyCode).toBe('MYR');
+    // });
 
     it('superadmin has all permissions on new channel', async () => {
         const { me } = await adminClient.query<Codegen.MeQuery>(ME);
@@ -368,6 +368,75 @@ describe('Channels', () => {
         });
         expect(product!.channels.map(c => c.id)).toEqual(['T_1']);
     });
+
+    describe('currencyCode support', () => {
+        beforeAll(async () => {
+            await adminClient.asSuperAdmin();
+            adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
+        });
+
+        it('initial currencyCode values', async () => {
+            const { channel } = await adminClient.query<
+                Codegen.GetChannelQuery,
+                Codegen.GetChannelQueryVariables
+            >(GET_CHANNEL, {
+                id: 'T_1',
+            });
+
+            expect(channel?.defaultCurrencyCode).toBe('USD');
+            expect(channel?.availableCurrencyCodes).toEqual(['USD']);
+        });
+
+        it('setting defaultCurrencyCode adds it to availableCurrencyCodes', async () => {
+            const { updateChannel } = await adminClient.query<
+                Codegen.UpdateChannelMutation,
+                Codegen.UpdateChannelMutationVariables
+            >(UPDATE_CHANNEL, {
+                input: {
+                    id: 'T_1',
+                    defaultCurrencyCode: CurrencyCode.MYR,
+                },
+            });
+            channelGuard.assertSuccess(updateChannel);
+            expect(updateChannel.defaultCurrencyCode).toBe('MYR');
+            expect(updateChannel.currencyCode).toBe('MYR');
+            expect(updateChannel.availableCurrencyCodes).toEqual(['USD', 'MYR']);
+        });
+
+        it('setting defaultCurrencyCode adds it to availableCurrencyCodes 2', async () => {
+            // As above, but this time we set the availableCurrencyCodes explicitly
+            // to exclude the defaultCurrencyCode
+            const { updateChannel } = await adminClient.query<
+                Codegen.UpdateChannelMutation,
+                Codegen.UpdateChannelMutationVariables
+            >(UPDATE_CHANNEL, {
+                input: {
+                    id: 'T_1',
+                    defaultCurrencyCode: CurrencyCode.AUD,
+                    availableCurrencyCodes: [CurrencyCode.GBP],
+                },
+            });
+            channelGuard.assertSuccess(updateChannel);
+            expect(updateChannel.defaultCurrencyCode).toBe('AUD');
+            expect(updateChannel.currencyCode).toBe('AUD');
+            expect(updateChannel.availableCurrencyCodes).toEqual(['GBP', 'AUD']);
+        });
+
+        it(
+            'cannot remove the defaultCurrencyCode from availableCurrencyCodes',
+            assertThrowsWithMessage(async () => {
+                await adminClient.query<
+                    Codegen.UpdateChannelMutation,
+                    Codegen.UpdateChannelMutationVariables
+                >(UPDATE_CHANNEL, {
+                    input: {
+                        id: 'T_1',
+                        availableCurrencyCodes: [CurrencyCode.GBP],
+                    },
+                });
+            }, 'availableCurrencyCodes must include the defaultCurrencyCode (AUD)'),
+        );
+    });
 });
 
 const DELETE_CHANNEL = gql`
@@ -379,6 +448,22 @@ const DELETE_CHANNEL = gql`
     }
 `;
 
+const GET_CHANNEL = gql`
+    query GetChannel($id: ID!) {
+        channel(id: $id) {
+            id
+            code
+            token
+            defaultCurrencyCode
+            availableCurrencyCodes
+            defaultLanguageCode
+            availableLanguageCodes
+            outOfStockThreshold
+            pricesIncludeTax
+        }
+    }
+`;
+
 const UPDATE_GLOBAL_LANGUAGES = gql`
     mutation UpdateGlobalLanguages($input: UpdateGlobalSettingsInput!) {
         updateGlobalSettings(input: $input) {

Fișier diff suprimat deoarece este prea mare
+ 17 - 8
packages/core/e2e/graphql/generated-e2e-admin-types.ts


+ 1 - 0
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -2022,6 +2022,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];

+ 1 - 0
packages/core/src/i18n/messages/en.json

@@ -1,6 +1,7 @@
 {
   "error": {
     "active-user-does-not-have-sufficient-permissions": "Active user does not have sufficient permissions",
+    "available-currency-codes-must-include-default": "availableCurrencyCodes must include the defaultCurrencyCode ({ defaultCurrencyCode })",
     "cannot-delete-role": "The role \"{ roleCode }\" cannot be deleted",
     "cannot-delete-sole-superadmin": "The sole SuperAdmin cannot be deleted",
     "cannot-locate-customer-for-user": "Cannot locate a Customer for the user",

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

@@ -322,6 +322,10 @@ export class ChannelService {
         }
         if (input.currencyCode || input.defaultCurrencyCode) {
             const newCurrencyCode = input.defaultCurrencyCode || input.currencyCode;
+            updatedChannel.availableCurrencyCodes = unique([
+                ...updatedChannel.availableCurrencyCodes,
+                updatedChannel.defaultCurrencyCode,
+            ]);
             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.
@@ -338,6 +342,14 @@ export class ChannelService {
                 await qb.execute();
             }
         }
+        if (
+            input.availableCurrencyCodes &&
+            !updatedChannel.availableCurrencyCodes.includes(updatedChannel.defaultCurrencyCode)
+        ) {
+            throw new UserInputError(`error.available-currency-codes-must-include-default`, {
+                defaultCurrencyCode: updatedChannel.defaultCurrencyCode,
+            });
+        }
         await this.connection.getRepository(ctx, Channel).save(updatedChannel, { reload: false });
         await this.customFieldRelationService.updateRelations(ctx, Channel, input, updatedChannel);
         await this.allChannels.refresh(ctx);

+ 2 - 1
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -3823,6 +3823,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];
@@ -4536,7 +4537,7 @@ export type ProductVariantListOptions = {
 
 export type ProductVariantPrice = {
   currencyCode: CurrencyCode;
-  price: Scalars['Int']['output'];
+  price: Scalars['Money']['output'];
 };
 
 /**

+ 2 - 1
packages/payments-plugin/e2e/graphql/generated-admin-types.ts

@@ -3823,6 +3823,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];
@@ -4536,7 +4537,7 @@ export type ProductVariantListOptions = {
 
 export type ProductVariantPrice = {
   currencyCode: CurrencyCode;
-  price: Scalars['Int']['output'];
+  price: Scalars['Money']['output'];
 };
 
 /**

+ 1 - 0
packages/payments-plugin/e2e/graphql/generated-shop-types.ts

@@ -2022,6 +2022,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];

+ 1 - 0
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts

@@ -2143,6 +2143,7 @@ export type OrderLine = Node & {
   proratedUnitPrice: Scalars['Money']['output'];
   /** The proratedUnitPrice including tax */
   proratedUnitPriceWithTax: Scalars['Money']['output'];
+  /** The quantity of items purchased */
   quantity: Scalars['Int']['output'];
   taxLines: Array<TaxLine>;
   taxRate: Scalars['Float']['output'];

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-admin.json


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
schema-shop.json


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff