Skip to content

Commit

Permalink
Merge branch 'rework-adjustments-and-taxes'
Browse files Browse the repository at this point in the history
Relates to #31 #26 #29
This merge introduces the basis of what seems to be a workable tax &
promotions system. There is still more to do, most importantly solving
the problem of how the admin can set the gross price of a
ProductVariant, as well as working out how the typical range of
promotion actions can be implemented (buy 1 get 1 free, money off order
etc).

However, this work can now be continued on the master branch.
  • Loading branch information
michaelbromley committed Oct 17, 2018
2 parents ff495fc + 9076d45 commit 60da565
Show file tree
Hide file tree
Showing 128 changed files with 4,023 additions and 2,518 deletions.
Expand Up @@ -74,7 +74,7 @@
[productVariantsFormArray]="productForm.get('variants')"
[taxCategories]="taxCategories$ | async"
#productVariantsList>
<button class="btn btn-sm btn-secondary" (click)="selectFacetValue(productVariantsList.selectedCountryIds)">
<button class="btn btn-sm btn-secondary" (click)="selectFacetValue(productVariantsList.selectedVariantIds)">
{{ 'catalog.apply-facets' | translate }}
</button>
</vdr-product-variants-list>
Expand Down
Expand Up @@ -4,10 +4,10 @@ import { ActivatedRoute, Router } from '@angular/router';
import { combineLatest, EMPTY, forkJoin, Observable } from 'rxjs';
import { map, mergeMap, take } from 'rxjs/operators';
import {
AdjustmentSource,
CreateProductInput,
LanguageCode,
ProductWithVariants,
TaxCategory,
UpdateProductInput,
UpdateProductVariantInput,
} from 'shared/generated-types';
Expand All @@ -34,7 +34,7 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
implements OnInit, OnDestroy {
product$: Observable<ProductWithVariants.Fragment>;
variants$: Observable<ProductWithVariants.Variants[]>;
taxCategories$: Observable<AdjustmentSource.Fragment[]>;
taxCategories$: Observable<TaxCategory.Fragment[]>;
customFields: CustomFieldConfig[];
customVariantFields: CustomFieldConfig[];
productForm: FormGroup;
Expand Down Expand Up @@ -69,9 +69,9 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
this.init();
this.product$ = this.entity$;
this.variants$ = this.product$.pipe(map(product => product.variants));
this.taxCategories$ = this.dataService.adjustmentSource
this.taxCategories$ = this.dataService.settings
.getTaxCategories()
.mapSingle(data => data.adjustmentSources.items);
.mapSingle(data => data.taxCategories);
}

ngOnDestroy() {
Expand Down Expand Up @@ -245,7 +245,7 @@ export class ProductDetailComponent extends BaseDetailComponent<ProductWithVaria
sku: variant.sku,
name: variantTranslation ? variantTranslation.name : '',
price: variant.price,
priceBeforeTax: variant.priceBeforeTax,
priceWithTax: variant.priceWithTax,
taxCategoryId: variant.taxCategory.id,
};

Expand Down
Expand Up @@ -40,20 +40,11 @@
</div>
</div>
<div class="pricing">
<div class="price-before-tax">
<clr-input-container>
<label>{{ 'catalog.price-before-tax' | translate }}</label>
<vdr-currency-input clrInput
[formControl]="formArray.get([i, 'priceBeforeTax'])"
(input)="setPrice(i)"></vdr-currency-input>
</clr-input-container>
</div>
<div class="tax-category">
<clr-select-container>
<label>{{ 'catalog.tax-category' | translate }}</label>
<select clrSelect name="options"
[formControl]="formArray.get([i, 'taxCategoryId'])"
(change)="setPrice(i)">
[formControl]="formArray.get([i, 'taxCategoryId'])">
<option *ngFor="let taxCategory of taxCategories"
[value]="taxCategory.id">{{ taxCategory.name }}</option>
</select>
Expand All @@ -63,10 +54,18 @@
<clr-input-container>
<label>{{ 'catalog.price' | translate }}</label>
<vdr-currency-input clrInput
[formControl]="formArray.get([i, 'price'])"
(input)="setPreTaxPrice(i)"></vdr-currency-input>
[formControl]="formArray.get([i, 'price'])"></vdr-currency-input>
</clr-input-container>
</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>
</div>
</div>
</div>
Expand Down
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { FormArray, FormControl } from '@angular/forms';
import { AdjustmentSource, ProductWithVariants } from 'shared/generated-types';
import { ProductWithVariants, TaxCategory } from 'shared/generated-types';

