فهرست منبع

Merge branch 'master' into minor

Michael Bromley 4 سال پیش
والد
کامیت
9346c7b673
37فایلهای تغییر یافته به همراه246 افزوده شده و 121 حذف شده
  1. BIN
      docs/content/developer-guide/channels/channels_currencies_diagram.png
  2. BIN
      docs/content/developer-guide/channels/channels_prices_diagram.png
  3. 35 0
      docs/content/developer-guide/channels/index.md
  4. 2 2
      docs/content/developer-guide/customizing-models.md
  5. 9 1
      docs/content/developer-guide/deployment.md
  6. 6 0
      docs/content/developer-guide/multi-tenant/index.md
  7. 1 1
      docs/content/developer-guide/payment-integrations/index.md
  8. 1 1
      docs/content/getting-started.md
  9. 3 3
      docs/content/plugins/plugin-architecture/_index.md
  10. 2 2
      docs/content/plugins/plugin-examples/extending-graphql-api.md
  11. 12 1
      docs/content/plugins/plugin-examples/using-job-queue-service.md
  12. 25 0
      docs/diagrams/channels-currencies-diagram.puml
  13. 28 0
      docs/diagrams/channels-prices-diagram.puml
  14. 3 3
      packages/admin-ui-plugin/build.ts
  15. 6 6
      packages/admin-ui/src/lib/core/src/common/generated-types.ts
  16. 1 1
      packages/admin-ui/src/lib/core/src/shared/directives/if-default-channel-active.directive.ts
  17. 1 1
      packages/admin-ui/src/lib/core/src/shared/directives/if-multichannel.directive.ts
  18. 6 6
      packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts
  19. 16 16
      packages/common/src/generated-shop-types.ts
  20. 6 6
      packages/common/src/generated-types.ts
  21. 1 0
      packages/core/e2e/custom-field-relations.e2e-spec.ts
  22. 6 6
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  23. 16 16
      packages/core/e2e/graphql/generated-e2e-shop-types.ts
  24. 4 0
      packages/core/src/api/config/generate-resolvers.ts
  25. 1 1
      packages/core/src/api/schema/admin-api/product.api.graphql
  26. 4 4
      packages/core/src/api/schema/common/common-error-results.graphql
  27. 1 1
      packages/core/src/api/schema/common/product-search.type.graphql
  28. 8 8
      packages/core/src/api/schema/shop-api/shop-error-results.graphql
  29. 3 3
      packages/core/src/api/schema/shop-api/shop.api.graphql
  30. 2 2
      packages/core/src/config/asset-storage-strategy/asset-storage-strategy.ts
  31. 8 1
      packages/core/src/entity/order-line/order-line.entity.ts
  32. 1 1
      packages/core/src/job-queue/subscribable-job.ts
  33. 6 6
      packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts
  34. 6 6
      packages/payments-plugin/e2e/graphql/generated-admin-types.ts
  35. 16 16
      packages/payments-plugin/e2e/graphql/generated-shop-types.ts
  36. 0 0
      schema-admin.json
  37. 0 0
      schema-shop.json

BIN
docs/content/developer-guide/channels/channels_currencies_diagram.png


BIN
docs/content/developer-guide/channels/channels_prices_diagram.png


+ 35 - 0
docs/content/developer-guide/channels/index.md

@@ -23,6 +23,41 @@ Use-cases of Channels include:
 * Creating distinct rules and inventory for different sales channels such as Amazon.
 * Specialized stores offering a subset of the main inventory.
 
+## Channels, Currencies & Prices
+
+Each Channel has an associated **currencyCode** property, which sets the currency for all monetary values in that channel.
+
+{{< figure src="channels_currencies_diagram.png" >}}
+
+Internally, there is a one-to-many relation from [ProductVariant]({{< relref "product-variant" >}}) to [ProductVariantPrice]({{< relref "product-variant-price" >}}). So the ProductVariant does _not_ hold a price for the product - this is actually stored on the ProductVariantPrice entity, and there will be one for each Channel to which the ProductVariant has been assigned.
+
+{{< figure src="channels_prices_diagram.png" >}}
+
+{{< alert "warning" >}}
+**Note:** in the diagram above that the ProductVariant is **always assigned to the default Channel**, and thus will have a price in the default channel too. Likewise, the default Channel also has a currencyCode.
+{{< /alert >}}
+
+### Use-case: single shop
+
+This is the simplest set-up. You just use the default Channel for everything
+
+### Use-case: Multiple separate shops
+
+Let's say you are running multiple distinct businesses, each with its own distinct inventory and possibly different currencies. In this case, you set up a Channel for each shop and create the Product & Variants in the relevant shop's Channel.
+
+The default Channel can then be used by the superadmin for administrative purposes, but other than that the default Channel would not be used. Storefronts would only target a specific shop's Channel.
+
+### Use-case: Multiple shops sharing inventory
+
+Let's say you have a single inventory but want to split it between multiple shops. There might be overlap in the inventory, e.g. the US & EU shops share 80% of inventory, and then the rest is specific to either shop.
+
+In this case, you can create the entire inventory in the default Channel and then assign the Products & ProductVariants to each Channel as needed, setting the price as appropriate for the currency used by each shop.
+
+{{< alert "warning" >}}
+**Note:** When creating a new Product & ProductVariants inside a sub-Channel, it will also **always get assigned to the default Channel**. If your sub-Channel uses a different currency from the default Channel, you should be aware that in the default Channel, that ProductVariant will be assigned the **same price** as it has in the sub-Channel. If the currency differs between the Channels, you need to make sure to set the correct price in the default Channel if you are exposing it to Customers via a storefront. 
+{{< /alert >}}
+
+
 ## How to set the channel when using the GraphQL API
 
 To specify which channel to use when making an API call, set the `'vendure-token'` header to match the token of the desired Channel.

