Merged PR 229: #805 #659 #841 Bestellpostensplit - Availability is sum of all supplier qtys Fix Disabled Guest Button In Create Customer Component & Set PreferredSupplier Availability for download articles

Related work items: #659, #805, #841
This commit is contained in:
Sebastian Neumair
2020-07-17 00:08:41 +00:00
committed by Lorenz Hilpert
5 changed files with 575 additions and 85 deletions

View File

@@ -32,6 +32,10 @@ export function getFormatedPublicationDate(publicationDate: Date): string {
);
}
export function allowedAvailabilityStatusCodes(code: number) {
return [2, 32, 256, 1024, 2048, 4096].findIndex(allowedCode => allowedCode === code) !== -1;
export function allowedAvailabilityStatusCodes(code: number): boolean {
return (
[2, 32, 256, 1024, 2048, 4096].findIndex(
(allowedCode) => allowedCode === code
) !== -1
);
}

View File

@@ -144,7 +144,6 @@ export class PayMethodComponent implements OnInit, OnDestroy {
);
}
if (deliveryShippingDestionation && !downloadShippingDestionation) {
// TODO HERE TTHE ERROR HAPPENS
return forkJoin(
this.store.dispatch(
new SetDestinationToCheckout(

View File

@@ -13,7 +13,9 @@
<div class="cart-content">
<div class="cart-list">
<hr />
<app-invoice-address [disableAddressChanging]="disableAddressChanging"></app-invoice-address>
<app-invoice-address
[disableAddressChanging]="disableAddressChanging"
></app-invoice-address>
<hr />
<app-notification-settings
*ngIf="showNotificationSettings"
@@ -22,33 +24,65 @@
></app-notification-settings>
<div *ngIf="takeNowCount$ | async" @removeItemCartTrigger>
<hr />
<app-take-now-cart (editItem)="enableDisableOrder($event)"></app-take-now-cart>
<app-take-now-cart
(editItem)="enableDisableOrder($event)"
></app-take-now-cart>
</div>
<div *ngIf="deliveryCount$ | async" @removeItemCartTrigger [@cartPaymentType]="getDeliveryDeleteAnimation()">
<div
*ngIf="deliveryCount$ | async"
@removeItemCartTrigger
[@cartPaymentType]="getDeliveryDeleteAnimation()"
>
<hr />
<app-delivery-cart [disableAddressChanging]="disableAddressChanging" (editItem)="enableDisableOrder($event)"></app-delivery-cart>
<app-delivery-cart
[disableAddressChanging]="disableAddressChanging"
(editItem)="enableDisableOrder($event)"
></app-delivery-cart>
</div>
<div *ngIf="pickUpCount$ | async" @removeItemCartTrigger [@cartPaymentType]="getPickUpDeleteAnimation()">
<div
*ngIf="pickUpCount$ | async"
@removeItemCartTrigger
[@cartPaymentType]="getPickUpDeleteAnimation()"
>
<hr />
<app-pick-up-cart (editItem)="enableDisableOrder($event)"></app-pick-up-cart>
<app-pick-up-cart
(editItem)="enableDisableOrder($event)"
></app-pick-up-cart>
</div>
<div *ngIf="downloadCount$ | async" @removeItemCartTrigger>
<hr />
<app-download-cart (editItem)="enableDisableOrder($event)"></app-download-cart>
<app-download-cart
(editItem)="enableDisableOrder($event)"
></app-download-cart>
</div>
</div>
</div>
<div class="cart-footer">
<div class="overview-container">
<span class="items">{{ total.items }} Artikel I {{ (cartData$ | async)?.promotionPoints }} Lesepunkte</span>
<span class="items"
>{{ total.items }} Artikel I
{{ (cartData$ | async)?.promotionPoints }} Lesepunkte</span
>
<div class="overview">
<div class="overview-price-container">
<span class="overview-price">Zwischensumme {{ total.price | bookPrice }} EUR</span>
<span *ngIf="(deliveryCount$ | async) > 0" class="overview-tax">ohne Versandkosten</span>
<span *ngIf="(deliveryCount$ | async) === 0" class="overview-tax">&nbsp;</span>
<span class="overview-price"
>Zwischensumme {{ total.price | bookPrice }} EUR</span
>
<span *ngIf="(deliveryCount$ | async) > 0" class="overview-tax"
>ohne Versandkosten</span
>
<span *ngIf="(deliveryCount$ | async) === 0" class="overview-tax"
>&nbsp;</span
>
</div>
<app-button (action)="next()" [disabled]="orderDisabled" [primary]="true" [load]="true" [stayOnPage]="true" #continue
<app-button
(action)="next()"
[disabled]="orderDisabled"
[primary]="true"
[load]="true"
[stayOnPage]="true"
#continue
>Bestellen</app-button
>
</div>
@@ -58,18 +92,25 @@
<ng-template #emptyCart>
<div class="cart-container-empty">
<div class="image-empty"><img src="assets/images/shopping_cart_white.svg" /></div>
<div class="image-empty">
<img src="assets/images/shopping_cart_white.svg" />
</div>
<div class="header-empty"><span>Ihr Warenkorb ist leer.</span></div>
<div class="content-empty"><span>Sie haben alle Artikel aus dem</span></div>
<div class="content-empty"><span>Warenkorb entfernt oder noch</span></div>
<div class="content-empty"><span>Artikel hinzugefügt.</span></div>
<div class="btn-empty">
<app-button [primary]="true" (action)="search()">Artikel suchen</app-button>
<app-button [primary]="true" (action)="search()"
>Artikel suchen</app-button
>
</div>
</div>
</ng-template>
<app-printer-selection #printModal (print)="print($event)"></app-printer-selection>
<app-printer-selection
#printModal
(print)="print($event)"
></app-printer-selection>
<app-pay-method
#payMethod

View File

@@ -1,20 +1,344 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ProductCheckoutComponent } from './product-checkout.component';
import { DeliveryOption } from 'apps/sales/src/app/core/models/delivery-option.model';
import {
ProductAvailabilityService,
BranchService,
} from '@sales/core-services';
import { of } from 'rxjs';
import { CheckoutType } from 'apps/sales/src/app/core/models/checkout-type.enum';
import { AvailabilityDTO } from '@swagger/availability';
import { NO_ERRORS_SCHEMA, ChangeDetectorRef } from '@angular/core';
import { BookPricePipe } from 'apps/sales/src/app/pipes/book-price.pipe';
import { ModalService } from '@libs/ui';
import { NgxsModule } from '@ngxs/store';
import { RouterTestingModule } from '@angular/router/testing';
import { DatePipe } from '@angular/common';
import { BranchDTO } from '@swagger/checkout';
import { ProductAvailability } from 'apps/sales/src/app/core/models/product-availability.model';
describe('ProductCheckoutComponent', () => {
const mockAvailabilityResponse: {
branchId: number;
type: CheckoutType;
av: AvailabilityDTO[];
} = {
branchId: 1,
type: CheckoutType.store,
av: [
{
itemId: 42455413,
requestReference: '0',
ean: '9783957260376',
shop: 3,
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
supplier: 'L',
supplierId: 3,
ssc: '999',
sscText: 'Lieferbar in 1-2 Tagen',
qty: 9.0,
isPrebooked: false,
at: '2020-07-16T14:00:00',
status: 1024,
preferred: 1,
requested: '2020-07-15T08:28:39.5751342Z',
requestStatusCode: '1',
requestMessage: 'OK',
},
{
itemId: 42455413,
requestReference: '0',
ean: '9783957260376',
shop: 3,
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
supplier: 'K',
supplierId: 4,
ssc: '999',
sscText: 'Lieferbar in 1-2 Tagen',
qty: 6.0,
isPrebooked: false,
at: '2020-07-16T14:00:00',
status: 1024,
preferred: 0,
requested: '2020-07-15T08:28:39.5754585Z',
requestStatusCode: '1',
requestMessage: 'OK',
},
{
itemId: 42455413,
requestReference: '0',
ean: '9783957260376',
shop: 3,
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
supplier: 'LV3',
supplierId: 13,
ssc: '999',
sscText: 'Lieferbar in 10-14Tagen',
isPrebooked: false,
at: '2020-07-29T00:00:00',
status: 1024,
preferred: 0,
requested: '2020-07-15T08:28:39.5757159Z',
requestStatusCode: '1',
requestMessage: 'OK',
},
],
};
const mockProductAvailabilities: ProductAvailability[] = [
{
av: {
at: '2020-07-15T14:00:00+02:00',
ean: '9783957260291',
isPrebooked: false,
itemId: 38219742,
logisticianId: 1002,
preferred: 1,
price: { value: { value: 9.99, currency: 'EUR' }, vat: { vatType: 2 } },
qty: 1,
requestMessage: 'Artikel ist lieferbar.',
requestReference: '0',
requestStatusCode: '1',
requested: '2020-07-15T15:19:50.8364336Z',
ssc: '005',
sscText: 'Sofort lieferbar (Download)',
status: 1024,
supplier: 'DIG',
supplierId: 41,
supplierProductNumber: '33161986',
},
itemId: 38219742,
branchId: 123,
quantity: 1,
status: 1024,
type: 3,
},
{
av: {
at: '2020-08-15T14:00:00+02:00',
ean: '9783957270291',
isPrebooked: false,
itemId: 38229742,
logisticianId: 1002,
preferred: 0,
price: { value: { value: 3.99, currency: 'EUR' }, vat: { vatType: 2 } },
qty: 1,
requestMessage: 'Artikel ist lieferbar.',
requestReference: '0',
requestStatusCode: '1',
requested: '2020-07-15T15:19:50.8364336Z',
ssc: '005',
sscText: 'Sofort lieferbar (Download)',
status: 1024,
supplier: 'DIG',
supplierId: 41,
supplierProductNumber: '33161986',
},
itemId: 38219742,
branchId: 456,
quantity: 1,
status: 1024,
type: 3,
},
];
fdescribe('ProductCheckoutComponent', () => {
let component: ProductCheckoutComponent;
let fixture: ComponentFixture<ProductCheckoutComponent>;
let productAvailabilityService: jasmine.SpyObj<ProductAvailabilityService>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ProductCheckoutComponent]
}).compileComponents();
imports: [NgxsModule.forRoot([]), RouterTestingModule],
declarations: [ProductCheckoutComponent, BookPricePipe],
providers: [
{
provide: ProductAvailabilityService,
useValue: jasmine.createSpyObj('productAvailabilityService', [
'getStoreAvailability',
]),
},
{
provide: ModalService,
useValue: jasmine.createSpy('modalService'),
},
{
provide: BranchService,
useValue: jasmine.createSpy('branchService'),
},
DatePipe,
ChangeDetectorRef,
],
schemas: [NO_ERRORS_SCHEMA],
})
.overrideTemplate(ProductCheckoutComponent, '<div>Test</div>')
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductCheckoutComponent);
component = fixture.componentInstance;
productAvailabilityService = TestBed.get(ProductAvailabilityService);
spyOn(component, 'ngOnInit').and.callFake(() => {});
fixture.detectChanges();
});
describe('get preferredDownloadAvailability', () => {
let availabilities: ProductAvailability[];
beforeEach(() => {
availabilities = mockProductAvailabilities;
component.isDownload = true;
component.availability = availabilities;
fixture.detectChanges();
});
it('should return the preferred availability', () => {
const expected = mockProductAvailabilities[0].av;
const result = component.preferredDownloadAvailability;
expect(result).toEqual(expected);
});
it('should return undefined if availability is undefined', () => {
component.availability = undefined;
fixture.detectChanges();
const result = component.preferredDownloadAvailability;
expect(result).toBeUndefined();
});
it('should return undefined if no matching availabilities exist', () => {
const availabilitiesWithTakeNowCheckoutType = mockProductAvailabilities.map(
(av) => ({ ...av, type: 0 })
);
component.availability = availabilitiesWithTakeNowCheckoutType;
fixture.detectChanges();
const result = component.preferredDownloadAvailability;
expect(result).toBeUndefined();
});
});
describe('setQuantityError', () => {
let numberOfItems: number;
beforeEach(() => {
spyOn(component, 'setQuantityError').and.callThrough();
numberOfItems = 16;
});
it('should return Exemplar if only 1 item is available', () => {
const limit = 1;
const expected = `${limit} Exemplar sofort lieferbar.`;
component.setQuantityError(numberOfItems, limit);
expect(component.quantityError).toEqual(expected);
});
it('should return Exemplar if more than 1 item is available', () => {
const limit = 10;
const expected = `${limit} Exemplare sofort lieferbar.`;
component.setQuantityError(numberOfItems, limit);
expect(component.quantityError).toEqual(expected);
});
it('should return a error message if requested items is larger than 999', () => {
const limit = 10;
numberOfItems = 1000;
const expected = `Achtung, Maximal ${limit} Exemplare bestellbar.`;
component.setQuantityError(numberOfItems, limit);
expect(component.quantityError).toEqual(expected);
});
});
describe('validateAvailability', () => {
beforeEach(() => {
spyOn(component, 'validateAvailability').and.callThrough();
component.book = {
product: { ean: 123 },
};
component.selectedBranch = { id: 789 } as BranchDTO;
});
it('should provide the sum of available quantities across all suppliers', async () => {
component.deliveryType = DeliveryOption.PICK_UP;
productAvailabilityService.getStoreAvailability.and.returnValue(
of(mockAvailabilityResponse)
);
fixture.detectChanges();
const expected = 15;
const result = await component.validateAvailability(9);
expect(result).toEqual(expected);
});
it('should return 0 if no supplier has any quantity available', async () => {
component.deliveryType = DeliveryOption.PICK_UP;
const responseWithOutQuantity = setMockAvailablityResponse(
mockAvailabilityResponse,
'qty',
0
);
productAvailabilityService.getStoreAvailability.and.returnValue(
of(responseWithOutQuantity)
);
fixture.detectChanges();
const expected = 0;
const result = await component.validateAvailability(9);
expect(result).toEqual(expected);
});
it('should return 0 if no availabliliy has an eligible (allowed) status code', async () => {
component.deliveryType = DeliveryOption.PICK_UP;
const responseWithFakeStatusCode = setMockAvailablityResponse(
mockAvailabilityResponse,
'status',
5
);
productAvailabilityService.getStoreAvailability.and.returnValue(
of(responseWithFakeStatusCode)
);
fixture.detectChanges();
const expected = 0;
const result = await component.validateAvailability(9);
expect(result).toEqual(expected);
});
});
});
function setMockAvailablityResponse(
mr: {
branchId: number;
type: CheckoutType;
av: AvailabilityDTO[];
},
property: keyof AvailabilityDTO,
value: string | number
): {
branchId: number;
type: CheckoutType;
av: AvailabilityDTO[];
} {
return {
...mr,
av: (mr.av.map(({ [property]: prop, ...rest }) => ({
rest,
property: value,
})) as unknown) as AvailabilityDTO[],
};
}