@Component({
selector: 'vdr-product-variants-list',
Expand All @@ -11,7 +11,7 @@ import { AdjustmentSource, ProductWithVariants } from 'shared/generated-types';
export class ProductVariantsListComponent {
@Input('productVariantsFormArray') formArray: FormArray;
@Input() variants: ProductWithVariants.Variants[];
@Input() taxCategories: AdjustmentSource.Fragment[];
@Input() taxCategories: TaxCategory[];
selectedVariantIds: string[] = [];

areAllSelected(): boolean {
Expand All @@ -38,44 +38,4 @@ export class ProductVariantsListComponent {
isVariantSelected(variantId: string): boolean {
return -1 < this.selectedVariantIds.indexOf(variantId);
}

/**
* Set the priceBeforeTax value whenever the price is changed based on the current taxRate.
*/
setPreTaxPrice(index: number) {
const { preTaxPriceControl, postTaxPriceControl, taxRate } = this.getPriceControlsAndTaxRate(index);
preTaxPriceControl.setValue(Math.round(postTaxPriceControl.value / (1 + taxRate / 100)));
}

/**
* Set the price (including tax) value whenever the priceBeforeTax or the taxRate is changed.
*/
setPrice(index: number) {
const { preTaxPriceControl, postTaxPriceControl, taxRate } = this.getPriceControlsAndTaxRate(index);
postTaxPriceControl.setValue(Math.round(preTaxPriceControl.value * (1 + taxRate / 100)));
}

private getPriceControlsAndTaxRate(
index: number,
): {
preTaxPriceControl: FormControl;
postTaxPriceControl: FormControl;
taxRate: number;
} {
const preTaxPriceControl = this.formArray.get([index, 'priceBeforeTax']);
const postTaxPriceControl = this.formArray.get([index, 'price']);
const taxCategoryIdControl = this.formArray.get([index, 'taxCategoryId']);
if (preTaxPriceControl && postTaxPriceControl && taxCategoryIdControl) {
const taxCategory = this.taxCategories.find(tc => tc.id === taxCategoryIdControl.value);
if (taxCategory) {
const taxRate = Number(taxCategory.actions[0].args[0].value);
return {
preTaxPriceControl: preTaxPriceControl as FormControl,
postTaxPriceControl: postTaxPriceControl as FormControl,
taxRate,
};
}
}
throw new Error(`Could not find the corresponding form controls.`);
}
}
Expand Up @@ -8,6 +8,7 @@ import { AffixedInputComponent } from '../../../shared/components/affixed-input/
import { ChipComponent } from '../../../shared/components/chip/chip.component';
import { CurrencyInputComponent } from '../../../shared/components/currency-input/currency-input.component';
import { SelectToggleComponent } from '../../../shared/components/select-toggle/select-toggle.component';
import { BackgroundColorFromDirective } from '../../../shared/directives/background-color-from.directive';
import { CreateOptionGroupFormComponent } from '../create-option-group-form/create-option-group-form.component';
import { SelectOptionGroupComponent } from '../select-option-group/select-option-group.component';

Expand All @@ -28,6 +29,7 @@ describe('ProductVariantsWizardComponent', () => {
ChipComponent,
CurrencyInputComponent,
AffixedInputComponent,
BackgroundColorFromDirective,
],
providers: [{ provide: NotificationService, useClass: MockNotificationService }],
}).compileComponents();
Expand Down
Expand Up @@ -6,6 +6,7 @@ import { DataService } from '../../../data/providers/data.service';
import { MockDataService } from '../../../data/providers/data.service.mock';
import { ChipComponent } from '../../../shared/components/chip/chip.component';
import { SelectToggleComponent } from '../../../shared/components/select-toggle/select-toggle.component';
import { BackgroundColorFromDirective } from '../../../shared/directives/background-color-from.directive';

import { SelectOptionGroupComponent } from './select-option-group.component';

Expand All @@ -16,7 +17,12 @@ describe('SelectOptionGroupComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule, TestingCommonModule],
declarations: [SelectOptionGroupComponent, SelectToggleComponent, ChipComponent],
declarations: [
SelectOptionGroupComponent,
SelectToggleComponent,
ChipComponent,
BackgroundColorFromDirective,
],
providers: [{ provide: DataService, useClass: MockDataService }],
}).compileComponents();
}));
Expand Down
Expand Up @@ -68,6 +68,13 @@
<clr-icon shape="administrator" size="20"></clr-icon>{{ 'nav.administrators' | translate }}
</a>
</li>
<li>
<a class="nav-link"
[routerLink]="['/settings', 'channels']"
routerLinkActive="active">
<clr-icon shape="layers" size="20"></clr-icon>{{ 'nav.channels' | translate }}
</a>
</li>
<li>
<a class="nav-link"
[routerLink]="['/settings', 'roles']"
Expand All @@ -79,7 +86,14 @@
<a class="nav-link"
[routerLink]="['/settings', 'tax-categories']"
routerLinkActive="active">
<clr-icon shape="calculator" size="20"></clr-icon>{{ 'nav.tax-categories' | translate }}
<clr-icon shape="view-list" size="20"></clr-icon>{{ 'nav.tax-categories' | translate }}
</a>
</li>
<li>
<a class="nav-link"
[routerLink]="['/settings', 'tax-rates']"
routerLinkActive="active">
<clr-icon shape="calculator" size="20"></clr-icon>{{ 'nav.tax-rates' | translate }}
</a>
</li>
<li>
Expand Down
85 changes: 0 additions & 85 deletions admin-ui/src/app/data/definitions/adjustment-source-definitions.ts

This file was deleted.

8 changes: 6 additions & 2 deletions admin-ui/src/app/data/definitions/product-definitions.ts
Expand Up @@ -18,11 +18,15 @@ export const PRODUCT_VARIANT_FRAGMENT = gql`
languageCode
name
price
priceBeforeTax
priceWithTax
taxRateApplied {
id
name
value
}
taxCategory {
id
name
taxRate
}
sku
options {
Expand Down

0 comments on commit 60da565

Please sign in to comment.