+ 2 - 2
docs/content/developer-guide/customizing-models.md

@@ -342,7 +342,7 @@ Some custom fields may be used internally in your business logic, or for integra
 
 * `public: false` means that it will not be exposed via the Shop API.
 * `readonly: true` means it will be exposed, but cannot be updated via the Admin API. It can only be changed programmatically in plugin code.
-* `internal: false` - means the field _will not_ be exposed via either the Shop or Admin GraphQL APIs. Internal custom fields are useful for purely internal implementation details.
+* `internal: false` - means the field _will_ be exposed via the GraphQL APIs (in this case on the Admin API due to the `public: false` setting). If it was set to `internal: true`, then the field would not be exposed _at all_ in either of the GraphQL APIs, and will not be visible in the Admin UI. Internal custom fields are useful for purely internal implementation details.
 
 ```TypeScript
 Customer: [
@@ -353,7 +353,7 @@ Customer: [
     readonly: true,
     internal: false,
   },
-]
+],
 ```
 
 ### Relations

+ 9 - 1
docs/content/developer-guide/deployment.md

@@ -143,11 +143,19 @@ This example is for Vercel, and assumes:
 * A `BASE_HREF` environment variable to be set to `/`
 * A public (output) directory set to `build/dist`
 * A build command set to `npm run build` or `yarn build`
-* A `build` script defined in your package.json:
+* A package.json like this:
     ```json
     {
+      "name": "standalone-admin-ui",
+      "version": "0.1.0",
+      "private": true,
       "scripts": {
         "build": "ts-node compile.ts"
+      },
+      "devDependencies": {
+        "@vendure/ui-devkit": "^1.4.5",
+        "ts-node": "^10.2.1",
+        "typescript": "~4.3.5"
       }
     }
     ```

+ 6 - 0
docs/content/developer-guide/multi-tenant/index.md

@@ -60,6 +60,12 @@ In the Admin UI, you can switch between active Channels using the switcher compo
 
 For example, switching to the `ace-parts` Channel, and then creating a new Product will assign that new Product to the `ace-parts` Channel (_and_ the default Channel, since _everything_ is assigned to the default Channel).
 