View File

@@ -1,5 +1,15 @@
import { Component, OnInit, Output, EventEmitter, Input, OnDestroy, ViewChild, ChangeDetectorRef, ViewRef } from '@angular/core';
import { ModalService, ButtonComponent, DeleteDropdownComponent } from '@libs/ui';
import {
Component,
OnInit,
Output,
EventEmitter,
Input,
OnDestroy,
ViewChild,
ChangeDetectorRef,
ViewRef,
} from '@angular/core';
import { ModalService, DeleteDropdownComponent } from '@libs/ui';
import { Router } from '@angular/router';
import { Store, Select } from '@ngxs/store';
import { ItemDTO } from '@swagger/cat';
@@ -7,7 +17,7 @@ import { DeliveryOption } from '../../../../core/models/delivery-option.model';
import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actions';
import { SetCartEntry } from '../../../../core/store/actions/cart-entry.actions';
import { Subject, Observable, of } from 'rxjs';
import { takeUntil, tap, map, filter, take } from 'rxjs/operators';
import { tap, map, filter, take } from 'rxjs/operators';
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
import { BranchSelectors } from '../../../../core/store/selectors/branch.selector';
@@ -15,7 +25,11 @@ import { ProductAvailability } from '../../../../core/models/product-availabilit
import { CheckoutType } from '../../../../core/models/checkout-type.enum';
import { BranchDTO } from '@swagger/checkout';
import { AvailabilityDTO } from '@swagger/availability';
import { mapToIterable, distance, objectNotNull } from '../../../../core/utils/app.utils';
import {
mapToIterable,
distance,
objectNotNull,
} from '../../../../core/utils/app.utils';
import { isNullOrUndefined } from 'util';
import { ProductAvailabilityService } from '../../../../core/services/product-availability.service';
import { BranchService } from '../../../../core/services/branch.service';
@@ -26,7 +40,6 @@ import { SearchDropdownComponent } from '@libs/ui/lib/search-dropdown';
import { AddBranchesIfNotLoaded } from 'apps/sales/src/app/core/store/actions/branch.actions';
import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/product.util';
@Component({
selector: 'app-checkout',
templateUrl: './product-checkout.component.html',
@@ -34,11 +47,17 @@ import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/pr
})
export class ProductCheckoutComponent implements OnInit, OnDestroy {
@Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
@Select(SharedSelectors.getBreadcrumbs) getBreadcrumbs$: Observable<Breadcrumb[]>;
@Select(BranchSelectors.getBranches) branches$: Observable<{ [key: number]: BranchDTO }>;
@Select(SharedSelectors.getBreadcrumbs) getBreadcrumbs$: Observable<
Breadcrumb[]
>;
@Select(BranchSelectors.getBranches) branches$: Observable<{
[key: number]: BranchDTO;
}>;
@ViewChild('deleteDropdown', { static: false }) deleteDropdown: DeleteDropdownComponent;
@ViewChild('branchesdropdown', { static: false }) branchesdd: SearchDropdownComponent;
@ViewChild('deleteDropdown', { static: false })
deleteDropdown: DeleteDropdownComponent;
@ViewChild('branchesdropdown', { static: false })
branchesdd: SearchDropdownComponent;
id = 'checkout-modal';
stepOne = true;
@@ -87,7 +106,10 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
@Input() availability: ProductAvailability[];
get takeNowAvailable() {
if (this.availability.filter((t) => t.type === CheckoutType.takeNow).length > 0) {
if (
this.availability.filter((t) => t.type === CheckoutType.takeNow).length >
0
) {
return of(true);
}
return of(false);
@@ -98,7 +120,8 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
this.availability.filter(
(t) =>
t.type === CheckoutType.store &&
(allowedAvailabilityStatusCodes(t.status) || (t.av && t.av.ssc === '830' && t.av.supplier === 'G'))
(allowedAvailabilityStatusCodes(t.status) ||
(t.av && t.av.ssc === '830' && t.av.supplier === 'G'))
).length > 0
) {
return of(true);
@@ -107,7 +130,13 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
}
get shippingAvailable() {
if (this.availability.filter((t) => t.type === CheckoutType.delivery && allowedAvailabilityStatusCodes(t.status)).length > 0) {
if (
this.availability.filter(
(t) =>
t.type === CheckoutType.delivery &&
allowedAvailabilityStatusCodes(t.status)
).length > 0
) {
return of(true);
}
return of(false);
@@ -188,6 +217,25 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
}
}
get preferredDownloadAvailability(): AvailabilityDTO {
if (this.availability) {
const downloadAvailabilites = this.availability.filter(
(availability) => availability.type === CheckoutType.donwload
);
if (downloadAvailabilites) {
const preferredProductAvailability = downloadAvailabilites.find(
(productAvailability) =>
!!productAvailability.av && !!productAvailability.av.preferred
);
if (preferredProductAvailability) {
return preferredProductAvailability.av;
}
}
}
}
constructor(
private modalService: ModalService,
private store: Store,
@@ -196,22 +244,21 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
private branchService: BranchService,
private datePipe: DatePipe,
private cdrf: ChangeDetectorRef
) { }
) {}
ngOnInit() {
if (this.isDownload) {
this.stepTwoImgType = 'download-type';
this.possibleItems = [1];
this.deliveryType = DeliveryOption.DOWNLOAD;
if (this.availability && this.availability.length > 0) {
const currAv = this.availability.filter((t) => t && t.type === CheckoutType.donwload);
if (currAv && currAv[0] && currAv[0].av) {
this.currentAvailability = currAv[0].av;
}
if (this.preferredDownloadAvailability) {
this.currentAvailability = this.preferredDownloadAvailability;
}
}
this.currency = this.book.catalogAvailability.price ? this.book.catalogAvailability.price.value.currency : null;
this.currency = this.book.catalogAvailability.price
? this.book.catalogAvailability.price.value.currency
: null;
this.currentPoints = this.book.promoPoints + '';
this.branches$
@@ -255,8 +302,12 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
});
this.branchList = branches;
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
const userBranch = branches.find((t) => t.branchNumber === userBranchNumber);
const userBranchNumber = this.store.selectSnapshot(
BranchSelectors.getUserBranch
);
const userBranch = branches.find(
(t) => t.branchNumber === userBranchNumber
);
if (!!userBranch) {
this.selectedBranch = userBranch;
this.currentLocation = userBranch.name;
@@ -277,29 +328,49 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
this.filteredBranches = this.branches;
if (!!userBranch) {
this.filteredBranches = this.branchesData
.sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch))
.sort((a: BranchDTO, b: BranchDTO) =>
this.branchSorterFn(a, b, userBranch)
)
.map((t) => t.name);
}
}
private branchSorterFn(a: BranchDTO, b: BranchDTO, userBranch: BranchDTO) {
const userLat = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.latitude : 0;
const userLong = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.longitude : 0;
const aLat = a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
const aLong = a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
const bLat = b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
const bLong = b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
const userLat =
userBranch.address && userBranch.address.geoLocation
? userBranch.address.geoLocation.latitude
: 0;
const userLong =
userBranch.address && userBranch.address.geoLocation
? userBranch.address.geoLocation.longitude
: 0;
const aLat =
a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
const aLong =
a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
const bLat =
b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
const bLong =
b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
const aDist = distance(userLat, userLong, aLat, aLong);
const bDist = distance(userLat, userLong, bLat, bLong);
return aDist - bDist;
}
branchSearch(term: string) {
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
const userBranchNumber = this.store.selectSnapshot(
BranchSelectors.getUserBranch
);
const branchList = [...this.branchesData];
const userBranch = branchList.find((t) => t.branchNumber === userBranchNumber);
const userBranch = branchList.find(
(t) => t.branchNumber === userBranchNumber
);
if (isNullOrUndefined(term) || term.length < 1) {
this.filteredBranches = branchList.sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch)).map((t) => t.name);
this.filteredBranches = branchList
.sort((a: BranchDTO, b: BranchDTO) =>
this.branchSorterFn(a, b, userBranch)
)
.map((t) => t.name);
return;
}
this.branchService
@@ -334,7 +405,9 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
this.store.dispatch(new AddBranchesIfNotLoaded(branches));
if (branches) {
branches.forEach((br) => {
const existsInBranchData = this.branchesData.find((t) => t.branchNumber === br.branchNumber);
const existsInBranchData = this.branchesData.find(
(t) => t.branchNumber === br.branchNumber
);
if (!existsInBranchData) {
this.branches.push(br.name);
this.branchesData.push(br);
@@ -351,26 +424,40 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
}
storeBranches(): Observable<number[]> {
return of(this.availability.filter((t) => t.type === CheckoutType.store).map((t) => t.branchId));
return of(
this.availability
.filter((t) => t.type === CheckoutType.store)
.map((t) => t.branchId)
);
}
// STEP ONE
selectLocation(location: string) {
this.branchLoad = true;
this.searchingNewBranch = true;
const branch = this.branchesData.find((b: BranchDTO) => b.name === location);
const branch = this.branchesData.find(
(b: BranchDTO) => b.name === location
);
// reset store availability
this.availability = [...this.availability.filter((t) => t.type !== CheckoutType.store)];
this.availability = [
...this.availability.filter((t) => t.type !== CheckoutType.store),
];
this.productAvailabilityService
.getStoreAvailability(this.book, this.book.product.ean, branch.id)
.pipe(
tap((response) => {
if (response.av && response.av.length > 0) {
this.currentLocation = location;
this.filteredBranches = [location, ...this.branches.filter((t) => t !== location)];
this.filteredBranches = [
location,
...this.branches.filter((t) => t !== location),
];
this.selectedBranch = branch;
const preferedAv = response.av.find((t) => t.preferred === 1);
this.currentPickUpDate = this.datePipe.transform(new Date(preferedAv.at), 'dd.MM.yy');
this.currentPickUpDate = this.datePipe.transform(
new Date(preferedAv.at),
'dd.MM.yy'
);
response.av.forEach((t) => {
this.availability.push({
itemId: t.itemId,
@@ -395,13 +482,19 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
return;
}
if (action === DeliveryOption.TAKE_NOW) {
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.takeNow).av;
this.currentAvailability = this.availability.find(
(t) => t.type === CheckoutType.takeNow
).av;
this.stepTwoImgType = 'Take_now';
this.deliveryType = DeliveryOption.TAKE_NOW;
this.confirmationBtnText = 'Reservieren';
// Set user branch as selected branch
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
const userBranch = this.branchList.find((t) => t.branchNumber === userBranchNumber);
const userBranchNumber = this.store.selectSnapshot(
BranchSelectors.getUserBranch
);
const userBranch = this.branchList.find(
(t) => t.branchNumber === userBranchNumber
);
if (!!userBranch) {
this.selectedBranch = userBranch;
this.currentLocation = userBranch.name;
@@ -410,12 +503,16 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
if (!this.availability.find((t) => t.type === CheckoutType.store)) {
return;
}
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.store).av;
this.currentAvailability = this.availability.find(
(t) => t.type === CheckoutType.store
).av;
this.stepTwoImgType = 'Package_Icon';
this.deliveryType = DeliveryOption.PICK_UP;
this.confirmationBtnText = 'Fortfahren';
} else {
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.delivery).av;
this.currentAvailability = this.availability.find(
(t) => t.type === CheckoutType.delivery
).av;
this.stepTwoImgType = 'truck_Icon';
this.deliveryType = DeliveryOption.DELIVERY;
this.confirmationBtnText = 'Fortfahren';
@@ -442,7 +539,8 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
this.loading = true;
const av = await this.validateAvailability(numberOfItems);
this.setQuantityError(numberOfItems, av);
const isTakeNowAndSelectedMoreThanInStock = this.deliveryType === DeliveryOption.TAKE_NOW && numberOfItems > av;
const isTakeNowAndSelectedMoreThanInStock =
this.deliveryType === DeliveryOption.TAKE_NOW && numberOfItems > av;
isTakeNowAndSelectedMoreThanInStock.ifFalse(() => {
this.addItemsToCartDisabled = false;
});
@@ -458,7 +556,9 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
if (numberOfItems > 999) {
this.quantityError = `Achtung, Maximal ${limit} Exemplare bestellbar.`;
} else if (numberOfItems > limit) {
this.quantityError = `${limit} Exemplar sofort lieferbar.`;
this.quantityError = `${limit} ${
limit > 1 ? 'Exemplare' : 'Exemplar'
} sofort lieferbar.`;
} else {
this.quantityError = null;
}
@@ -478,24 +578,31 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
).toPromise();
} else if (this.deliveryType === DeliveryOption.PICK_UP) {
const storeAvailability = await this.productAvailabilityService
.getStoreAvailability(this.book, this.book.product.ean, this.selectedBranch.id, numberOfItems)
.getStoreAvailability(
this.book,
this.book.product.ean,
this.selectedBranch.id,
numberOfItems
)
.pipe(
map((response) => {
if (response && response.av) {
const preferedAv = response.av.find((t) => t && t.preferred === 1);
let preferredQty = 0;
if (preferedAv) {
this.currentAvailability = preferedAv;
this._pikUpPrice = preferedAv.price.value.value;
if (allowedAvailabilityStatusCodes(preferedAv.status)) {
preferredQty = preferedAv.qty;
}
this.detectChanges();
}
if (preferredQty) {
return preferredQty;
}
return 0;
const eligibleAvailabilities = response.av
.filter(
(availability) =>
allowedAvailabilityStatusCodes(availability.status) &&
availability.qty
)
.map((availability) => availability.qty);
const totalAvailableQuantity = eligibleAvailabilities.reduce(
(acc, curr) => acc + curr,
0
);
this.detectChanges();
return totalAvailableQuantity;
}
return 0;
})
@@ -505,11 +612,18 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
return storeAvailability;
} else if (this.deliveryType === DeliveryOption.DELIVERY) {
const shippingAvailability = await this.productAvailabilityService
.getShippingAvailability(this.book, this.book.product.ean, +this.selectedBranch.branchNumber, numberOfItems)
.getShippingAvailability(
this.book,
this.book.product.ean,
+this.selectedBranch.branchNumber,
numberOfItems
)
.pipe(
map((response) => {
if (response && response.av) {
const preferedAv = response.av.find((t) => t && t.preferred === 1);
const preferedAv = response.av.find(
(t) => t && t.preferred === 1
);
let preferredQty = 0;
if (preferedAv) {
this.currentAvailability = preferedAv;
@@ -572,11 +686,15 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
private redirectRoute() {
if (this.customerAlredySet) {
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true));
this.store.dispatch(
new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true)
);
this.store.dispatch(new ChangeCurrentRoute('/cart/review'));
this.router.navigate(['/cart/review']);
} else {
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true));
this.store.dispatch(
new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true)
);
this.store.dispatch(new ChangeCurrentRoute('/customer/search'));
this.router.navigate(['/customer/search']);
}
@@ -630,7 +748,11 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
detectChanges() {
setTimeout(() => {
if (this.cdrf !== null && this.cdrf !== undefined && !(this.cdrf as ViewRef).destroyed) {
if (
this.cdrf !== null &&
this.cdrf !== undefined &&
!(this.cdrf as ViewRef).destroyed
) {
this.cdrf.detectChanges();
}
}, 0);