Browse Source

feat: Implement pricesIncludeTax option on Channel

Relates to #31, aims to solve the "net vs gross prices" problem outlined therein.

Next step is to get the tax calculations working correctly for Orders when tax is included in price.
Michael Bromley 7 years ago
parent
commit
24030ae1d0
33 changed files with 351 additions and 138 deletions
  1. 2 0
      admin-ui/src/app/catalog/catalog.module.ts
  2. 1 0
      admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts
  3. 3 9
      admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html
  4. 1 1
      admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts
  5. 7 0
      admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.html
  6. 4 0
      admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.scss
  7. 66 0
      admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.ts
  8. 1 1
      admin-ui/src/app/data/client-state/client-resolvers.ts
  9. 0 0
      admin-ui/src/app/data/definitions/client-definitions.ts
  10. 1 0
      admin-ui/src/app/data/definitions/product-definitions.ts
  11. 10 0
      admin-ui/src/app/data/definitions/settings-definitions.ts
  12. 1 1
      admin-ui/src/app/data/providers/client-data.service.ts
  13. 1 0
      admin-ui/src/app/data/providers/data.service.mock.ts
  14. 8 0
      admin-ui/src/app/data/providers/settings-data.service.ts
  15. 6 0
      admin-ui/src/app/settings/components/channel-detail/channel-detail.component.html
  16. 4 0
      admin-ui/src/app/settings/components/channel-detail/channel-detail.component.ts
  17. 1 0
      admin-ui/src/app/settings/providers/routing/channel-resolver.ts
  18. 4 0
      admin-ui/src/i18n-messages/en.json
  19. 4 0
      admin-ui/src/styles/styles.scss
  20. 0 0
      schema.json
  21. 1 0
      server/mock-data/mock-data.service.ts
  22. 6 0
      server/src/api/resolvers/channel.resolver.ts
  23. 1 0
      server/src/api/types/channel.api.graphql
  24. 2 0
      server/src/entity/channel/channel.entity.ts
  25. 3 0
      server/src/entity/channel/channel.graphql
  26. 7 2
      server/src/entity/product-variant/product-variant.entity.ts
  27. 2 1
      server/src/entity/product-variant/product-variant.graphql
  28. 11 4
      server/src/entity/tax-rate/tax-rate.entity.ts
  29. 1 0
      server/src/service/providers/channel.service.ts
  30. 33 14
      server/src/service/providers/product-variant.service.ts
  31. 1 1
      server/src/service/providers/product.service.ts
  32. 11 0
      server/src/service/providers/tax-rate.service.ts
  33. 147 104
      shared/generated-types.ts

+ 2 - 0
admin-ui/src/app/catalog/catalog.module.ts

@@ -22,6 +22,7 @@ import { ProductVariantsListComponent } from './components/product-variants-list
 import { ProductVariantsWizardComponent } from './components/product-variants-wizard/product-variants-wizard.component';
 import { ProductVariantsWizardComponent } from './components/product-variants-wizard/product-variants-wizard.component';
 import { SelectOptionGroupDialogComponent } from './components/select-option-group-dialog/select-option-group-dialog.component';
 import { SelectOptionGroupDialogComponent } from './components/select-option-group-dialog/select-option-group-dialog.component';
 import { SelectOptionGroupComponent } from './components/select-option-group/select-option-group.component';
 import { SelectOptionGroupComponent } from './components/select-option-group/select-option-group.component';
+import { VariantPriceDetailComponent } from './components/variant-price-detail/variant-price-detail.component';
 import { FacetResolver } from './providers/routing/facet-resolver';
 import { FacetResolver } from './providers/routing/facet-resolver';
 import { ProductResolver } from './providers/routing/product-resolver';
 import { ProductResolver } from './providers/routing/product-resolver';
 
 
@@ -47,6 +48,7 @@ import { ProductResolver } from './providers/routing/product-resolver';
         ProductAssetsComponent,
         ProductAssetsComponent,
         AssetPickerDialogComponent,
         AssetPickerDialogComponent,
         AssetFileInputComponent,
         AssetFileInputComponent,
+        VariantPriceDetailComponent,
     ],
     ],
     entryComponents: [
     entryComponents: [
         AssetPickerDialogComponent,
         AssetPickerDialogComponent,

+ 1 - 0
admin-ui/src/app/catalog/components/product-detail/product-detail.component.ts

@@ -245,6 +245,7 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
                     sku: variant.sku,
                     sku: variant.sku,
                     name: variantTranslation ? variantTranslation.name : '',
                     name: variantTranslation ? variantTranslation.name : '',
                     price: variant.price,
                     price: variant.price,
+                    priceIncludesTax: variant.priceIncludesTax,
                     priceWithTax: variant.priceWithTax,
                     priceWithTax: variant.priceWithTax,
                     taxCategoryId: variant.taxCategory.id,
                     taxCategoryId: variant.taxCategory.id,
                 };
                 };

+ 3 - 9
admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.html

@@ -57,15 +57,9 @@
                                             [formControl]="formArray.get([i, 'price'])"></vdr-currency-input>
                                             [formControl]="formArray.get([i, 'price'])"></vdr-currency-input>
                     </clr-input-container>
                     </clr-input-container>
                 </div>
                 </div>
-                <div class="price-with-tax">
-                    <clr-input-container>
-                        <label>{{ 'catalog.price-including-tax' | translate }}</label>
-                        <vdr-currency-input clrInput
-                                            [formControl]="formArray.get([i, 'priceWithTax'])"></vdr-currency-input>                    </clr-input-container>
-                </div>
-                <div class="price-with-tax-preview">
-                    {{ formArray.get([i, 'price']).value * ( 1 + variant.taxRateApplied.value / 100) }}
-                </div>
+                <vdr-variant-price-detail [price]="formArray.get([i, 'price'])!.value"
+                                          [priceIncludesTax]="variant.priceIncludesTax"
+                                          [taxCategoryId]="formArray.get([i, 'taxCategoryId'])!.value"></vdr-variant-price-detail>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>

+ 1 - 1
admin-ui/src/app/catalog/components/product-variants-list/product-variants-list.component.ts

@@ -1,5 +1,5 @@
 import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
 import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-import { FormArray, FormControl } from '@angular/forms';
