mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1958: Refactoring Checkout: Migration von prozess-basierter zu warenkorb-basierter Architektur mit neuer Data-Access-Library und verbesserter Typsicherheit
refactor(checkout): migrate purchase options to shopping cart-based architecture Replace processId with shoppingCartId in purchase options modal and related components Add new checkout data-access library with facades, services, and schemas Update PurchaseOptionsService to use new checkout facade pattern Migrate state management from process-based to shopping cart-based approach Update selectors and store to handle shoppingCartId instead of processId Improve type safety with Zod schemas for checkout operations Add proper error handling and logging throughout checkout services Update article details and checkout review components to use new patterns BREAKING CHANGE: Purchase options modal now requires shoppingCartId instead of processId Related work items: #5350
This commit is contained in:
committed by
Nino Righi
parent
334436c737
commit
100cbb5020
@@ -142,7 +142,6 @@ export class ApplicationServiceAdapter extends ApplicationService {
|
||||
|
||||
patchProcessData(processId: number, data: Record<string, unknown>): void {
|
||||
const currentProcess = this.#tabService.entityMap()[processId];
|
||||
|
||||
const currentData: TabMetadata =
|
||||
(currentProcess?.metadata?.['process_data'] as TabMetadata) ?? {};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,48 @@
|
||||
import { ItemType, PriceDTO, PriceValueDTO, VATValueDTO } from '@generated/swagger/checkout-api';
|
||||
import { OrderType, PurchaseOption } from './store';
|
||||
|
||||
export const PURCHASE_OPTIONS: PurchaseOption[] = [
|
||||
'in-store',
|
||||
'pickup',
|
||||
'delivery',
|
||||
'dig-delivery',
|
||||
'b2b-delivery',
|
||||
'download',
|
||||
];
|
||||
|
||||
export const DELIVERY_PURCHASE_OPTIONS: PurchaseOption[] = ['delivery', 'dig-delivery', 'b2b-delivery'];
|
||||
|
||||
export const PURCHASE_OPTION_TO_ORDER_TYPE: { [purchaseOption: string]: OrderType } = {
|
||||
'in-store': 'Rücklage',
|
||||
pickup: 'Abholung',
|
||||
delivery: 'Versand',
|
||||
'dig-delivery': 'Versand',
|
||||
'b2b-delivery': 'Versand',
|
||||
};
|
||||
|
||||
export const GIFT_CARD_TYPE = 66560 as ItemType;
|
||||
|
||||
export const DEFAULT_PRICE_DTO: PriceDTO = { value: { value: undefined }, vat: { vatType: 0 } };
|
||||
|
||||
export const DEFAULT_PRICE_VALUE: PriceValueDTO = { value: 0, currency: 'EUR' };
|
||||
|
||||
export const DEFAULT_VAT_VALUE: VATValueDTO = { value: 0 };
|
||||
|
||||
export const GIFT_CARD_MAX_PRICE = 200;
|
||||
|
||||
export const PRICE_PATTERN = /^\d+(,\d{1,2})?$/;
|
||||
import {
|
||||
ItemType,
|
||||
PriceDTO,
|
||||
PriceValueDTO,
|
||||
VATValueDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { PurchaseOption } from './store';
|
||||
import { OrderType } from '@isa/checkout/data-access';
|
||||
|
||||
export const PURCHASE_OPTIONS: PurchaseOption[] = [
|
||||
'in-store',
|
||||
'pickup',
|
||||
'delivery',
|
||||
'dig-delivery',
|
||||
'b2b-delivery',
|
||||
'download',
|
||||
];
|
||||
|
||||
export const DELIVERY_PURCHASE_OPTIONS: PurchaseOption[] = [
|
||||
'delivery',
|
||||
'dig-delivery',
|
||||
'b2b-delivery',
|
||||
];
|
||||
|
||||
export const PURCHASE_OPTION_TO_ORDER_TYPE: {
|
||||
[purchaseOption: string]: OrderType;
|
||||
} = {
|
||||
'in-store': 'Rücklage',
|
||||
'pickup': 'Abholung',
|
||||
'delivery': 'Versand',
|
||||
'dig-delivery': 'Versand',
|
||||
'b2b-delivery': 'Versand',
|
||||
};
|
||||
|
||||
export const GIFT_CARD_TYPE = 66560 as ItemType;
|
||||
|
||||
export const DEFAULT_PRICE_DTO: PriceDTO = {
|
||||
value: { value: undefined },
|
||||
vat: { vatType: 0 },
|
||||
};
|
||||
|
||||
export const DEFAULT_PRICE_VALUE: PriceValueDTO = { value: 0, currency: 'EUR' };
|
||||
|
||||
export const DEFAULT_VAT_VALUE: VATValueDTO = { value: 0 };
|
||||
|
||||
export const GIFT_CARD_MAX_PRICE = 200;
|
||||
|
||||
export const PRICE_PATTERN = /^\d+(,\d{1,2})?$/;
|
||||
|
||||
@@ -1,145 +1,181 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, TrackByFunction, HostBinding } from '@angular/core';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { PurchaseOptionsModalData } from './purchase-options-modal.data';
|
||||
|
||||
import { PurchaseOptionsListHeaderComponent } from './purchase-options-list-header';
|
||||
import { PurchaseOptionsListItemComponent } from './purchase-options-list-item';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Subject, zip } from 'rxjs';
|
||||
import {
|
||||
DeliveryPurchaseOptionTileComponent,
|
||||
DownloadPurchaseOptionTileComponent,
|
||||
InStorePurchaseOptionTileComponent,
|
||||
PickupPurchaseOptionTileComponent,
|
||||
} from './purchase-options-tile';
|
||||
import { isGiftCard, Item, PurchaseOption, PurchaseOptionsStore } from './store';
|
||||
import { delay, map, shareReplay, skip, switchMap, takeUntil, tap } from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString } from '@generated/swagger/cat-search-api';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-purchase-options-modal',
|
||||
templateUrl: 'purchase-options-modal.component.html',
|
||||
styleUrls: ['purchase-options-modal.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [provideComponentStore(PurchaseOptionsStore)],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PurchaseOptionsListHeaderComponent,
|
||||
PurchaseOptionsListItemComponent,
|
||||
DeliveryPurchaseOptionTileComponent,
|
||||
InStorePurchaseOptionTileComponent,
|
||||
PickupPurchaseOptionTileComponent,
|
||||
DownloadPurchaseOptionTileComponent,
|
||||
],
|
||||
})
|
||||
export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
|
||||
get type() {
|
||||
return this._uiModalRef.data.type;
|
||||
}
|
||||
|
||||
@HostBinding('attr.data-loading')
|
||||
get fetchingData() {
|
||||
return this.store.fetchingAvailabilities.length > 0;
|
||||
}
|
||||
|
||||
items$ = this.store.items$;
|
||||
|
||||
hasPrice$ = this.items$.pipe(
|
||||
switchMap((items) =>
|
||||
items.map((item) => {
|
||||
let isArchive = false;
|
||||
const features = item?.features as KeyValueDTOOfStringAndString[];
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
|
||||
if (!!features && Array.isArray(features)) {
|
||||
isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC');
|
||||
}
|
||||
return zip(
|
||||
this.store
|
||||
?.getPrice$(item?.id)
|
||||
.pipe(
|
||||
map((price) =>
|
||||
isArchive
|
||||
? !!price?.value?.value && price?.vat !== undefined && price?.vat?.value !== undefined
|
||||
: !!price?.value?.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
switchMap((hasPrices) => hasPrices),
|
||||
map((hasPrices) => {
|
||||
const containsItemWithNoPrice = hasPrices?.filter((hasPrice) => hasPrice === false) ?? [];
|
||||
return containsItemWithNoPrice?.length === 0;
|
||||
}),
|
||||
);
|
||||
|
||||
purchasingOptions$ = this.store.getPurchaseOptionsInAvailabilities$;
|
||||
|
||||
isDownloadOnly$ = this.purchasingOptions$.pipe(
|
||||
map((purchasingOptions) => purchasingOptions.length === 1 && purchasingOptions[0] === 'download'),
|
||||
);
|
||||
|
||||
isGiftCardOnly$ = this.store.items$.pipe(map((items) => items.every((item) => isGiftCard(item, this.store.type))));
|
||||
|
||||
hasDownload$ = this.purchasingOptions$.pipe(map((purchasingOptions) => purchasingOptions.includes('download')));
|
||||
|
||||
canContinue$ = this.store.canContinue$;
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
saving = false;
|
||||
|
||||
constructor(
|
||||
private _uiModalRef: UiModalRef<string, PurchaseOptionsModalData>,
|
||||
public store: PurchaseOptionsStore,
|
||||
) {
|
||||
this.store.initialize(this._uiModalRef.data);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.items$.pipe(takeUntil(this._onDestroy$), skip(1), delay(100)).subscribe((items) => {
|
||||
if (items.length === 0) {
|
||||
this._uiModalRef.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._uiModalRef.data?.preSelectOption?.option) {
|
||||
this.store.setPurchaseOption(this._uiModalRef.data?.preSelectOption?.option);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
itemTrackBy: TrackByFunction<Item> = (_, item) => item.id;
|
||||
|
||||
showOption(option: PurchaseOption): boolean {
|
||||
return this._uiModalRef.data?.preSelectOption?.showOptionOnly
|
||||
? this._uiModalRef.data?.preSelectOption?.option === option
|
||||
: true;
|
||||
}
|
||||
|
||||
async save(action: string) {
|
||||
if (this.saving) {
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
|
||||
try {
|
||||
await this.store.save();
|
||||
|
||||
if (this.store.items.length === 0) {
|
||||
this._uiModalRef.close(action);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
TrackByFunction,
|
||||
HostBinding,
|
||||
} from '@angular/core';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { PurchaseOptionsModalContext } from './purchase-options-modal.data';
|
||||
|
||||
import { PurchaseOptionsListHeaderComponent } from './purchase-options-list-header';
|
||||
import { PurchaseOptionsListItemComponent } from './purchase-options-list-item';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Subject, zip } from 'rxjs';
|
||||
import {
|
||||
DeliveryPurchaseOptionTileComponent,
|
||||
DownloadPurchaseOptionTileComponent,
|
||||
InStorePurchaseOptionTileComponent,
|
||||
PickupPurchaseOptionTileComponent,
|
||||
} from './purchase-options-tile';
|
||||
import {
|
||||
isGiftCard,
|
||||
Item,
|
||||
PurchaseOption,
|
||||
PurchaseOptionsStore,
|
||||
} from './store';
|
||||
import {
|
||||
delay,
|
||||
map,
|
||||
shareReplay,
|
||||
skip,
|
||||
switchMap,
|
||||
takeUntil,
|
||||
tap,
|
||||
} from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString } from '@generated/swagger/cat-search-api';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-purchase-options-modal',
|
||||
templateUrl: 'purchase-options-modal.component.html',
|
||||
styleUrls: ['purchase-options-modal.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [provideComponentStore(PurchaseOptionsStore)],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PurchaseOptionsListHeaderComponent,
|
||||
PurchaseOptionsListItemComponent,
|
||||
DeliveryPurchaseOptionTileComponent,
|
||||
InStorePurchaseOptionTileComponent,
|
||||
PickupPurchaseOptionTileComponent,
|
||||
DownloadPurchaseOptionTileComponent,
|
||||
],
|
||||
})
|
||||
export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
|
||||
get type() {
|
||||
return this._uiModalRef.data.type;
|
||||
}
|
||||
|
||||
@HostBinding('attr.data-loading')
|
||||
get fetchingData() {
|
||||
return this.store.fetchingAvailabilities.length > 0;
|
||||
}
|
||||
|
||||
items$ = this.store.items$;
|
||||
|
||||
hasPrice$ = this.items$.pipe(
|
||||
switchMap((items) =>
|
||||
items.map((item) => {
|
||||
let isArchive = false;
|
||||
const features = item?.features as KeyValueDTOOfStringAndString[];
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
|
||||
if (!!features && Array.isArray(features)) {
|
||||
isArchive = !!features?.find(
|
||||
(feature) => feature?.enabled === true && feature?.key === 'ARC',
|
||||
);
|
||||
}
|
||||
return zip(
|
||||
this.store
|
||||
?.getPrice$(item?.id)
|
||||
.pipe(
|
||||
map((price) =>
|
||||
isArchive
|
||||
? !!price?.value?.value &&
|
||||
price?.vat !== undefined &&
|
||||
price?.vat?.value !== undefined
|
||||
: !!price?.value?.value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
switchMap((hasPrices) => hasPrices),
|
||||
map((hasPrices) => {
|
||||
const containsItemWithNoPrice =
|
||||
hasPrices?.filter((hasPrice) => hasPrice === false) ?? [];
|
||||
return containsItemWithNoPrice?.length === 0;
|
||||
}),
|
||||
);
|
||||
|
||||
purchasingOptions$ = this.store.getPurchaseOptionsInAvailabilities$;
|
||||
|
||||
isDownloadOnly$ = this.purchasingOptions$.pipe(
|
||||
map(
|
||||
(purchasingOptions) =>
|
||||
purchasingOptions.length === 1 && purchasingOptions[0] === 'download',
|
||||
),
|
||||
);
|
||||
|
||||
isGiftCardOnly$ = this.store.items$.pipe(
|
||||
map((items) => items.every((item) => isGiftCard(item, this.store.type))),
|
||||
);
|
||||
|
||||
hasDownload$ = this.purchasingOptions$.pipe(
|
||||
map((purchasingOptions) => purchasingOptions.includes('download')),
|
||||
);
|
||||
|
||||
canContinue$ = this.store.canContinue$;
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
saving = false;
|
||||
|
||||
constructor(
|
||||
private _uiModalRef: UiModalRef<string, PurchaseOptionsModalContext>,
|
||||
public store: PurchaseOptionsStore,
|
||||
) {
|
||||
this.store.initialize(this._uiModalRef.data);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.items$
|
||||
.pipe(takeUntil(this._onDestroy$), skip(1), delay(100))
|
||||
.subscribe((items) => {
|
||||
if (items.length === 0) {
|
||||
this._uiModalRef.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._uiModalRef.data?.preSelectOption?.option) {
|
||||
this.store.setPurchaseOption(
|
||||
this._uiModalRef.data?.preSelectOption?.option,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
itemTrackBy: TrackByFunction<Item> = (_, item) => item.id;
|
||||
|
||||
showOption(option: PurchaseOption): boolean {
|
||||
return this._uiModalRef.data?.preSelectOption?.showOptionOnly
|
||||
? this._uiModalRef.data?.preSelectOption?.option === option
|
||||
: true;
|
||||
}
|
||||
|
||||
async save(action: string) {
|
||||
if (this.saving) {
|
||||
return;
|
||||
}
|
||||
this.saving = true;
|
||||
|
||||
try {
|
||||
await this.store.save();
|
||||
|
||||
if (this.store.items.length === 0) {
|
||||
this._uiModalRef.close(action);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
this.saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import { ShoppingCartItemDTO, BranchDTO } from '@generated/swagger/checkout-api';
|
||||
import { ActionType, PurchaseOption } from './store';
|
||||
|
||||
export interface PurchaseOptionsModalData {
|
||||
processId: number;
|
||||
type: ActionType;
|
||||
items: Array<ItemDTO | ShoppingCartItemDTO>;
|
||||
pickupBranch?: BranchDTO;
|
||||
inStoreBranch?: BranchDTO;
|
||||
preSelectOption?: { option: PurchaseOption; showOptionOnly?: boolean };
|
||||
}
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
ShoppingCartItemDTO,
|
||||
BranchDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { Customer } from '@isa/crm/data-access';
|
||||
import { ActionType, PurchaseOption } from './store';
|
||||
|
||||
export interface PurchaseOptionsModalData {
|
||||
tabId: number;
|
||||
shoppingCartId: number;
|
||||
type: ActionType;
|
||||
items: Array<ItemDTO | ShoppingCartItemDTO>;
|
||||
pickupBranch?: BranchDTO;
|
||||
inStoreBranch?: BranchDTO;
|
||||
preSelectOption?: { option: PurchaseOption; showOptionOnly?: boolean };
|
||||
}
|
||||
|
||||
export interface PurchaseOptionsModalContext {
|
||||
shoppingCartId: number;
|
||||
type: ActionType;
|
||||
items: Array<ItemDTO | ShoppingCartItemDTO>;
|
||||
selectedCustomer?: Customer;
|
||||
selectedBranch?: BranchDTO;
|
||||
pickupBranch?: BranchDTO;
|
||||
inStoreBranch?: BranchDTO;
|
||||
preSelectOption?: { option: PurchaseOption; showOptionOnly?: boolean };
|
||||
}
|
||||
|
||||
@@ -1,16 +1,48 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UiModalRef, UiModalService } from '@ui/modal';
|
||||
import { PurchaseOptionsModalComponent } from './purchase-options-modal.component';
|
||||
import { PurchaseOptionsModalData } from './purchase-options-modal.data';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PurchaseOptionsModalService {
|
||||
constructor(private _uiModal: UiModalService) {}
|
||||
|
||||
open(data: PurchaseOptionsModalData): UiModalRef<string, PurchaseOptionsModalData> {
|
||||
return this._uiModal.open<string, PurchaseOptionsModalData>({
|
||||
content: PurchaseOptionsModalComponent,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { UiModalRef, UiModalService } from '@ui/modal';
|
||||
import { PurchaseOptionsModalComponent } from './purchase-options-modal.component';
|
||||
import {
|
||||
PurchaseOptionsModalData,
|
||||
PurchaseOptionsModalContext,
|
||||
} from './purchase-options-modal.data';
|
||||
import {
|
||||
SelectedCustomerFacade,
|
||||
CustomerFacade,
|
||||
Customer,
|
||||
} from '@isa/crm/data-access';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PurchaseOptionsModalService {
|
||||
#uiModal = inject(UiModalService);
|
||||
#selectedCustomerFacade = inject(SelectedCustomerFacade);
|
||||
#customerFacade = inject(CustomerFacade);
|
||||
|
||||
async open(
|
||||
data: PurchaseOptionsModalData,
|
||||
): Promise<UiModalRef<string, PurchaseOptionsModalData>> {
|
||||
const context: PurchaseOptionsModalContext = {
|
||||
...data,
|
||||
};
|
||||
|
||||
context.selectedCustomer = await this.#getSelectedCustomer(data);
|
||||
|
||||
return this.#uiModal.open<string, PurchaseOptionsModalContext>({
|
||||
content: PurchaseOptionsModalComponent,
|
||||
data: context,
|
||||
});
|
||||
}
|
||||
|
||||
#getSelectedCustomer({
|
||||
tabId,
|
||||
}: {
|
||||
tabId: number;
|
||||
}): Promise<Customer | undefined> {
|
||||
const customerId = this.#selectedCustomerFacade.get(tabId);
|
||||
|
||||
if (!customerId) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return this.#customerFacade.fetchCustomer({ customerId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,158 +1,180 @@
|
||||
import { PriceDTO } from '@generated/swagger/availability-api';
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import { AvailabilityDTO, OLAAvailabilityDTO, ShoppingCartItemDTO } from '@generated/swagger/checkout-api';
|
||||
import { GIFT_CARD_TYPE } from '../constants';
|
||||
import {
|
||||
ActionType,
|
||||
Item,
|
||||
ItemData,
|
||||
ItemPayloadWithSourceId,
|
||||
OrderType,
|
||||
PurchaseOption,
|
||||
} from './purchase-options.types';
|
||||
|
||||
export function isItemDTO(item: any, type: ActionType): item is ItemDTO {
|
||||
return type === 'add';
|
||||
}
|
||||
|
||||
export function isItemDTOArray(items: any, type: ActionType): items is ItemDTO[] {
|
||||
return type === 'add';
|
||||
}
|
||||
|
||||
export function isShoppingCartItemDTO(item: any, type: ActionType): item is ShoppingCartItemDTO {
|
||||
return type === 'update';
|
||||
}
|
||||
|
||||
export function isShoppingCartItemDTOArray(items: any, type: ActionType): items is ShoppingCartItemDTO[] {
|
||||
return type === 'update';
|
||||
}
|
||||
|
||||
export function mapToItemData(item: Item, type: ActionType): ItemData {
|
||||
const price: PriceDTO = {};
|
||||
|
||||
if (isItemDTO(item, type)) {
|
||||
price.value = item?.catalogAvailability?.price?.value ?? {};
|
||||
price.vat = item?.catalogAvailability?.price?.vat ?? {};
|
||||
|
||||
return {
|
||||
ean: item.product.ean,
|
||||
itemId: item.id,
|
||||
price,
|
||||
sourceId: item.id,
|
||||
quantity: item.quantity ?? 1,
|
||||
};
|
||||
} else {
|
||||
price.value = item?.unitPrice?.value ?? {};
|
||||
price.vat = item?.unitPrice?.vat ?? {};
|
||||
|
||||
return {
|
||||
ean: item.product.ean,
|
||||
itemId: Number(item.product.catalogProductNumber),
|
||||
price,
|
||||
sourceId: item.id,
|
||||
quantity: item.quantity ?? 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function isDownload(item: Item): boolean {
|
||||
return item.product.format === 'DL' || item.product.format === 'EB';
|
||||
}
|
||||
|
||||
export function isGiftCard(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.type === GIFT_CARD_TYPE;
|
||||
} else {
|
||||
return item?.itemType === GIFT_CARD_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
export function isArchive(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.features?.some((f) => f.key === 'ARC');
|
||||
} else {
|
||||
return !!item?.features?.['ARC'];
|
||||
}
|
||||
}
|
||||
|
||||
export function mapToItemPayload({
|
||||
item,
|
||||
quantity,
|
||||
availability,
|
||||
type,
|
||||
}: {
|
||||
item: ItemDTO | ShoppingCartItemDTO;
|
||||
quantity: number;
|
||||
availability: AvailabilityDTO;
|
||||
type: ActionType;
|
||||
}): ItemPayloadWithSourceId {
|
||||
return {
|
||||
availabilities: [mapToOlaAvailability({ item, quantity, availability, type })],
|
||||
id: String(getCatalogId(item, type)),
|
||||
sourceId: item.id,
|
||||
};
|
||||
}
|
||||
|
||||
export function getCatalogId(item: ItemDTO | ShoppingCartItemDTO, type: ActionType): number | string {
|
||||
return isItemDTO(item, type) ? item.id : item.product.catalogProductNumber;
|
||||
}
|
||||
|
||||
export function mapToOlaAvailability({
|
||||
availability,
|
||||
item,
|
||||
quantity,
|
||||
type,
|
||||
}: {
|
||||
availability: AvailabilityDTO;
|
||||
item: ItemDTO | ShoppingCartItemDTO;
|
||||
quantity: number;
|
||||
type: ActionType;
|
||||
}): OLAAvailabilityDTO {
|
||||
return {
|
||||
status: availability?.availabilityType,
|
||||
at: availability?.estimatedShippingDate,
|
||||
ean: item?.product?.ean,
|
||||
itemId: Number(getCatalogId(item, type)),
|
||||
format: item?.product?.format,
|
||||
isPrebooked: availability?.isPrebooked,
|
||||
logisticianId: availability?.logistician?.id,
|
||||
price: availability?.price,
|
||||
qty: quantity,
|
||||
ssc: availability?.ssc,
|
||||
sscText: availability?.sscText,
|
||||
supplierId: availability?.supplier?.id,
|
||||
supplierProductNumber: availability?.supplierProductNumber,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrderTypeForPurchaseOption(purchaseOption: PurchaseOption): OrderType | undefined {
|
||||
switch (purchaseOption) {
|
||||
case 'delivery':
|
||||
case 'dig-delivery':
|
||||
case 'b2b-delivery':
|
||||
return 'Versand';
|
||||
case 'pickup':
|
||||
return 'Abholung';
|
||||
case 'in-store':
|
||||
return 'Rücklage';
|
||||
case 'download':
|
||||
return 'Download';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getPurchaseOptionForOrderType(orderType: OrderType): PurchaseOption | undefined {
|
||||
switch (orderType) {
|
||||
case 'Versand':
|
||||
return 'delivery';
|
||||
case 'Abholung':
|
||||
return 'pickup';
|
||||
case 'Rücklage':
|
||||
return 'in-store';
|
||||
case 'Download':
|
||||
return 'download';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
import { PriceDTO } from '@generated/swagger/availability-api';
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
AvailabilityDTO,
|
||||
OLAAvailabilityDTO,
|
||||
ShoppingCartItemDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { GIFT_CARD_TYPE } from '../constants';
|
||||
import {
|
||||
ActionType,
|
||||
Item,
|
||||
ItemData,
|
||||
ItemPayloadWithSourceId,
|
||||
PurchaseOption,
|
||||
} from './purchase-options.types';
|
||||
import { OrderType } from '@isa/checkout/data-access';
|
||||
|
||||
export function isItemDTO(item: any, type: ActionType): item is ItemDTO {
|
||||
return type === 'add';
|
||||
}
|
||||
|
||||
export function isItemDTOArray(
|
||||
items: any,
|
||||
type: ActionType,
|
||||
): items is ItemDTO[] {
|
||||
return type === 'add';
|
||||
}
|
||||
|
||||
export function isShoppingCartItemDTO(
|
||||
item: any,
|
||||
type: ActionType,
|
||||
): item is ShoppingCartItemDTO {
|
||||
return type === 'update';
|
||||
}
|
||||
|
||||
export function isShoppingCartItemDTOArray(
|
||||
items: any,
|
||||
type: ActionType,
|
||||
): items is ShoppingCartItemDTO[] {
|
||||
return type === 'update';
|
||||
}
|
||||
|
||||
export function mapToItemData(item: Item, type: ActionType): ItemData {
|
||||
const price: PriceDTO = {};
|
||||
|
||||
if (isItemDTO(item, type)) {
|
||||
price.value = item?.catalogAvailability?.price?.value ?? {};
|
||||
price.vat = item?.catalogAvailability?.price?.vat ?? {};
|
||||
|
||||
return {
|
||||
ean: item.product.ean,
|
||||
itemId: item.id,
|
||||
price,
|
||||
sourceId: item.id,
|
||||
quantity: item.quantity ?? 1,
|
||||
};
|
||||
} else {
|
||||
price.value = item?.unitPrice?.value ?? {};
|
||||
price.vat = item?.unitPrice?.vat ?? {};
|
||||
|
||||
return {
|
||||
ean: item.product.ean,
|
||||
itemId: Number(item.product.catalogProductNumber),
|
||||
price,
|
||||
sourceId: item.id,
|
||||
quantity: item.quantity ?? 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function isDownload(item: Item): boolean {
|
||||
return item.product.format === 'DL' || item.product.format === 'EB';
|
||||
}
|
||||
|
||||
export function isGiftCard(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.type === GIFT_CARD_TYPE;
|
||||
} else {
|
||||
return item?.itemType === GIFT_CARD_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
export function isArchive(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.features?.some((f) => f.key === 'ARC');
|
||||
} else {
|
||||
return !!item?.features?.['ARC'];
|
||||
}
|
||||
}
|
||||
|
||||
export function mapToItemPayload({
|
||||
item,
|
||||
quantity,
|
||||
availability,
|
||||
type,
|
||||
}: {
|
||||
item: ItemDTO | ShoppingCartItemDTO;
|
||||
quantity: number;
|
||||
availability: AvailabilityDTO;
|
||||
type: ActionType;
|
||||
}): ItemPayloadWithSourceId {
|
||||
return {
|
||||
availabilities: [
|
||||
mapToOlaAvailability({ item, quantity, availability, type }),
|
||||
],
|
||||
id: String(getCatalogId(item, type)),
|
||||
sourceId: item.id,
|
||||
};
|
||||
}
|
||||
|
||||
export function getCatalogId(
|
||||
item: ItemDTO | ShoppingCartItemDTO,
|
||||
type: ActionType,
|
||||
): number | string {
|
||||
return isItemDTO(item, type) ? item.id : item.product.catalogProductNumber;
|
||||
}
|
||||
|
||||
export function mapToOlaAvailability({
|
||||
availability,
|
||||
item,
|
||||
quantity,
|
||||
type,
|
||||
}: {
|
||||
availability: AvailabilityDTO;
|
||||
item: ItemDTO | ShoppingCartItemDTO;
|
||||
quantity: number;
|
||||
type: ActionType;
|
||||
}): OLAAvailabilityDTO {
|
||||
return {
|
||||
status: availability?.availabilityType,
|
||||
at: availability?.estimatedShippingDate,
|
||||
ean: item?.product?.ean,
|
||||
itemId: Number(getCatalogId(item, type)),
|
||||
format: item?.product?.format,
|
||||
isPrebooked: availability?.isPrebooked,
|
||||
logisticianId: availability?.logistician?.id,
|
||||
price: availability?.price,
|
||||
qty: quantity,
|
||||
ssc: availability?.ssc,
|
||||
sscText: availability?.sscText,
|
||||
supplierId: availability?.supplier?.id,
|
||||
supplierProductNumber: availability?.supplierProductNumber,
|
||||
};
|
||||
}
|
||||
|
||||
export function getOrderTypeForPurchaseOption(
|
||||
purchaseOption: PurchaseOption,
|
||||
): OrderType | undefined {
|
||||
switch (purchaseOption) {
|
||||
case 'delivery':
|
||||
case 'dig-delivery':
|
||||
case 'b2b-delivery':
|
||||
return 'Versand';
|
||||
case 'pickup':
|
||||
return 'Abholung';
|
||||
case 'in-store':
|
||||
return 'Rücklage';
|
||||
case 'download':
|
||||
return 'Download';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function getPurchaseOptionForOrderType(
|
||||
orderType: OrderType,
|
||||
): PurchaseOption | undefined {
|
||||
switch (orderType) {
|
||||
case 'Versand':
|
||||
return 'delivery';
|
||||
case 'Abholung':
|
||||
return 'pickup';
|
||||
case 'Rücklage':
|
||||
return 'in-store';
|
||||
case 'Download':
|
||||
return 'download';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,178 +1,240 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import {
|
||||
AddToShoppingCartDTO,
|
||||
AvailabilityDTO,
|
||||
EntityDTOContainerOfDestinationDTO,
|
||||
ItemPayload,
|
||||
ItemsResult,
|
||||
ShoppingCartDTO,
|
||||
UpdateShoppingCartItemDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay, take } from 'rxjs/operators';
|
||||
import { Branch, ItemData } from './purchase-options.types';
|
||||
import { memorize } from '@utils/common';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PurchaseOptionsService {
|
||||
constructor(
|
||||
private _availabilityService: DomainAvailabilityService,
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _auth: AuthService,
|
||||
private _app: ApplicationService,
|
||||
) {}
|
||||
|
||||
getVats$() {
|
||||
return this._omsService.getVATs();
|
||||
}
|
||||
|
||||
getSelectedBranchForProcess(processId: number): Observable<Branch> {
|
||||
return this._app.getSelectedBranch$(processId).pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
getCustomerFeatures(processId: number): Observable<Record<string, string>> {
|
||||
return this._checkoutService.getCustomerFeatures({ processId }).pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
@memorize()
|
||||
fetchDefaultBranch(): Observable<Branch> {
|
||||
return this.getBranch({ branchNumber: this._auth.getClaimByKey('branch_no') }).pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
fetchPickupAvailability(item: ItemData, quantity: number, branch: Branch): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService
|
||||
.getPickUpAvailability({
|
||||
branch,
|
||||
quantity,
|
||||
item,
|
||||
})
|
||||
.pipe(map((res) => (Array.isArray(res) ? res[0] : undefined)));
|
||||
}
|
||||
|
||||
fetchInStoreAvailability(item: ItemData, quantity: number, branch: Branch): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getTakeAwayAvailability({
|
||||
item,
|
||||
quantity,
|
||||
branch,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDeliveryAvailability(item: ItemData, quantity: number): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDigDeliveryAvailability(item: ItemData, quantity: number): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDigDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchB2bDeliveryAvailability(item: ItemData, quantity: number): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getB2bDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDownloadAvailability(item: ItemData): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDownloadAvailability({
|
||||
item,
|
||||
});
|
||||
}
|
||||
|
||||
isAvailable(availability: AvailabilityDTO): boolean {
|
||||
return this._availabilityService.isAvailable({ availability });
|
||||
}
|
||||
|
||||
fetchCanAdd(processId: number, orderType: string, payload: ItemPayload[]): Observable<ItemsResult[]> {
|
||||
return this._checkoutService.canAddItems({
|
||||
processId,
|
||||
orderType,
|
||||
payload,
|
||||
});
|
||||
}
|
||||
|
||||
removeItemFromShoppingCart(processId: number, shoppingCartItemId: number): Promise<ShoppingCartDTO> {
|
||||
return this._checkoutService
|
||||
.updateItemInShoppingCart({
|
||||
processId,
|
||||
shoppingCartItemId,
|
||||
update: {
|
||||
availability: null,
|
||||
quantity: 0,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
getInStoreDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 1, targetBranch: { id: branch.id } },
|
||||
};
|
||||
}
|
||||
|
||||
getPickupDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 1, targetBranch: { id: branch.id } },
|
||||
};
|
||||
}
|
||||
|
||||
getDeliveryDestination(availability: AvailabilityDTO): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 2, logistician: availability?.logistician },
|
||||
};
|
||||
}
|
||||
|
||||
getDownloadDestination(availability: AvailabilityDTO): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 16, logistician: availability?.logistician },
|
||||
};
|
||||
}
|
||||
|
||||
addItemToShoppingCart(processId: number, items: AddToShoppingCartDTO[]) {
|
||||
return this._checkoutService.addItemToShoppingCart({
|
||||
processId,
|
||||
items,
|
||||
});
|
||||
}
|
||||
|
||||
updateItemInShoppingCart(processId: number, shoppingCartItemId: number, payload: UpdateShoppingCartItemDTO) {
|
||||
return this._checkoutService.updateItemInShoppingCart({
|
||||
processId,
|
||||
shoppingCartItemId,
|
||||
update: payload,
|
||||
});
|
||||
}
|
||||
|
||||
@memorize({ comparer: (_) => true })
|
||||
getBranches(): Observable<Branch[]> {
|
||||
return this._availabilityService.getBranches().pipe(
|
||||
map((branches) => {
|
||||
return branches.filter((branch) => branch.isShippingEnabled == true);
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
getBranch(params: { id: number }): Observable<Branch>;
|
||||
getBranch(params: { branchNumber: string }): Observable<Branch>;
|
||||
getBranch(params: { id: number; branchNumber: string }): Observable<Branch>;
|
||||
getBranch(params: { id?: number; branchNumber?: string }): Observable<Branch> {
|
||||
return this.getBranches().pipe(
|
||||
map((branches) => {
|
||||
const branch = branches.find((branch) => branch.id == params.id || branch.branchNumber == params.branchNumber);
|
||||
return branch;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import {
|
||||
AddToShoppingCartDTO,
|
||||
AvailabilityDTO,
|
||||
EntityDTOContainerOfDestinationDTO,
|
||||
ItemPayload,
|
||||
ItemsResult,
|
||||
ShoppingCartDTO,
|
||||
UpdateShoppingCartItemDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay, take } from 'rxjs/operators';
|
||||
import { Branch, ItemData } from './purchase-options.types';
|
||||
import { memorize } from '@utils/common';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { OrderType, PurchaseOptionsFacade } from '@isa/checkout/data-access';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PurchaseOptionsService {
|
||||
#purchaseOptionsFacade = inject(PurchaseOptionsFacade);
|
||||
|
||||
constructor(
|
||||
private _availabilityService: DomainAvailabilityService,
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _auth: AuthService,
|
||||
private _app: ApplicationService,
|
||||
) {}
|
||||
|
||||
getVats$() {
|
||||
return this._omsService.getVATs();
|
||||
}
|
||||
|
||||
getSelectedBranchForProcess(processId: number): Observable<Branch> {
|
||||
return this._app
|
||||
.getSelectedBranch$(processId)
|
||||
.pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
getCustomerFeatures(processId: number): Observable<Record<string, string>> {
|
||||
return this._checkoutService
|
||||
.getCustomerFeatures({ processId })
|
||||
.pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
@memorize()
|
||||
fetchDefaultBranch(): Observable<Branch> {
|
||||
return this.getBranch({
|
||||
branchNumber: this._auth.getClaimByKey('branch_no'),
|
||||
}).pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
fetchPickupAvailability(
|
||||
item: ItemData,
|
||||
quantity: number,
|
||||
branch: Branch,
|
||||
): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService
|
||||
.getPickUpAvailability({
|
||||
branch,
|
||||
quantity,
|
||||
item,
|
||||
})
|
||||
.pipe(map((res) => (Array.isArray(res) ? res[0] : undefined)));
|
||||
}
|
||||
|
||||
fetchInStoreAvailability(
|
||||
item: ItemData,
|
||||
quantity: number,
|
||||
branch: Branch,
|
||||
): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getTakeAwayAvailability({
|
||||
item,
|
||||
quantity,
|
||||
branch,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDeliveryAvailability(
|
||||
item: ItemData,
|
||||
quantity: number,
|
||||
): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDigDeliveryAvailability(
|
||||
item: ItemData,
|
||||
quantity: number,
|
||||
): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDigDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchB2bDeliveryAvailability(
|
||||
item: ItemData,
|
||||
quantity: number,
|
||||
): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getB2bDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
});
|
||||
}
|
||||
|
||||
fetchDownloadAvailability(item: ItemData): Observable<AvailabilityDTO> {
|
||||
return this._availabilityService.getDownloadAvailability({
|
||||
item,
|
||||
});
|
||||
}
|
||||
|
||||
isAvailable(availability: AvailabilityDTO): boolean {
|
||||
return this._availabilityService.isAvailable({ availability });
|
||||
}
|
||||
|
||||
fetchCanAdd(
|
||||
shoppingCartId: number,
|
||||
orderType: OrderType,
|
||||
payload: ItemPayload[],
|
||||
customerFeatures: Record<string, string>,
|
||||
): Promise<ItemsResult[]> {
|
||||
return this.#purchaseOptionsFacade.canAddItems({
|
||||
shoppingCartId,
|
||||
payload: payload.map((p) => ({
|
||||
...p,
|
||||
customerFeatures: customerFeatures,
|
||||
orderType: orderType,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
removeItemFromShoppingCart(
|
||||
shoppingCartId: number,
|
||||
shoppingCartItemId: number,
|
||||
): Promise<ShoppingCartDTO> {
|
||||
const shoppingCart = this.#purchaseOptionsFacade.removeItem({
|
||||
shoppingCartId,
|
||||
shoppingCartItemId,
|
||||
});
|
||||
|
||||
return shoppingCart;
|
||||
}
|
||||
|
||||
getInStoreDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 1, targetBranch: { id: branch.id } },
|
||||
};
|
||||
}
|
||||
|
||||
getPickupDestination(branch: Branch): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 1, targetBranch: { id: branch.id } },
|
||||
};
|
||||
}
|
||||
|
||||
getDeliveryDestination(
|
||||
availability: AvailabilityDTO,
|
||||
): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 2, logistician: availability?.logistician },
|
||||
};
|
||||
}
|
||||
|
||||
getDownloadDestination(
|
||||
availability: AvailabilityDTO,
|
||||
): EntityDTOContainerOfDestinationDTO {
|
||||
return {
|
||||
data: { target: 16, logistician: availability?.logistician },
|
||||
};
|
||||
}
|
||||
|
||||
async addItemToShoppingCart(
|
||||
shoppingCartId: number,
|
||||
items: AddToShoppingCartDTO[],
|
||||
) {
|
||||
const shoppingCart = await this.#purchaseOptionsFacade.addItem({
|
||||
shoppingCartId,
|
||||
items,
|
||||
});
|
||||
console.log('added item to cart', { shoppingCart });
|
||||
this._checkoutService.updateProcessCount(
|
||||
this._app.activatedProcessId,
|
||||
shoppingCart,
|
||||
);
|
||||
return shoppingCart;
|
||||
}
|
||||
|
||||
async updateItemInShoppingCart(
|
||||
shoppingCartId: number,
|
||||
shoppingCartItemId: number,
|
||||
payload: UpdateShoppingCartItemDTO,
|
||||
) {
|
||||
const shoppingCart = await this.#purchaseOptionsFacade.updateItem({
|
||||
shoppingCartId,
|
||||
shoppingCartItemId,
|
||||
values: payload,
|
||||
});
|
||||
console.log('updated item in cart', { shoppingCart });
|
||||
this._checkoutService.updateProcessCount(
|
||||
this._app.activatedProcessId,
|
||||
shoppingCart,
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ comparer: (_) => true })
|
||||
getBranches(): Observable<Branch[]> {
|
||||
return this._availabilityService.getBranches().pipe(
|
||||
map((branches) => {
|
||||
return branches.filter((branch) => branch.isShippingEnabled == true);
|
||||
}),
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
getBranch(params: { id: number }): Observable<Branch>;
|
||||
getBranch(params: { branchNumber: string }): Observable<Branch>;
|
||||
getBranch(params: { id: number; branchNumber: string }): Observable<Branch>;
|
||||
getBranch(params: {
|
||||
id?: number;
|
||||
branchNumber?: string;
|
||||
}): Observable<Branch> {
|
||||
return this.getBranches().pipe(
|
||||
map((branches) => {
|
||||
const branch = branches.find(
|
||||
(branch) =>
|
||||
branch.id == params.id ||
|
||||
branch.branchNumber == params.branchNumber,
|
||||
);
|
||||
return branch;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import { PriceDTO } from '@generated/swagger/checkout-api';
|
||||
import {
|
||||
ActionType,
|
||||
Availability,
|
||||
Branch,
|
||||
CanAdd,
|
||||
FetchingAvailability,
|
||||
Item,
|
||||
PurchaseOption,
|
||||
} from './purchase-options.types';
|
||||
|
||||
export interface PurchaseOptionsState {
|
||||
type: ActionType;
|
||||
|
||||
processId: number;
|
||||
|
||||
items: Item[];
|
||||
|
||||
availabilities: Availability[];
|
||||
|
||||
canAddResults: CanAdd[];
|
||||
|
||||
purchaseOption: PurchaseOption;
|
||||
|
||||
selectedItemIds: number[];
|
||||
|
||||
prices: { [itemId: number]: PriceDTO };
|
||||
|
||||
defaultBranch: Branch;
|
||||
|
||||
pickupBranch: Branch;
|
||||
|
||||
inStoreBranch: Branch;
|
||||
|
||||
customerFeatures: Record<string, string>;
|
||||
|
||||
fetchingAvailabilities: Array<FetchingAvailability>;
|
||||
}
|
||||
import { PriceDTO } from '@generated/swagger/checkout-api';
|
||||
import {
|
||||
ActionType,
|
||||
Availability,
|
||||
Branch,
|
||||
CanAdd,
|
||||
FetchingAvailability,
|
||||
Item,
|
||||
PurchaseOption,
|
||||
} from './purchase-options.types';
|
||||
|
||||
export interface PurchaseOptionsState {
|
||||
shoppingCartId: number;
|
||||
|
||||
type: ActionType;
|
||||
|
||||
items: Item[];
|
||||
|
||||
availabilities: Availability[];
|
||||
|
||||
canAddResults: CanAdd[];
|
||||
|
||||
purchaseOption: PurchaseOption;
|
||||
|
||||
selectedItemIds: number[];
|
||||
|
||||
prices: { [itemId: number]: PriceDTO };
|
||||
|
||||
defaultBranch: Branch;
|
||||
|
||||
pickupBranch: Branch;
|
||||
|
||||
inStoreBranch: Branch;
|
||||
|
||||
customerFeatures: Record<string, string>;
|
||||
|
||||
fetchingAvailabilities: Array<FetchingAvailability>;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,56 @@
|
||||
import { ItemData as AvailabilityItemData } from '@domain/availability';
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import { AvailabilityDTO, BranchDTO, ItemPayload, ShoppingCartItemDTO } from '@generated/swagger/checkout-api';
|
||||
|
||||
export type ActionType = 'add' | 'update';
|
||||
|
||||
export type PurchaseOption =
|
||||
| 'delivery'
|
||||
| 'dig-delivery'
|
||||
| 'b2b-delivery'
|
||||
| 'pickup'
|
||||
| 'in-store'
|
||||
| 'download'
|
||||
| 'catalog';
|
||||
|
||||
export type OrderType = 'Rücklage' | 'Abholung' | 'Versand' | 'Download';
|
||||
|
||||
export type ItemDTOWithQuantity = ItemDTO & { quantity?: number };
|
||||
|
||||
export type Item = ItemDTOWithQuantity | ShoppingCartItemDTO;
|
||||
|
||||
export type Branch = BranchDTO;
|
||||
|
||||
export type Availability = {
|
||||
itemId: number;
|
||||
purchaseOption: PurchaseOption;
|
||||
data: AvailabilityDTO & { priceMaintained?: boolean; orderDeadline?: string; firstDayOfSale?: string };
|
||||
ean?: string;
|
||||
};
|
||||
|
||||
export type ItemData = AvailabilityItemData & { sourceId: number; quantity: number };
|
||||
|
||||
export type ItemPayloadWithSourceId = ItemPayload & { sourceId: number };
|
||||
|
||||
export type CanAdd = { itemId: number; purchaseOption: PurchaseOption; canAdd: boolean; message?: string };
|
||||
|
||||
export type FetchingAvailability = { id: string; itemId: number; purchaseOption?: PurchaseOption };
|
||||
import { ItemData as AvailabilityItemData } from '@domain/availability';
|
||||
import { ItemDTO } from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
AvailabilityDTO,
|
||||
BranchDTO,
|
||||
ItemPayload,
|
||||
ShoppingCartItemDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
|
||||
export type ActionType = 'add' | 'update';
|
||||
|
||||
export type PurchaseOption =
|
||||
| 'delivery'
|
||||
| 'dig-delivery'
|
||||
| 'b2b-delivery'
|
||||
| 'pickup'
|
||||
| 'in-store'
|
||||
| 'download'
|
||||
| 'catalog';
|
||||
|
||||
export type ItemDTOWithQuantity = ItemDTO & { quantity?: number };
|
||||
|
||||
export type Item = ItemDTOWithQuantity | ShoppingCartItemDTO;
|
||||
|
||||
export type Branch = BranchDTO;
|
||||
|
||||
export type Availability = {
|
||||
itemId: number;
|
||||
purchaseOption: PurchaseOption;
|
||||
data: AvailabilityDTO & {
|
||||
priceMaintained?: boolean;
|
||||
orderDeadline?: string;
|
||||
firstDayOfSale?: string;
|
||||
};
|
||||
ean?: string;
|
||||
};
|
||||
|
||||
export type ItemData = AvailabilityItemData & {
|
||||
sourceId: number;
|
||||
quantity: number;
|
||||
};
|
||||
|
||||
export type ItemPayloadWithSourceId = ItemPayload & { sourceId: number };
|
||||
|
||||
export type CanAdd = {
|
||||
itemId: number;
|
||||
purchaseOption: PurchaseOption;
|
||||
canAdd: boolean;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export type FetchingAvailability = {
|
||||
id: string;
|
||||
itemId: number;
|
||||
purchaseOption?: PurchaseOption;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,185 +1,237 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { tapResponse } from '@ngrx/operators';
|
||||
|
||||
import { NotificationChannel, PayerDTO, ShoppingCartDTO, ShoppingCartItemDTO } from '@generated/swagger/checkout-api';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { first, map, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface CheckoutReviewState {
|
||||
payer: PayerDTO;
|
||||
shoppingCart: ShoppingCartDTO;
|
||||
shoppingCartItems: ShoppingCartItemDTO[];
|
||||
fetching: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CheckoutReviewStore extends ComponentStore<CheckoutReviewState> {
|
||||
orderCompleted = new Subject<void>();
|
||||
|
||||
get shoppingCart() {
|
||||
return this.get((s) => s.shoppingCart);
|
||||
}
|
||||
set shoppingCart(shoppingCart: ShoppingCartDTO) {
|
||||
this.patchState({ shoppingCart });
|
||||
}
|
||||
readonly shoppingCart$ = this.select((s) => s.shoppingCart);
|
||||
|
||||
get shoppingCartItems() {
|
||||
return this.get((s) => s.shoppingCartItems);
|
||||
}
|
||||
set shoppingCartItems(shoppingCartItems: ShoppingCartItemDTO[]) {
|
||||
this.patchState({ shoppingCartItems });
|
||||
}
|
||||
readonly shoppingCartItems$ = this.select((s) => s.shoppingCartItems);
|
||||
|
||||
get fetching() {
|
||||
return this.get((s) => s.fetching);
|
||||
}
|
||||
set fetching(fetching: boolean) {
|
||||
this.patchState({ fetching });
|
||||
}
|
||||
readonly fetching$ = this.select((s) => s.fetching);
|
||||
|
||||
customerFeatures$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) => this._domainCheckoutService.getCustomerFeatures({ processId })),
|
||||
);
|
||||
|
||||
payer$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) => this._domainCheckoutService.getPayer({ processId })),
|
||||
);
|
||||
|
||||
buyer$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) => this._domainCheckoutService.getBuyer({ processId })),
|
||||
);
|
||||
|
||||
showBillingAddress$ = this.shoppingCartItems$.pipe(
|
||||
withLatestFrom(this.customerFeatures$),
|
||||
map(
|
||||
([items, customerFeatures]) =>
|
||||
items.some(
|
||||
(item) =>
|
||||
item.features?.orderType === 'Versand' ||
|
||||
item.features?.orderType === 'B2B-Versand' ||
|
||||
item.features?.orderType === 'DIG-Versand',
|
||||
) || !!customerFeatures?.b2b,
|
||||
),
|
||||
);
|
||||
|
||||
checkNotificationChannelControl$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
notificationChannelLoading$ = new Subject<boolean>();
|
||||
|
||||
notificationsControl: UntypedFormGroup;
|
||||
|
||||
constructor(
|
||||
private _domainCheckoutService: DomainCheckoutService,
|
||||
private _application: ApplicationService,
|
||||
private _uiModal: UiModalService,
|
||||
) {
|
||||
super({ payer: undefined, shoppingCart: undefined, shoppingCartItems: [], fetching: false });
|
||||
}
|
||||
|
||||
loadShoppingCart = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => (this.fetching = true)),
|
||||
withLatestFrom(this._application.activatedProcessId$),
|
||||
switchMap(([_, processId]) => {
|
||||
return this._domainCheckoutService.getShoppingCart({ processId, latest: true }).pipe(
|
||||
tapResponse(
|
||||
(shoppingCart) => {
|
||||
const shoppingCartItems = shoppingCart?.items?.map((item) => item.data) || [];
|
||||
this.patchState({
|
||||
shoppingCart,
|
||||
shoppingCartItems,
|
||||
});
|
||||
},
|
||||
(err) => {},
|
||||
() => {},
|
||||
),
|
||||
);
|
||||
}),
|
||||
tap(() => (this.fetching = false)),
|
||||
),
|
||||
);
|
||||
|
||||
async onNotificationChange(notificationChannels?: NotificationChannel[]) {
|
||||
this.notificationChannelLoading$.next(true);
|
||||
|
||||
try {
|
||||
const control = this.notificationsControl?.getRawValue();
|
||||
const notificationChannel = notificationChannels
|
||||
? (notificationChannels.reduce((val, current) => val | current, 0) as NotificationChannel)
|
||||
: control?.notificationChannel?.selected || 0;
|
||||
const processId = await this._application.activatedProcessId$.pipe(first()).toPromise();
|
||||
const email = control?.notificationChannel?.email;
|
||||
const mobile = control?.notificationChannel?.mobile;
|
||||
|
||||
// Check if E-Mail and Mobilnumber is available if E-Mail or SMS checkbox is active
|
||||
if (notificationChannel === 3 && (!email || !mobile)) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else if (notificationChannel === 2 && !mobile) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else if (notificationChannel === 1 && !email) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else {
|
||||
this.checkNotificationChannelControl$.next(true);
|
||||
}
|
||||
|
||||
// NotificationChannel nur speichern, wenn Haken und Value gesetzt
|
||||
let setNotificationChannel = 0;
|
||||
if ((notificationChannel & 1) === 1 && email) {
|
||||
setNotificationChannel += 1;
|
||||
}
|
||||
if ((notificationChannel & 2) === 2 && mobile) {
|
||||
setNotificationChannel += 2;
|
||||
}
|
||||
|
||||
if (notificationChannel > 0) {
|
||||
this.setCommunicationDetails({ processId, notificationChannel, email, mobile });
|
||||
}
|
||||
this._domainCheckoutService.setNotificationChannels({
|
||||
processId,
|
||||
notificationChannels: (setNotificationChannel as NotificationChannel) || 0,
|
||||
});
|
||||
} catch (error) {
|
||||
this._uiModal.open({
|
||||
content: UiErrorModalComponent,
|
||||
data: error,
|
||||
title: 'Fehler beim setzen des Benachrichtigungskanals',
|
||||
});
|
||||
}
|
||||
|
||||
this.notificationChannelLoading$.next(false);
|
||||
}
|
||||
|
||||
setCommunicationDetails({
|
||||
processId,
|
||||
notificationChannel,
|
||||
email,
|
||||
mobile,
|
||||
}: {
|
||||
processId: number;
|
||||
notificationChannel: number;
|
||||
email: string;
|
||||
mobile: string;
|
||||
}) {
|
||||
const emailValid = this.notificationsControl?.get('notificationChannel')?.get('email')?.valid;
|
||||
const mobileValid = this.notificationsControl?.get('notificationChannel')?.get('mobile')?.valid;
|
||||
|
||||
if (notificationChannel === 3 && emailValid && mobileValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({ processId, email, mobile });
|
||||
} else if (notificationChannel === 1 && emailValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({ processId, email });
|
||||
} else if (notificationChannel === 2 && mobileValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({ processId, mobile });
|
||||
}
|
||||
}
|
||||
}
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { tapResponse } from '@ngrx/operators';
|
||||
|
||||
import {
|
||||
NotificationChannel,
|
||||
PayerDTO,
|
||||
ShoppingCartDTO,
|
||||
ShoppingCartItemDTO,
|
||||
} from '@generated/swagger/checkout-api';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import {
|
||||
first,
|
||||
map,
|
||||
switchMap,
|
||||
takeUntil,
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
export interface CheckoutReviewState {
|
||||
payer: PayerDTO;
|
||||
shoppingCart: ShoppingCartDTO;
|
||||
shoppingCartItems: ShoppingCartItemDTO[];
|
||||
fetching: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CheckoutReviewStore extends ComponentStore<CheckoutReviewState> {
|
||||
orderCompleted = new Subject<void>();
|
||||
|
||||
get shoppingCart() {
|
||||
return this.get((s) => s.shoppingCart);
|
||||
}
|
||||
set shoppingCart(shoppingCart: ShoppingCartDTO) {
|
||||
this.patchState({ shoppingCart });
|
||||
}
|
||||
readonly shoppingCart$ = this.select((s) => s.shoppingCart);
|
||||
|
||||
get shoppingCartItems() {
|
||||
return this.get((s) => s.shoppingCartItems);
|
||||
}
|
||||
set shoppingCartItems(shoppingCartItems: ShoppingCartItemDTO[]) {
|
||||
this.patchState({ shoppingCartItems });
|
||||
}
|
||||
readonly shoppingCartItems$ = this.select((s) => s.shoppingCartItems);
|
||||
|
||||
get fetching() {
|
||||
return this.get((s) => s.fetching);
|
||||
}
|
||||
set fetching(fetching: boolean) {
|
||||
this.patchState({ fetching });
|
||||
}
|
||||
readonly fetching$ = this.select((s) => s.fetching);
|
||||
|
||||
customerFeatures$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) =>
|
||||
this._domainCheckoutService.getCustomerFeatures({ processId }),
|
||||
),
|
||||
);
|
||||
|
||||
payer$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) =>
|
||||
this._domainCheckoutService.getPayer({ processId }),
|
||||
),
|
||||
);
|
||||
|
||||
buyer$ = this._application.activatedProcessId$.pipe(
|
||||
takeUntil(this.orderCompleted),
|
||||
switchMap((processId) =>
|
||||
this._domainCheckoutService.getBuyer({ processId }),
|
||||
),
|
||||
);
|
||||
|
||||
showBillingAddress$ = this.shoppingCartItems$.pipe(
|
||||
withLatestFrom(this.customerFeatures$),
|
||||
map(
|
||||
([items, customerFeatures]) =>
|
||||
items.some(
|
||||
(item) =>
|
||||
item.features?.orderType === 'Versand' ||
|
||||
item.features?.orderType === 'B2B-Versand' ||
|
||||
item.features?.orderType === 'DIG-Versand',
|
||||
) || !!customerFeatures?.b2b,
|
||||
),
|
||||
);
|
||||
|
||||
checkNotificationChannelControl$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
notificationChannelLoading$ = new Subject<boolean>();
|
||||
|
||||
notificationsControl: UntypedFormGroup;
|
||||
|
||||
constructor(
|
||||
private _domainCheckoutService: DomainCheckoutService,
|
||||
private _application: ApplicationService,
|
||||
private _uiModal: UiModalService,
|
||||
) {
|
||||
super({
|
||||
payer: undefined,
|
||||
shoppingCart: undefined,
|
||||
shoppingCartItems: [],
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
|
||||
loadShoppingCart = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => (this.fetching = true)),
|
||||
withLatestFrom(this._application.activatedProcessId$),
|
||||
switchMap(([_, processId]) => {
|
||||
return this._domainCheckoutService
|
||||
.getShoppingCart({ processId, latest: true })
|
||||
.pipe(
|
||||
tapResponse(
|
||||
(shoppingCart) => {
|
||||
console.log('Loaded shopping cart', { shoppingCart });
|
||||
const shoppingCartItems =
|
||||
shoppingCart?.items?.map((item) => item.data) || [];
|
||||
this.patchState({
|
||||
shoppingCart,
|
||||
shoppingCartItems,
|
||||
});
|
||||
},
|
||||
(err) => {},
|
||||
() => {},
|
||||
),
|
||||
);
|
||||
}),
|
||||
tap(() => (this.fetching = false)),
|
||||
),
|
||||
);
|
||||
|
||||
async onNotificationChange(notificationChannels?: NotificationChannel[]) {
|
||||
this.notificationChannelLoading$.next(true);
|
||||
|
||||
try {
|
||||
const control = this.notificationsControl?.getRawValue();
|
||||
const notificationChannel = notificationChannels
|
||||
? (notificationChannels.reduce(
|
||||
(val, current) => val | current,
|
||||
0,
|
||||
) as NotificationChannel)
|
||||
: control?.notificationChannel?.selected || 0;
|
||||
const processId = await this._application.activatedProcessId$
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const email = control?.notificationChannel?.email;
|
||||
const mobile = control?.notificationChannel?.mobile;
|
||||
|
||||
// Check if E-Mail and Mobilnumber is available if E-Mail or SMS checkbox is active
|
||||
if (notificationChannel === 3 && (!email || !mobile)) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else if (notificationChannel === 2 && !mobile) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else if (notificationChannel === 1 && !email) {
|
||||
this.checkNotificationChannelControl$.next(false);
|
||||
} else {
|
||||
this.checkNotificationChannelControl$.next(true);
|
||||
}
|
||||
|
||||
// NotificationChannel nur speichern, wenn Haken und Value gesetzt
|
||||
let setNotificationChannel = 0;
|
||||
if ((notificationChannel & 1) === 1 && email) {
|
||||
setNotificationChannel += 1;
|
||||
}
|
||||
if ((notificationChannel & 2) === 2 && mobile) {
|
||||
setNotificationChannel += 2;
|
||||
}
|
||||
|
||||
if (notificationChannel > 0) {
|
||||
this.setCommunicationDetails({
|
||||
processId,
|
||||
notificationChannel,
|
||||
email,
|
||||
mobile,
|
||||
});
|
||||
}
|
||||
this._domainCheckoutService.setNotificationChannels({
|
||||
processId,
|
||||
notificationChannels:
|
||||
(setNotificationChannel as NotificationChannel) || 0,
|
||||
});
|
||||
} catch (error) {
|
||||
this._uiModal.open({
|
||||
content: UiErrorModalComponent,
|
||||
data: error,
|
||||
title: 'Fehler beim setzen des Benachrichtigungskanals',
|
||||
});
|
||||
}
|
||||
|
||||
this.notificationChannelLoading$.next(false);
|
||||
}
|
||||
|
||||
setCommunicationDetails({
|
||||
processId,
|
||||
notificationChannel,
|
||||
email,
|
||||
mobile,
|
||||
}: {
|
||||
processId: number;
|
||||
notificationChannel: number;
|
||||
email: string;
|
||||
mobile: string;
|
||||
}) {
|
||||
const emailValid = this.notificationsControl
|
||||
?.get('notificationChannel')
|
||||
?.get('email')?.valid;
|
||||
const mobileValid = this.notificationsControl
|
||||
?.get('notificationChannel')
|
||||
?.get('mobile')?.valid;
|
||||
|
||||
if (notificationChannel === 3 && emailValid && mobileValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({
|
||||
processId,
|
||||
email,
|
||||
mobile,
|
||||
});
|
||||
} else if (notificationChannel === 1 && emailValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({
|
||||
processId,
|
||||
email,
|
||||
});
|
||||
} else if (notificationChannel === 2 && mobileValid) {
|
||||
this._domainCheckoutService.setBuyerCommunicationDetails({
|
||||
processId,
|
||||
mobile,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
(click)="$event?.preventDefault(); $event?.stopPropagation()"
|
||||
>
|
||||
<shared-icon icon="shopping-cart-bold" [size]="22"></shared-icon>
|
||||
<span class="shopping-cart-count-label ml-2">{{
|
||||
cartItemCount$ | async
|
||||
}}</span>
|
||||
<span class="shopping-cart-count-label ml-2">{{ cartCount() }}</span>
|
||||
</button>
|
||||
}
|
||||
</a>
|
||||
|
||||
@@ -63,6 +63,14 @@ export class ShellProcessBarItemComponent
|
||||
return 'count' in pdata;
|
||||
});
|
||||
|
||||
cartCount = computed(() => {
|
||||
const tab = this.tab();
|
||||
|
||||
const pdata = tab.metadata?.process_data as { count?: number };
|
||||
|
||||
return pdata?.count ?? 0;
|
||||
});
|
||||
|
||||
currentLocationUrlTree = computed(() => {
|
||||
const tab = this.tab();
|
||||
const current = tab.location.locations[tab.location.current];
|
||||
@@ -112,7 +120,6 @@ export class ShellProcessBarItemComponent
|
||||
this.initQueryParams$();
|
||||
this.initIsActive$();
|
||||
this.initShowCloseButton$();
|
||||
this.initCartItemCount$();
|
||||
}
|
||||
|
||||
scrollIntoView() {
|
||||
@@ -171,15 +178,6 @@ export class ShellProcessBarItemComponent
|
||||
}
|
||||
}
|
||||
|
||||
initCartItemCount$() {
|
||||
this.cartItemCount$ = this.process$.pipe(
|
||||
switchMap((process) =>
|
||||
this._checkout?.getShoppingCart({ processId: process?.id }),
|
||||
),
|
||||
map((cart) => cart?.items?.length ?? 0),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._process$.complete();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user