hotfix(customer-card-deactivate): Hotfix due to Keycard ISA Login Error

This commit is contained in:
Nino
2025-10-24 11:18:25 +02:00
parent 1b6b726036
commit 353864e2f0
25 changed files with 1060 additions and 786 deletions

View File

@@ -1,4 +1,4 @@
<ng-container *ifRole="'Store'">
<!-- <ng-container *ifRole="'Store'">
@if (customerType !== 'b2b') {
<shared-checkbox
[ngModel]="p4mUser"
@@ -8,15 +8,17 @@
Kundenkarte
</shared-checkbox>
}
</ng-container>
</ng-container> -->
@for (option of filteredOptions$ | async; track option) {
@if (option?.enabled !== false) {
<shared-checkbox
[ngModel]="option.value === customerType"
(ngModelChange)="setValue({ customerType: $event ? option.value : undefined })"
(ngModelChange)="
setValue({ customerType: $event ? option.value : undefined })
"
[disabled]="isOptionDisabled(option)"
[name]="option.value"
>
>
{{ option.label }}
</shared-checkbox>
}

View File

@@ -21,7 +21,13 @@ import { OptionDTO } from '@generated/swagger/checkout-api';
import { UiCheckboxComponent } from '@ui/checkbox';
import { first, isBoolean, isString } from 'lodash';
import { combineLatest, Observable, Subject } from 'rxjs';
import { distinctUntilChanged, filter, map, shareReplay, switchMap } from 'rxjs/operators';
import {
distinctUntilChanged,
filter,
map,
shareReplay,
switchMap,
} from 'rxjs/operators';
export interface CustomerTypeSelectorState {
processId: number;
@@ -58,18 +64,18 @@ export class CustomerTypeSelectorComponent
@Input()
get value() {
if (this.p4mUser) {
return `${this.customerType}-p4m`;
}
// if (this.p4mUser) {
// return `${this.customerType}-p4m`;
// }
return this.customerType;
}
set value(value: string) {
if (value.includes('-p4m')) {
this.p4mUser = true;
this.customerType = value.replace('-p4m', '');
} else {
this.customerType = value;
}
// if (value.includes('-p4m')) {
// this.p4mUser = true;
// this.customerType = value.replace('-p4m', '');
// } else {
this.customerType = value;
// }
}
@Output()
@@ -111,30 +117,35 @@ export class CustomerTypeSelectorComponent
get filteredOptions$() {
const options$ = this.select((s) => s.options).pipe(distinctUntilChanged());
const p4mUser$ = this.select((s) => s.p4mUser).pipe(distinctUntilChanged());
const customerType$ = this.select((s) => s.customerType).pipe(distinctUntilChanged());
const customerType$ = this.select((s) => s.customerType).pipe(
distinctUntilChanged(),
);
return combineLatest([options$, p4mUser$, customerType$]).pipe(
filter(([options]) => options?.length > 0),
map(([options, p4mUser, customerType]) => {
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
const initial = {
p4mUser: this.p4mUser,
customerType: this.customerType,
};
let result: OptionDTO[] = options;
if (p4mUser) {
result = result.filter((o) => o.value === 'store' || (o.value === 'webshop' && o.enabled !== false));
// if (p4mUser) {
// result = result.filter((o) => o.value === 'store' || (o.value === 'webshop' && o.enabled !== false));
result = result.map((o) => {
if (o.value === 'store') {
return { ...o, enabled: false };
}
return o;
});
}
// result = result.map((o) => {
// if (o.value === 'store') {
// return { ...o, enabled: false };
// }
// return o;
// });
// }
if (customerType === 'b2b' && this.p4mUser) {
this.p4mUser = false;
}
if (initial.p4mUser !== this.p4mUser || initial.customerType !== this.customerType) {
this.setValue({ customerType: this.customerType, p4mUser: this.p4mUser });
}
// if (initial.p4mUser !== this.p4mUser || initial.customerType !== this.customerType) {
// this.setValue({ customerType: this.customerType, p4mUser: this.p4mUser });
// }
return result;
}),
@@ -224,42 +235,53 @@ export class CustomerTypeSelectorComponent
if (typeof value === 'string') {
this.value = value;
} else {
if (isBoolean(value.p4mUser)) {
this.p4mUser = value.p4mUser;
}
// if (isBoolean(value.p4mUser)) {
// this.p4mUser = value.p4mUser;
// }
if (isString(value.customerType)) {
this.customerType = value.customerType;
} else if (this.p4mUser) {
// Implementierung wie im PBI #3467 beschrieben
// wenn customerType nicht gesetzt wird und p4mUser true ist,
// dann customerType auf store setzen.
// wenn dies nicht möglich ist da der Warenkob keinen store Kunden zulässt,
// dann customerType auf webshop setzen.
// wenn dies nicht möglich ist da der Warenkob keinen webshop Kunden zulässt,
// dann customerType auf den ersten verfügbaren setzen und p4mUser auf false setzen.
if (this.enabledOptions.some((o) => o.value === 'store')) {
this.customerType = 'store';
} else if (this.enabledOptions.some((o) => o.value === 'webshop')) {
this.customerType = 'webshop';
} else {
this.p4mUser = false;
const includesGuest = this.enabledOptions.some((o) => o.value === 'guest');
this.customerType = includesGuest ? 'guest' : first(this.enabledOptions)?.value;
}
// } else if (this.p4mUser) {
// // Implementierung wie im PBI #3467 beschrieben
// // wenn customerType nicht gesetzt wird und p4mUser true ist,
// // dann customerType auf store setzen.
// // wenn dies nicht möglich ist da der Warenkob keinen store Kunden zulässt,
// // dann customerType auf webshop setzen.
// // wenn dies nicht möglich ist da der Warenkob keinen webshop Kunden zulässt,
// // dann customerType auf den ersten verfügbaren setzen und p4mUser auf false setzen.
// if (this.enabledOptions.some((o) => o.value === 'store')) {
// this.customerType = 'store';
// } else if (this.enabledOptions.some((o) => o.value === 'webshop')) {
// this.customerType = 'webshop';
// } else {
// this.p4mUser = false;
// const includesGuest = this.enabledOptions.some(
// (o) => o.value === 'guest',
// );
// this.customerType = includesGuest
// ? 'guest'
// : first(this.enabledOptions)?.value;
// }
} else {
// wenn customerType nicht gesetzt wird und p4mUser false ist,
// dann customerType auf den ersten verfügbaren setzen der nicht mit dem aktuellen customerType übereinstimmt.
this.customerType =
first(this.enabledOptions.filter((o) => o.value === this.customerType))?.value ?? this.customerType;
first(
this.enabledOptions.filter((o) => o.value === this.customerType),
)?.value ?? this.customerType;
}
}
if (this.customerType !== initial.customerType || this.p4mUser !== initial.p4mUser) {
if (
this.customerType !== initial.customerType ||
this.p4mUser !== initial.p4mUser
) {
this.onChange(this.value);
this.onTouched();
this.valueChanges.emit(this.value);
}
this.checkboxes?.find((c) => c.name === this.customerType)?.writeValue(true);
this.checkboxes
?.find((c) => c.name === this.customerType)
?.writeValue(true);
}
}

View File

@@ -7,6 +7,6 @@ export * from './interests';
export * from './name';
export * from './newsletter';
export * from './organisation';
export * from './p4m-number';
// export * from './p4m-number';
export * from './phone-numbers';
export * from './form-block';

View File

@@ -1,4 +1,4 @@
// start:ng42.barrel
export * from './p4m-number-form-block.component';
export * from './p4m-number-form-block.module';
// end:ng42.barrel
// // start:ng42.barrel
// export * from './p4m-number-form-block.component';
// export * from './p4m-number-form-block.module';
// // end:ng42.barrel

View File

@@ -1,4 +1,4 @@
<shared-form-control label="Kundenkartencode" class="flex-grow">
<!-- <shared-form-control label="Kundenkartencode" class="flex-grow">
<input
placeholder="Kundenkartencode"
class="input-control"
@@ -13,4 +13,4 @@
<button type="button" (click)="scan()">
<shared-icon icon="barcode-scan" [size]="32"></shared-icon>
</button>
}
} -->

View File

@@ -1,49 +1,49 @@
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { UntypedFormControl, Validators } from '@angular/forms';
import { FormBlockControl } from '../form-block';
import { ScanAdapterService } from '@adapter/scan';
// import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
// import { UntypedFormControl, Validators } from '@angular/forms';
// import { FormBlockControl } from '../form-block';
// import { ScanAdapterService } from '@adapter/scan';
@Component({
selector: 'app-p4m-number-form-block',
templateUrl: 'p4m-number-form-block.component.html',
styleUrls: ['p4m-number-form-block.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class P4mNumberFormBlockComponent extends FormBlockControl<string> {
get tabIndexEnd() {
return this.tabIndexStart;
}
// @Component({
// selector: 'app-p4m-number-form-block',
// templateUrl: 'p4m-number-form-block.component.html',
// styleUrls: ['p4m-number-form-block.component.scss'],
// changeDetection: ChangeDetectionStrategy.OnPush,
// standalone: false,
// })
// export class P4mNumberFormBlockComponent extends FormBlockControl<string> {
// get tabIndexEnd() {
// return this.tabIndexStart;
// }
constructor(
private scanAdapter: ScanAdapterService,
private changeDetectorRef: ChangeDetectorRef,
) {
super();
}
// constructor(
// private scanAdapter: ScanAdapterService,
// private changeDetectorRef: ChangeDetectorRef,
// ) {
// super();
// }
updateValidators(): void {
this.control.setValidators([...this.getValidatorFn()]);
this.control.setAsyncValidators(this.getAsyncValidatorFn());
this.control.updateValueAndValidity();
}
// updateValidators(): void {
// this.control.setValidators([...this.getValidatorFn()]);
// this.control.setAsyncValidators(this.getAsyncValidatorFn());
// this.control.updateValueAndValidity();
// }
initializeControl(data?: string): void {
this.control = new UntypedFormControl(data ?? '', [Validators.required], this.getAsyncValidatorFn());
}
// initializeControl(data?: string): void {
// this.control = new UntypedFormControl(data ?? '', [Validators.required], this.getAsyncValidatorFn());
// }
_patchValue(update: { previous: string; current: string }): void {
this.control.patchValue(update.current);
}
// _patchValue(update: { previous: string; current: string }): void {
// this.control.patchValue(update.current);
// }
scan() {
this.scanAdapter.scan().subscribe((result) => {
this.control.patchValue(result);
this.changeDetectorRef.markForCheck();
});
}
// scan() {
// this.scanAdapter.scan().subscribe((result) => {
// this.control.patchValue(result);
// this.changeDetectorRef.markForCheck();
// });
// }
canScan() {
return this.scanAdapter.isReady();
}
}
// canScan() {
// return this.scanAdapter.isReady();
// }
// }

View File

@@ -1,14 +1,14 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// import { NgModule } from '@angular/core';
// import { CommonModule } from '@angular/common';
import { P4mNumberFormBlockComponent } from './p4m-number-form-block.component';
import { ReactiveFormsModule } from '@angular/forms';
import { IconComponent } from '@shared/components/icon';
import { FormControlComponent } from '@shared/components/form-control';
// import { P4mNumberFormBlockComponent } from './p4m-number-form-block.component';
// import { ReactiveFormsModule } from '@angular/forms';
// import { IconComponent } from '@shared/components/icon';
// import { FormControlComponent } from '@shared/components/form-control';
@NgModule({
imports: [CommonModule, ReactiveFormsModule, FormControlComponent, IconComponent],
exports: [P4mNumberFormBlockComponent],
declarations: [P4mNumberFormBlockComponent],
})
export class P4mNumberFormBlockModule {}
// @NgModule({
// imports: [CommonModule, ReactiveFormsModule, FormControlComponent, IconComponent],
// exports: [P4mNumberFormBlockComponent],
// declarations: [P4mNumberFormBlockComponent],
// })
// export class P4mNumberFormBlockModule {}

View File

@@ -1,5 +1,12 @@
import { HttpErrorResponse } from '@angular/common/http';
import { ChangeDetectorRef, Directive, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
import {
ChangeDetectorRef,
Directive,
OnDestroy,
OnInit,
ViewChild,
inject,
} from '@angular/core';
import {
AbstractControl,
AsyncValidatorFn,
@@ -11,7 +18,12 @@ import {
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
import { AddressDTO, CustomerDTO, PayerDTO, ShippingAddressDTO } from '@generated/swagger/crm-api';
import {
AddressDTO,
CustomerDTO,
PayerDTO,
ShippingAddressDTO,
} from '@generated/swagger/crm-api';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiValidators } from '@ui/validators';
import { isNull } from 'lodash';
@@ -42,7 +54,10 @@ import {
mapCustomerInfoDtoToCustomerCreateFormData,
} from './customer-create-form-data';
import { AddressSelectionModalService } from '../modals';
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services/navigation';
import {
CustomerCreateNavigation,
CustomerSearchNavigation,
} from '@shared/services/navigation';
@Directive()
export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
@@ -104,7 +119,12 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
);
this.processId$
.pipe(startWith(undefined), bufferCount(2, 1), takeUntil(this.onDestroy$), delay(100))
.pipe(
startWith(undefined),
bufferCount(2, 1),
takeUntil(this.onDestroy$),
delay(100),
)
.subscribe(async ([previous, current]) => {
if (previous === undefined) {
await this._initFormData();
@@ -155,7 +175,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
}
}
async addOrUpdateBreadcrumb(processId: number, formData: CustomerCreateFormData) {
async addOrUpdateBreadcrumb(
processId: number,
formData: CustomerCreateFormData,
) {
await this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
key: processId,
name: 'Kundendaten erfassen',
@@ -195,7 +218,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
console.log('customerTypeChanged', customerType);
}
addFormBlock(key: keyof CustomerCreateFormData, block: FormBlock<any, AbstractControl>) {
addFormBlock(
key: keyof CustomerCreateFormData,
block: FormBlock<any, AbstractControl>,
) {
this.form.addControl(key, block.control);
this.cdr.markForCheck();
}
@@ -232,7 +258,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
return true;
}
// Check Year + Month
else if (inputDate.getFullYear() === minBirthDate.getFullYear() && inputDate.getMonth() < minBirthDate.getMonth()) {
else if (
inputDate.getFullYear() === minBirthDate.getFullYear() &&
inputDate.getMonth() < minBirthDate.getMonth()
) {
return true;
}
// Check Year + Month + Day
@@ -279,70 +308,80 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
);
};
checkLoyalityCardValidator: AsyncValidatorFn = (control) => {
return of(control.value).pipe(
delay(500),
mergeMap((value) => {
const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
map((response) => {
if (response.error) {
throw response.message;
}
// checkLoyalityCardValidator: AsyncValidatorFn = (control) => {
// return of(control.value).pipe(
// delay(500),
// mergeMap((value) => {
// const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
// return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
// map((response) => {
// if (response.error) {
// throw response.message;
// }
/**
* #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
* Fall1: Kundenkarte hat Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
* Fall2: Kundenkarte hat keine Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
*/
if (response.result && response.result.customer) {
const customer = response.result.customer;
const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
// /**
// * #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
// * Fall1: Kundenkarte hat Daten in point4more:
// * Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
// * Fall2: Kundenkarte hat keine Daten in point4more:
// * Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
// */
// if (response.result && response.result.customer) {
// const customer = response.result.customer;
// const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
if (data.name.firstName && data.name.lastName) {
// Fall1
this._formData.next(data);
} else {
// Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
const current = this.formData;
current._meta = data._meta;
current.p4m = data.p4m;
}
}
// if (data.name.firstName && data.name.lastName) {
// // Fall1
// this._formData.next(data);
// } else {
// // Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
// const current = this.formData;
// current._meta = data._meta;
// current.p4m = data.p4m;
// }
// }
return null;
}),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
if (error?.error?.invalidProperties?.loyaltyCardNumber) {
return of({ invalid: error.error.invalidProperties.loyaltyCardNumber });
} else {
return of({ invalid: 'Kundenkartencode ist ungültig' });
}
}
}),
);
}),
tap(() => {
control.markAsTouched();
this.cdr.markForCheck();
}),
);
};
// return null;
// }),
// catchError((error) => {
// if (error instanceof HttpErrorResponse) {
// if (error?.error?.invalidProperties?.loyaltyCardNumber) {
// return of({ invalid: error.error.invalidProperties.loyaltyCardNumber });
// } else {
// return of({ invalid: 'Kundenkartencode ist ungültig' });
// }
// }
// }),
// );
// }),
// tap(() => {
// control.markAsTouched();
// this.cdr.markForCheck();
// }),
// );
// };
async navigateToCustomerDetails(customer: CustomerDTO) {
const processId = await this.processId$.pipe(first()).toPromise();
const route = this.customerSearchNavigation.detailsRoute({ processId, customerId: customer.id, customer });
const route = this.customerSearchNavigation.detailsRoute({
processId,
customerId: customer.id,
customer,
});
return this.router.navigate(route.path, { queryParams: route.urlTree.queryParams });
return this.router.navigate(route.path, {
queryParams: route.urlTree.queryParams,
});
}
async validateAddressData(address: AddressDTO): Promise<AddressDTO> {
const addressValidationResult = await this.addressVlidationModal.validateAddress(address);
const addressValidationResult =
await this.addressVlidationModal.validateAddress(address);
if (addressValidationResult !== undefined && (addressValidationResult as any) !== 'continue') {
if (
addressValidationResult !== undefined &&
(addressValidationResult as any) !== 'continue'
) {
address = addressValidationResult;
}
@@ -389,7 +428,9 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
} catch (error) {
this.form.enable();
setTimeout(() => {
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
this.addressFormBlock.setAddressValidationError(
error.error.invalidProperties,
);
}, 10);
return;
@@ -397,7 +438,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
}
}
if (data.birthDate && isNull(UiValidators.date(new UntypedFormControl(data.birthDate)))) {
if (
data.birthDate &&
isNull(UiValidators.date(new UntypedFormControl(data.birthDate)))
) {
customer.dateOfBirth = data.birthDate;
}
@@ -406,11 +450,15 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
if (this.validateShippingAddress) {
try {
billingAddress.address = await this.validateAddressData(billingAddress.address);
billingAddress.address = await this.validateAddressData(
billingAddress.address,
);
} catch (error) {
this.form.enable();
setTimeout(() => {
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
this.addressFormBlock.setAddressValidationError(
error.error.invalidProperties,
);
}, 10);
return;
@@ -426,15 +474,21 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
}
if (data.deviatingDeliveryAddress?.deviatingAddress) {
const shippingAddress = this.mapToShippingAddress(data.deviatingDeliveryAddress);
const shippingAddress = this.mapToShippingAddress(
data.deviatingDeliveryAddress,
);
if (this.validateShippingAddress) {
try {
shippingAddress.address = await this.validateAddressData(shippingAddress.address);
shippingAddress.address = await this.validateAddressData(
shippingAddress.address,
);
} catch (error) {
this.form.enable();
setTimeout(() => {
this.deviatingDeliveryAddressFormBlock.setAddressValidationError(error.error.invalidProperties);
this.deviatingDeliveryAddressFormBlock.setAddressValidationError(
error.error.invalidProperties,
);
}, 10);
return;
@@ -474,7 +528,13 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
};
}
mapToBillingAddress({ name, address, email, organisation, phoneNumbers }: DeviatingAddressFormBlockData): PayerDTO {
mapToBillingAddress({
name,
address,
email,
organisation,
phoneNumbers,
}: DeviatingAddressFormBlockData): PayerDTO {
return {
gender: name?.gender,
title: name?.title,

View File

@@ -1,10 +1,10 @@
import { NgModule } from '@angular/core';
import { CreateB2BCustomerModule } from './create-b2b-customer/create-b2b-customer.module';
import { CreateGuestCustomerModule } from './create-guest-customer';
import { CreateP4MCustomerModule } from './create-p4m-customer';
// import { CreateP4MCustomerModule } from './create-p4m-customer';
import { CreateStoreCustomerModule } from './create-store-customer/create-store-customer.module';
import { CreateWebshopCustomerModule } from './create-webshop-customer/create-webshop-customer.module';
import { UpdateP4MWebshopCustomerModule } from './update-p4m-webshop-customer';
// import { UpdateP4MWebshopCustomerModule } from './update-p4m-webshop-customer';
import { CreateCustomerComponent } from './create-customer.component';
@NgModule({
@@ -13,8 +13,8 @@ import { CreateCustomerComponent } from './create-customer.component';
CreateGuestCustomerModule,
CreateStoreCustomerModule,
CreateWebshopCustomerModule,
CreateP4MCustomerModule,
UpdateP4MWebshopCustomerModule,
// CreateP4MCustomerModule,
// UpdateP4MWebshopCustomerModule,
CreateCustomerComponent,
],
exports: [
@@ -22,8 +22,8 @@ import { CreateCustomerComponent } from './create-customer.component';
CreateGuestCustomerModule,
CreateStoreCustomerModule,
CreateWebshopCustomerModule,
CreateP4MCustomerModule,
UpdateP4MWebshopCustomerModule,
// CreateP4MCustomerModule,
// UpdateP4MWebshopCustomerModule,
CreateCustomerComponent,
],
})

View File

@@ -1,9 +1,9 @@
@if (formData$ | async; as data) {
<!-- @if (formData$ | async; as data) {
<form (keydown.enter)="$event.preventDefault()">
<h1 class="title flex flex-row items-center justify-center">
Kundendaten erfassen
<!-- <span
class="rounded-full ml-4 h-8 w-8 text-xl text-center border-2 border-solid border-brand text-brand">i</span> -->
<span
class="rounded-full ml-4 h-8 w-8 text-xl text-center border-2 border-solid border-brand text-brand">i</span>
</h1>
<p class="description">
Um Sie als Kunde beim nächsten
@@ -135,4 +135,4 @@
</button>
</div>
</form>
}
} -->

View File

@@ -1,292 +1,292 @@
import { Component, ChangeDetectionStrategy, ViewChild, OnInit } from '@angular/core';
import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
import { Result } from '@domain/defs';
import { CustomerDTO, CustomerInfoDTO, KeyValueDTOOfStringAndString } from '@generated/swagger/crm-api';
import { UiErrorModalComponent, UiModalResult } from '@ui/modal';
import { NEVER, Observable, of } from 'rxjs';
import { catchError, distinctUntilChanged, first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
import {
AddressFormBlockComponent,
AddressFormBlockData,
DeviatingAddressFormBlockComponent,
} from '../../components/form-blocks';
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
import { WebshopCustomnerAlreadyExistsModalComponent, WebshopCustomnerAlreadyExistsModalData } from '../../modals';
import { validateEmail } from '../../validators/email-validator';
import { AbstractCreateCustomer } from '../abstract-create-customer';
import { encodeFormData, mapCustomerDtoToCustomerCreateFormData } from '../customer-create-form-data';
import { zipCodeValidator } from '../../validators/zip-code-validator';
// import { Component, ChangeDetectionStrategy, ViewChild, OnInit } from '@angular/core';
// import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
// import { Result } from '@domain/defs';
// import { CustomerDTO, CustomerInfoDTO, KeyValueDTOOfStringAndString } from '@generated/swagger/crm-api';
// import { UiErrorModalComponent, UiModalResult } from '@ui/modal';
// import { NEVER, Observable, of } from 'rxjs';
// import { catchError, distinctUntilChanged, first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
// import {
// AddressFormBlockComponent,
// AddressFormBlockData,
// DeviatingAddressFormBlockComponent,
// } from '../../components/form-blocks';
// import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
// import { WebshopCustomnerAlreadyExistsModalComponent, WebshopCustomnerAlreadyExistsModalData } from '../../modals';
// import { validateEmail } from '../../validators/email-validator';
// import { AbstractCreateCustomer } from '../abstract-create-customer';
// import { encodeFormData, mapCustomerDtoToCustomerCreateFormData } from '../customer-create-form-data';
// import { zipCodeValidator } from '../../validators/zip-code-validator';
@Component({
selector: 'app-create-p4m-customer',
templateUrl: 'create-p4m-customer.component.html',
styleUrls: ['../create-customer.scss', 'create-p4m-customer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class CreateP4MCustomerComponent extends AbstractCreateCustomer implements OnInit {
validateAddress = true;
// @Component({
// selector: 'app-create-p4m-customer',
// templateUrl: 'create-p4m-customer.component.html',
// styleUrls: ['../create-customer.scss', 'create-p4m-customer.component.scss'],
// changeDetection: ChangeDetectionStrategy.OnPush,
// standalone: false,
// })
// export class CreateP4MCustomerComponent extends AbstractCreateCustomer implements OnInit {
// validateAddress = true;
validateShippingAddress = true;
// validateShippingAddress = true;
get _customerType() {
return this.activatedRoute.snapshot.data.customerType;
}
// get _customerType() {
// return this.activatedRoute.snapshot.data.customerType;
// }
get customerType() {
return `${this._customerType}-p4m`;
}
// get customerType() {
// return `${this._customerType}-p4m`;
// }
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
// nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
title: [],
};
// nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
// firstName: [Validators.required],
// lastName: [Validators.required],
// gender: [Validators.required],
// title: [],
// };
emailRequiredMark: boolean;
// emailRequiredMark: boolean;
emailValidatorFn: ValidatorFn[];
// emailValidatorFn: ValidatorFn[];
asyncEmailVlaidtorFn: AsyncValidatorFn[];
// asyncEmailVlaidtorFn: AsyncValidatorFn[];
asyncLoyaltyCardValidatorFn: AsyncValidatorFn[];
// asyncLoyaltyCardValidatorFn: AsyncValidatorFn[];
shippingAddressRequiredMarks: (keyof AddressFormBlockData)[] = [
'street',
'streetNumber',
'zipCode',
'city',
'country',
];
// shippingAddressRequiredMarks: (keyof AddressFormBlockData)[] = [
// 'street',
// 'streetNumber',
// 'zipCode',
// 'city',
// 'country',
// ];
shippingAddressValidators: Record<string, ValidatorFn[]> = {
street: [Validators.required],
streetNumber: [Validators.required],
zipCode: [Validators.required, zipCodeValidator()],
city: [Validators.required],
country: [Validators.required],
};
// shippingAddressValidators: Record<string, ValidatorFn[]> = {
// street: [Validators.required],
// streetNumber: [Validators.required],
// zipCode: [Validators.required, zipCodeValidator()],
// city: [Validators.required],
// country: [Validators.required],
// };
addressRequiredMarks: (keyof AddressFormBlockData)[];
// addressRequiredMarks: (keyof AddressFormBlockData)[];
addressValidatorFns: Record<string, ValidatorFn[]>;
// addressValidatorFns: Record<string, ValidatorFn[]>;
@ViewChild(AddressFormBlockComponent, { static: false })
addressFormBlock: AddressFormBlockComponent;
// @ViewChild(AddressFormBlockComponent, { static: false })
// addressFormBlock: AddressFormBlockComponent;
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
// @ViewChild(DeviatingAddressFormBlockComponent, { static: false })
// deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
agbValidatorFns = [Validators.requiredTrue];
// agbValidatorFns = [Validators.requiredTrue];
birthDateValidatorFns = [];
// birthDateValidatorFns = [];
existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
// existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
ngOnInit(): void {
super.ngOnInit();
this.initMarksAndValidators();
this.existingCustomer$ = this.customerExists$.pipe(
distinctUntilChanged(),
switchMap((exists) => {
if (exists) {
return this.fetchCustomerInfo();
}
return of(null);
}),
);
// ngOnInit(): void {
// super.ngOnInit();
// this.initMarksAndValidators();
// this.existingCustomer$ = this.customerExists$.pipe(
// distinctUntilChanged(),
// switchMap((exists) => {
// if (exists) {
// return this.fetchCustomerInfo();
// }
// return of(null);
// }),
// );
this.existingCustomer$
.pipe(
takeUntil(this.onDestroy$),
switchMap((info) => {
if (info) {
return this.customerService.getCustomer(info.id, 2).pipe(
map((res) => res.result),
catchError((err) => NEVER),
);
}
return NEVER;
}),
withLatestFrom(this.processId$),
)
.subscribe(([customer, processId]) => {
if (customer) {
this.modal
.open({
content: WebshopCustomnerAlreadyExistsModalComponent,
data: {
customer,
processId,
} as WebshopCustomnerAlreadyExistsModalData,
title: 'Es existiert bereits ein Onlinekonto mit dieser E-Mail-Adresse',
})
.afterClosed$.subscribe(async (result: UiModalResult<boolean>) => {
if (result.data) {
this.navigateToUpdatePage(customer);
} else {
this.formData.email = '';
this.cdr.markForCheck();
}
});
}
});
}
// this.existingCustomer$
// .pipe(
// takeUntil(this.onDestroy$),
// switchMap((info) => {
// if (info) {
// return this.customerService.getCustomer(info.id, 2).pipe(
// map((res) => res.result),
// catchError((err) => NEVER),
// );
// }
// return NEVER;
// }),
// withLatestFrom(this.processId$),
// )
// .subscribe(([customer, processId]) => {
// if (customer) {
// this.modal
// .open({
// content: WebshopCustomnerAlreadyExistsModalComponent,
// data: {
// customer,
// processId,
// } as WebshopCustomnerAlreadyExistsModalData,
// title: 'Es existiert bereits ein Onlinekonto mit dieser E-Mail-Adresse',
// })
// .afterClosed$.subscribe(async (result: UiModalResult<boolean>) => {
// if (result.data) {
// this.navigateToUpdatePage(customer);
// } else {
// this.formData.email = '';
// this.cdr.markForCheck();
// }
// });
// }
// });
// }
async navigateToUpdatePage(customer: CustomerDTO) {
const processId = await this.processId$.pipe(first()).toPromise();
this.router.navigate(['/kunde', processId, 'customer', 'create', 'webshop-p4m', 'update'], {
queryParams: {
formData: encodeFormData({
...mapCustomerDtoToCustomerCreateFormData(customer),
p4m: this.formData.p4m,
}),
},
});
}
// async navigateToUpdatePage(customer: CustomerDTO) {
// const processId = await this.processId$.pipe(first()).toPromise();
// this.router.navigate(['/kunde', processId, 'customer', 'create', 'webshop-p4m', 'update'], {
// queryParams: {
// formData: encodeFormData({
// ...mapCustomerDtoToCustomerCreateFormData(customer),
// p4m: this.formData.p4m,
// }),
// },
// });
// }
initMarksAndValidators() {
this.asyncLoyaltyCardValidatorFn = [this.checkLoyalityCardValidator];
this.birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
if (this._customerType === 'webshop') {
this.emailRequiredMark = true;
this.emailValidatorFn = [Validators.required, Validators.email, validateEmail];
this.asyncEmailVlaidtorFn = [this.emailExistsValidator];
this.addressRequiredMarks = this.shippingAddressRequiredMarks;
this.addressValidatorFns = this.shippingAddressValidators;
} else {
this.emailRequiredMark = false;
this.emailValidatorFn = [Validators.email, validateEmail];
}
}
// initMarksAndValidators() {
// this.asyncLoyaltyCardValidatorFn = [this.checkLoyalityCardValidator];
// this.birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
// if (this._customerType === 'webshop') {
// this.emailRequiredMark = true;
// this.emailValidatorFn = [Validators.required, Validators.email, validateEmail];
// this.asyncEmailVlaidtorFn = [this.emailExistsValidator];
// this.addressRequiredMarks = this.shippingAddressRequiredMarks;
// this.addressValidatorFns = this.shippingAddressValidators;
// } else {
// this.emailRequiredMark = false;
// this.emailValidatorFn = [Validators.email, validateEmail];
// }
// }
fetchCustomerInfo(): Observable<CustomerDTO | null> {
const email = this.formData.email;
return this.customerService.getOnlineCustomerByEmail(email).pipe(
map((result) => {
if (result) {
return result;
}
return null;
}),
catchError((err) => {
this.modal.open({
content: UiErrorModalComponent,
data: err,
});
return [null];
}),
);
}
// fetchCustomerInfo(): Observable<CustomerDTO | null> {
// const email = this.formData.email;
// return this.customerService.getOnlineCustomerByEmail(email).pipe(
// map((result) => {
// if (result) {
// return result;
// }
// return null;
// }),
// catchError((err) => {
// this.modal.open({
// content: UiErrorModalComponent,
// data: err,
// });
// return [null];
// }),
// );
// }
getInterests(): KeyValueDTOOfStringAndString[] {
const interests: KeyValueDTOOfStringAndString[] = [];
// getInterests(): KeyValueDTOOfStringAndString[] {
// const interests: KeyValueDTOOfStringAndString[] = [];
for (const key in this.formData.interests) {
if (this.formData.interests[key]) {
interests.push({ key, group: 'KUBI_INTERESSEN' });
}
}
// for (const key in this.formData.interests) {
// if (this.formData.interests[key]) {
// interests.push({ key, group: 'KUBI_INTERESSEN' });
// }
// }
return interests;
}
// return interests;
// }
getNewsletter(): KeyValueDTOOfStringAndString | undefined {
if (this.formData.newsletter) {
return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
}
}
// getNewsletter(): KeyValueDTOOfStringAndString | undefined {
// if (this.formData.newsletter) {
// return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
// }
// }
static MapCustomerInfoDtoToCustomerDto(customerInfoDto: CustomerInfoDTO): CustomerDTO {
return {
address: customerInfoDto.address,
agentComment: customerInfoDto.agentComment,
bonusCard: customerInfoDto.bonusCard,
campaignCode: customerInfoDto.campaignCode,
communicationDetails: customerInfoDto.communicationDetails,
createdInBranch: customerInfoDto.createdInBranch,
customerGroup: customerInfoDto.customerGroup,
customerNumber: customerInfoDto.customerNumber,
customerStatus: customerInfoDto.customerStatus,
customerType: customerInfoDto.customerType,
dateOfBirth: customerInfoDto.dateOfBirth,
features: customerInfoDto.features,
firstName: customerInfoDto.firstName,
lastName: customerInfoDto.lastName,
gender: customerInfoDto.gender,
hasOnlineAccount: customerInfoDto.hasOnlineAccount,
isGuestAccount: customerInfoDto.isGuestAccount,
label: customerInfoDto.label,
notificationChannels: customerInfoDto.notificationChannels,
organisation: customerInfoDto.organisation,
title: customerInfoDto.title,
id: customerInfoDto.id,
pId: customerInfoDto.pId,
};
}
// static MapCustomerInfoDtoToCustomerDto(customerInfoDto: CustomerInfoDTO): CustomerDTO {
// return {
// address: customerInfoDto.address,
// agentComment: customerInfoDto.agentComment,
// bonusCard: customerInfoDto.bonusCard,
// campaignCode: customerInfoDto.campaignCode,
// communicationDetails: customerInfoDto.communicationDetails,
// createdInBranch: customerInfoDto.createdInBranch,
// customerGroup: customerInfoDto.customerGroup,
// customerNumber: customerInfoDto.customerNumber,
// customerStatus: customerInfoDto.customerStatus,
// customerType: customerInfoDto.customerType,
// dateOfBirth: customerInfoDto.dateOfBirth,
// features: customerInfoDto.features,
// firstName: customerInfoDto.firstName,
// lastName: customerInfoDto.lastName,
// gender: customerInfoDto.gender,
// hasOnlineAccount: customerInfoDto.hasOnlineAccount,
// isGuestAccount: customerInfoDto.isGuestAccount,
// label: customerInfoDto.label,
// notificationChannels: customerInfoDto.notificationChannels,
// organisation: customerInfoDto.organisation,
// title: customerInfoDto.title,
// id: customerInfoDto.id,
// pId: customerInfoDto.pId,
// };
// }
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
const isWebshop = this._customerType === 'webshop';
let res: Result<CustomerDTO>;
// async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
// const isWebshop = this._customerType === 'webshop';
// let res: Result<CustomerDTO>;
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
// const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
if (customerDto) {
customer = { ...customerDto, ...customer };
} else if (customerInfoDto) {
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
}
// if (customerDto) {
// customer = { ...customerDto, ...customer };
// } else if (customerInfoDto) {
// customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
// }
const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
if (p4mFeature) {
p4mFeature.value = this.formData.p4m;
} else {
customer.features.push({
key: 'p4mUser',
value: this.formData.p4m,
});
}
// const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
// if (p4mFeature) {
// p4mFeature.value = this.formData.p4m;
// } else {
// customer.features.push({
// key: 'p4mUser',
// value: this.formData.p4m,
// });
// }
const interests = this.getInterests();
// const interests = this.getInterests();
if (interests.length > 0) {
customer.features?.push(...interests);
// TODO: Klärung wie Interessen zukünftig gespeichert werden
// await this._loyaltyCardService
// .LoyaltyCardSaveInteressen({
// customerId: res.result.id,
// interessen: this.getInterests(),
// })
// .toPromise();
}
// if (interests.length > 0) {
// customer.features?.push(...interests);
// // TODO: Klärung wie Interessen zukünftig gespeichert werden
// // await this._loyaltyCardService
// // .LoyaltyCardSaveInteressen({
// // customerId: res.result.id,
// // interessen: this.getInterests(),
// // })
// // .toPromise();
// }
const newsletter = this.getNewsletter();
// const newsletter = this.getNewsletter();
if (newsletter) {
customer.features.push(newsletter);
} else {
customer.features = customer.features.filter(
(feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
);
}
// if (newsletter) {
// customer.features.push(newsletter);
// } else {
// customer.features = customer.features.filter(
// (feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
// );
// }
if (isWebshop) {
if (customer.id > 0) {
if (this.formData?._meta?.hasLocalityCard) {
res = await this.customerService.updateStoreP4MToWebshopP4M(customer);
} else {
res = await this.customerService.updateToP4MOnlineCustomer(customer);
}
} else {
res = await this.customerService.createOnlineCustomer(customer).toPromise();
}
} else {
res = await this.customerService.createStoreCustomer(customer).toPromise();
}
// if (isWebshop) {
// if (customer.id > 0) {
// if (this.formData?._meta?.hasLocalityCard) {
// res = await this.customerService.updateStoreP4MToWebshopP4M(customer);
// } else {
// res = await this.customerService.updateToP4MOnlineCustomer(customer);
// }
// } else {
// res = await this.customerService.createOnlineCustomer(customer).toPromise();
// }
// } else {
// res = await this.customerService.createStoreCustomer(customer).toPromise();
// }
return res.result;
}
}
// return res.result;
// }
// }

View File

@@ -1,45 +1,45 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// import { NgModule } from '@angular/core';
// import { CommonModule } from '@angular/common';
import { CreateP4MCustomerComponent } from './create-p4m-customer.component';
import {
AddressFormBlockModule,
BirthDateFormBlockModule,
InterestsFormBlockModule,
NameFormBlockModule,
OrganisationFormBlockModule,
P4mNumberFormBlockModule,
NewsletterFormBlockModule,
DeviatingAddressFormBlockComponentModule,
AcceptAGBFormBlockModule,
EmailFormBlockModule,
PhoneNumbersFormBlockModule,
} from '../../components/form-blocks';
import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
import { UiSpinnerModule } from '@ui/spinner';
import { UiIconModule } from '@ui/icon';
import { RouterModule } from '@angular/router';
// import { CreateP4MCustomerComponent } from './create-p4m-customer.component';
// import {
// AddressFormBlockModule,
// BirthDateFormBlockModule,
// InterestsFormBlockModule,
// NameFormBlockModule,
// OrganisationFormBlockModule,
// P4mNumberFormBlockModule,
// NewsletterFormBlockModule,
// DeviatingAddressFormBlockComponentModule,
// AcceptAGBFormBlockModule,
// EmailFormBlockModule,
// PhoneNumbersFormBlockModule,
// } from '../../components/form-blocks';
// import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
// import { UiSpinnerModule } from '@ui/spinner';
// import { UiIconModule } from '@ui/icon';
// import { RouterModule } from '@angular/router';
@NgModule({
imports: [
CommonModule,
CustomerTypeSelectorModule,
AddressFormBlockModule,
BirthDateFormBlockModule,
InterestsFormBlockModule,
NameFormBlockModule,
OrganisationFormBlockModule,
P4mNumberFormBlockModule,
NewsletterFormBlockModule,
DeviatingAddressFormBlockComponentModule,
AcceptAGBFormBlockModule,
EmailFormBlockModule,
PhoneNumbersFormBlockModule,
UiSpinnerModule,
UiIconModule,
RouterModule,
],
exports: [CreateP4MCustomerComponent],
declarations: [CreateP4MCustomerComponent],
})
export class CreateP4MCustomerModule {}
// @NgModule({
// imports: [
// CommonModule,
// CustomerTypeSelectorModule,
// AddressFormBlockModule,
// BirthDateFormBlockModule,
// InterestsFormBlockModule,
// NameFormBlockModule,
// OrganisationFormBlockModule,
// P4mNumberFormBlockModule,
// NewsletterFormBlockModule,
// DeviatingAddressFormBlockComponentModule,
// AcceptAGBFormBlockModule,
// EmailFormBlockModule,
// PhoneNumbersFormBlockModule,
// UiSpinnerModule,
// UiIconModule,
// RouterModule,
// ],
// exports: [CreateP4MCustomerComponent],
// declarations: [CreateP4MCustomerComponent],
// })
// export class CreateP4MCustomerModule {}

View File

@@ -1,2 +1,2 @@
export * from './create-p4m-customer.component';
export * from './create-p4m-customer.module';
// export * from './create-p4m-customer.component';
// export * from './create-p4m-customer.module';

View File

@@ -1,6 +1,6 @@
import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core';
import { ValidatorFn, Validators } from '@angular/forms';
import { CustomerDTO } from '@generated/swagger/crm-api';
import { CustomerDTO, CustomerInfoDTO } from '@generated/swagger/crm-api';
import { map } from 'rxjs/operators';
import {
AddressFormBlockComponent,
@@ -10,13 +10,16 @@ import {
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
import { validateEmail } from '../../validators/email-validator';
import { AbstractCreateCustomer } from '../abstract-create-customer';
import { CreateP4MCustomerComponent } from '../create-p4m-customer';
// import { CreateP4MCustomerComponent } from '../create-p4m-customer';
import { zipCodeValidator } from '../../validators/zip-code-validator';
@Component({
selector: 'app-create-webshop-customer',
templateUrl: 'create-webshop-customer.component.html',
styleUrls: ['../create-customer.scss', 'create-webshop-customer.component.scss'],
styleUrls: [
'../create-customer.scss',
'create-webshop-customer.component.scss',
],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
@@ -26,7 +29,11 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
validateAddress = true;
validateShippingAddress = true;
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
nameRequiredMarks: (keyof NameFormBlockData)[] = [
'gender',
'firstName',
'lastName',
];
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
@@ -35,7 +42,13 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
title: [],
};
addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
addressRequiredMarks: (keyof AddressFormBlockData)[] = [
'street',
'streetNumber',
'zipCode',
'city',
'country',
];
addressValidators: Record<string, ValidatorFn[]> = {
street: [Validators.required],
@@ -68,7 +81,11 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
if (customerDto) {
customer = { ...customerDto, ...customer };
} else if (customerInfoDto) {
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
customer = {
// ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto),
...this.mapCustomerInfoDtoToCustomerDto(customerInfoDto),
...customer,
};
}
const res = await this.customerService.updateToOnlineCustomer(customer);
@@ -80,4 +97,34 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
.toPromise();
}
}
mapCustomerInfoDtoToCustomerDto(
customerInfoDto: CustomerInfoDTO,
): CustomerDTO {
return {
address: customerInfoDto.address,
agentComment: customerInfoDto.agentComment,
bonusCard: customerInfoDto.bonusCard,
campaignCode: customerInfoDto.campaignCode,
communicationDetails: customerInfoDto.communicationDetails,
createdInBranch: customerInfoDto.createdInBranch,
customerGroup: customerInfoDto.customerGroup,
customerNumber: customerInfoDto.customerNumber,
customerStatus: customerInfoDto.customerStatus,
customerType: customerInfoDto.customerType,
dateOfBirth: customerInfoDto.dateOfBirth,
features: customerInfoDto.features,
firstName: customerInfoDto.firstName,
lastName: customerInfoDto.lastName,
gender: customerInfoDto.gender,
hasOnlineAccount: customerInfoDto.hasOnlineAccount,
isGuestAccount: customerInfoDto.isGuestAccount,
label: customerInfoDto.label,
notificationChannels: customerInfoDto.notificationChannels,
organisation: customerInfoDto.organisation,
title: customerInfoDto.title,
id: customerInfoDto.id,
pId: customerInfoDto.pId,
};
}
}

View File

@@ -1,7 +1,7 @@
import { CustomerDTO, Gender } from '@generated/swagger/crm-api';
export interface CreateCustomerQueryParams {
p4mNumber?: string;
// p4mNumber?: string;
customerId?: number;
gender?: Gender;
title?: string;

View File

@@ -1,6 +1,6 @@
export * from './create-b2b-customer';
export * from './create-guest-customer';
export * from './create-p4m-customer';
// export * from './create-p4m-customer';
export * from './create-store-customer';
export * from './create-webshop-customer';
export * from './defs';

View File

@@ -1,4 +1,4 @@
@if (formData$ | async; as data) {
<!-- @if (formData$ | async; as data) {
<form (keydown.enter)="$event.preventDefault()">
<h1 class="title flex flex-row items-center justify-center">Kundenkartendaten erfasen</h1>
<p class="description">Bitte erfassen Sie die Kundenkarte</p>
@@ -106,4 +106,4 @@
</button>
</div>
</form>
}
} -->

View File

@@ -1,156 +1,156 @@
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
import { Result } from '@domain/defs';
import { CustomerDTO, KeyValueDTOOfStringAndString, PayerDTO } from '@generated/swagger/crm-api';
import { AddressFormBlockData } from '../../components/form-blocks';
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
import { AbstractCreateCustomer } from '../abstract-create-customer';
import { CreateP4MCustomerComponent } from '../create-p4m-customer';
// import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
// import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
// import { Result } from '@domain/defs';
// import { CustomerDTO, KeyValueDTOOfStringAndString, PayerDTO } from '@generated/swagger/crm-api';
// import { AddressFormBlockData } from '../../components/form-blocks';
// import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
// import { AbstractCreateCustomer } from '../abstract-create-customer';
// import { CreateP4MCustomerComponent } from '../create-p4m-customer';
@Component({
selector: 'page-update-p4m-webshop-customer',
templateUrl: 'update-p4m-webshop-customer.component.html',
styleUrls: ['../create-customer.scss', 'update-p4m-webshop-customer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer implements OnInit {
customerType = 'webshop-p4m/update';
// @Component({
// selector: 'page-update-p4m-webshop-customer',
// templateUrl: 'update-p4m-webshop-customer.component.html',
// styleUrls: ['../create-customer.scss', 'update-p4m-webshop-customer.component.scss'],
// changeDetection: ChangeDetectionStrategy.OnPush,
// standalone: false,
// })
// export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer implements OnInit {
// customerType = 'webshop-p4m/update';
validateAddress = true;
// validateAddress = true;
validateShippingAddress = true;
// validateShippingAddress = true;
agbValidatorFns = [Validators.requiredTrue];
// agbValidatorFns = [Validators.requiredTrue];
birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
// birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
// nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
title: [],
};
// nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
// firstName: [Validators.required],
// lastName: [Validators.required],
// gender: [Validators.required],
// title: [],
// };
addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
// addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
addressValidatorFns: Record<string, ValidatorFn[]> = {
street: [Validators.required],
streetNumber: [Validators.required],
zipCode: [Validators.required],
city: [Validators.required],
country: [Validators.required],
};
// addressValidatorFns: Record<string, ValidatorFn[]> = {
// street: [Validators.required],
// streetNumber: [Validators.required],
// zipCode: [Validators.required],
// city: [Validators.required],
// country: [Validators.required],
// };
asyncLoyaltyCardValidatorFn: AsyncValidatorFn[] = [this.checkLoyalityCardValidator];
// asyncLoyaltyCardValidatorFn: AsyncValidatorFn[] = [this.checkLoyalityCardValidator];
get billingAddress(): PayerDTO | undefined {
const payers = this.formData?._meta?.customerDto?.payers;
// get billingAddress(): PayerDTO | undefined {
// const payers = this.formData?._meta?.customerDto?.payers;
if (!payers || payers.length === 0) {
return undefined;
}
// if (!payers || payers.length === 0) {
// return undefined;
// }
// the default payer is the payer with the latest isDefault(Date) value
const defaultPayer = payers.reduce((prev, curr) =>
new Date(prev.isDefault) > new Date(curr.isDefault) ? prev : curr,
);
// // the default payer is the payer with the latest isDefault(Date) value
// const defaultPayer = payers.reduce((prev, curr) =>
// new Date(prev.isDefault) > new Date(curr.isDefault) ? prev : curr,
// );
return defaultPayer.payer.data;
}
// return defaultPayer.payer.data;
// }
get shippingAddress() {
const shippingAddresses = this.formData?._meta?.customerDto?.shippingAddresses;
// get shippingAddress() {
// const shippingAddresses = this.formData?._meta?.customerDto?.shippingAddresses;
if (!shippingAddresses || shippingAddresses.length === 0) {
return undefined;
}
// if (!shippingAddresses || shippingAddresses.length === 0) {
// return undefined;
// }
// the default shipping address is the shipping address with the latest isDefault(Date) value
const defaultShippingAddress = shippingAddresses.reduce((prev, curr) =>
new Date(prev.data.isDefault) > new Date(curr.data.isDefault) ? prev : curr,
);
// // the default shipping address is the shipping address with the latest isDefault(Date) value
// const defaultShippingAddress = shippingAddresses.reduce((prev, curr) =>
// new Date(prev.data.isDefault) > new Date(curr.data.isDefault) ? prev : curr,
// );
return defaultShippingAddress.data;
}
// return defaultShippingAddress.data;
// }
ngOnInit() {
super.ngOnInit();
}
// ngOnInit() {
// super.ngOnInit();
// }
getInterests(): KeyValueDTOOfStringAndString[] {
const interests: KeyValueDTOOfStringAndString[] = [];
// getInterests(): KeyValueDTOOfStringAndString[] {
// const interests: KeyValueDTOOfStringAndString[] = [];
for (const key in this.formData.interests) {
if (this.formData.interests[key]) {
interests.push({ key, group: 'KUBI_INTERESSEN' });
}
}
// for (const key in this.formData.interests) {
// if (this.formData.interests[key]) {
// interests.push({ key, group: 'KUBI_INTERESSEN' });
// }
// }
return interests;
}
// return interests;
// }
getNewsletter(): KeyValueDTOOfStringAndString | undefined {
if (this.formData.newsletter) {
return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
}
}
// getNewsletter(): KeyValueDTOOfStringAndString | undefined {
// if (this.formData.newsletter) {
// return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
// }
// }
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
let res: Result<CustomerDTO>;
// async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
// let res: Result<CustomerDTO>;
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
// const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
if (customerDto) {
customer = { ...customerDto, shippingAddresses: [], payers: [], ...customer };
// if (customerDto) {
// customer = { ...customerDto, shippingAddresses: [], payers: [], ...customer };
if (customerDto.shippingAddresses?.length) {
customer.shippingAddresses.unshift(...customerDto.shippingAddresses);
}
if (customerDto.payers?.length) {
customer.payers.unshift(...customerDto.payers);
}
} else if (customerInfoDto) {
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
}
// if (customerDto.shippingAddresses?.length) {
// customer.shippingAddresses.unshift(...customerDto.shippingAddresses);
// }
// if (customerDto.payers?.length) {
// customer.payers.unshift(...customerDto.payers);
// }
// } else if (customerInfoDto) {
// customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
// }
const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
if (p4mFeature) {
p4mFeature.value = this.formData.p4m;
} else {
customer.features.push({
key: 'p4mUser',
value: this.formData.p4m,
});
}
// const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
// if (p4mFeature) {
// p4mFeature.value = this.formData.p4m;
// } else {
// customer.features.push({
// key: 'p4mUser',
// value: this.formData.p4m,
// });
// }
const interests = this.getInterests();
// const interests = this.getInterests();
if (interests.length > 0) {
customer.features?.push(...interests);
// TODO: Klärung wie Interessen zukünftig gespeichert werden
// await this._loyaltyCardService
// .LoyaltyCardSaveInteressen({
// customerId: res.result.id,
// interessen: this.getInterests(),
// })
// .toPromise();
}
// if (interests.length > 0) {
// customer.features?.push(...interests);
// // TODO: Klärung wie Interessen zukünftig gespeichert werden
// // await this._loyaltyCardService
// // .LoyaltyCardSaveInteressen({
// // customerId: res.result.id,
// // interessen: this.getInterests(),
// // })
// // .toPromise();
// }
const newsletter = this.getNewsletter();
// const newsletter = this.getNewsletter();
if (newsletter) {
customer.features.push(newsletter);
} else {
customer.features = customer.features.filter(
(feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
);
}
// if (newsletter) {
// customer.features.push(newsletter);
// } else {
// customer.features = customer.features.filter(
// (feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
// );
// }
res = await this.customerService.updateToP4MOnlineCustomer(customer);
// res = await this.customerService.updateToP4MOnlineCustomer(customer);
return res.result;
}
}
// return res.result;
// }
// }

View File

@@ -1,48 +1,48 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// import { NgModule } from '@angular/core';
// import { CommonModule } from '@angular/common';
import { UpdateP4MWebshopCustomerComponent } from './update-p4m-webshop-customer.component';
// import { UpdateP4MWebshopCustomerComponent } from './update-p4m-webshop-customer.component';
import {
AddressFormBlockModule,
BirthDateFormBlockModule,
InterestsFormBlockModule,
NameFormBlockModule,
OrganisationFormBlockModule,
P4mNumberFormBlockModule,
NewsletterFormBlockModule,
DeviatingAddressFormBlockComponentModule,
AcceptAGBFormBlockModule,
EmailFormBlockModule,
PhoneNumbersFormBlockModule,
} from '../../components/form-blocks';
import { UiFormControlModule } from '@ui/form-control';
import { UiInputModule } from '@ui/input';
import { CustomerPipesModule } from '@shared/pipes/customer';
import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
import { UiSpinnerModule } from '@ui/spinner';
// import {
// AddressFormBlockModule,
// BirthDateFormBlockModule,
// InterestsFormBlockModule,
// NameFormBlockModule,
// OrganisationFormBlockModule,
// P4mNumberFormBlockModule,
// NewsletterFormBlockModule,
// DeviatingAddressFormBlockComponentModule,
// AcceptAGBFormBlockModule,
// EmailFormBlockModule,
// PhoneNumbersFormBlockModule,
// } from '../../components/form-blocks';
// import { UiFormControlModule } from '@ui/form-control';
// import { UiInputModule } from '@ui/input';
// import { CustomerPipesModule } from '@shared/pipes/customer';
// import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
// import { UiSpinnerModule } from '@ui/spinner';
@NgModule({
imports: [
CommonModule,
CustomerTypeSelectorModule,
AddressFormBlockModule,
BirthDateFormBlockModule,
InterestsFormBlockModule,
NameFormBlockModule,
OrganisationFormBlockModule,
P4mNumberFormBlockModule,
NewsletterFormBlockModule,
DeviatingAddressFormBlockComponentModule,
AcceptAGBFormBlockModule,
EmailFormBlockModule,
PhoneNumbersFormBlockModule,
UiFormControlModule,
UiInputModule,
CustomerPipesModule,
UiSpinnerModule,
],
exports: [UpdateP4MWebshopCustomerComponent],
declarations: [UpdateP4MWebshopCustomerComponent],
})
export class UpdateP4MWebshopCustomerModule {}
// @NgModule({
// imports: [
// CommonModule,
// CustomerTypeSelectorModule,
// AddressFormBlockModule,
// BirthDateFormBlockModule,
// InterestsFormBlockModule,
// NameFormBlockModule,
// OrganisationFormBlockModule,
// P4mNumberFormBlockModule,
// NewsletterFormBlockModule,
// DeviatingAddressFormBlockComponentModule,
// AcceptAGBFormBlockModule,
// EmailFormBlockModule,
// PhoneNumbersFormBlockModule,
// UiFormControlModule,
// UiInputModule,
// CustomerPipesModule,
// UiSpinnerModule,
// ],
// exports: [UpdateP4MWebshopCustomerComponent],
// declarations: [UpdateP4MWebshopCustomerComponent],
// })
// export class UpdateP4MWebshopCustomerModule {}

View File

@@ -1,15 +1,33 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, effect, untracked } from '@angular/core';
import {
Component,
ChangeDetectionStrategy,
OnInit,
OnDestroy,
inject,
effect,
untracked,
} from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { BehaviorSubject, Subject, Subscription, fromEvent } from 'rxjs';
import {
BehaviorSubject,
Subject,
Subscription,
firstValueFrom,
fromEvent,
} from 'rxjs';
import { CustomerSearchStore } from './store/customer-search.store';
import { provideComponentStore } from '@ngrx/component-store';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { delay, filter, first, switchMap, takeUntil } from 'rxjs/operators';
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services/navigation';
import {
CustomerCreateNavigation,
CustomerSearchNavigation,
} from '@shared/services/navigation';
import { CustomerSearchMainAutocompleteProvider } from './providers/customer-search-main-autocomplete.provider';
import { FilterAutocompleteProvider } from '@shared/components/filter';
import { toSignal } from '@angular/core/rxjs-interop';
import { provideCancelSearchSubject } from '@shared/services/cancel-subject';
import { injectFeedbackErrorDialog } from '@isa/ui/dialog';
@Component({
selector: 'page-customer-search',
@@ -28,6 +46,7 @@ import { provideCancelSearchSubject } from '@shared/services/cancel-subject';
standalone: false,
})
export class CustomerSearchComponent implements OnInit, OnDestroy {
#errorFeedbackDialog = injectFeedbackErrorDialog();
private _store = inject(CustomerSearchStore);
private _activatedRoute = inject(ActivatedRoute);
private _router = inject(Router);
@@ -37,7 +56,11 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
private searchStore = inject(CustomerSearchStore);
keyEscPressed = toSignal(fromEvent(document, 'keydown').pipe(filter((e: KeyboardEvent) => e.key === 'Escape')));
keyEscPressed = toSignal(
fromEvent(document, 'keydown').pipe(
filter((e: KeyboardEvent) => e.key === 'Escape'),
),
);
get breadcrumb() {
let breadcrumb: string;
@@ -53,7 +76,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
private _breadcrumbs$ = this._store.processId$.pipe(
filter((id) => !!id),
switchMap((id) => this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer')),
switchMap((id) =>
this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer'),
),
);
side$ = new BehaviorSubject<string | undefined>(undefined);
@@ -97,53 +122,77 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
this.checkDetailsBreadcrumb();
});
this._eventsSubscription = this._router.events.pipe(takeUntil(this._onDestroy$)).subscribe((event) => {
if (event instanceof NavigationEnd) {
this.checkAndUpdateProcessId();
this.checkAndUpdateSide();
this.checkAndUpdateCustomerId();
this.checkBreadcrumbs();
}
});
this._store.customerListResponse$
this._eventsSubscription = this._router.events
.pipe(takeUntil(this._onDestroy$))
.subscribe(async ([response, filter, processId, restored, skipNavigation]) => {
if (this._store.processId === processId) {
if (skipNavigation) {
return;
}
if (response.hits === 1) {
// Navigate to details page
const customer = response.result[0];
if (customer.id < 0) {
// navigate to create customer
const route = this._createNavigation.upgradeCustomerRoute({ processId, customerInfo: customer });
await this._router.navigate(route.path, { queryParams: route.queryParams });
return;
} else {
const route = this._navigation.detailsRoute({ processId, customerId: customer.id });
await this._router.navigate(route.path, { queryParams: filter.getQueryParams() });
}
} else if (response.hits > 1) {
const route = this._navigation.listRoute({ processId, filter });
if (
(['details'].includes(this.breadcrumb) &&
response?.result?.some((c) => c.id === this._store.customerId)) ||
restored
) {
await this._router.navigate([], { queryParams: route.queryParams });
} else {
await this._router.navigate(route.path, { queryParams: route.queryParams });
}
}
.subscribe((event) => {
if (event instanceof NavigationEnd) {
this.checkAndUpdateProcessId();
this.checkAndUpdateSide();
this.checkAndUpdateCustomerId();
this.checkBreadcrumbs();
}
});
this._store.customerListResponse$
.pipe(takeUntil(this._onDestroy$))
.subscribe(
async ([response, filter, processId, restored, skipNavigation]) => {
if (this._store.processId === processId) {
if (skipNavigation) {
return;
}
if (response.hits === 1) {
// Navigate to details page
const customer = response.result[0];
if (customer.id < 0) {
// #5375 - Zusätzlich soll bei Kunden bei denen ein Upgrade möglich ist ein Dialog angezeigt werden, dass Kundenneuanlage mit Kundenkarte nicht möglich ist
await firstValueFrom(
this.#errorFeedbackDialog({
data: {
errorMessage:
'Kundenneuanlage mit Kundenkarte nicht möglich',
},
}).closed,
);
// navigate to create customer
// const route = this._createNavigation.upgradeCustomerRoute({ processId, customerInfo: customer });
// await this._router.navigate(route.path, { queryParams: route.queryParams });
return;
} else {
const route = this._navigation.detailsRoute({
processId,
customerId: customer.id,
});
await this._router.navigate(route.path, {
queryParams: filter.getQueryParams(),
});
}
} else if (response.hits > 1) {
const route = this._navigation.listRoute({ processId, filter });
if (
(['details'].includes(this.breadcrumb) &&
response?.result?.some(
(c) => c.id === this._store.customerId,
)) ||
restored
) {
await this._router.navigate([], {
queryParams: route.queryParams,
});
} else {
await this._router.navigate(route.path, {
queryParams: route.queryParams,
});
}
}
this.checkBreadcrumbs();
}
},
);
}
ngOnDestroy(): void {
@@ -169,7 +218,11 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
this._store.setProcessId(processId);
this._store.reset(this._activatedRoute.snapshot.queryParams);
if (!['main', 'filter'].some((s) => s === this.breadcrumb)) {
const skipNavigation = ['orders', 'order-details', 'order-details-history'].includes(this.breadcrumb);
const skipNavigation = [
'orders',
'order-details',
'order-details-history',
].includes(this.breadcrumb);
this._store.search({ skipNavigation });
}
}
@@ -229,7 +282,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
const mainBreadcrumb = await this.getMainBreadcrumb();
if (!mainBreadcrumb) {
const navigation = this._navigation.defaultRoute({ processId: this._store.processId });
const navigation = this._navigation.defaultRoute({
processId: this._store.processId,
});
const breadcrumb: Breadcrumb = {
key: this._store.processId,
tags: ['customer', 'search', 'main'],
@@ -242,14 +297,19 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
this._breadcrumbService.addBreadcrumb(breadcrumb);
} else {
this._breadcrumbService.patchBreadcrumb(mainBreadcrumb.id, {
params: { ...this.snapshot.queryParams, ...(mainBreadcrumb.params ?? {}) },
params: {
...this.snapshot.queryParams,
...(mainBreadcrumb.params ?? {}),
},
});
}
}
async getCreateCustomerBreadcrumb(): Promise<Breadcrumb | undefined> {
const breadcrumbs = await this.getBreadcrumbs();
return breadcrumbs.find((b) => b.tags.includes('create') && b.tags.includes('customer'));
return breadcrumbs.find(
(b) => b.tags.includes('create') && b.tags.includes('customer'),
);
}
async checkCreateCustomerBreadcrumb() {
@@ -262,7 +322,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
async getSearchBreadcrumb(): Promise<Breadcrumb | undefined> {
const breadcrumbs = await this.getBreadcrumbs();
return breadcrumbs.find((b) => b.tags.includes('list') && b.tags.includes('search'));
return breadcrumbs.find(
(b) => b.tags.includes('list') && b.tags.includes('search'),
);
}
async checkSearchBreadcrumb() {
@@ -288,7 +350,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
const name = this._store.queryParams?.main_qs || 'Suche';
if (!searchBreadcrumb) {
const navigation = this._navigation.listRoute({ processId: this._store.processId });
const navigation = this._navigation.listRoute({
processId: this._store.processId,
});
const breadcrumb: Breadcrumb = {
key: this._store.processId,
tags: ['customer', 'search', 'list'],
@@ -300,7 +364,10 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
this._breadcrumbService.addBreadcrumb(breadcrumb);
} else {
this._breadcrumbService.patchBreadcrumb(searchBreadcrumb.id, { params: this.snapshot.queryParams, name });
this._breadcrumbService.patchBreadcrumb(searchBreadcrumb.id, {
params: this.snapshot.queryParams,
name,
});
}
} else {
if (searchBreadcrumb) {
@@ -311,7 +378,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
async getDetailsBreadcrumb(): Promise<Breadcrumb | undefined> {
const breadcrumbs = await this.getBreadcrumbs();
return breadcrumbs.find((b) => b.tags.includes('details') && b.tags.includes('search'));
return breadcrumbs.find(
(b) => b.tags.includes('details') && b.tags.includes('search'),
);
}
async checkDetailsBreadcrumb() {
@@ -333,7 +402,8 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
].includes(this.breadcrumb)
) {
const customer = this._store.customer;
const fullName = `${customer?.firstName ?? ''} ${customer?.lastName ?? ''}`.trim();
const fullName =
`${customer?.firstName ?? ''} ${customer?.lastName ?? ''}`.trim();
if (!detailsBreadcrumb) {
const navigation = this._navigation.detailsRoute({
@@ -515,7 +585,10 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
async checkOrderDetailsBreadcrumb() {
const orderDetailsBreadcrumb = await this.getOrderDetailsBreadcrumb();
if (this.breadcrumb === 'order-details' || this.breadcrumb === 'order-details-history') {
if (
this.breadcrumb === 'order-details' ||
this.breadcrumb === 'order-details-history'
) {
if (!orderDetailsBreadcrumb) {
const navigation = this._navigation.orderDetialsRoute({
processId: this._store.processId,
@@ -546,7 +619,8 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
}
async checkOrderDetailsHistoryBreadcrumb() {
const orderDetailsHistoryBreadcrumb = await this.getOrderDetailsHistoryBreadcrumb();
const orderDetailsHistoryBreadcrumb =
await this.getOrderDetailsHistoryBreadcrumb();
if (this.breadcrumb === 'order-details-history') {
if (!orderDetailsHistoryBreadcrumb) {
@@ -569,7 +643,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
this._breadcrumbService.addBreadcrumb(breadcrumb);
}
} else if (orderDetailsHistoryBreadcrumb) {
this._breadcrumbService.removeBreadcrumb(orderDetailsHistoryBreadcrumb.id);
this._breadcrumbService.removeBreadcrumb(
orderDetailsHistoryBreadcrumb.id,
);
}
}

View File

@@ -1,5 +1,10 @@
import { inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Params, Router, RouterStateSnapshot } from '@angular/router';
import {
ActivatedRouteSnapshot,
Params,
Router,
RouterStateSnapshot,
} from '@angular/router';
import { DomainCheckoutService } from '@domain/checkout';
import { CustomerCreateFormData, decodeFormData } from '../create-customer';
import { CustomerCreateNavigation } from '@shared/services/navigation';
@@ -9,7 +14,10 @@ export class CustomerCreateGuard {
private checkoutService = inject(DomainCheckoutService);
private customerCreateNavigation = inject(CustomerCreateNavigation);
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
async canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Promise<boolean> {
// exit with true if canActivateChild will be called
if (route.firstChild) {
return true;
@@ -19,10 +27,15 @@ export class CustomerCreateGuard {
const processId = this.getProcessId(route);
const formData = this.getFormData(route);
const canActivateCustomerType = await this.setableCustomerTypes(processId, formData);
const canActivateCustomerType = await this.setableCustomerTypes(
processId,
formData,
);
if (canActivateCustomerType[customerType] !== true) {
customerType = Object.keys(canActivateCustomerType).find((key) => canActivateCustomerType[key]);
customerType = Object.keys(canActivateCustomerType).find(
(key) => canActivateCustomerType[key],
);
}
await this.navigate(processId, customerType, route.queryParams);
@@ -30,9 +43,14 @@ export class CustomerCreateGuard {
return true;
}
async canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
async canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Promise<boolean> {
const processId = this.getProcessId(route);
const customerType = route.routeConfig.path?.replace('create/', '')?.replace('/update', '');
const customerType = route.routeConfig.path
?.replace('create/', '')
?.replace('/update', '');
if (customerType === 'create-customer-main') {
return true;
@@ -40,29 +58,39 @@ export class CustomerCreateGuard {
const formData = this.getFormData(route);
const canActivateCustomerType = await this.setableCustomerTypes(processId, formData);
const canActivateCustomerType = await this.setableCustomerTypes(
processId,
formData,
);
if (canActivateCustomerType[customerType]) {
return true;
}
const activatableCustomerType = Object.keys(canActivateCustomerType)?.find((key) => canActivateCustomerType[key]);
const activatableCustomerType = Object.keys(canActivateCustomerType)?.find(
(key) => canActivateCustomerType[key],
);
await this.navigate(processId, activatableCustomerType, route.queryParams);
return false;
}
async setableCustomerTypes(processId: number, formData: CustomerCreateFormData): Promise<Record<string, boolean>> {
const res = await this.checkoutService.getSetableCustomerTypes(processId).toPromise();
async setableCustomerTypes(
processId: number,
formData: CustomerCreateFormData,
): Promise<Record<string, boolean>> {
const res = await this.checkoutService
.getSetableCustomerTypes(processId)
.toPromise();
if (res.store) {
res['store-p4m'] = true;
}
// if (res.store) {
// res['store-p4m'] = true;
// }
if (res.webshop) {
res['webshop-p4m'] = true;
}
// if (res.webshop) {
// res['webshop-p4m'] = true;
// }
if (formData?._meta) {
const customerType = formData._meta.customerType;
@@ -107,7 +135,11 @@ export class CustomerCreateGuard {
return {};
}
navigate(processId: number, customerType: string, queryParams: Params): Promise<boolean> {
navigate(
processId: number,
customerType: string,
queryParams: Params,
): Promise<boolean> {
const path = this.customerCreateNavigation.createCustomerRoute({
customerType,
processId,

View File

@@ -31,7 +31,9 @@ export class CantAddCustomerToCartModalComponent {
get option() {
return (
this.ref.data.upgradeableTo?.options.values.find((upgradeOption) =>
this.ref.data.required.options.values.some((requiredOption) => upgradeOption.key === requiredOption.key),
this.ref.data.required.options.values.some(
(requiredOption) => upgradeOption.key === requiredOption.key,
),
) || { value: this.queryParams }
);
}
@@ -39,7 +41,9 @@ export class CantAddCustomerToCartModalComponent {
get queryParams() {
let option = this.ref.data.required?.options.values.find((f) => f.selected);
if (!option) {
option = this.ref.data.required?.options.values.find((f) => (isBoolean(f.enabled) ? f.enabled : true));
option = this.ref.data.required?.options.values.find((f) =>
isBoolean(f.enabled) ? f.enabled : true,
);
}
return option ? { customertype: option.value } : {};
}
@@ -57,27 +61,29 @@ export class CantAddCustomerToCartModalComponent {
const queryParams: Record<string, string> = {};
if (customer) {
queryParams['formData'] = encodeFormData(mapCustomerDtoToCustomerCreateFormData(customer));
queryParams['formData'] = encodeFormData(
mapCustomerDtoToCustomerCreateFormData(customer),
);
}
if (option === 'webshop' && attributes.some((a) => a.key === 'p4mUser')) {
const nav = this.customerCreateNavigation.createCustomerRoute({
processId: this.applicationService.activatedProcessId,
customerType: 'webshop-p4m',
});
this.router.navigate(nav.path, {
queryParams: { ...nav.queryParams, ...queryParams },
});
} else {
const nav = this.customerCreateNavigation.createCustomerRoute({
processId: this.applicationService.activatedProcessId,
customerType: option as any,
});
// if (option === 'webshop' && attributes.some((a) => a.key === 'p4mUser')) {
// const nav = this.customerCreateNavigation.createCustomerRoute({
// processId: this.applicationService.activatedProcessId,
// customerType: 'webshop-p4m',
// });
// this.router.navigate(nav.path, {
// queryParams: { ...nav.queryParams, ...queryParams },
// });
// } else {
const nav = this.customerCreateNavigation.createCustomerRoute({
processId: this.applicationService.activatedProcessId,
customerType: option as any,
});
this.router.navigate(nav.path, {
queryParams: { ...nav.queryParams, ...queryParams },
});
}
this.router.navigate(nav.path, {
queryParams: { ...nav.queryParams, ...queryParams },
});
// }
this.ref.close();
}

View File

@@ -1,7 +1,11 @@
<div class="font-bold text-center border-t border-b border-solid border-disabled-customer -mx-4 py-4">
<div
class="font-bold text-center border-t border-b border-solid border-disabled-customer -mx-4 py-4"
>
{{ customer?.communicationDetails?.email }}
</div>
<div class="grid grid-flow-row gap-1 text-sm font-bold border-b border-solid border-disabled-customer -mx-4 py-4 px-14">
<div
class="grid grid-flow-row gap-1 text-sm font-bold border-b border-solid border-disabled-customer -mx-4 py-4 px-14"
>
@if (customer?.organisation?.name) {
<span>{{ customer?.organisation?.name }}</span>
}
@@ -16,23 +20,26 @@
</div>
<div class="grid grid-flow-col gap-4 justify-around mt-12">
<button class="border-2 border-solid border-brand rounded-full font-bold text-brand px-6 py-3 text-lg" (click)="close(false)">
<button
class="border-2 border-solid border-brand rounded-full font-bold text-brand px-6 py-3 text-lg"
(click)="close(false)"
>
neues Onlinekonto anlegen
</button>
@if (!isWebshopWithP4M) {
<button
class="border-2 border-solid border-brand rounded-full font-bold text-white px-6 py-3 text-lg bg-brand"
(click)="close(true)"
>
>
Daten übernehmen
</button>
}
@if (isWebshopWithP4M) {
<!-- @if (isWebshopWithP4M) {
<button
class="border-2 border-solid border-brand rounded-full font-bold text-white px-6 py-3 text-lg bg-brand"
(click)="selectCustomer()"
>
Datensatz auswählen
</button>
}
} -->
</div>

View File

@@ -9,11 +9,11 @@ import { CustomerCreateGuard } from './guards/customer-create.guard';
import {
CreateB2BCustomerComponent,
CreateGuestCustomerComponent,
CreateP4MCustomerComponent,
// CreateP4MCustomerComponent,
CreateStoreCustomerComponent,
CreateWebshopCustomerComponent,
} from './create-customer';
import { UpdateP4MWebshopCustomerComponent } from './create-customer/update-p4m-webshop-customer';
// import { UpdateP4MWebshopCustomerComponent } from './create-customer/update-p4m-webshop-customer';
import { CreateCustomerComponent } from './create-customer/create-customer.component';
import { CustomerDataEditB2BComponent } from './customer-search/edit-main-view/customer-data-edit-b2b.component';
import { CustomerDataEditB2CComponent } from './customer-search/edit-main-view/customer-data-edit-b2c.component';
@@ -40,8 +40,16 @@ export const routes: Routes = [
path: '',
component: CustomerSearchComponent,
children: [
{ path: 'search', component: CustomerMainViewComponent, data: { side: 'main', breadcrumb: 'main' } },
{ path: 'search/list', component: CustomerResultsMainViewComponent, data: { breadcrumb: 'search' } },
{
path: 'search',
component: CustomerMainViewComponent,
data: { side: 'main', breadcrumb: 'main' },
},
{
path: 'search/list',
component: CustomerResultsMainViewComponent,
data: { breadcrumb: 'search' },
},
{
path: 'search/filter',
component: CustomerFilterMainViewComponent,
@@ -80,7 +88,10 @@ export const routes: Routes = [
{
path: 'search/:customerId/orders/:orderId/:orderItemId/history',
component: CustomerOrderDetailsHistoryMainViewComponent,
data: { side: 'order-details', breadcrumb: 'order-details-history' },
data: {
side: 'order-details',
breadcrumb: 'order-details-history',
},
},
{
path: 'search/:customerId/edit/b2b',
@@ -140,13 +151,13 @@ export const routes: Routes = [
{ path: 'create/webshop', component: CreateWebshopCustomerComponent },
{ path: 'create/b2b', component: CreateB2BCustomerComponent },
{ path: 'create/guest', component: CreateGuestCustomerComponent },
{ path: 'create/webshop-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'webshop' } },
{ path: 'create/store-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'store' } },
{
path: 'create/webshop-p4m/update',
component: UpdateP4MWebshopCustomerComponent,
data: { customerType: 'webshop' },
},
// { path: 'create/webshop-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'webshop' } },
// { path: 'create/store-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'store' } },
// {
// path: 'create/webshop-p4m/update',
// component: UpdateP4MWebshopCustomerComponent,
// data: { customerType: 'webshop' },
// },
{
path: 'create-customer-main',
outlet: 'side',

View File

@@ -3,7 +3,10 @@ import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
import { NavigationRoute } from './defs/navigation-route';
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from 'apps/isa-app/src/page/customer';
import {
encodeFormData,
mapCustomerInfoDtoToCustomerCreateFormData,
} from 'apps/isa-app/src/page/customer';
@Injectable({ providedIn: 'root' })
export class CustomerCreateNavigation {
@@ -33,7 +36,9 @@ export class CustomerCreateNavigation {
navigateToDefault(params: { processId: NumberInput }): Promise<boolean> {
const route = this.defaultRoute(params);
return this._router.navigate(route.path, { queryParams: route.queryParams });
return this._router.navigate(route.path, {
queryParams: route.queryParams,
});
}
createCustomerRoute(params: {
@@ -54,7 +59,9 @@ export class CustomerCreateNavigation {
];
let formData = params?.customerInfo
? encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(params.customerInfo))
? encodeFormData(
mapCustomerInfoDtoToCustomerCreateFormData(params.customerInfo),
)
: undefined;
const urlTree = this._router.createUrlTree(path, {
@@ -79,7 +86,9 @@ export class CustomerCreateNavigation {
processId: NumberInput;
customerInfo: CustomerInfoDTO;
}): NavigationRoute {
const formData = encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(customerInfo));
const formData = encodeFormData(
mapCustomerInfoDtoToCustomerCreateFormData(customerInfo),
);
const path = [
'/kunde',
coerceNumberProperty(processId),
@@ -88,14 +97,16 @@ export class CustomerCreateNavigation {
outlets: {
primary: [
'create',
customerInfo?.features?.find((feature) => feature.key === 'webshop') ? 'webshop-p4m' : 'store-p4m',
// customerInfo?.features?.find((feature) => feature.key === 'webshop') ? 'webshop-p4m' : 'store-p4m',
],
side: 'create-customer-main',
},
},
];
const urlTree = this._router.createUrlTree(path, { queryParams: { formData } });
const urlTree = this._router.createUrlTree(path, {
queryParams: { formData },
});
return {
path,