+import { FormArray } from '@angular/forms';
 import { ProductWithVariants, TaxCategory } from 'shared/generated-types';
 import { ProductWithVariants, TaxCategory } from 'shared/generated-types';
 
 
 @Component({
 @Component({

+ 7 - 0
admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.html

@@ -0,0 +1,7 @@
+<label class="clr-control-label">{{ 'catalog.taxes' | translate }}</label>
+<div *ngIf="priceIncludesTax">
+    {{ 'catalog.price-includes-tax-at' | translate : { rate: (taxRate$ | async) } }}
+</div>
+<div *ngIf="!priceIncludesTax">
+    {{ 'catalog.price-with-tax-in-default-zone' | translate: { price: (grossPrice$ | async | currency), rate: (taxRate$ | async) } }}
+</div>

+ 4 - 0
admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.scss

@@ -0,0 +1,4 @@
+:host {
+    display: flex;
+    flex-direction: column;
+}

+ 66 - 0
admin-ui/src/app/catalog/components/variant-price-detail/variant-price-detail.component.ts

@@ -0,0 +1,66 @@
+import { ChangeDetectionStrategy, Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
+import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { DataService } from '../../../data/providers/data.service';
+
+@Component({
+    selector: 'vdr-variant-price-detail',
+    templateUrl: './variant-price-detail.component.html',
+    styleUrls: ['./variant-price-detail.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class VariantPriceDetailComponent implements OnInit, OnChanges {
+    @Input() priceIncludesTax: boolean;
+    @Input() price: number;
+    @Input() taxCategoryId: string;
+
+    grossPrice$: Observable<number>;
+    taxRate$: Observable<number>;
+
+    private priceChange$ = new BehaviorSubject<number>(0);
+    private taxCategoryIdChange$ = new BehaviorSubject<string>('');
+
+    constructor(private dataService: DataService) {}
+
+    ngOnInit() {
+        const taxRates$ = this.dataService.settings
+            .getTaxRates(99999, 0)
+            .mapStream(data => data.taxRates.items);
+        const activeChannel$ = this.dataService.settings
+            .getActiveChannel()
+            .mapStream(data => data.activeChannel);
+
+        this.taxRate$ = combineLatest(activeChannel$, taxRates$, this.taxCategoryIdChange$).pipe(
+            map(([channel, taxRates, taxCategoryId]) => {
+                const defaultTaxZone = channel.defaultTaxZone;
+                if (!defaultTaxZone) {
+                    return 0;
+                }
+                const applicableRate = taxRates.find(
+                    taxRate => taxRate.zone.id === defaultTaxZone.id && taxRate.category.id === taxCategoryId,
+                );
+
+                if (!applicableRate) {
+                    return 0;
+                }
+                return applicableRate.value;
+            }),
+        );
+
+        this.grossPrice$ = combineLatest(this.taxRate$, this.priceChange$).pipe(
+            map(([taxRate, price]) => {
+                return Math.round(price * ((100 + taxRate) / 100)) / 100;
+            }),
+        );
+    }
+
+    ngOnChanges(changes: SimpleChanges): void {
+        if ('price' in changes) {
+            this.priceChange$.next(changes.price.currentValue);
+        }
+        if ('taxCategoryId' in changes) {
+            this.taxCategoryIdChange$.next(changes.taxCategoryId.currentValue);
+        }
+    }
+}

+ 1 - 1
admin-ui/src/app/data/client-state/client-resolvers.ts

@@ -9,7 +9,7 @@ import {
     SetUiLanguage,
     SetUiLanguage,
 } from 'shared/generated-types';
 } from 'shared/generated-types';
 
 
-import { GET_NEWTORK_STATUS } from '../definitions/local-definitions';
+import { GET_NEWTORK_STATUS } from '../definitions/client-definitions';
 
 
 export type ResolverContext = {
 export type ResolverContext = {
     cache: InMemoryCache;
     cache: InMemoryCache;

+ 0 - 0
admin-ui/src/app/data/definitions/local-definitions.ts → admin-ui/src/app/data/definitions/client-definitions.ts


+ 1 - 0
admin-ui/src/app/data/definitions/product-definitions.ts

@@ -18,6 +18,7 @@ export const PRODUCT_VARIANT_FRAGMENT = gql`
         languageCode
         languageCode
         name
         name
         price
         price
+        priceIncludesTax
         priceWithTax
         priceWithTax
         taxRateApplied {
         taxRateApplied {
             id
             id

+ 10 - 0
admin-ui/src/app/data/definitions/settings-definitions.ts

@@ -221,6 +221,7 @@ export const CHANNEL_FRAGMENT = gql`
         id
         id
         code
         code
         token
         token
+        pricesIncludeTax
         defaultLanguageCode
         defaultLanguageCode
         defaultShippingZone {
         defaultShippingZone {
             id
             id
@@ -251,6 +252,15 @@ export const GET_CHANNEL = gql`
     ${CHANNEL_FRAGMENT}
     ${CHANNEL_FRAGMENT}
 `;
 `;
 
 
+export const GET_ACTIVE_CHANNEL = gql`
+    query GetActiveChannel {
+        activeChannel {
+            ...Channel
+        }
+    }
+    ${CHANNEL_FRAGMENT}
+`;
+
 export const CREATE_CHANNEL = gql`
 export const CREATE_CHANNEL = gql`
     mutation CreateChannel($input: CreateChannelInput!) {
     mutation CreateChannel($input: CreateChannelInput!) {
         createChannel(input: $input) {
         createChannel(input: $input) {

+ 1 - 1
admin-ui/src/app/data/providers/client-data.service.ts

@@ -18,7 +18,7 @@ import {
     SET_AS_LOGGED_IN,
     SET_AS_LOGGED_IN,
     SET_AS_LOGGED_OUT,
     SET_AS_LOGGED_OUT,
     SET_UI_LANGUAGE,
     SET_UI_LANGUAGE,
-} from '../definitions/local-definitions';
+} from '../definitions/client-definitions';
 
 
 import { BaseDataService } from './base-data.service';
 import { BaseDataService } from './base-data.service';
 
 

+ 1 - 0
admin-ui/src/app/data/providers/data.service.mock.ts

@@ -109,6 +109,7 @@ export class MockDataService implements DataServiceMock {
         updateTaxRate: spyObservable('updateTaxRate'),
         updateTaxRate: spyObservable('updateTaxRate'),
         getChannels: spyQueryResult('getChannels'),
         getChannels: spyQueryResult('getChannels'),
         getChannel: spyQueryResult('getChannel'),
         getChannel: spyQueryResult('getChannel'),
+        getActiveChannel: spyQueryResult('getActiveChannel'),
         createChannel: spyObservable('createChannel'),
         createChannel: spyObservable('createChannel'),
         updateChannel: spyObservable('updateChannel'),
         updateChannel: spyObservable('updateChannel'),
     };
     };

+ 8 - 0
admin-ui/src/app/data/providers/settings-data.service.ts

@@ -10,6 +10,7 @@ import {
     CreateTaxRateInput,
     CreateTaxRateInput,
     CreateZone,
     CreateZone,
     CreateZoneInput,
     CreateZoneInput,
+    GetActiveChannel,
     GetChannel,
     GetChannel,
     GetChannels,
     GetChannels,
     GetCountry,
     GetCountry,
@@ -40,6 +41,7 @@ import {
     CREATE_TAX_CATEGORY,
     CREATE_TAX_CATEGORY,
     CREATE_TAX_RATE,
     CREATE_TAX_RATE,
     CREATE_ZONE,
     CREATE_ZONE,
+    GET_ACTIVE_CHANNEL,
     GET_CHANNEL,
     GET_CHANNEL,
     GET_CHANNELS,
     GET_CHANNELS,
     GET_COUNTRY,
     GET_COUNTRY,
@@ -192,6 +194,12 @@ export class SettingsDataService {
         });
         });
     }
     }
 
 
+    getActiveChannel() {
+        return this.baseDataService.query<GetActiveChannel.Query, GetActiveChannel.Variables>(
+            GET_ACTIVE_CHANNEL,
+        );
+    }
+
     createChannel(input: CreateChannelInput) {
     createChannel(input: CreateChannelInput) {
         return this.baseDataService.mutate<CreateChannel.Mutation, CreateChannel.Variables>(CREATE_CHANNEL, {
         return this.baseDataService.mutate<CreateChannel.Mutation, CreateChannel.Variables>(CREATE_CHANNEL, {
             input,
             input,

+ 6 - 0
admin-ui/src/app/settings/components/channel-detail/channel-detail.component.html

@@ -20,6 +20,12 @@
         <vdr-form-field [label]="'common.code' | translate" for="code">
         <vdr-form-field [label]="'common.code' | translate" for="code">
             <input id="code" type="text" formControlName="code">
             <input id="code" type="text" formControlName="code">
         </vdr-form-field>
         </vdr-form-field>
+        <vdr-form-field [label]="'settings.prices-include-tax' | translate" for="pricesIncludeTax">
+            <div class="toggle-switch">
+                <input type="checkbox" id="pricesIncludeTax" formControlName="pricesIncludeTax">
+                <label for="pricesIncludeTax"></label>
+            </div>
+        </vdr-form-field>
         <vdr-form-field [label]="'settings.default-tax-zone' | translate" for="defaultTaxZoneId">
         <vdr-form-field [label]="'settings.default-tax-zone' | translate" for="defaultTaxZoneId">
             <select clrSelect name="defaultTaxZoneId"
             <select clrSelect name="defaultTaxZoneId"
                     formControlName="defaultTaxZoneId">
                     formControlName="defaultTaxZoneId">

+ 4 - 0
admin-ui/src/app/settings/components/channel-detail/channel-detail.component.ts

@@ -35,6 +35,7 @@ export class ChannelDetailComponent extends BaseDetailComponent<Channel.Fragment
         this.channelForm = this.formBuilder.group({
         this.channelForm = this.formBuilder.group({
             code: ['', Validators.required],
             code: ['', Validators.required],
             token: ['', Validators.required],
             token: ['', Validators.required],
+            pricesIncludeTax: [false],
             defaultShippingZoneId: [''],
             defaultShippingZoneId: [''],
             defaultTaxZoneId: [''],
             defaultTaxZoneId: [''],
         });
         });
@@ -60,6 +61,7 @@ export class ChannelDetailComponent extends BaseDetailComponent<Channel.Fragment
         const formValue = this.channelForm.value;
         const formValue = this.channelForm.value;
         const input = {
         const input = {
             code: formValue.code,
             code: formValue.code,
+            pricesIncludeTax: formValue.pricesIncludeTax,
             defaultShippingZoneId: formValue.defaultShippingZoneId,
             defaultShippingZoneId: formValue.defaultShippingZoneId,
             defaultTaxZoneId: formValue.defaultTaxZoneId,
             defaultTaxZoneId: formValue.defaultTaxZoneId,
         } as CreateChannelInput;
         } as CreateChannelInput;
@@ -92,6 +94,7 @@ export class ChannelDetailComponent extends BaseDetailComponent<Channel.Fragment
                     const input = {
                     const input = {
                         id: channel.id,
                         id: channel.id,
                         code: formValue.code,
                         code: formValue.code,
+                        pricesIncludeTax: formValue.pricesIncludeTax,
                         defaultShippingZoneId: formValue.defaultShippingZoneId,
                         defaultShippingZoneId: formValue.defaultShippingZoneId,
                         defaultTaxZoneId: formValue.defaultTaxZoneId,
                         defaultTaxZoneId: formValue.defaultTaxZoneId,
                     } as UpdateChannelInput;
                     } as UpdateChannelInput;
@@ -121,6 +124,7 @@ export class ChannelDetailComponent extends BaseDetailComponent<Channel.Fragment
         this.channelForm.patchValue({
         this.channelForm.patchValue({
             code: entity.code,
             code: entity.code,
             token: entity.token,
             token: entity.token,
+            pricesIncludeTax: entity.pricesIncludeTax,
             defaultShippingZoneId: entity.defaultShippingZone ? entity.defaultShippingZone.id : '',
             defaultShippingZoneId: entity.defaultShippingZone ? entity.defaultShippingZone.id : '',
             defaultTaxZoneId: entity.defaultTaxZone ? entity.defaultTaxZone.id : '',
             defaultTaxZoneId: entity.defaultTaxZone ? entity.defaultTaxZone.id : '',
         });
         });

+ 1 - 0
admin-ui/src/app/settings/providers/routing/channel-resolver.ts

@@ -17,6 +17,7 @@ export class ChannelResolver extends BaseEntityResolver<Channel.Fragment> {
                 id: '',
                 id: '',
                 code: '',
                 code: '',
                 token: '',
                 token: '',
+                pricesIncludeTax: false,
                 defaultLanguageCode: getDefaultLanguage(),
                 defaultLanguageCode: getDefaultLanguage(),
                 defaultShippingZone: {} as any,
                 defaultShippingZone: {} as any,
                 defaultTaxZone: {} as any,
                 defaultTaxZone: {} as any,

+ 4 - 0
admin-ui/src/i18n-messages/en.json

@@ -48,6 +48,8 @@
     "option-group-options-tooltip": "Enter each option on a new line in the default language ({ defaultLanguage })",
     "option-group-options-tooltip": "Enter each option on a new line in the default language ({ defaultLanguage })",
     "original-asset-size": "Source size",
     "original-asset-size": "Source size",
     "price": "Price",
     "price": "Price",
+    "price-includes-tax-at": "Price includes tax at { rate }%",
+    "price-with-tax-in-default-zone": "Price with { rate }% tax: { price }",
     "product": "Product",
     "product": "Product",
     "product-name": "Product name",
     "product-name": "Product name",
     "product-option-groups": "Option groups",
     "product-option-groups": "Option groups",
@@ -60,6 +62,7 @@
     "sku": "SKU",
     "sku": "SKU",
     "slug": "Slug",
     "slug": "Slug",
     "tax-category": "Tax category",
     "tax-category": "Tax category",
+    "taxes": "Taxes",
     "truncated-options-count": "{count} further {count, plural, one {option} other {options}}",
     "truncated-options-count": "{count} further {count, plural, one {option} other {options}}",
     "upload-assets": "Upload assets",
     "upload-assets": "Upload assets",
     "values": "Values",
     "values": "Values",
@@ -153,6 +156,7 @@
     "order": "Order",
     "order": "Order",
     "password": "Password",
     "password": "Password",
     "permissions": "Permissions",
     "permissions": "Permissions",
+    "prices-include-tax": "Prices include tax for the default Zone",
     "rate": "Rate",
     "rate": "Rate",
     "read": "Read",
     "read": "Read",
     "remove-countries-from-zone": "Remove countries from zone...",
     "remove-countries-from-zone": "Remove countries from zone...",

+ 4 - 0
admin-ui/src/styles/styles.scss

@@ -13,3 +13,7 @@ a:link, a:visited {
         color: darken($color-warning, 20%);
         color: darken($color-warning, 20%);
     }
     }
 }
 }
+
+.clr-form-control {
+    margin-top: 0;
+}

File diff suppressed because it is too large
+ 0 - 0
schema.json


+ 1 - 0
server/mock-data/mock-data.service.ts

@@ -67,6 +67,7 @@ export class MockDataService {
                 {
                 {
                     input: {
                     input: {
                         code,
                         code,
+                        pricesIncludeTax: true,
                         token: `${code}_token`,
                         token: `${code}_token`,
                         defaultLanguageCode: LanguageCode.en,
                         defaultLanguageCode: LanguageCode.en,
                     },
                     },

+ 6 - 0
server/src/api/resolvers/channel.resolver.ts

@@ -29,6 +29,12 @@ export class ChannelResolver {
         return this.channelService.findOne(args.id);
         return this.channelService.findOne(args.id);
     }
     }
 
 
+    @Query()
+    @Allow(Permission.Public)
+    async activeChannel(@Ctx() ctx: RequestContext): Promise<Channel> {
+        return ctx.channel;
+    }
+
     @Mutation()
     @Mutation()
     @Allow(Permission.SuperAdmin)
     @Allow(Permission.SuperAdmin)
     @Decode('defaultTaxZoneId', 'defaultShippingZoneId')
     @Decode('defaultTaxZoneId', 'defaultShippingZoneId')

+ 1 - 0
server/src/api/types/channel.api.graphql

@@ -1,6 +1,7 @@
 type Query {
 type Query {
     channels: [Channel!]!
     channels: [Channel!]!
     channel(id: ID!): Channel
     channel(id: ID!): Channel
+    activeChannel: Channel!
 }
 }
 
 
 type Mutation {
 type Mutation {

+ 2 - 0
server/src/entity/channel/channel.entity.ts

@@ -28,6 +28,8 @@ export class Channel extends VendureEntity {
     @ManyToOne(type => Zone)
     @ManyToOne(type => Zone)
     defaultShippingZone: Zone;
     defaultShippingZone: Zone;
 
 
+    @Column() pricesIncludeTax: boolean;
+
     private generateToken(): string {
     private generateToken(): string {
         const randomString = () =>
         const randomString = () =>
             Math.random()
             Math.random()

+ 3 - 0
server/src/entity/channel/channel.graphql

@@ -7,12 +7,14 @@ type Channel implements Node {
     defaultTaxZone: Zone
     defaultTaxZone: Zone
     defaultShippingZone: Zone
     defaultShippingZone: Zone
     defaultLanguageCode: LanguageCode!
     defaultLanguageCode: LanguageCode!
+    pricesIncludeTax: Boolean!
 }
 }
 
 
 input CreateChannelInput {
 input CreateChannelInput {
     code: String!
     code: String!
     token: String!
     token: String!
     defaultLanguageCode: LanguageCode!
     defaultLanguageCode: LanguageCode!
+    pricesIncludeTax: Boolean!
     defaultTaxZoneId: ID
     defaultTaxZoneId: ID
     defaultShippingZoneId: ID
     defaultShippingZoneId: ID
 }
 }
@@ -22,6 +24,7 @@ input UpdateChannelInput {
     code: String
     code: String
     token: String
     token: String
     defaultLanguageCode: LanguageCode
     defaultLanguageCode: LanguageCode
+    pricesIncludeTax: Boolean
     defaultTaxZoneId: ID
     defaultTaxZoneId: ID
     defaultShippingZoneId: ID
     defaultShippingZoneId: ID
 }
 }

+ 7 - 2
server/src/entity/product-variant/product-variant.entity.ts

@@ -36,12 +36,17 @@ export class ProductVariant extends VendureEntity implements Translatable, HasCu
     /**
     /**
      * Calculated at run-time
      * Calculated at run-time
      */
      */
-    priceWithTax?: number;
+    priceIncludesTax: boolean;
 
 
     /**
     /**
      * Calculated at run-time
      * Calculated at run-time
      */
      */
-    taxRateApplied?: TaxRate;
+    priceWithTax: number;
+
+    /**
+     * Calculated at run-time
+     */
+    taxRateApplied: TaxRate;
 
 
     @ManyToOne(type => TaxCategory)
     @ManyToOne(type => TaxCategory)
     taxCategory: TaxCategory;
     taxCategory: TaxCategory;

+ 2 - 1
server/src/entity/product-variant/product-variant.graphql

@@ -6,8 +6,9 @@ type ProductVariant implements Node {
     sku: String!
     sku: String!
     name: String!
     name: String!
     price: Int!
     price: Int!
+    priceIncludesTax: Boolean!
     priceWithTax: Int!
     priceWithTax: Int!
-    taxRateApplied: TaxRate
+    taxRateApplied: TaxRate!
     taxCategory: TaxCategory!
     taxCategory: TaxCategory!
     options: [ProductOption!]!
     options: [ProductOption!]!
     facetValues: [FacetValue!]!
     facetValues: [FacetValue!]!

+ 11 - 4
server/src/entity/tax-rate/tax-rate.entity.ts

@@ -35,10 +35,17 @@ export class TaxRate extends AdjustmentSource {
     customerGroup?: CustomerGroup;
     customerGroup?: CustomerGroup;
 
 
     /**
     /**
-     * Returns the tax applicable to the given price.
+     * Returns the tax component of a given gross price.
      */
      */
-    getTax(price: number): number {
-        return this.round(price * (this.value / 100));
+    taxComponentOf(grossPrice: number): number {
+        return this.round(grossPrice - grossPrice / ((100 + this.value) / 100));
+    }
+
+    /**
+     * Returns the tax applicable to the given net price.
+     */
+    taxPayableOn(netPrice: number): number {
+        return this.round(netPrice * (this.value / 100));
     }
     }
 
 
     apply(price: number): Adjustment {
     apply(price: number): Adjustment {
@@ -46,7 +53,7 @@ export class TaxRate extends AdjustmentSource {
             type: this.type,
             type: this.type,
             adjustmentSource: this.getSourceId(),
             adjustmentSource: this.getSourceId(),
             description: this.name,
             description: this.name,
-            amount: this.getTax(price),
+            amount: this.taxPayableOn(price),
         };
         };
     }
     }
 
 

+ 1 - 0
server/src/service/providers/channel.service.ts

@@ -137,6 +137,7 @@ export class ChannelService {
             const newDefaultChannel = new Channel({
             const newDefaultChannel = new Channel({
                 code: DEFAULT_CHANNEL_CODE,
                 code: DEFAULT_CHANNEL_CODE,
                 defaultLanguageCode: DEFAULT_LANGUAGE_CODE,
                 defaultLanguageCode: DEFAULT_LANGUAGE_CODE,
+                pricesIncludeTax: false,
                 token: defaultChannelToken,
                 token: defaultChannelToken,
             });
             });
             await this.connection.manager.save(newDefaultChannel);
             await this.connection.manager.save(newDefaultChannel);

+ 33 - 14
server/src/service/providers/product-variant.service.ts

@@ -9,11 +9,13 @@ import { RequestContext } from '../../api/common/request-context';
 import { DEFAULT_LANGUAGE_CODE } from '../../common/constants';
 import { DEFAULT_LANGUAGE_CODE } from '../../common/constants';
 import { Translated } from '../../common/types/locale-types';
 import { Translated } from '../../common/types/locale-types';
 import { assertFound, idsAreEqual } from '../../common/utils';
 import { assertFound, idsAreEqual } from '../../common/utils';
+import { Channel } from '../../entity/channel/channel.entity';
 import { FacetValue } from '../../entity/facet-value/facet-value.entity';
 import { FacetValue } from '../../entity/facet-value/facet-value.entity';
 import { ProductOption } from '../../entity/product-option/product-option.entity';
 import { ProductOption } from '../../entity/product-option/product-option.entity';
 import { ProductVariantTranslation } from '../../entity/product-variant/product-variant-translation.entity';
 import { ProductVariantTranslation } from '../../entity/product-variant/product-variant-translation.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import { Product } from '../../entity/product/product.entity';
 import { Product } from '../../entity/product/product.entity';
+import { TaxRate } from '../../entity/tax-rate/tax-rate.entity';
 import { Zone } from '../../entity/zone/zone.entity';
 import { Zone } from '../../entity/zone/zone.entity';
 import { I18nError } from '../../i18n/i18n-error';
 import { I18nError } from '../../i18n/i18n-error';
 import { createTranslatable } from '../helpers/create-translatable';
 import { createTranslatable } from '../helpers/create-translatable';
@@ -41,7 +43,7 @@ export class ProductVariantService {
             .then(result => {
             .then(result => {
                 if (result) {
                 if (result) {
                     return translateDeep(
                     return translateDeep(
-                        this.applyChannelPriceAndTax(result, ctx.channelId, ctx.channel.defaultTaxZone),
+                        this.applyChannelPriceAndTax(result, ctx.channel, ctx.channel.defaultTaxZone),
                         ctx.languageCode,
                         ctx.languageCode,
                     );
                     );
                 }
                 }
@@ -90,7 +92,7 @@ export class ProductVariantService {
             }),
             }),
         );
         );
         return translateDeep(
         return translateDeep(
-            this.applyChannelPriceAndTax(variant, ctx.channelId, ctx.channel.defaultTaxZone),
+            this.applyChannelPriceAndTax(variant, ctx.channel, ctx.channel.defaultTaxZone),
             DEFAULT_LANGUAGE_CODE,
             DEFAULT_LANGUAGE_CODE,
             ['options', 'facetValues'],
             ['options', 'facetValues'],
         );
         );
@@ -168,7 +170,7 @@ export class ProductVariantService {
 
 
         return variants.map(v =>
         return variants.map(v =>
             translateDeep(
             translateDeep(
-                this.applyChannelPriceAndTax(v, ctx.channelId, ctx.channel.defaultTaxZone),
+                this.applyChannelPriceAndTax(v, ctx.channel, ctx.channel.defaultTaxZone),
                 DEFAULT_LANGUAGE_CODE,
                 DEFAULT_LANGUAGE_CODE,
                 ['options', 'facetValues'],
                 ['options', 'facetValues'],
             ),
             ),
@@ -178,22 +180,39 @@ export class ProductVariantService {
     /**
     /**
      * Populates the `price` field with the price for the specified channel.
      * Populates the `price` field with the price for the specified channel.
      */
      */
-    applyChannelPriceAndTax(variant: ProductVariant, channelId: ID, taxZone: Zone): ProductVariant {
-        const channelPrice = variant.productVariantPrices.find(p => idsAreEqual(p.channelId, channelId));
+    applyChannelPriceAndTax(variant: ProductVariant, channel: Channel, taxZone: Zone): ProductVariant {
+        const channelPrice = variant.productVariantPrices.find(p => idsAreEqual(p.channelId, channel.id));
         if (!channelPrice) {
         if (!channelPrice) {
             throw new I18nError(`error.no-price-found-for-channel`);
             throw new I18nError(`error.no-price-found-for-channel`);
         }
         }
-        variant.price = channelPrice.price;
-
-        const applicableTaxRate = this.taxRateService
-            .getActiveTaxRates()
-            .find(r => r.test(taxZone, variant.taxCategory));
-        if (applicableTaxRate) {
-            variant.priceWithTax = variant.price + applicableTaxRate.getTax(variant.price);
-            variant.taxRateApplied = applicableTaxRate;
+        const applicableTaxRate = this.taxRateService.getApplicableTaxRate(taxZone, variant.taxCategory);
+
+        if (channel.pricesIncludeTax) {
+            const isDefaultZone = taxZone.id === channel.defaultTaxZone.id;
+            if (isDefaultZone) {
+                const grossPrice = channelPrice.price;
+                variant.priceIncludesTax = true;
+                variant.price = grossPrice;
+                variant.priceWithTax = grossPrice;
+            } else {
+                const taxRateForDefaultZone = this.taxRateService.getApplicableTaxRate(
+                    channel.defaultTaxZone,
+                    variant.taxCategory,
+                );
+                const grossPriceInDefaultZone = channelPrice.price;
+                const netPrice =
+                    grossPriceInDefaultZone - taxRateForDefaultZone.taxComponentOf(grossPriceInDefaultZone);
+                variant.price = netPrice;
+                variant.priceIncludesTax = false;
+                variant.priceWithTax = netPrice + applicableTaxRate.taxPayableOn(netPrice);
+            }
         } else {
         } else {
-            variant.priceWithTax = variant.price;
+            const netPrice = channelPrice.price;
+            variant.price = netPrice;
+            variant.priceIncludesTax = false;
+            variant.priceWithTax = netPrice + applicableTaxRate.taxPayableOn(netPrice);
         }
         }
+        variant.taxRateApplied = applicableTaxRate;
         return variant;
         return variant;
     }
     }
 
 

+ 1 - 1
server/src/service/providers/product.service.ts

@@ -170,7 +170,7 @@ export class ProductService {
         product.variants = product.variants.map(variant => {
         product.variants = product.variants.map(variant => {
             return this.productVariantService.applyChannelPriceAndTax(
             return this.productVariantService.applyChannelPriceAndTax(
                 variant,
                 variant,
-                ctx.channelId,
+                ctx.channel,
                 ctx.channel.defaultTaxZone,
                 ctx.channel.defaultTaxZone,
             );
             );
         });
         });

+ 11 - 0
server/src/service/providers/tax-rate.service.ts

@@ -20,6 +20,12 @@ export class TaxRateService {
      * per request.
      * per request.
      */
      */
     private activeTaxRates: TaxRate[] = [];
     private activeTaxRates: TaxRate[] = [];
+    private readonly defaultTaxRate = new TaxRate({
+        value: 0,
+        enabled: true,
+        name: 'No configured tax rate',
+        id: '0',
+    });
 
 
     constructor(@InjectConnection() private connection: Connection) {}
     constructor(@InjectConnection() private connection: Connection) {}
 
 
@@ -89,6 +95,11 @@ export class TaxRateService {
         return this.activeTaxRates;
         return this.activeTaxRates;
     }
     }
 
 
+    getApplicableTaxRate(zone: Zone, taxCategory: TaxCategory): TaxRate {
+        const rate = this.getActiveTaxRates().find(r => r.test(zone, taxCategory));
+        return rate || this.defaultTaxRate;
+    }
+
     private async updateActiveTaxRates() {
     private async updateActiveTaxRates() {
         this.activeTaxRates = await this.connection.getRepository(TaxRate).find({
         this.activeTaxRates = await this.connection.getRepository(TaxRate).find({
             relations: ['category', 'zone', 'customerGroup'],
             relations: ['category', 'zone', 'customerGroup'],

+ 147 - 104
shared/generated-types.ts

@@ -46,6 +46,7 @@ export interface Query {
     me?: CurrentUser | null;
     me?: CurrentUser | null;
     channels: Channel[];
     channels: Channel[];
     channel?: Channel | null;
     channel?: Channel | null;
+    activeChannel: Channel;
     config: Config;
     config: Config;
     countries: CountryList;
     countries: CountryList;
     country?: Country | null;
     country?: Country | null;
@@ -123,6 +124,7 @@ export interface Channel extends Node {
     defaultTaxZone?: Zone | null;
     defaultTaxZone?: Zone | null;
     defaultShippingZone?: Zone | null;
     defaultShippingZone?: Zone | null;
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
+    pricesIncludeTax: boolean;
 }
 }
 
 
 export interface Zone extends Node {
 export interface Zone extends Node {
@@ -284,6 +286,7 @@ export interface OrderLine extends Node {
     productVariant: ProductVariant;
     productVariant: ProductVariant;
     featuredAsset?: Asset | null;
     featuredAsset?: Asset | null;
     unitPrice: number;
     unitPrice: number;
+    unitPriceWithPromotions: number;
     unitPriceWithTax: number;
     unitPriceWithTax: number;
     quantity: number;
     quantity: number;
     items: OrderItem[];
     items: OrderItem[];
@@ -300,8 +303,9 @@ export interface ProductVariant extends Node {
     sku: string;
     sku: string;
     name: string;
     name: string;
     price: number;
     price: number;
+    priceIncludesTax: boolean;
     priceWithTax: number;
     priceWithTax: number;
-    taxRateApplied?: TaxRate | null;
+    taxRateApplied: TaxRate;
     taxCategory: TaxCategory;
     taxCategory: TaxCategory;
     options: ProductOption[];
     options: ProductOption[];
     facetValues: FacetValue[];
     facetValues: FacetValue[];
@@ -831,6 +835,7 @@ export interface CreateChannelInput {
     code: string;
     code: string;
     token: string;
     token: string;
     defaultLanguageCode: LanguageCode;
     defaultLanguageCode: LanguageCode;
+    pricesIncludeTax: boolean;
     defaultTaxZoneId?: string | null;
     defaultTaxZoneId?: string | null;
     defaultShippingZoneId?: string | null;
     defaultShippingZoneId?: string | null;
 }
 }
@@ -840,6 +845,7 @@ export interface UpdateChannelInput {
     code?: string | null;
     code?: string | null;
     token?: string | null;
     token?: string | null;
     defaultLanguageCode?: LanguageCode | null;
     defaultLanguageCode?: LanguageCode | null;
+    pricesIncludeTax?: boolean | null;
     defaultTaxZoneId?: string | null;
     defaultTaxZoneId?: string | null;
     defaultShippingZoneId?: string | null;
     defaultShippingZoneId?: string | null;
 }
 }
@@ -1625,6 +1631,7 @@ export namespace QueryResolvers {
         me?: MeResolver<CurrentUser | null, any, Context>;
         me?: MeResolver<CurrentUser | null, any, Context>;
         channels?: ChannelsResolver<Channel[], any, Context>;
         channels?: ChannelsResolver<Channel[], any, Context>;
         channel?: ChannelResolver<Channel | null, any, Context>;
         channel?: ChannelResolver<Channel | null, any, Context>;
+        activeChannel?: ActiveChannelResolver<Channel, any, Context>;
         config?: ConfigResolver<Config, any, Context>;
         config?: ConfigResolver<Config, any, Context>;
         countries?: CountriesResolver<CountryList, any, Context>;
         countries?: CountriesResolver<CountryList, any, Context>;
         country?: CountryResolver<Country | null, any, Context>;
         country?: CountryResolver<Country | null, any, Context>;
@@ -1713,6 +1720,11 @@ export namespace QueryResolvers {
         id: string;
         id: string;
     }
     }
 
 
+    export type ActiveChannelResolver<R = Channel, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
     export type ConfigResolver<R = Config, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type ConfigResolver<R = Config, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type CountriesResolver<R = CountryList, Parent = any, Context = any> = Resolver<
     export type CountriesResolver<R = CountryList, Parent = any, Context = any> = Resolver<
         R,
         R,
@@ -2061,6 +2073,7 @@ export namespace ChannelResolvers {
         defaultTaxZone?: DefaultTaxZoneResolver<Zone | null, any, Context>;
         defaultTaxZone?: DefaultTaxZoneResolver<Zone | null, any, Context>;
         defaultShippingZone?: DefaultShippingZoneResolver<Zone | null, any, Context>;
         defaultShippingZone?: DefaultShippingZoneResolver<Zone | null, any, Context>;
         defaultLanguageCode?: DefaultLanguageCodeResolver<LanguageCode, any, Context>;
         defaultLanguageCode?: DefaultLanguageCodeResolver<LanguageCode, any, Context>;
+        pricesIncludeTax?: PricesIncludeTaxResolver<boolean, any, Context>;
     }
     }
 
 
     export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type IdResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
@@ -2083,6 +2096,11 @@ export namespace ChannelResolvers {
         Parent,
         Parent,
         Context
         Context
     >;
     >;
+    export type PricesIncludeTaxResolver<R = boolean, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
 }
 }
 
 
 export namespace ZoneResolvers {
 export namespace ZoneResolvers {
@@ -2509,6 +2527,7 @@ export namespace OrderLineResolvers {
         productVariant?: ProductVariantResolver<ProductVariant, any, Context>;
         productVariant?: ProductVariantResolver<ProductVariant, any, Context>;
         featuredAsset?: FeaturedAssetResolver<Asset | null, any, Context>;
         featuredAsset?: FeaturedAssetResolver<Asset | null, any, Context>;
         unitPrice?: UnitPriceResolver<number, any, Context>;
         unitPrice?: UnitPriceResolver<number, any, Context>;
+        unitPriceWithPromotions?: UnitPriceWithPromotionsResolver<number, any, Context>;
         unitPriceWithTax?: UnitPriceWithTaxResolver<number, any, Context>;
         unitPriceWithTax?: UnitPriceWithTaxResolver<number, any, Context>;
         quantity?: QuantityResolver<number, any, Context>;
         quantity?: QuantityResolver<number, any, Context>;
         items?: ItemsResolver<OrderItem[], any, Context>;
         items?: ItemsResolver<OrderItem[], any, Context>;
@@ -2531,6 +2550,11 @@ export namespace OrderLineResolvers {
         Context
         Context
     >;
     >;
     export type UnitPriceResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type UnitPriceResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type UnitPriceWithPromotionsResolver<R = number, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
     export type UnitPriceWithTaxResolver<R = number, Parent = any, Context = any> = Resolver<
     export type UnitPriceWithTaxResolver<R = number, Parent = any, Context = any> = Resolver<
         R,
         R,
         Parent,
         Parent,
@@ -2556,8 +2580,9 @@ export namespace ProductVariantResolvers {
         sku?: SkuResolver<string, any, Context>;
         sku?: SkuResolver<string, any, Context>;
         name?: NameResolver<string, any, Context>;
         name?: NameResolver<string, any, Context>;
         price?: PriceResolver<number, any, Context>;
         price?: PriceResolver<number, any, Context>;
+        priceIncludesTax?: PriceIncludesTaxResolver<boolean, any, Context>;
         priceWithTax?: PriceWithTaxResolver<number, any, Context>;
         priceWithTax?: PriceWithTaxResolver<number, any, Context>;
-        taxRateApplied?: TaxRateAppliedResolver<TaxRate | null, any, Context>;
+        taxRateApplied?: TaxRateAppliedResolver<TaxRate, any, Context>;
         taxCategory?: TaxCategoryResolver<TaxCategory, any, Context>;
         taxCategory?: TaxCategoryResolver<TaxCategory, any, Context>;
         options?: OptionsResolver<ProductOption[], any, Context>;
         options?: OptionsResolver<ProductOption[], any, Context>;
         facetValues?: FacetValuesResolver<FacetValue[], any, Context>;
         facetValues?: FacetValuesResolver<FacetValue[], any, Context>;
@@ -2576,8 +2601,13 @@ export namespace ProductVariantResolvers {
     export type SkuResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type SkuResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type NameResolver<R = string, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type PriceResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type PriceResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
+    export type PriceIncludesTaxResolver<R = boolean, Parent = any, Context = any> = Resolver<
+        R,
+        Parent,
+        Context
+    >;
     export type PriceWithTaxResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type PriceWithTaxResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type TaxRateAppliedResolver<R = TaxRate | null, Parent = any, Context = any> = Resolver<
+    export type TaxRateAppliedResolver<R = TaxRate, Parent = any, Context = any> = Resolver<
         R,
         R,
         Parent,
         Parent,
         Context
         Context
@@ -3809,106 +3839,6 @@ export namespace GetCurrentUser {
     export type Me = CurrentUser.Fragment;
     export type Me = CurrentUser.Fragment;
 }
 }
 
 
-export namespace GetServerConfig {
-    export type Variables = {};
-
-    export type Query = {
-        __typename?: 'Query';
-        config: Config;
-    };
-
-    export type Config = {
-        __typename?: 'Config';
-        customFields?: Json | null;
-    };
-}
-
-export namespace CreateFacet {
-    export type Variables = {
-        input: CreateFacetInput;
-    };
-
-    export type Mutation = {
-        __typename?: 'Mutation';
-        createFacet: CreateFacet;
-    };
-
-    export type CreateFacet = FacetWithValues.Fragment;
-}
-
-export namespace UpdateFacet {
-    export type Variables = {
-        input: UpdateFacetInput;
-    };
-
-    export type Mutation = {
-        __typename?: 'Mutation';
-        updateFacet: UpdateFacet;
-    };
-
-    export type UpdateFacet = FacetWithValues.Fragment;
-}
-
-export namespace CreateFacetValues {
-    export type Variables = {
-        input: CreateFacetValueInput[];
-    };
-
-    export type Mutation = {
-        __typename?: 'Mutation';
-        createFacetValues: CreateFacetValues[];
-    };
-
-    export type CreateFacetValues = FacetValue.Fragment;
-}
-
-export namespace UpdateFacetValues {
-    export type Variables = {
-        input: UpdateFacetValueInput[];
-    };
-
-    export type Mutation = {
-        __typename?: 'Mutation';
-        updateFacetValues: UpdateFacetValues[];
-    };
-
-    export type UpdateFacetValues = FacetValue.Fragment;
-}
-
-export namespace GetFacetList {
-    export type Variables = {
-        options?: FacetListOptions | null;
-        languageCode?: LanguageCode | null;
-    };
-
-    export type Query = {
-        __typename?: 'Query';
-        facets: Facets;
-    };
-
-    export type Facets = {
-        __typename?: 'FacetList';
-        items: Items[];
-        totalItems: number;
-    };
-
-    export type Items = FacetWithValues.Fragment;
-}
-
-export namespace GetFacetWithValues {
-    export type Variables = {
-        id: string;
-        languageCode?: LanguageCode | null;
-    };
-
-    export type Query = {
-        __typename?: 'Query';
-        facet?: Facet | null;
-    };
-
-    export type Facet = FacetWithValues.Fragment;
-}
-
 export namespace RequestStarted {
 export namespace RequestStarted {
     export type Variables = {};
     export type Variables = {};
 
 
@@ -4017,6 +3947,106 @@ export namespace GetUiState {
     };
     };
 }
 }
 
 
+export namespace GetServerConfig {
+    export type Variables = {};
+
+    export type Query = {
+        __typename?: 'Query';
+        config: Config;
+    };
+
+    export type Config = {
+        __typename?: 'Config';
+        customFields?: Json | null;
+    };
+}
+
+export namespace CreateFacet {
+    export type Variables = {
+        input: CreateFacetInput;
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        createFacet: CreateFacet;
+    };
+
+    export type CreateFacet = FacetWithValues.Fragment;
+}
+
+export namespace UpdateFacet {
+    export type Variables = {
+        input: UpdateFacetInput;
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        updateFacet: UpdateFacet;
+    };
+
+    export type UpdateFacet = FacetWithValues.Fragment;
+}
+
+export namespace CreateFacetValues {
+    export type Variables = {
+        input: CreateFacetValueInput[];
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        createFacetValues: CreateFacetValues[];
+    };
+
+    export type CreateFacetValues = FacetValue.Fragment;
+}
+
+export namespace UpdateFacetValues {
+    export type Variables = {
+        input: UpdateFacetValueInput[];
+    };
+
+    export type Mutation = {
+        __typename?: 'Mutation';
+        updateFacetValues: UpdateFacetValues[];
+    };
+
+    export type UpdateFacetValues = FacetValue.Fragment;
+}
+
+export namespace GetFacetList {
+    export type Variables = {
+        options?: FacetListOptions | null;
+        languageCode?: LanguageCode | null;
+    };
+
+    export type Query = {
+        __typename?: 'Query';
+        facets: Facets;
+    };
+
+    export type Facets = {
+        __typename?: 'FacetList';
+        items: Items[];
+        totalItems: number;
+    };
+
+    export type Items = FacetWithValues.Fragment;
+}
+
+export namespace GetFacetWithValues {
+    export type Variables = {
+        id: string;
+        languageCode?: LanguageCode | null;
+    };
+
+    export type Query = {
+        __typename?: 'Query';
+        facet?: Facet | null;
+    };
+
+    export type Facet = FacetWithValues.Fragment;
+}
+
 export namespace GetOrderList {
 export namespace GetOrderList {
     export type Variables = {
     export type Variables = {
         options?: OrderListOptions | null;
         options?: OrderListOptions | null;
@@ -4627,6 +4657,17 @@ export namespace GetChannel {
     export type Channel = Channel.Fragment;
     export type Channel = Channel.Fragment;
 }
 }
 
 
+export namespace GetActiveChannel {
+    export type Variables = {};
+
+    export type Query = {
+        __typename?: 'Query';
+        activeChannel: ActiveChannel;
+    };
+
+    export type ActiveChannel = Channel.Fragment;
+}
+
 export namespace CreateChannel {
 export namespace CreateChannel {
     export type Variables = {
     export type Variables = {
         input: CreateChannelInput;
         input: CreateChannelInput;
@@ -4783,8 +4824,9 @@ export namespace ProductVariant {
         languageCode: LanguageCode;
         languageCode: LanguageCode;
         name: string;
         name: string;
         price: number;
         price: number;
+        priceIncludesTax: boolean;
         priceWithTax: number;
         priceWithTax: number;
-        taxRateApplied?: TaxRateApplied | null;
+        taxRateApplied: TaxRateApplied;
         taxCategory: TaxCategory;
         taxCategory: TaxCategory;
         sku: string;
         sku: string;
         options: Options[];
         options: Options[];
@@ -4996,6 +5038,7 @@ export namespace Channel {
         id: string;
         id: string;
         code: string;
         code: string;
         token: string;
         token: string;
+        pricesIncludeTax: boolean;
         defaultLanguageCode: LanguageCode;
         defaultLanguageCode: LanguageCode;
         defaultShippingZone?: DefaultShippingZone | null;
         defaultShippingZone?: DefaultShippingZone | null;
         defaultTaxZone?: DefaultTaxZone | null;
         defaultTaxZone?: DefaultTaxZone | null;

Some files were not shown because too many files changed in this diff