+{{< alert "warning" >}}
+**Note:** Care must be taken if you log in with the superadmin account in the default Channel, especially with regard to prices and currencies.
+
+See more details see the Channels guide on [Channels, currencies & prices]({{< relref "/docs/developer-guide/channels" >}}#channels-currencies--prices), and in particular the [multiple shops use-cases]({{< relref "/docs/developer-guide/channels" >}}#use-case-multiple-separate-shops)
+{{< /alert >}}
+
 ## The Storefront
 
 Your storefront applications will need to specify which channel they are interested in. This is done by adding a **query parameter** or **header** to each API requests, with the key being `vendure-token` and the value being the target Channel's `token` property.

+ 1 - 1
docs/content/developer-guide/payment-integrations/index.md

@@ -243,4 +243,4 @@ The checkout flow works as follows:
 
 When integrating with a system like this, you would need to create a Controller to accept POST redirects from the payment processor (usually a success and a failure URL), as well as serve a POST form on your store frontend.
 
-With a hosted payment form the payment is already authorized by the time the card processor makes the POST request to Vendure, possibly settled even, so the payment handler won't do anything in particular - just return the data it has been passed. The validation of the POST request is done in the controller or service and the payment amount and payment refernce are just passed to the payment handler which passes them on.
+With a hosted payment form the payment is already authorized by the time the card processor makes the POST request to Vendure, possibly settled even, so the payment handler won't do anything in particular - just return the data it has been passed. The validation of the POST request is done in the controller or service and the payment amount and payment reference are just passed to the payment handler which passes them on.

+ 1 - 1
docs/content/getting-started.md

@@ -16,7 +16,7 @@ weight: 0
  
 ## Installation with @vendure/create
 
-The recommended way to get started with Vendure is by using the [@vendure/create](https://github.com/vendure-ecommerce/vendure/tree/master/packages/create) tool. This is a command-line tool which will scaffold and configure your new Vendure project and install all dependiencies.
+The recommended way to get started with Vendure is by using the [@vendure/create](https://github.com/vendure-ecommerce/vendure/tree/master/packages/create) tool. This is a command-line tool which will scaffold and configure your new Vendure project and install all dependencies.
 
 {{% tab "npx" %}}
 ```sh

+ 3 - 3
docs/content/plugins/plugin-architecture/_index.md

@@ -8,10 +8,10 @@ showtoc: true
 
 {{< figure src="plugin_architecture.png" >}}
 
-A plugin in Vendure is a specialized Nestjs Module which is decorated with the [`VendurePlugin` class decorator]({{< relref "vendure-plugin" >}}). This diagram illustrates the how a plugin can integrate with and extend Vendure.
+A plugin in Vendure is a specialized Nestjs Module which is decorated with the [`VendurePlugin` class decorator]({{< relref "vendure-plugin" >}}). This diagram illustrates how a plugin can integrate with and extend Vendure.
  
-1. A Plugin may define logic to be run by the [Vendure Worker]({{< relref "/docs/developer-guide/vendure-worker" >}}). This is suitable for long-running or resource-intensive tasks and is done by providing controllers via the [`workers` metadata property]({{< relref "vendure-plugin-metadata" >}}#workers).
+1. A Plugin may define logic to be run by the [Vendure Worker]({{< relref "/docs/developer-guide/vendure-worker" >}}). This is suitable for long-running or resource-intensive tasks.
 2. A Plugin can modify any aspect of server configuration via the [`configuration` metadata property]({{< relref "vendure-plugin-metadata" >}}#configuration).
 3. A Plugin can extend the GraphQL APIs via the [`shopApiExtensions` metadata property]({{< relref "vendure-plugin-metadata" >}}#shopapiextensions) and the [`adminApiExtensions` metadata property]({{< relref "vendure-plugin-metadata" >}}#adminapiextensions).
-4. A Plugin can interact with Vendure by importing the [`PluginCommonModule`]({{< relref "plugin-common-module" >}}), by which it may inject any of the core Vendure services (which are responsible for all interaction with the database as well as business logic). Additionally a plugin may define new database entities via the [`entities` metadata property]({{< relref "vendure-plugin-metadata" >}}#entities) and otherwise define any other providers and controllers just like any [Nestjs module](https://docs.nestjs.com/modules).
+4. A Plugin can interact with Vendure by importing the [`PluginCommonModule`]({{< relref "plugin-common-module" >}}), by which it may inject any of the core Vendure services (which are responsible for all interaction with the database as well as business logic). Additionally, a plugin may define new database entities via the [`entities` metadata property]({{< relref "vendure-plugin-metadata" >}}#entities) and otherwise define any other providers and controllers just like any [Nestjs module](https://docs.nestjs.com/modules).
 5. A Plugin can run arbitrary code, which allows it to make use of external services. For example, a plugin could interface with a cloud storage provider, a payment gateway, or a video encoding service.

+ 2 - 2
docs/content/plugins/plugin-examples/extending-graphql-api.md

@@ -157,8 +157,8 @@ import { Ctx, RequestContext, Product } from '@vendure/core';
 export class FieldOverrideExampleResolver {
   
   @ResolveField()
-  description(@Ctx() ctx: RequestContext, @Parent() variant: Product) {
-    return this.wrapInFormatting(ctx, variant.id);
+  description(@Ctx() ctx: RequestContext, @Parent() product: Product) {
+    return this.wrapInFormatting(ctx, product.id);
   }
   
   private wrapInFormatting(ctx: RequestContext, id: ID): string {

+ 12 - 1
docs/content/plugins/plugin-examples/using-job-queue-service.md

@@ -76,7 +76,18 @@ class ProductVideoService implements OnModuleInit {
   }
 }
 ```
-The `ProductVideoService` is in charge of setting up the JobQueue and adding jobs to that queue.
+The `ProductVideoService` is in charge of setting up the JobQueue and adding jobs to that queue. Calling 
+
+```TypeScript
+productVideoService.transcodeForProduct(id, url);
+```
+
+will add a transcoding job to the queue.
+
+{{< alert warning >}}
+**Note:** plugin code typically gets executed on both the server _and_ the worker. Therefore, you sometimes need to explicitly check
+what context you are in. This can be done with the [ProcessContext]({{< relref "process-context" >}}) provider.
+{{< /alert >}}
 
 ```TypeScript
 // product-video.plugin.ts

+ 25 - 0
docs/diagrams/channels-currencies-diagram.puml

@@ -0,0 +1,25 @@
+@startuml
+!include theme.puml
+title Channels & Currencies
+
+node DefaultChannel as "Default Channel" {
+}
+
+package ChannelA as "US Channel" {
+   [currencyCode: USD]
+}
+
+package ChannelB as "EU Channel" {
+   [currencyCode: EUR]
+}
+
+package ChannelC as "UK Channel" {
+  [currencyCode: GBP]
+}
+
+DefaultChannel -down-> ChannelA
+DefaultChannel -down-> ChannelB
+DefaultChannel -down-> ChannelC
+
+
+@enduml

+ 28 - 0
docs/diagrams/channels-prices-diagram.puml

@@ -0,0 +1,28 @@
+@startuml
+!include theme.puml
+title Channels & Prices
+
+package ProductVariant {
+  [name\nsku\nassets\n...]
+}
+
+package PriceUSD as "ProductVariantPrice" {
+  [US Channel\nprice (in USD)]
+}
+package PriceEUR as "ProductVariantPrice" {
+  [EU Channel\nprice (in EUR)]
+}
+package PriceGBP as "ProductVariantPrice" {
+  [UK Channel\nprice (in GBP)]
+}
+package PriceDefault as "ProductVariantPrice" {
+  [Default Channel\nprice] #LightGray
+}
+
+
+ProductVariant -down-> PriceUSD
+ProductVariant -down-> PriceEUR
+ProductVariant -down-> PriceGBP
+ProductVariant -right-> PriceDefault
+
+@enduml

+ 3 - 3
packages/admin-ui-plugin/build.ts

@@ -6,16 +6,16 @@ import path from 'path';
 const compiledUiDir = path.join(__dirname, 'lib/admin-ui');
 console.log('Building admin-ui from source...');
 
-fs.remove(compiledUiDir);
+fs.removeSync(compiledUiDir);
 
 const adminUiDir = path.join(__dirname, '../admin-ui');
-const buildProcess = spawn('yarn', [`--cwd ${adminUiDir}`, 'run', 'build:app'], {
+const buildProcess = spawn('yarn', [`--cwd "${adminUiDir}"`, 'run', 'build:app'], {
     cwd: adminUiDir,
     shell: true,
     stdio: 'inherit',
 });
 
-buildProcess.on('close', (code) => {
+buildProcess.on('close', code => {
     if (code === 0) {
         fs.copySync(path.join(__dirname, '../admin-ui/dist'), compiledUiDir);
     } else {

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

@@ -1357,7 +1357,7 @@ export type Discount = {
   amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
   __typename?: 'EmailAddressConflictError';
   errorCode: ErrorCode;
@@ -3062,7 +3062,7 @@ export type NativeAuthInput = {
   password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
   __typename?: 'NativeAuthStrategyError';
   errorCode: ErrorCode;
@@ -3071,7 +3071,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
   __typename?: 'NegativeQuantityError';
   errorCode: ErrorCode;
@@ -3251,7 +3251,7 @@ export type OrderItem = Node & {
   refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
   __typename?: 'OrderLimitError';
   errorCode: ErrorCode;
@@ -4086,7 +4086,7 @@ export type Query = {
   paymentMethodHandlers: Array<ConfigurableOperationDefinition>;
   paymentMethods: PaymentMethodList;
   pendingSearchIndexUpdates: Scalars['Int'];
-  /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+  /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
   product?: Maybe<Product>;
   productOptionGroup?: Maybe<ProductOptionGroup>;
   productOptionGroups: Array<ProductOptionGroup>;
@@ -4558,7 +4558,7 @@ export type SearchResult = {
   facetValueIds: Array<Scalars['ID']>;
   /** An array of ids of the Collections in which this result appears */
   collectionIds: Array<Scalars['ID']>;
-  /** A relevence score for the result. Differs between database implementations */
+  /** A relevance score for the result. Differs between database implementations */
   score: Scalars['Float'];
 };
 

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/directives/if-default-channel-active.directive.ts

@@ -26,7 +26,7 @@ export class IfDefaultChannelActiveDirective extends IfDirectiveBase<[]> {
     }
 
     /**
-     * A template to show if the current user does not have the speicified permission.
+     * A template to show if the current user does not have the specified permission.
      */
     @Input()
     set vdrIfMultichannelElse(templateRef: TemplateRef<any> | null) {

+ 1 - 1
packages/admin-ui/src/lib/core/src/shared/directives/if-multichannel.directive.ts

@@ -35,7 +35,7 @@ export class IfMultichannelDirective extends IfDirectiveBase<[]> {
     }
 
     /**
-     * A template to show if the current user does not have the speicified permission.
+     * A template to show if the current user does not have the specified permission.
      */
     @Input()
     set vdrIfMultichannelElse(templateRef: TemplateRef<any> | null) {

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

@@ -1329,7 +1329,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2852,7 +2852,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2860,7 +2860,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3028,7 +3028,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3831,7 +3831,7 @@ export type Query = {
     pendingSearchIndexUpdates: Scalars['Int'];
     /** List Products */
     products: ProductList;
-    /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
     product?: Maybe<Product>;
     /** List ProductVariants either all or for the specific product. */
     productVariants: ProductVariantList;
@@ -4252,7 +4252,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

+ 16 - 16
packages/common/src/generated-shop-types.ts

@@ -61,7 +61,7 @@ export enum AdjustmentType {
     DISTRIBUTED_ORDER_PROMOTION = 'DISTRIBUTED_ORDER_PROMOTION',
 }
 
-/** Retured when attemting to set the Customer for an Order when already logged in. */
+/** Returned when attempting to set the Customer for an Order when already logged in. */
 export type AlreadyLoggedInError = ErrorResult & {
     __typename?: 'AlreadyLoggedInError';
     errorCode: ErrorCode;
@@ -858,7 +858,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     __typename?: 'EmailAddressConflictError';
     errorCode: ErrorCode;
@@ -1115,7 +1115,7 @@ export type IdOperators = {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is valid, but has
+ * Returned if the token used to change a Customer's email address is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type IdentifierChangeTokenExpiredError = ErrorResult & {
@@ -1125,7 +1125,7 @@ export type IdentifierChangeTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is either
+ * Returned if the token used to change a Customer's email address is either
  * invalid or does not match any expected tokens.
  */
 export type IdentifierChangeTokenInvalidError = ErrorResult & {
@@ -1534,7 +1534,7 @@ export enum LogicalOperator {
     OR = 'OR',
 }
 
-/** Retured when attemting to register or verify a customer account without a password, when one is required. */
+/** Returned when attempting to register or verify a customer account without a password, when one is required. */
 export type MissingPasswordError = ErrorResult & {
     __typename?: 'MissingPasswordError';
     errorCode: ErrorCode;
@@ -1584,7 +1584,7 @@ export type Mutation = {
      *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _without_ a password. The Customer is then
      *    verified and authenticated in one step.
      * 2. **The Customer is registered _without_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
-     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosed password of the Customer. The Customer is then
+     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosen password of the Customer. The Customer is then
      *    verified and authenticated in one step.
      *
      * _If `authOptions.requireVerification` is set to `false`:_
@@ -1605,7 +1605,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -1747,7 +1747,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     __typename?: 'NativeAuthStrategyError';
     errorCode: ErrorCode;
@@ -1760,7 +1760,7 @@ export type NativeAuthenticationResult =
     | NotVerifiedError
     | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     __typename?: 'NegativeQuantityError';
     errorCode: ErrorCode;
@@ -1937,7 +1937,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     __typename?: 'OrderLimitError';
     errorCode: ErrorCode;
@@ -2086,7 +2086,7 @@ export type PaginatedList = {
     totalItems: Scalars['Int'];
 };
 
-/** Retured when attemting to verify a customer account with a password, when a password has already been set. */
+/** Returned when attempting to verify a customer account with a password, when a password has already been set. */
 export type PasswordAlreadySetError = ErrorResult & {
     __typename?: 'PasswordAlreadySetError';
     errorCode: ErrorCode;
@@ -2094,7 +2094,7 @@ export type PasswordAlreadySetError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to reset a Customer's password is valid, but has
+ * Returned if the token used to reset a Customer's password is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type PasswordResetTokenExpiredError = ErrorResult & {
@@ -2104,7 +2104,7 @@ export type PasswordResetTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to reset a Customer's password is either
+ * Returned if the token used to reset a Customer's password is either
  * invalid or does not match any expected tokens.
  */
 export type PasswordResetTokenInvalidError = ErrorResult & {
@@ -2629,7 +2629,7 @@ export type Query = {
      * general anonymous access to Order data.
      */
     orderByCode?: Maybe<Order>;
-    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is specified, an error will result. */
     product?: Maybe<Product>;
     /** Get a list of Products */
     products: ProductList;
@@ -2799,7 +2799,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 
@@ -3079,7 +3079,7 @@ export type VerificationTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the verification token (used to verify a Customer's email address) is either
+ * Returned if the verification token (used to verify a Customer's email address) is either
  * invalid or does not match any expected tokens.
  */
 export type VerificationTokenInvalidError = ErrorResult & {

+ 6 - 6
packages/common/src/generated-types.ts

@@ -1349,7 +1349,7 @@ export type Discount = {
   amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
   __typename?: 'EmailAddressConflictError';
   errorCode: ErrorCode;
@@ -3003,7 +3003,7 @@ export type NativeAuthInput = {
   password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
   __typename?: 'NativeAuthStrategyError';
   errorCode: ErrorCode;
@@ -3012,7 +3012,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
   __typename?: 'NegativeQuantityError';
   errorCode: ErrorCode;
@@ -3187,7 +3187,7 @@ export type OrderItem = Node & {
   refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
   __typename?: 'OrderLimitError';
   errorCode: ErrorCode;
@@ -4024,7 +4024,7 @@ export type Query = {
   pendingSearchIndexUpdates: Scalars['Int'];
   /** List Products */
   products: ProductList;
-  /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+  /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
   product?: Maybe<Product>;
   /** List ProductVariants either all or for the specific product. */
   productVariants: ProductVariantList;
@@ -4490,7 +4490,7 @@ export type SearchResult = {
   facetValueIds: Array<Scalars['ID']>;
   /** An array of ids of the Collections in which this result appears */
   collectionIds: Array<Scalars['ID']>;
-  /** A relevence score for the result. Differs between database implementations */
+  /** A relevance score for the result. Differs between database implementations */
   score: Scalars['Float'];
 };
 

+ 1 - 0
packages/core/e2e/custom-field-relations.e2e-spec.ts

@@ -74,6 +74,7 @@ customFieldConfig.Product?.push(
     { name: 'cfProductVariant', type: 'relation', entity: ProductVariant, list: false },
     { name: 'cfProduct', type: 'relation', entity: Product, list: false },
     { name: 'cfShippingMethod', type: 'relation', entity: ShippingMethod, list: false },
+    { name: 'cfInternalAsset', type: 'relation', entity: Asset, list: false, internal: true },
 );
 
 const customConfig = mergeConfig(testConfig(), {

+ 6 - 6
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -1329,7 +1329,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2852,7 +2852,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2860,7 +2860,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3028,7 +3028,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3831,7 +3831,7 @@ export type Query = {
     pendingSearchIndexUpdates: Scalars['Int'];
     /** List Products */
     products: ProductList;
-    /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
     product?: Maybe<Product>;
     /** List ProductVariants either all or for the specific product. */
     productVariants: ProductVariantList;
@@ -4252,7 +4252,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

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

@@ -59,7 +59,7 @@ export enum AdjustmentType {
     DISTRIBUTED_ORDER_PROMOTION = 'DISTRIBUTED_ORDER_PROMOTION',
 }
 
-/** Retured when attemting to set the Customer for an Order when already logged in. */
+/** Returned when attempting to set the Customer for an Order when already logged in. */
 export type AlreadyLoggedInError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -826,7 +826,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1072,7 +1072,7 @@ export type IdOperators = {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is valid, but has
+ * Returned if the token used to change a Customer's email address is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type IdentifierChangeTokenExpiredError = ErrorResult & {
@@ -1081,7 +1081,7 @@ export type IdentifierChangeTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is either
+ * Returned if the token used to change a Customer's email address is either
  * invalid or does not match any expected tokens.
  */
 export type IdentifierChangeTokenInvalidError = ErrorResult & {
@@ -1482,7 +1482,7 @@ export enum LogicalOperator {
     OR = 'OR',
 }
 
-/** Retured when attemting to register or verify a customer account without a password, when one is required. */
+/** Returned when attempting to register or verify a customer account without a password, when one is required. */
 export type MissingPasswordError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1530,7 +1530,7 @@ export type Mutation = {
      *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _without_ a password. The Customer is then
      *    verified and authenticated in one step.
      * 2. **The Customer is registered _without_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
-     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosed password of the Customer. The Customer is then
+     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosen password of the Customer. The Customer is then
      *    verified and authenticated in one step.
      *
      * _If `authOptions.requireVerification` is set to `false`:_
@@ -1551,7 +1551,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -1693,7 +1693,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1705,7 +1705,7 @@ export type NativeAuthenticationResult =
     | NotVerifiedError
     | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1876,7 +1876,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2018,14 +2018,14 @@ export type PaginatedList = {
     totalItems: Scalars['Int'];
 };
 
-/** Retured when attemting to verify a customer account with a password, when a password has already been set. */
+/** Returned when attempting to verify a customer account with a password, when a password has already been set. */
 export type PasswordAlreadySetError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
 };
 
 /**
- * Retured if the token used to reset a Customer's password is valid, but has
+ * Returned if the token used to reset a Customer's password is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type PasswordResetTokenExpiredError = ErrorResult & {
@@ -2034,7 +2034,7 @@ export type PasswordResetTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to reset a Customer's password is either
+ * Returned if the token used to reset a Customer's password is either
  * invalid or does not match any expected tokens.
  */
 export type PasswordResetTokenInvalidError = ErrorResult & {
@@ -2539,7 +2539,7 @@ export type Query = {
      * general anonymous access to Order data.
      */
     orderByCode?: Maybe<Order>;
-    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is specified, an error will result. */
     product?: Maybe<Product>;
     /** Get a list of Products */
     products: ProductList;
@@ -2702,7 +2702,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 
@@ -2962,7 +2962,7 @@ export type VerificationTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the verification token (used to verify a Customer's email address) is either
+ * Returned if the verification token (used to verify a Customer's email address) is either
  * invalid or does not match any expected tokens.
  */
 export type VerificationTokenInvalidError = ErrorResult & {

+ 4 - 0
packages/core/src/api/config/generate-resolvers.ts

@@ -166,6 +166,10 @@ function generateCustomFieldRelationResolvers(
             };
         }
         for (const fieldDef of relationCustomFields) {
+            if (fieldDef.internal === true) {
+                // Do not create any resolvers for internal relations
+                continue;
+            }
             const relationResolver: IFieldResolver<any, any> = async (
                 source: any,
                 args: any,

+ 1 - 1
packages/core/src/api/schema/admin-api/product.api.graphql

@@ -1,7 +1,7 @@
 type Query {
     "List Products"
     products(options: ProductListOptions): ProductList!
-    "Get a Product either by id or slug. If neither id nor slug is speicified, an error will result."
+    "Get a Product either by id or slug. If neither id nor slug is specified, an error will result."
     product(id: ID, slug: String): Product
     "List ProductVariants either all or for the specific product."
     productVariants(options: ProductVariantListOptions, productId: ID): ProductVariantList!

+ 4 - 4
packages/core/src/api/schema/common/common-error-results.graphql

@@ -1,4 +1,4 @@
-"Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured."
+"Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured."
 type NativeAuthStrategyError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
@@ -20,20 +20,20 @@ type OrderStateTransitionError implements ErrorResult {
     toState: String!
 }
 
-"Retured when attemting to create a Customer with an email address already registered to an existing User."
+"Returned when attempting to create a Customer with an email address already registered to an existing User."
 type EmailAddressConflictError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
 }
 
-"Retured when the maximum order size limit has been reached."
+"Returned when the maximum order size limit has been reached."
 type OrderLimitError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
     maxItems: Int!
 }
 
-"Retured when attemting to set a negative OrderLine quantity."
+"Returned when attempting to set a negative OrderLine quantity."
 type NegativeQuantityError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!

+ 1 - 1
packages/core/src/api/schema/common/product-search.type.graphql

@@ -50,7 +50,7 @@ type SearchResult {
     facetValueIds: [ID!]!
     "An array of ids of the Collections in which this result appears"
     collectionIds: [ID!]!
-    "A relevence score for the result. Differs between database implementations"
+    "A relevance score for the result. Differs between database implementations"
     score: Float!
 }
 

+ 8 - 8
packages/core/src/api/schema/shop-api/shop-error-results.graphql

@@ -66,26 +66,26 @@ type CouponCodeLimitError implements ErrorResult {
     limit: Int!
 }
 
-"Retured when attemting to set the Customer for an Order when already logged in."
+"Returned when attempting to set the Customer for an Order when already logged in."
 type AlreadyLoggedInError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
 }
 
-"Retured when attemting to register or verify a customer account without a password, when one is required."
+"Returned when attempting to register or verify a customer account without a password, when one is required."
 type MissingPasswordError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
 }
 
-"Retured when attemting to verify a customer account with a password, when a password has already been set."
+"Returned when attempting to verify a customer account with a password, when a password has already been set."
 type PasswordAlreadySetError implements ErrorResult {
     errorCode: ErrorCode!
     message: String!
 }
 
 """
-Retured if the verification token (used to verify a Customer's email address) is either
+Returned if the verification token (used to verify a Customer's email address) is either
 invalid or does not match any expected tokens.
 """
 type VerificationTokenInvalidError implements ErrorResult {
@@ -103,7 +103,7 @@ type VerificationTokenExpiredError implements ErrorResult {
 }
 
 """
-Retured if the token used to change a Customer's email address is either
+Returned if the token used to change a Customer's email address is either
 invalid or does not match any expected tokens.
 """
 type IdentifierChangeTokenInvalidError implements ErrorResult {
@@ -112,7 +112,7 @@ type IdentifierChangeTokenInvalidError implements ErrorResult {
 }
 
 """
-Retured if the token used to change a Customer's email address is valid, but has
+Returned if the token used to change a Customer's email address is valid, but has
 expired according to the `verificationTokenDuration` setting in the AuthOptions.
 """
 type IdentifierChangeTokenExpiredError implements ErrorResult {
@@ -121,7 +121,7 @@ type IdentifierChangeTokenExpiredError implements ErrorResult {
 }
 
 """
-Retured if the token used to reset a Customer's password is either
+Returned if the token used to reset a Customer's password is either
 invalid or does not match any expected tokens.
 """
 type PasswordResetTokenInvalidError implements ErrorResult {
@@ -130,7 +130,7 @@ type PasswordResetTokenInvalidError implements ErrorResult {
 }
 
 """
-Retured if the token used to reset a Customer's password is valid, but has
+Returned if the token used to reset a Customer's password is valid, but has
 expired according to the `verificationTokenDuration` setting in the AuthOptions.
 """
 type PasswordResetTokenExpiredError implements ErrorResult {

+ 3 - 3
packages/core/src/api/schema/shop-api/shop.api.graphql

@@ -39,7 +39,7 @@ type Query {
     general anonymous access to Order data.
     """
     orderByCode(code: String!): Order
-    "Get a Product either by id or slug. If neither 'id' nor 'slug' is speicified, an error will result."
+    "Get a Product either by id or slug. If neither 'id' nor 'slug' is specified, an error will result."
     product(id: ID, slug: String): Product
     "Get a list of Products"
     products(options: ProductListOptions): ProductList!
@@ -89,7 +89,7 @@ type Mutation {
        verificationToken would then be passed to the `verifyCustomerAccount` mutation _without_ a password. The Customer is then
        verified and authenticated in one step.
     2. **The Customer is registered _without_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
-       verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosed password of the Customer. The Customer is then
+       verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosen password of the Customer. The Customer is then
        verified and authenticated in one step.
 
     _If `authOptions.requireVerification` is set to `false`:_
@@ -110,7 +110,7 @@ type Mutation {
     """
     Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
 
-    If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
+    If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
     provided here.
     """
     verifyCustomerAccount(token: String!, password: String): VerifyCustomerAccountResult!

+ 2 - 2
packages/core/src/config/asset-storage-strategy/asset-storage-strategy.ts

@@ -27,14 +27,14 @@ export interface AssetStorageStrategy extends InjectableStrategy {
 
     /**
      * @description
-     * Reads a file based on an identifier which was generated by the a writeFile
+     * Reads a file based on an identifier which was generated by the writeFile
      * method, and returns the as a Buffer.
      */
     readFileToBuffer(identifier: string): Promise<Buffer>;
 
     /**
      * @description
-     * Reads a file based on an identifier which was generated by the a writeFile
+     * Reads a file based on an identifier which was generated by the writeFile
      * method, and returns the file as a Stream.
      */
     readFileToStream(identifier: string): Promise<Stream>;

+ 8 - 1
packages/core/src/entity/order-line/order-line.entity.ts

@@ -281,10 +281,17 @@ export class OrderLine extends VendureEntity implements HasCustomFields {
         this.items.forEach(item => item.clearAdjustments(type));
     }
 
+    /**
+     * @description
+     * Fetches the specified property of the first active (non-cancelled) OrderItem.
+     * If all OrderItems are cancelled (e.g. in a full cancelled Order), then fetches from
+     * the first OrderItem.
+     */
     private firstActiveItemPropOr<K extends keyof OrderItem>(
         prop: K,
         defaultVal: OrderItem[K],
     ): OrderItem[K] {
-        return this.activeItems.length ? this.activeItems[0][prop] : defaultVal;
+        const items = this.activeItems.length ? this.activeItems : this.items;
+        return items.length ? items[0][prop] : defaultVal;
     }
 }

+ 1 - 1
packages/core/src/job-queue/subscribable-job.ts

@@ -81,7 +81,7 @@ export class SubscribableJob<T extends JobData<T> = any> extends Job<T> {
                     return strategy.findOne(id);
                 }),
                 filter(notNullOrUndefined),
-                distinctUntilChanged((a, b) => a?.progress === b?.progress),
+                distinctUntilChanged((a, b) => a?.progress === b?.progress || a?.state === b?.state),
                 takeWhile(
                     job =>
                         job?.state !== JobState.FAILED &&

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

@@ -1329,7 +1329,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2852,7 +2852,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2860,7 +2860,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3028,7 +3028,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3831,7 +3831,7 @@ export type Query = {
     pendingSearchIndexUpdates: Scalars['Int'];
     /** List Products */
     products: ProductList;
-    /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
     product?: Maybe<Product>;
     /** List ProductVariants either all or for the specific product. */
     productVariants: ProductVariantList;
@@ -4252,7 +4252,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

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

@@ -1329,7 +1329,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2852,7 +2852,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2860,7 +2860,7 @@ export type NativeAuthStrategyError = ErrorResult & {
 
 export type NativeAuthenticationResult = CurrentUser | InvalidCredentialsError | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3028,7 +3028,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -3831,7 +3831,7 @@ export type Query = {
     pendingSearchIndexUpdates: Scalars['Int'];
     /** List Products */
     products: ProductList;
-    /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither id nor slug is specified, an error will result. */
     product?: Maybe<Product>;
     /** List ProductVariants either all or for the specific product. */
     productVariants: ProductVariantList;
@@ -4252,7 +4252,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 

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

@@ -59,7 +59,7 @@ export enum AdjustmentType {
     DISTRIBUTED_ORDER_PROMOTION = 'DISTRIBUTED_ORDER_PROMOTION',
 }
 
-/** Retured when attemting to set the Customer for an Order when already logged in. */
+/** Returned when attempting to set the Customer for an Order when already logged in. */
 export type AlreadyLoggedInError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -826,7 +826,7 @@ export type Discount = {
     amountWithTax: Scalars['Int'];
 };
 
-/** Retured when attemting to create a Customer with an email address already registered to an existing User. */
+/** Returned when attempting to create a Customer with an email address already registered to an existing User. */
 export type EmailAddressConflictError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1072,7 +1072,7 @@ export type IdOperators = {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is valid, but has
+ * Returned if the token used to change a Customer's email address is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type IdentifierChangeTokenExpiredError = ErrorResult & {
@@ -1081,7 +1081,7 @@ export type IdentifierChangeTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to change a Customer's email address is either
+ * Returned if the token used to change a Customer's email address is either
  * invalid or does not match any expected tokens.
  */
 export type IdentifierChangeTokenInvalidError = ErrorResult & {
@@ -1482,7 +1482,7 @@ export enum LogicalOperator {
     OR = 'OR',
 }
 
-/** Retured when attemting to register or verify a customer account without a password, when one is required. */
+/** Returned when attempting to register or verify a customer account without a password, when one is required. */
 export type MissingPasswordError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1530,7 +1530,7 @@ export type Mutation = {
      *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _without_ a password. The Customer is then
      *    verified and authenticated in one step.
      * 2. **The Customer is registered _without_ a password**. A verificationToken will be created (and typically emailed to the Customer). That
-     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosed password of the Customer. The Customer is then
+     *    verificationToken would then be passed to the `verifyCustomerAccount` mutation _with_ the chosen password of the Customer. The Customer is then
      *    verified and authenticated in one step.
      *
      * _If `authOptions.requireVerification` is set to `false`:_
@@ -1551,7 +1551,7 @@ export type Mutation = {
     /**
      * Verify a Customer email address with the token sent to that address. Only applicable if `authOptions.requireVerification` is set to true.
      *
-     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the a password _must_ be
+     * If the Customer was not registered with a password in the `registerCustomerAccount` mutation, the password _must_ be
      * provided here.
      */
     verifyCustomerAccount: VerifyCustomerAccountResult;
@@ -1693,7 +1693,7 @@ export type NativeAuthInput = {
     password: Scalars['String'];
 };
 
-/** Retured when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
+/** Returned when attempting an operation that relies on the NativeAuthStrategy, if that strategy is not configured. */
 export type NativeAuthStrategyError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1705,7 +1705,7 @@ export type NativeAuthenticationResult =
     | NotVerifiedError
     | NativeAuthStrategyError;
 
-/** Retured when attemting to set a negative OrderLine quantity. */
+/** Returned when attempting to set a negative OrderLine quantity. */
 export type NegativeQuantityError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -1876,7 +1876,7 @@ export type OrderItem = Node & {
     refundId?: Maybe<Scalars['ID']>;
 };
 
-/** Retured when the maximum order size limit has been reached. */
+/** Returned when the maximum order size limit has been reached. */
 export type OrderLimitError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
@@ -2018,14 +2018,14 @@ export type PaginatedList = {
     totalItems: Scalars['Int'];
 };
 
-/** Retured when attemting to verify a customer account with a password, when a password has already been set. */
+/** Returned when attempting to verify a customer account with a password, when a password has already been set. */
 export type PasswordAlreadySetError = ErrorResult & {
     errorCode: ErrorCode;
     message: Scalars['String'];
 };
 
 /**
- * Retured if the token used to reset a Customer's password is valid, but has
+ * Returned if the token used to reset a Customer's password is valid, but has
  * expired according to the `verificationTokenDuration` setting in the AuthOptions.
  */
 export type PasswordResetTokenExpiredError = ErrorResult & {
@@ -2034,7 +2034,7 @@ export type PasswordResetTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the token used to reset a Customer's password is either
+ * Returned if the token used to reset a Customer's password is either
  * invalid or does not match any expected tokens.
  */
 export type PasswordResetTokenInvalidError = ErrorResult & {
@@ -2539,7 +2539,7 @@ export type Query = {
      * general anonymous access to Order data.
      */
     orderByCode?: Maybe<Order>;
-    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is speicified, an error will result. */
+    /** Get a Product either by id or slug. If neither 'id' nor 'slug' is specified, an error will result. */
     product?: Maybe<Product>;
     /** Get a list of Products */
     products: ProductList;
@@ -2702,7 +2702,7 @@ export type SearchResult = {
     facetValueIds: Array<Scalars['ID']>;
     /** An array of ids of the Collections in which this result appears */
     collectionIds: Array<Scalars['ID']>;
-    /** A relevence score for the result. Differs between database implementations */
+    /** A relevance score for the result. Differs between database implementations */
     score: Scalars['Float'];
 };
 
@@ -2962,7 +2962,7 @@ export type VerificationTokenExpiredError = ErrorResult & {
 };
 
 /**
- * Retured if the verification token (used to verify a Customer's email address) is either
+ * Returned if the verification token (used to verify a Customer's email address) is either
  * invalid or does not match any expected tokens.
  */
 export type VerificationTokenInvalidError = ErrorResult & {

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
schema-admin.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
schema-shop.json


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است