Merged PR 1104: #2662 Listenbestellung Download OLA im Warenkorb bei Bestellen Klick

#2662 Listenbestellung Download OLA im Warenkorb bei Bestellen Klick

Related work items: #2662
This commit is contained in:
Andreas Schickinger
2022-03-15 09:00:11 +00:00
committed by Lorenz Hilpert
parent 92958f4b22
commit 12fe8b46c3
11 changed files with 130 additions and 5 deletions

View File

@@ -24,7 +24,7 @@ import {
import { DisplayOrderDTO, OrderCheckoutService, ReorderValues } from '@swagger/oms';
import { isNullOrUndefined, memorize } from '@utils/common';
import { combineLatest, Observable, of, concat, isObservable, throwError } from 'rxjs';
import { bufferCount, catchError, filter, first, map, mergeMap, shareReplay, tap, withLatestFrom } from 'rxjs/operators';
import { bufferCount, catchError, filter, first, map, mergeMap, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import * as DomainCheckoutSelectors from './store/domain-checkout.selectors';
import * as DomainCheckoutActions from './store/domain-checkout.actions';
@@ -217,6 +217,22 @@ export class DomainCheckoutService {
);
}
updateShoppingCartItemAvailability({
shoppingCartId,
shoppingCartItemId,
availability,
}: {
shoppingCartId: number;
shoppingCartItemId: number;
availability: AvailabilityDTO;
}) {
return this.storeCheckoutService.StoreCheckoutUpdateShoppingCartItemAvailability({
shoppingCartId,
shoppingCartItemId,
availability,
});
}
updateItemInShoppingCart({
processId,
shoppingCartItemId,
@@ -314,6 +330,10 @@ export class DomainCheckoutService {
);
}
getOlaErrors({ processId }: { processId: number }): Observable<number[]> {
return this.store.select(DomainCheckoutSelectors.selectOlaErrorsByProcessId, { processId });
}
setPayment({ processId, paymentType }: { processId: number; paymentType: PaymentType }): Observable<CheckoutDTO> {
return this.getCheckout({ processId }).pipe(
first(),
@@ -383,6 +403,53 @@ export class DomainCheckoutService {
);
}
checkAvailabilities({ processId }: { processId: number }): Observable<any> {
const shoppingCart$ = this.getShoppingCart({ processId }).pipe(first());
const itemsToCheck$ = shoppingCart$.pipe(
map((cart) => cart?.items?.filter((item) => item?.data?.features?.orderType === 'Download' && !item.data.availability.lastRequest))
);
return itemsToCheck$.pipe(
withLatestFrom(shoppingCart$),
switchMap(async ([items, cart]) => {
const errorIds = [];
for (const item of items) {
const availability = await this.availabilityService
.getDownloadAvailability({
item: {
ean: item.data.product.ean,
itemId: Number(item.data.product.catalogProductNumber),
price: item.data.availability.price,
},
})
.toPromise();
if (!availability || !this.availabilityService.isAvailable({ availability })) {
errorIds.push(item.id);
} else {
await this.updateShoppingCartItemAvailability({
shoppingCartId: cart.id,
shoppingCartItemId: item.id,
availability: {
...availability,
lastRequest: new Date().toISOString(),
},
}).toPromise();
}
}
this.setOlaErrors({ processId, errorIds });
if (errorIds.length > 0) {
throw throwError(new Error(`Artikel nicht verfügbar`));
} else {
return of(undefined);
}
})
);
}
updateAvailabilities({ processId }: { processId: number }): Observable<any> {
const shoppingCart$ = this.getShoppingCart({ processId }).pipe(first());
const itemsToUpdate$ = shoppingCart$.pipe(
@@ -545,6 +612,8 @@ export class DomainCheckoutService {
})
);
const checkAvailabilities$ = this.checkAvailabilities({ processId });
const updateAvailabilities$ = this.updateAvailabilities({ processId });
const setPaymentType$ = itemOrderOptions$.pipe(
@@ -610,6 +679,7 @@ export class DomainCheckoutService {
mergeMap((_) => refreshShoppingCart$.pipe(tap(console.log.bind(window, 'refreshShoppingCart$')))),
mergeMap((_) => setSpecialComment$.pipe(tap(console.log.bind(window, 'setSpecialComment$')))),
mergeMap((_) => refreshCheckout$.pipe(tap(console.log.bind(window, 'refreshCheckout$')))),
mergeMap((_) => checkAvailabilities$.pipe(tap(console.log.bind(window, 'checkAvailabilities$')))),
mergeMap((_) => updateAvailabilities$.pipe(tap(console.log.bind(window, 'updateAvailabilities$')))),
mergeMap((_) => setBuyer$.pipe(tap(console.log.bind(window, 'setBuyer$')))),
mergeMap((_) => setNotificationChannels$.pipe(tap(console.log.bind(window, 'setNotificationChannels$')))),
@@ -765,6 +835,15 @@ export class DomainCheckoutService {
this.store.dispatch(DomainCheckoutActions.setCustomerFeatures({ processId, customerFeatures }));
}
setOlaErrors({ processId, errorIds }: { processId: number; errorIds: number[] }) {
this.store.dispatch(
DomainCheckoutActions.setOlaError({
processId,
olaErrorIds: errorIds,
})
);
}
getCustomerFeatures({ processId }: { processId: number }): Observable<{ [key: string]: string }> {
return this.store.select(DomainCheckoutSelectors.selectCustomerFeaturesByProcessId, { processId });
}

View File

@@ -12,4 +12,5 @@ export interface CheckoutEntity {
orders: DisplayOrderDTO[];
specialComment: string;
notificationChannels: NotificationChannel;
olaErrorIds: number[];
}

View File

@@ -57,3 +57,5 @@ export const setBuyer = createAction(`${prefix} Set Buyer`, props<{ processId: n
export const setPayer = createAction(`${prefix} Set Payer`, props<{ processId: number; payer: PayerDTO }>());
export const setSpecialComment = createAction(`${prefix} Set Agent Comment`, props<{ processId: number; agentComment: string }>());
export const setOlaError = createAction(`${prefix} Set Ola Error`, props<{ processId: number; olaErrorIds: number[] }>());

View File

@@ -72,7 +72,12 @@ const _domainCheckoutReducer = createReducer(
return storeCheckoutAdapter.setOne(entity, s);
}),
on(DomainCheckoutActions.removeProcess, (s, { processId }) => storeCheckoutAdapter.removeOne(processId, s)),
on(DomainCheckoutActions.setOrders, (s, { orders }) => ({ ...s, orders }))
on(DomainCheckoutActions.setOrders, (s, { orders }) => ({ ...s, orders })),
on(DomainCheckoutActions.setOlaError, (s, { processId, olaErrorIds }) => {
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
entity.olaErrorIds = olaErrorIds;
return storeCheckoutAdapter.setOne(entity, s);
})
);
export function domainCheckoutReducer(state, action) {
@@ -94,6 +99,7 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
buyer: undefined,
specialComment: '',
notificationChannels: 0,
olaErrorIds: [],
};
}

View File

@@ -56,3 +56,8 @@ export const selectBuyerCommunicationDetails = createSelector(
);
export const selectOrders = createSelector(storeFeatureSelector, (s) => s.orders);
export const selectOlaErrorsByProcessId = createSelector(
selectEntities,
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.olaErrorIds
);

View File

@@ -244,6 +244,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy {
availability: {
availabilityType: item?.catalogAvailability?.status,
price: item?.catalogAvailability?.price,
supplierProductNumber: item?.ids?.dig ? String(item.ids?.dig) : item?.product?.supplierProductNumber,
},
product: {
catalogProductNumber: String(item?.id),

View File

@@ -166,7 +166,7 @@
</strong>
<span class="shipping-cost-info">ohne Versandkosten</span>
</div>
<button class="cta-primary" (click)="order()" [disabled]="primaryCtaDisabled$ | async">
<button class="cta-primary" (click)="order()" [disabled]="showOrderButtonSpinner">
<ui-spinner [show]="showOrderButtonSpinner">
{{ primaryCtaLabel$ | async }}
</ui-spinner>

View File

@@ -19,6 +19,7 @@ import { ResponseArgsOfItemDTO } from '@swagger/cat';
import { PurchasingOptionsListModalComponent } from '../modals/purchasing-options-list-modal';
import { PurchasingOptionsListModalData } from '../modals/purchasing-options-list-modal/purchasing-options-list-modal.data';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { time } from 'console';
export interface CheckoutReviewComponentState {
shoppingCart: ShoppingCartDTO;

View File

@@ -39,6 +39,10 @@
{{ item?.product?.manufacturer | substr: 18 }} | {{ item?.product?.ean }} <br />
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
{{ item?.product?.publicationDate | date }}
<div class="item-availability-message" *ngIf="olaError$ | async">
Artikel nicht verfügbar
</div>
</div>
<div class="item-price-stock">

View File

@@ -103,6 +103,10 @@
.item-info {
grid-area: item-info;
.item-availability-message {
@apply text-dark-goldenrod font-bold text-sm whitespace-nowrap;
}
}
.item-availability {

View File

@@ -1,7 +1,11 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ApplicationService } from '@core/application';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import { ComponentStore } from '@ngrx/component-store';
import { ShoppingCartItemDTO } from '@swagger/checkout';
import { first, map, shareReplay } from 'rxjs/operators';
import { combineLatest } from 'rxjs';
import { filter, first, map, shareReplay, switchMap } from 'rxjs/operators';
export interface ShoppingCartItemComponentState {
item: ShoppingCartItemDTO;
@@ -79,7 +83,25 @@ export class ShoppingCartItemComponent extends ComponentStore<ShoppingCartItemCo
shareReplay()
);
constructor() {
isDownloadAvailable$ = combineLatest([this.item$, this.orderType$]).pipe(
filter(([_, orderType]) => orderType === 'Download'),
switchMap(([item]) =>
this.availabilityService.getDownloadAvailability({
item: { ean: item.product.ean, price: item.availability.price, itemId: +item.product.catalogProductNumber },
})
),
map((availability) => availability && this.availabilityService.isAvailable({ availability }))
);
olaError$ = this.checkoutService
.getOlaErrors({ processId: this.application.activatedProcessId })
.pipe(map((ids) => ids?.find((id) => id === this.item.id)));
constructor(
private availabilityService: DomainAvailabilityService,
private checkoutService: DomainCheckoutService,
private application: ApplicationService
) {
super({ item: undefined, orderType: '' });
}