Merge branch 'develop' into release/3.0

This commit is contained in:
Lorenz Hilpert
2023-08-07 07:07:36 +02:00
13 changed files with 148 additions and 25 deletions

View File

@@ -8,7 +8,7 @@ import {
StoreCheckoutSupplierService,
SupplierDTO,
} from '@swagger/checkout';
import { combineLatest, Observable, of } from 'rxjs';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import {
AvailabilityRequestDTO,
AvailabilityService,
@@ -21,11 +21,15 @@ import { isArray, memorize } from '@utils/common';
import { LogisticianDTO, LogisticianService } from '@swagger/oms';
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
import { PriceDTO } from '@swagger/availability';
import { AvailabilityByBranchDTO, ItemData } from './defs';
import { AvailabilityByBranchDTO, ItemData, Ssc } from './defs';
import { Availability } from './defs/availability';
@Injectable()
export class DomainAvailabilityService {
// Ticket #3378 Keep Result List Items and Details Page SSC in sync
sscs$ = new BehaviorSubject<Array<Ssc>>([]);
sscsObs$ = this.sscs$.asObservable();
constructor(
private _availabilityService: AvailabilityService,
private _logisticanService: LogisticianService,

View File

@@ -1,3 +1,4 @@
export * from './availability-by-branch-dto';
export * from './availability';
export * from './item-data';
export * from './ssc';

View File

@@ -0,0 +1,5 @@
export interface Ssc {
itemId?: number;
ssc?: string;
sscText?: string;
}

View File

@@ -234,7 +234,7 @@
<div class="page-article-details__ssc flex justify-end my-2 font-bold text-lg">
<div class="w-52 h-px-20 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="fetchingAvailabilities$ | async"></div>
<ng-container *ngIf="!(fetchingAvailabilities$ | async)">
<div *ngIf="store.sscText$ | async; let sscText">
<div class="text-right" *ngIf="store.sscText$ | async; let sscText">
{{ sscText }}
</div>
</ng-container>

View File

@@ -267,7 +267,8 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
this.deliveryB2BAvailability$,
this.downloadAvailability$,
]).pipe(
map(([item, isDownload, pickupAvailability, deliveryDigAvailability, deliveryB2BAvailability, downloadAvailability]) => {
withLatestFrom(this.domainAvailabilityService.sscs$),
map(([[item, isDownload, pickupAvailability, deliveryDigAvailability, deliveryB2BAvailability, downloadAvailability], sscs]) => {
let availability: AvailabilityDTO;
if (isDownload) {
@@ -282,15 +283,30 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
}
}
let ssc = '';
let sscText = 'Keine Lieferanten vorhanden';
if (item?.catalogAvailability?.supplier === 'S' && !isDownload) {
return [item?.catalogAvailability?.ssc, item?.catalogAvailability?.sscText].filter((f) => !!f).join(' - ');
ssc = item?.catalogAvailability?.ssc;
sscText = item?.catalogAvailability?.sscText;
return [ssc, sscText].filter((f) => !!f).join(' - ');
}
if (availability?.ssc || availability?.sscText) {
return [availability?.ssc, availability?.sscText].filter((f) => !!f).join(' - ');
ssc = availability?.ssc;
sscText = availability?.sscText;
const sscExists = !!sscs?.find((ssc) => !!item?.id && ssc?.itemId === item.id);
const sscEqualsCatalogSsc = ssc === item.catalogAvailability.ssc && sscText === item.catalogAvailability.sscText;
// To keep result list in sync with details page
if (!sscExists && !sscEqualsCatalogSsc) {
this.domainAvailabilityService.sscs$.next([...sscs, { itemId: item?.id, ssc, sscText }]);
}
}
return 'Keine Lieferanten vorhanden';
return [ssc, sscText].filter((f) => !!f).join(' - ');
})
);

View File

@@ -115,10 +115,10 @@
class="page-search-result-item__item-ssc desktop-small:text-p3 w-full text-right overflow-hidden text-ellipsis whitespace-nowrap"
[class.page-search-result-item__item-ssc-main]="mainOutletActive"
>
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="mainOutletActive">
{{ item?.catalogAvailability?.ssc }} - {{ item?.catalogAvailability?.sscText }}
</div>
<strong>{{ item?.catalogAvailability?.ssc }}</strong> - {{ item?.catalogAvailability?.sscText }}
<ng-container *ngIf="ssc$ | async; let ssc">
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="mainOutletActive">{{ ssc?.ssc }} - {{ ssc?.sscText }}</div>
<strong>{{ ssc?.ssc }}</strong> - {{ ssc?.sscText }}
</ng-container>
</div>
</div>
</a>

View File

@@ -8,7 +8,7 @@ import { ItemDTO } from '@swagger/cat';
import { DateAdapter } from '@ui/common';
import { isEqual } from 'lodash';
import { combineLatest } from 'rxjs';
import { debounceTime, switchMap, map, shareReplay, filter } from 'rxjs/operators';
import { debounceTime, switchMap, map, filter } from 'rxjs/operators';
import { ArticleSearchService } from '../article-search.store';
import { ProductCatalogNavigationService } from '@shared/services';
@@ -126,6 +126,17 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
)
);
ssc$ = this._availability.sscsObs$.pipe(
debounceTime(100),
map((sscs) => {
const updatedSsc = sscs?.find((ssc) => this.item?.id === ssc?.itemId);
return {
ssc: updatedSsc?.ssc ?? this.item?.catalogAvailability?.ssc,
sscText: updatedSsc?.sscText ?? this.item?.catalogAvailability?.sscText,
};
})
);
constructor(
private _dateAdapter: DateAdapter,
private _datePipe: DatePipe,

View File

@@ -9,10 +9,13 @@
</a>
</div>
<h1 class="text-center text-2xl font-bold">Kundenkarte</h1>
<p class="text-center text-xl">
<p class="text-center text-xl" *ngIf="!(noDataFound$ | async)">
Alle Infos zu Ihrer Kundenkarte <br />
und allen Partnerkarten.
</p>
<p class="text-center text-xl" *ngIf="noDataFound$ | async">
Keine Kundenkarte gefunden.
</p>
<page-customer-kundenkarte
class="justify-self-center"
*ngFor="let karte of primaryKundenkarte$ | async"

View File

@@ -1,13 +1,14 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { CustomerSearchStore } from '../store';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { Subject, combineLatest } from 'rxjs';
import { map, share, switchMap } from 'rxjs/operators';
import { Subject, combineLatest, of } from 'rxjs';
import { catchError, map, share, switchMap } from 'rxjs/operators';
import { CrmCustomerService } from '@domain/crm';
import { KundenkarteComponent } from '../../components/kundenkarte';
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { IconComponent } from '@shared/components/icon';
import { CustomerSearchNavigation } from '../../navigations';
import { BonusCardInfoDTO } from '@swagger/crm';
@Component({
selector: 'page-customer-kundenkarte-main-view',
@@ -25,11 +26,16 @@ export class KundenkarteMainViewComponent implements OnInit, OnDestroy {
kundenkarte$ = this.customerId$.pipe(
switchMap((customerId) =>
this._customerService.getCustomerCard(customerId).pipe(map((response) => response.result?.filter((f) => f.isActive)))
this._customerService.getCustomerCard(customerId).pipe(
map((response) => response.result?.filter((f) => f.isActive)),
catchError(() => of<BonusCardInfoDTO[]>([]))
)
),
share()
);
noDataFound$ = this.kundenkarte$.pipe(map((kundenkarte) => kundenkarte?.length == 0));
primaryKundenkarte$ = this.kundenkarte$.pipe(map((kundenkarte) => kundenkarte?.filter((k) => k.isPrimary)));
partnerKundenkarte$ = this.kundenkarte$.pipe(map((kundenkarte) => kundenkarte?.filter((k) => !k.isPrimary)));

View File

@@ -11,5 +11,6 @@
[hint]="hint$ | async"
(scan)="search($event)"
[scanner]="true"
(hintCleared)="clearHint()"
></ui-searchbox>
</div>

View File

@@ -59,8 +59,12 @@ export class FinishShippingDocumentComponent implements OnInit, OnDestroy {
this._onDestroy$.complete();
}
search(query: string) {
clearHint() {
this.hint$.next('');
}
search(query: string) {
query = query?.trim();
if (!query) {
this.hint$.next('Ungültige Eingabe');
return;

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store';
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO, CheckoutDTO, ShoppingCartDTO, ShoppingCartItemDTO } from '@swagger/checkout';
import { DomainCheckoutService } from '@domain/checkout';
import { mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { catchError, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import {
BranchService,
DisplayOrderDTO,
@@ -12,12 +12,13 @@ import {
ResponseArgsOfIEnumerableOfBranchDTO,
ResponseArgsOfValueTupleOfIEnumerableOfDisplayOrderDTOAndIEnumerableOfKeyValueDTOOfStringAndString,
} from '@swagger/oms';
import { Observable } from 'rxjs';
import { Observable, of, zip } from 'rxjs';
import { AuthService } from '@core/auth';
import { UiModalService } from '@ui/modal';
import { getCatalogProductNumber } from './catalog-product-number';
import { ItemDTO } from '@swagger/cat';
import { DomainAvailabilityService } from '@domain/availability';
import { HttpErrorResponse } from '@angular/common/http';
export interface KulturpassOrderModalState {
orderItemListItem?: OrderItemListItemDTO;
@@ -200,9 +201,18 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
addItemToShoppingCart = this.effect((item$: Observable<ItemDTO>) =>
item$.pipe(
mergeMap((item) =>
this._availabilityService
.getTakeAwayAvailability({
mergeMap((item) => {
const takeAwayAvailability$ = this._availabilityService.getTakeAwayAvailability({
item: {
ean: item.product.ean,
itemId: item.id,
price: item.catalogAvailability.price,
},
quantity: this.itemQuantityByCatalogProductNumber(getCatalogProductNumber(item)) + 1,
});
const deliveryAvailability$ = this._availabilityService
.getDeliveryAvailability({
item: {
ean: item.product.ean,
itemId: item.id,
@@ -210,12 +220,45 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
},
quantity: this.itemQuantityByCatalogProductNumber(getCatalogProductNumber(item)) + 1,
})
.pipe(tapResponse(this.handleAddItemToShoppingCartResponse(item), this.handleAddItemToShoppingCartError))
)
.pipe(
catchError((err) => {
return of(undefined);
})
);
return zip(takeAwayAvailability$, deliveryAvailability$).pipe(
tapResponse(this.handleAddItemToShoppingCartResponse2(item), this.handleAddItemToShoppingCartError)
);
})
)
);
handleAddItemToShoppingCartResponse = (item: ItemDTO) => (availability: AvailabilityDTO) => {
handleAddItemToShoppingCartResponse2 = (item: ItemDTO) => ([takeAwayAvailability, deliveryAvailability]: [
AvailabilityDTO,
AvailabilityDTO
]) => {
const isPriceMaintained = deliveryAvailability['priceMaintained'] ?? false;
const offlinePrice = takeAwayAvailability.price?.value?.value ?? -1;
const onlinePrice = deliveryAvailability?.price?.value?.value ?? -1;
const availability = takeAwayAvailability;
/**
* Onlinepreis ist niedliger als der Offlinepreis
* wenn der Artikel nicht Preisgebunden ist, wird der Onlinepreis genommen
* wenn der Artikel Preisgebunden ist, wird der Ladenpreis verwendet
*/
/**
* Offlinepreis ist niedliger als der Onlinepreis
* wenn der Artikel nicht Preisgebunden ist, wird der Ladenpreis genommen
* wenn der Artikel Preisgebunden ist, wird der Ladenpreis verwendet
*/
if (onlinePrice < offlinePrice && !isPriceMaintained) {
availability.price = deliveryAvailability.price;
}
this.setAvailability({
catalogProductNumber: getCatalogProductNumber(item),
availability: availability,
@@ -240,6 +283,31 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
this.addItem(addToShoppingCartDTO);
};
// handleAddItemToShoppingCartResponse = (item: ItemDTO) => (availability: AvailabilityDTO) => {
// this.setAvailability({
// catalogProductNumber: getCatalogProductNumber(item),
// availability: availability,
// });
// const addToShoppingCartDTO: AddToShoppingCartDTO = {
// quantity: 1,
// availability: availability,
// destination: {
// data: {
// target: 1,
// targetBranch: { id: this.branch.id },
// },
// },
// promotion: {
// points: 0,
// },
// itemType: item.type,
// product: { catalogProductNumber: getCatalogProductNumber(item), ...item.product },
// };
// this.addItem(addToShoppingCartDTO);
// };
handleAddItemToShoppingCartError = (err: any) => {
this._modal.error('Fehler beim Hinzufügen des Artikels', err);
};

View File

@@ -82,6 +82,9 @@ export class UiSearchboxNextComponent extends UiFormControlDirective<any>
@Input()
hint: string = '';
@Output()
hintCleared = new EventEmitter<void>();
@Input()
autocompleteValueSelector: (item: any) => string = (item: any) => item;
@@ -196,6 +199,7 @@ export class UiSearchboxNextComponent extends UiFormControlDirective<any>
clearHint() {
this.hint = '';
this.focused.emit(true);
this.hintCleared.emit();
this.cdr.markForCheck();
}