Skip to content

Commit

Permalink
feat(core): Add ProductVariantPriceSelectionStrategy
Browse files Browse the repository at this point in the history
Relates to #1691
This adds a new configurable strategy which allows you to define the logic by which a variant price
is selected based on factors like active channel and currency code.
  • Loading branch information
michaelbromley committed Feb 22, 2023
1 parent 24e558b commit efe23d1
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { RequestContext } from '../../api/common/request-context';
import { idsAreEqual } from '../../common/utils';
import { ProductVariantPrice } from '../../entity/product-variant/product-variant-price.entity';

import { ProductVariantPriceSelectionStrategy } from './product-variant-price-selection-strategy';

/**
* @description
* The default strategy for selecting the price for a ProductVariant in a given Channel.
*/
export class DefaultProductVariantPriceSelectionStrategy implements ProductVariantPriceSelectionStrategy {
selectPrice(ctx: RequestContext, prices: ProductVariantPrice[]) {
return prices.find(p => idsAreEqual(p.channelId, ctx.channelId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RequestContext } from '../../api/common/request-context';
import { InjectableStrategy } from '../../common/types/injectable-strategy';
import { ProductVariantPrice } from '../../entity/product-variant/product-variant-price.entity';

export interface ProductVariantPriceSelectionStrategy extends InjectableStrategy {
selectPrice(
ctx: RequestContext,
prices: ProductVariantPrice[],
): ProductVariantPrice | undefined | Promise<ProductVariantPrice | undefined>;
}
9 changes: 7 additions & 2 deletions packages/core/src/config/config.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,12 @@ export class ConfigModule implements OnApplicationBootstrap, OnApplicationShutdo
private getInjectableStrategies(): InjectableStrategy[] {
const { assetNamingStrategy, assetPreviewStrategy, assetStorageStrategy } =
this.configService.assetOptions;
const { productVariantPriceCalculationStrategy, stockDisplayStrategy, stockLocationStrategy } =
this.configService.catalogOptions;
const {
productVariantPriceCalculationStrategy,
productVariantPriceSelectionStrategy,
stockDisplayStrategy,
stockLocationStrategy,
} = this.configService.catalogOptions;
const {
adminAuthenticationStrategy,
shopAuthenticationStrategy,
Expand Down Expand Up @@ -135,6 +139,7 @@ export class ConfigModule implements OnApplicationBootstrap, OnApplicationShutdo
orderSellerStrategy,
shippingLineAssignmentStrategy,
stockLocationStrategy,
productVariantPriceSelectionStrategy,
];
}

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/config/default-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { DefaultPasswordValidationStrategy } from './auth/default-password-valid
import { NativeAuthenticationStrategy } from './auth/native-authentication-strategy';
import { defaultCollectionFilters } from './catalog/default-collection-filters';
import { DefaultProductVariantPriceCalculationStrategy } from './catalog/default-product-variant-price-calculation-strategy';
import { DefaultProductVariantPriceSelectionStrategy } from './catalog/default-product-variant-price-selection-strategy';
import { DefaultStockDisplayStrategy } from './catalog/default-stock-display-strategy';
import { DefaultStockLocationStrategy } from './catalog/default-stock-location-strategy';
import { AutoIncrementIdStrategy } from './entity/auto-increment-id-strategy';
Expand Down Expand Up @@ -103,6 +104,7 @@ export const defaultConfig: RuntimeVendureConfig = {
},
catalogOptions: {
collectionFilters: defaultCollectionFilters,
productVariantPriceSelectionStrategy: new DefaultProductVariantPriceSelectionStrategy(),
productVariantPriceCalculationStrategy: new DefaultProductVariantPriceCalculationStrategy(),
stockDisplayStrategy: new DefaultStockDisplayStrategy(),
stockLocationStrategy: new DefaultStockLocationStrategy(),
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ export * from './auth/password-hashing-strategy';
export * from './auth/password-validation-strategy';
export * from './catalog/collection-filter';
export * from './catalog/default-collection-filters';
export * from './catalog/default-product-variant-price-selection-strategy';
export * from './catalog/default-stock-display-strategy';
export * from './catalog/default-stock-location-strategy';
export * from './catalog/product-variant-price-calculation-strategy';
export * from './catalog/product-variant-price-selection-strategy';
export * from './catalog/stock-display-strategy';
export * from './catalog/stock-location-strategy';
export * from './config.module';
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/config/vendure-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { PasswordHashingStrategy } from './auth/password-hashing-strategy';
import { PasswordValidationStrategy } from './auth/password-validation-strategy';
import { CollectionFilter } from './catalog/collection-filter';
import { ProductVariantPriceCalculationStrategy } from './catalog/product-variant-price-calculation-strategy';
import { ProductVariantPriceSelectionStrategy } from './catalog/product-variant-price-selection-strategy';
import { StockDisplayStrategy } from './catalog/stock-display-strategy';
import { StockLocationStrategy } from './catalog/stock-location-strategy';
import { CustomFields } from './custom-field/custom-field-types';
Expand Down Expand Up @@ -647,6 +648,15 @@ export interface CatalogOptions {
* @default defaultCollectionFilters
*/
collectionFilters?: Array<CollectionFilter<any>>;
/**
* @description
* Defines the strategy used to select the price of a ProductVariant, based on factors
* such as the active Channel and active CurrencyCode.
*
* @since 2.0.0
* @default DefaultProductVariantPriceSelectionStrategy
*/
productVariantPriceSelectionStrategy?: ProductVariantPriceSelectionStrategy;
/**
* @description
* Defines the strategy used for calculating the price of ProductVariants based
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ export class ProductPriceApplicator {
ctx: RequestContext,
order?: Order,
): Promise<ProductVariant> {
const channelPrice = variant.productVariantPrices.find(p => idsAreEqual(p.channelId, ctx.channelId));
const { productVariantPriceSelectionStrategy, productVariantPriceCalculationStrategy } =
this.configService.catalogOptions;
const channelPrice = await productVariantPriceSelectionStrategy.selectPrice(
ctx,
variant.productVariantPrices,
);
if (!channelPrice) {
throw new InternalServerError(`error.no-price-found-for-channel`, {
variantId: variant.id,
Expand All @@ -78,7 +83,6 @@ export class ProductPriceApplicator {
() => this.taxRateService.getApplicableTaxRate(ctx, activeTaxZone, variant.taxCategory),
);

const { productVariantPriceCalculationStrategy } = this.configService.catalogOptions;
const { price, priceIncludesTax } = await productVariantPriceCalculationStrategy.calculate({
inputPrice: channelPrice.price,
taxCategory: variant.taxCategory,
Expand Down

0 comments on commit efe23d1

Please sign in to comment.