mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'develop' into release/3.0
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './availability-by-branch-dto';
|
||||
export * from './availability';
|
||||
export * from './item-data';
|
||||
export * from './ssc';
|
||||
|
||||
5
apps/domain/availability/src/lib/defs/ssc.ts
Normal file
5
apps/domain/availability/src/lib/defs/ssc.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface Ssc {
|
||||
itemId?: number;
|
||||
ssc?: string;
|
||||
sscText?: string;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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(' - ');
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
[hint]="hint$ | async"
|
||||
(scan)="search($event)"
|
||||
[scanner]="true"
|
||||
(hintCleared)="clearHint()"
|
||||
></ui-searchbox>
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user