mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1382: Kubi
Related work items: #3228, #3230, #3289, #3467, #3471, #3478
This commit is contained in:
committed by
Andreas Schickinger
parent
4ab3a3b3cf
commit
fdaceb9bf8
31
angular.json
31
angular.json
@@ -3629,6 +3629,37 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@ui/form-field": {
|
||||
"projectType": "library",
|
||||
"root": "apps/ui/form-field",
|
||||
"sourceRoot": "apps/ui/form-field/src",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"project": "apps/ui/form-field/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "apps/ui/form-field/src/test.ts",
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.spec.json",
|
||||
"karmaConfig": "apps/ui/form-field/karma.conf.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "isa-app"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Injectable, Injector, Optional, SkipSelf } from '@angular/core';
|
||||
import { ActionHandler } from './action-handler.interface';
|
||||
import { FEATURE_ACTION_HANDLERS, ROOT_ACTION_HANDLERS } from './tokens';
|
||||
|
||||
@Injectable()
|
||||
export class CommandService {
|
||||
constructor(private injector: Injector) {}
|
||||
constructor(private injector: Injector, @Optional() @SkipSelf() private _parent: CommandService) {}
|
||||
|
||||
async handleCommand<T>(command: string, data?: T): Promise<T> {
|
||||
const actions = this.getActions(command);
|
||||
@@ -15,7 +15,7 @@ export class CommandService {
|
||||
console.error('CommandService.handleCommand', 'Action Handler does not exist', { action });
|
||||
throw new Error('Action Handler does not exist');
|
||||
}
|
||||
console.log('handle command', handler, data);
|
||||
|
||||
data = await handler.handler(data);
|
||||
}
|
||||
return data;
|
||||
@@ -25,10 +25,16 @@ export class CommandService {
|
||||
return command?.split('|') || [];
|
||||
}
|
||||
|
||||
getActionHandler(action: string): ActionHandler {
|
||||
getActionHandler(action: string): ActionHandler | undefined {
|
||||
const featureActionHandlers: ActionHandler[] = this.injector.get(FEATURE_ACTION_HANDLERS, []);
|
||||
const rootActionHandlers: ActionHandler[] = this.injector.get(ROOT_ACTION_HANDLERS, []);
|
||||
|
||||
return [...featureActionHandlers, ...rootActionHandlers].find((handler) => handler.action === action);
|
||||
let handler = [...featureActionHandlers, ...rootActionHandlers].find((handler) => handler.action === action);
|
||||
|
||||
if (this._parent && !handler) {
|
||||
handler = this._parent.getActionHandler(action);
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import * as DomainCheckoutActions from './store/domain-checkout.actions';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CustomerDTO, EntityDTOContainerOfAttributeDTO } from '@swagger/crm';
|
||||
|
||||
@Injectable()
|
||||
export class DomainCheckoutService {
|
||||
@@ -838,10 +839,6 @@ export class DomainCheckoutService {
|
||||
.pipe(map((response) => response.result));
|
||||
}
|
||||
|
||||
setCustomerFeatures({ processId, customerFeatures }: { processId: number; customerFeatures: { [key: string]: string } }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setCustomerFeatures({ processId, customerFeatures }));
|
||||
}
|
||||
|
||||
setOlaErrors({ processId, errorIds }: { processId: number; errorIds: number[] }) {
|
||||
this.store.dispatch(
|
||||
DomainCheckoutActions.setOlaError({
|
||||
@@ -867,6 +864,14 @@ export class DomainCheckoutService {
|
||||
this.store.dispatch(DomainCheckoutActions.removeProcess({ processId }));
|
||||
}
|
||||
|
||||
setCustomer({ processId, customerDto }: { processId: number; customerDto: CustomerDTO }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setCustomer({ processId, customer: customerDto }));
|
||||
}
|
||||
|
||||
getCustomer({ processId }: { processId: number }): Observable<CustomerDTO> {
|
||||
return this.store.select(DomainCheckoutSelectors.selectCustomerByProcessId, { processId });
|
||||
}
|
||||
|
||||
setPayer({ processId, payer }: { processId: number; payer: PayerDTO }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setPayer({ processId, payer }));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { BuyerDTO, CheckoutDTO, NotificationChannel, PayerDTO, ShippingAddressDTO, ShoppingCartDTO } from '@swagger/checkout';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO } from '@swagger/oms';
|
||||
|
||||
export interface CheckoutEntity {
|
||||
processId: number;
|
||||
checkout: CheckoutDTO;
|
||||
shoppingCart: ShoppingCartDTO;
|
||||
customerFeatures: { [key: string]: string };
|
||||
customer: CustomerDTO;
|
||||
payer: PayerDTO;
|
||||
buyer: BuyerDTO;
|
||||
shippingAddress: ShippingAddressDTO;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
BuyerDTO,
|
||||
PayerDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO, DisplayOrderItemDTO } from '@swagger/oms';
|
||||
|
||||
const prefix = '[DOMAIN-CHECKOUT]';
|
||||
@@ -38,11 +39,6 @@ export const setCheckoutDestination = createAction(
|
||||
props<{ processId: number; destination: DestinationDTO }>()
|
||||
);
|
||||
|
||||
export const setCustomerFeatures = createAction(
|
||||
`${prefix} Set Customer Features`,
|
||||
props<{ processId: number; customerFeatures: { [key: string]: string } }>()
|
||||
);
|
||||
|
||||
export const setShippingAddress = createAction(
|
||||
`${prefix} Set Shipping Address`,
|
||||
props<{ processId: number; shippingAddress: ShippingAddressDTO }>()
|
||||
@@ -63,3 +59,5 @@ export const setPayer = createAction(`${prefix} Set Payer`, props<{ processId: n
|
||||
export const setSpecialComment = createAction(`${prefix} Set Agent Comment`, props<{ processId: number; agentComment: string }>());
|
||||
|
||||
export const setOlaError = createAction(`${prefix} Set Ola Error`, props<{ processId: number; olaErrorIds: number[] }>());
|
||||
|
||||
export const setCustomer = createAction(`${prefix} Set Customer`, props<{ processId: number; customer: CustomerDTO }>());
|
||||
|
||||
@@ -46,11 +46,6 @@ const _domainCheckoutReducer = createReducer(
|
||||
};
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setCustomerFeatures, (s, { processId, customerFeatures }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.customerFeatures = customerFeatures;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setShippingAddress, (s, { processId, shippingAddress }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.shippingAddress = shippingAddress;
|
||||
@@ -100,6 +95,11 @@ const _domainCheckoutReducer = createReducer(
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.olaErrorIds = olaErrorIds;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setCustomer, (s, { processId, customer }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.customer = customer;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -115,7 +115,6 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
processId,
|
||||
checkout: undefined,
|
||||
shoppingCart: undefined,
|
||||
customerFeatures: undefined,
|
||||
shippingAddress: undefined,
|
||||
orders: [],
|
||||
payer: undefined,
|
||||
@@ -123,6 +122,7 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
specialComment: '',
|
||||
notificationChannels: 0,
|
||||
olaErrorIds: [],
|
||||
customer: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { CheckoutEntity } from './defs/checkout.entity';
|
||||
import { storeCheckoutAdapter, storeFeatureSelector } from './domain-checkout.state';
|
||||
|
||||
@@ -22,7 +23,7 @@ export const selectCheckoutByProcessId = createSelector(
|
||||
|
||||
export const selectCustomerFeaturesByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.customerFeatures
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => getCusomterFeatures(entities[processId]?.customer)
|
||||
);
|
||||
|
||||
export const selectShippingAddressByProcessId = createSelector(
|
||||
@@ -61,3 +62,19 @@ export const selectOlaErrorsByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.olaErrorIds
|
||||
);
|
||||
|
||||
export const selectCustomerByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.customer
|
||||
);
|
||||
|
||||
function getCusomterFeatures(custoemr: CustomerDTO): { [key: string]: string } {
|
||||
const customerFeatures = custoemr?.features ?? [];
|
||||
const features: { [key: string]: string } = {};
|
||||
|
||||
for (const feature of customerFeatures) {
|
||||
features[feature.key] = feature.key;
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,10 @@ export class CrmCustomerService {
|
||||
communicationDetails: customer?.communicationDetails,
|
||||
});
|
||||
|
||||
return this.customerService.CustomerCreateCustomer({ ...customer, customerType: 16, notificationChannels });
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: { ...customer, customerType: 16, notificationChannels },
|
||||
modifiers: [{ key: 'b2b', group: 'customertype' }],
|
||||
});
|
||||
}
|
||||
|
||||
createOnlineCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
@@ -157,7 +160,10 @@ export class CrmCustomerService {
|
||||
];
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateOnlineCustomer({ ...payload, notificationChannels });
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: { ...payload, notificationChannels },
|
||||
modifiers: [{ key: 'webshop', group: 'customertype' }],
|
||||
});
|
||||
}
|
||||
|
||||
createGuestCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
@@ -202,20 +208,44 @@ export class CrmCustomerService {
|
||||
];
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateOnlineCustomer(payload);
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: payload,
|
||||
modifiers: [{ key: 'webshop', group: 'customertype' }],
|
||||
});
|
||||
}
|
||||
|
||||
createBranchCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
createStoreCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails: customer?.communicationDetails,
|
||||
});
|
||||
return this.customerService.CustomerCreateCustomer({ ...customer, customerType: 8, notificationChannels });
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: { ...customer, customerType: 8, notificationChannels },
|
||||
modifiers: [{ key: 'store', group: 'customertype' }],
|
||||
});
|
||||
}
|
||||
|
||||
validateAddress(address: AddressDTO): Observable<Result<AddressDTO[]>> {
|
||||
return this.addressService.AddressValidateAddress(address);
|
||||
}
|
||||
|
||||
getOnlineCustomerByEmail(email: string): Observable<CustomerInfoDTO | null> {
|
||||
return this.getCustomers(email, {
|
||||
take: 1,
|
||||
filter: {
|
||||
customertype: 'webshop',
|
||||
},
|
||||
}).pipe(
|
||||
map((r) => {
|
||||
if (r.hits === 1) {
|
||||
return r.result[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
catchError((err) => [null])
|
||||
);
|
||||
}
|
||||
|
||||
private cachedCountriesFailed = false;
|
||||
private cachedCountries: ReplaySubject<Result<CountryDTO[]>>;
|
||||
getCountries(): Observable<Result<CountryDTO[]>> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { DialogOfString } from '@swagger/crm';
|
||||
|
||||
export interface Result<T> {
|
||||
/** Ergebnis */
|
||||
result?: T;
|
||||
@@ -13,4 +15,6 @@ export interface Result<T> {
|
||||
|
||||
/** Fehlerhafte Daten */
|
||||
invalidProperties?: { [key: string]: string };
|
||||
|
||||
dialog?: DialogOfString;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
PrintRequestOfIEnumerableOfPriceQRCodeDTO,
|
||||
PrintService,
|
||||
ResponseArgs,
|
||||
LoyaltyCardPrintService,
|
||||
} from '@swagger/print';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, filter, map, switchMap, timeout } from 'rxjs/operators';
|
||||
@@ -25,7 +26,8 @@ export class DomainPrinterService {
|
||||
private oMSPrintService: OMSPrintService,
|
||||
private catalogPrintService: CatalogPrintService,
|
||||
private checkoutPrintService: CheckoutPrintService,
|
||||
private eisPublicDocumentService: EISPublicDocumentService
|
||||
private eisPublicDocumentService: EISPublicDocumentService,
|
||||
private _loyaltyCardPrintService: LoyaltyCardPrintService
|
||||
) {}
|
||||
|
||||
getAvailablePrinters(): Observable<Printer[] | { error: string }> {
|
||||
@@ -143,6 +145,13 @@ export class DomainPrinterService {
|
||||
});
|
||||
}
|
||||
|
||||
printKubiAgb({ p4mCode, printer }: { p4mCode: string; printer: string }) {
|
||||
return this._loyaltyCardPrintService.LoyaltyCardPrintPrintLoyaltyCardAGB({
|
||||
printer,
|
||||
data: p4mCode,
|
||||
});
|
||||
}
|
||||
|
||||
printProduct({ item, printer }: { item: ItemDTO; printer: string }): Observable<ResponseArgs> {
|
||||
const params = <PrintRequestOfIEnumerableOfItemDTO>{
|
||||
printer: printer,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { PlatformModule } from '@angular/cdk/platform';
|
||||
|
||||
import { Config, ConfigModule, JsonConfigLoader } from '@core/config';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { CoreCommandModule } from '@core/command';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -30,6 +31,7 @@ import { IsaLogProvider } from './providers';
|
||||
import { IsaErrorHandler } from './providers/isa.error-handler';
|
||||
import { ScanAdapterModule } from '@adapter/scan';
|
||||
import { RootStateService } from './store/root-state.service';
|
||||
import * as Commands from './commands';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
@@ -70,6 +72,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
useConfigLoader: JsonConfigLoader,
|
||||
jsonConfigLoaderUrl: '/config/config.json',
|
||||
}),
|
||||
CoreCommandModule.forRoot(Object.values(Commands)),
|
||||
CoreLoggerModule.forRoot(),
|
||||
AppStoreModule,
|
||||
AuthModule.forRoot(),
|
||||
|
||||
14
apps/isa-app/src/app/commands/close.command.ts
Normal file
14
apps/isa-app/src/app/commands/close.command.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
|
||||
/** Dummy Command um Fehlermeldungen aus dem Diloag zu verhinden */
|
||||
@Injectable()
|
||||
export class CloseCommand extends ActionHandler<any> {
|
||||
constructor() {
|
||||
super('CLOSE');
|
||||
}
|
||||
|
||||
handler(ctx: any): any {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
35
apps/isa-app/src/app/commands/create-customer.command.ts
Normal file
35
apps/isa-app/src/app/commands/create-customer.command.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Injectable()
|
||||
export class CreateCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _router: Router, private _application: ApplicationService) {
|
||||
super('CREATE_CUSTOMER');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
let customerType: string;
|
||||
if (data.result.length > 0) {
|
||||
const customerInfo = data.result[0];
|
||||
if (customerInfo.features) {
|
||||
if (customerInfo.features.some((f) => f.key === 'store')) {
|
||||
customerType = 'store';
|
||||
}
|
||||
if (customerInfo.features.some((f) => f.key === 'webshop')) {
|
||||
customerType = 'webshop';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!customerType) {
|
||||
customerType = 'store';
|
||||
}
|
||||
|
||||
await this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'create', customerType]);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from 'apps/page/customer/src/lib/create-customer';
|
||||
|
||||
@Injectable()
|
||||
export class CreateKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _router: Router, private _application: ApplicationService) {
|
||||
super('CREATE_KUBI_CUSTOMER');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
let customerType: string;
|
||||
let formData: string;
|
||||
if (data.result.length > 0) {
|
||||
const customerInfo = data.result[0];
|
||||
const fd = mapCustomerInfoDtoToCustomerCreateFormData(customerInfo);
|
||||
formData = encodeFormData({
|
||||
...fd,
|
||||
agb: false,
|
||||
});
|
||||
if (customerInfo.features) {
|
||||
if (customerInfo.features.some((f) => f.key === 'store')) {
|
||||
customerType = 'store';
|
||||
}
|
||||
if (customerInfo.features.some((f) => f.key === 'webshop')) {
|
||||
customerType = 'webshop';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!customerType) {
|
||||
customerType = 'store';
|
||||
}
|
||||
|
||||
await this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'create', `${customerType}-p4m`], {
|
||||
queryParams: { formData },
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
4
apps/isa-app/src/app/commands/index.ts
Normal file
4
apps/isa-app/src/app/commands/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './close.command';
|
||||
export * from './create-customer.command';
|
||||
export * from './create-kubi-customer.command';
|
||||
export * from './print-kubi-agb.command';
|
||||
34
apps/isa-app/src/app/commands/print-kubi-agb.command.ts
Normal file
34
apps/isa-app/src/app/commands/print-kubi-agb.command.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
|
||||
@Injectable()
|
||||
export class PrintKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _uiModal: UiModalService, private _printerService: DomainPrinterService) {
|
||||
super('PRINT_KUBI_AGB');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
const customerInfo = data.result ? data.result[0] : undefined;
|
||||
let p4mCode: string;
|
||||
if (customerInfo) {
|
||||
p4mCode = customerInfo.features.find((f) => f.key === 'p4mUser').value;
|
||||
}
|
||||
|
||||
await this._uiModal
|
||||
.open({
|
||||
content: PrintModalComponent,
|
||||
config: { showScrollbarY: false },
|
||||
data: {
|
||||
printerType: 'Label',
|
||||
print: (printer) => this._printerService.printKubiAgb({ printer, p4mCode }).toPromise(),
|
||||
} as PrintModalData,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@
|
||||
Weiter einkaufen
|
||||
</button>
|
||||
<button *ngIf="canUpgrade$ | async" class="cta-upgrade-customer" (click)="continue('add-customer-data')">
|
||||
Kundendaten hinzufügen
|
||||
Kundendaten erfassen
|
||||
</button>
|
||||
<button
|
||||
*ngIf="showTakeAwayButton$ | async"
|
||||
|
||||
@@ -10,6 +10,10 @@ import { PurchasingOptionsModalData } from './purchasing-options-modal.data';
|
||||
import { PurchasingOptionsModalStore } from './purchasing-options-modal.store';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import {
|
||||
encodeFormData,
|
||||
mapCustomerDtoToCustomerCreateFormData,
|
||||
} from 'apps/page/customer/src/lib/create-customer/customer-create-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'page-purchasing-options-modal',
|
||||
@@ -111,10 +115,7 @@ export class PurchasingOptionsModalComponent {
|
||||
switchMap((processId) => this.checkoutService.getCustomerFeatures({ processId }))
|
||||
);
|
||||
|
||||
readonly customerId$ = this.application.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.checkoutService.getBuyer({ processId })),
|
||||
map((buyer) => buyer.source)
|
||||
);
|
||||
readonly customer$ = this.application.activatedProcessId$.pipe(switchMap((processId) => this.checkoutService.getCustomer({ processId })));
|
||||
|
||||
price$ = combineLatest([
|
||||
this.purchasingOptionsModalStore.selectAvailabilities,
|
||||
@@ -228,7 +229,7 @@ export class PurchasingOptionsModalComponent {
|
||||
this.activeSpinner = navigate ? 'continue-shopping' : 'continue';
|
||||
try {
|
||||
const processId = await this.purchasingOptionsModalStore.selectProcessId.pipe(first()).toPromise();
|
||||
const customer = await this.checkoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
const buyer = await this.checkoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
const item = await this.item$.pipe(first()).toPromise();
|
||||
const quantity = await this.quantity$.pipe(first()).toPromise();
|
||||
const availability = await this.availability$.pipe(first()).toPromise();
|
||||
@@ -238,6 +239,7 @@ export class PurchasingOptionsModalComponent {
|
||||
const canAdd = await this.canAdd$.pipe(first()).toPromise();
|
||||
const customPrice = await this.purchasingOptionsModalStore.selectCustomPrice.pipe(first()).toPromise();
|
||||
const customVat = await this.purchasingOptionsModalStore.selectCustomVat.pipe(first()).toPromise();
|
||||
const customer = await this.checkoutService.getCustomer({ processId }).pipe(first()).toPromise();
|
||||
|
||||
if (canAdd || navigate === 'add-customer-data') {
|
||||
const newItem: AddToShoppingCartDTO = {
|
||||
@@ -340,7 +342,7 @@ export class PurchasingOptionsModalComponent {
|
||||
} else if (navigate === 'continue') {
|
||||
// Set filter for navigation to customer search if customer is not set
|
||||
let filter: { [key: string]: string };
|
||||
if (!customer) {
|
||||
if (!buyer) {
|
||||
filter = await this.customerFeatures$
|
||||
.pipe(
|
||||
first(),
|
||||
@@ -357,10 +359,15 @@ export class PurchasingOptionsModalComponent {
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'cart', 'review']);
|
||||
}
|
||||
} else if (navigate === 'add-customer-data') {
|
||||
const upgradeCustomerId = await this.customerId$.pipe(first()).toPromise();
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop'], {
|
||||
queryParams: { upgradeCustomerId },
|
||||
});
|
||||
if (customer?.attributes.some((attr) => attr.data.key === 'p4mUser')) {
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop-p4m'], {
|
||||
queryParams: { formData: encodeFormData(mapCustomerDtoToCustomerCreateFormData(customer)) },
|
||||
});
|
||||
} else {
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop'], {
|
||||
queryParams: { upgradeCustomerId: customer?.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('PurchasingOptionsModalComponent.continue', error);
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ChangeDetectorRef, Directive, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, AsyncValidatorFn, FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CustomerDTO, ShippingAddressDTO } from '@swagger/crm';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isNull } from 'lodash';
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs';
|
||||
import { first, map, distinctUntilChanged, shareReplay, delay, mergeMap, catchError, tap, debounceTime, takeUntil } from 'rxjs/operators';
|
||||
import { AddressFormBlockComponent, DeviatingAddressFormBlockComponent, DeviatingAddressFormBlockData } from '../form-blocks';
|
||||
import { FormBlock } from '../form-blocks/form-block';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CustomerCreateFormData, decodeFormData, encodeFormData } from './customer-create-form-data';
|
||||
|
||||
@Directive()
|
||||
export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
protected onDestroy$ = new Subject<void>();
|
||||
|
||||
abstract validateAddress?: boolean;
|
||||
abstract validateShippingAddress?: boolean;
|
||||
|
||||
private _formData = new BehaviorSubject<CustomerCreateFormData>({});
|
||||
|
||||
formData$ = this._formData.asObservable();
|
||||
|
||||
get formData() {
|
||||
return this._formData.getValue();
|
||||
}
|
||||
|
||||
form = new FormGroup({});
|
||||
|
||||
processId$: Observable<number>;
|
||||
|
||||
busy$ = new BehaviorSubject(false);
|
||||
|
||||
customerExists$ = new Subject<boolean>();
|
||||
|
||||
@ViewChild(DeviatingAddressFormBlockComponent)
|
||||
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
|
||||
@ViewChild(AddressFormBlockComponent)
|
||||
addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
constructor(
|
||||
protected activatedRoute: ActivatedRoute,
|
||||
protected router: Router,
|
||||
protected customerService: CrmCustomerService,
|
||||
protected addressVlidationModal: AddressSelectionModalService,
|
||||
protected modal: UiModalService,
|
||||
protected breadcrumb: BreadcrumbService,
|
||||
protected cdr: ChangeDetectorRef
|
||||
) {
|
||||
this._initProcessId$();
|
||||
this._initFormData();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initBreadcrumb();
|
||||
}
|
||||
|
||||
async initBreadcrumb() {
|
||||
await this.cleanupBreadcrumb();
|
||||
await this.addOrUpdateBreadcrumb();
|
||||
}
|
||||
|
||||
async cleanupBreadcrumb() {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer', 'main']).pipe(first()).toPromise();
|
||||
for (const crumb of crumbs) {
|
||||
await this.breadcrumb.removeBreadcrumbsAfter(crumb.id);
|
||||
}
|
||||
}
|
||||
|
||||
async addOrUpdateBreadcrumb() {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Kundendaten erfassen',
|
||||
path: this.getPath(),
|
||||
params: this.getQueryParams(),
|
||||
tags: ['customer', 'create'],
|
||||
section: 'customer',
|
||||
});
|
||||
}
|
||||
|
||||
getPath() {
|
||||
let path = '';
|
||||
let current = this.activatedRoute;
|
||||
|
||||
while (current) {
|
||||
path = current.snapshot.url.join('/') + '/' + path;
|
||||
current = current.parent;
|
||||
}
|
||||
|
||||
while (path.includes('//')) {
|
||||
path = path.replace('//', '/');
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
getQueryParams(): Record<string, string> {
|
||||
const formData = this.formData ? encodeFormData(this.formData) : undefined;
|
||||
return { ...this.activatedRoute.snapshot.queryParams, formData };
|
||||
}
|
||||
|
||||
private _initProcessId$(): void {
|
||||
this.processId$ = this.activatedRoute.parent.parent.data.pipe(
|
||||
map((data) => +data.processId),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
private _initFormData(): void {
|
||||
const formData = this.activatedRoute.snapshot.queryParams.formData;
|
||||
|
||||
if (formData) {
|
||||
const parsedFormData = decodeFormData(formData);
|
||||
this._formData.next(parsedFormData);
|
||||
}
|
||||
}
|
||||
|
||||
async customerTypeChanged(customerType: string) {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
this.router.navigate(['/kunde', processId, 'customer', 'create', customerType], {
|
||||
queryParams: this.getQueryParams(),
|
||||
});
|
||||
}
|
||||
|
||||
addFormBlock(key: keyof CustomerCreateFormData, block: FormBlock<any, AbstractControl>) {
|
||||
this.form.addControl(key, block.control);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
patchFormData(key: keyof CustomerCreateFormData, value: any) {
|
||||
this._formData.next({ ...this.formData, [key]: value });
|
||||
}
|
||||
|
||||
emailExistsValidator: AsyncValidatorFn = (control) => {
|
||||
return of(control.value).pipe(
|
||||
tap((_) => this.customerExists$.next(false)),
|
||||
delay(500),
|
||||
mergeMap((value) => {
|
||||
return this.customerService.emailExists(value).pipe(
|
||||
map((response) => {
|
||||
if (response?.result) {
|
||||
return { exists: response?.message };
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error?.error?.invalidProperties?.email) {
|
||||
return of({ invalid: error.error.invalidProperties.email });
|
||||
} else {
|
||||
return of({ invalid: 'E-Mail ist ungültig' });
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
tap((error) => {
|
||||
if (error) {
|
||||
this.customerExists$.next(true);
|
||||
}
|
||||
control.markAsTouched();
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
async navigateToCustomerDetails(customer: CustomerDTO) {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
return this.router.navigate(['/kunde', processId, 'customer', customer.id]);
|
||||
}
|
||||
|
||||
async validateAddressData(address: AddressDTO): Promise<AddressDTO> {
|
||||
const addressValidationResult = await this.addressVlidationModal.validateAddress(address);
|
||||
|
||||
if (addressValidationResult !== undefined && addressValidationResult !== 'continue') {
|
||||
address = addressValidationResult;
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
async getCustomerFromFormData(): Promise<CustomerDTO> {
|
||||
const data: CustomerCreateFormData = this.form.value;
|
||||
|
||||
const customer: CustomerDTO = {
|
||||
communicationDetails: {},
|
||||
attributes: [],
|
||||
features: [],
|
||||
};
|
||||
|
||||
if (data.name) {
|
||||
customer.gender = data.name.gender;
|
||||
customer.title = data.name.title;
|
||||
customer.firstName = data.name.firstName;
|
||||
customer.lastName = data.name.lastName;
|
||||
}
|
||||
|
||||
if (data.organisation) {
|
||||
customer.organisation = data.organisation;
|
||||
}
|
||||
|
||||
if (data.email) {
|
||||
customer.communicationDetails.email = data.email;
|
||||
}
|
||||
|
||||
if (data.phoneNumbers) {
|
||||
customer.communicationDetails.mobile = data.phoneNumbers.mobile;
|
||||
customer.communicationDetails.phone = data.phoneNumbers.phone;
|
||||
}
|
||||
|
||||
if (data.address) {
|
||||
customer.address = data.address;
|
||||
|
||||
if (this.validateAddress) {
|
||||
try {
|
||||
const address = await this.validateAddressData(customer.address);
|
||||
this.addressFormBlock.data = address;
|
||||
customer.address = address;
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
setTimeout(() => {
|
||||
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
|
||||
}, 10);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.birthDate && isNull(UiValidators.date(new FormControl(data.birthDate)))) {
|
||||
customer.dateOfBirth = data.birthDate;
|
||||
}
|
||||
|
||||
if (data.deviatingDeliveryAddress?.deviatingAddress) {
|
||||
const shippingAddress = this.mapToShippingAddress(data.deviatingDeliveryAddress);
|
||||
|
||||
if (this.validateShippingAddress) {
|
||||
try {
|
||||
debugger;
|
||||
shippingAddress.address = await this.validateAddressData(shippingAddress.address);
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
setTimeout(() => {
|
||||
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
|
||||
}, 10);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
customer.shippingAddresses = [
|
||||
{
|
||||
data: shippingAddress,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return customer;
|
||||
}
|
||||
|
||||
mapToShippingAddress({ name, address, email, organisation, phoneNumbers }: DeviatingAddressFormBlockData): ShippingAddressDTO {
|
||||
return {
|
||||
gender: name?.gender,
|
||||
title: name?.title,
|
||||
firstName: name?.firstName,
|
||||
lastName: name?.lastName,
|
||||
address,
|
||||
communicationDetails: {
|
||||
email: email ? email : null,
|
||||
mobile: phoneNumbers?.mobile ? phoneNumbers.mobile : null,
|
||||
phone: phoneNumbers?.phone ? phoneNumbers.phone : null,
|
||||
},
|
||||
organisation,
|
||||
isDefault: new Date().toJSON(),
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next();
|
||||
this.onDestroy$.complete();
|
||||
this.busy$.complete();
|
||||
this._formData.complete();
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (this.form.invalid) {
|
||||
this.form.markAllAsTouched();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.busy$.next(true);
|
||||
this.form.disable();
|
||||
const customer: CustomerDTO = await this.getCustomerFromFormData();
|
||||
|
||||
if (!customer) {
|
||||
this.form.enable();
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await this.saveCustomer(customer);
|
||||
|
||||
if (!!response) {
|
||||
this.navigateToCustomerDetails(response);
|
||||
}
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
this.modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
data: error,
|
||||
});
|
||||
} finally {
|
||||
this.busy$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
abstract saveCustomer(customer: CustomerDTO): Promise<CustomerDTO>;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service <br />
|
||||
zu ermöglichen, legen wir Ihnen <br />
|
||||
gerne ein Kundenkonto an. <br />
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="b2b"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
>
|
||||
</app-customer-type-selector>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[requiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[validatorFns]="organisationFormBlockValidators"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
>
|
||||
</app-address-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
>
|
||||
</app-email-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
>
|
||||
</app-phone-numbers-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
[data]="data.deviationgDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviationgDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviationgDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[organisationRequiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[organisationValidatorFns]="organisationFormBlockValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
[email]="true"
|
||||
[phoneNumbers]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-b2b-customer',
|
||||
templateUrl: 'create-b2b-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-b2b-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateB2BCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
|
||||
organisationFormBlockRequiredMarks = ['name'];
|
||||
|
||||
organisationFormBlockValidators: Record<string, ValidatorFn[]> = {
|
||||
name: [Validators.required],
|
||||
};
|
||||
|
||||
addressRequiredMarks = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
addressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
|
||||
emailFormBlockValidators = [Validators.email, validateEmail];
|
||||
|
||||
deviatingNameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
gender: [Validators.required],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
return await this.customerService
|
||||
.createB2BCustomer(customer)
|
||||
.pipe(map((r) => r.result))
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CreateB2BCustomerComponent } from './create-b2b-customer.component';
|
||||
import { OrganisationFormBlockModule } from '../../form-blocks/organisation';
|
||||
import { NameFormBlockModule } from '../../form-blocks/name';
|
||||
import { AddressFormBlockModule } from '../../form-blocks/address';
|
||||
import { CustomerTypeSelectorModule } from '../../shared/customer-type-selector';
|
||||
import { DeviatingAddressFormBlockComponentModule, EmailFormBlockModule } from '../../form-blocks';
|
||||
import { PhoneNumbersFormBlockModule } from '../../form-blocks/phone-numbers/phone-numbers-form-block.module';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
OrganisationFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
CustomerTypeSelectorModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [CreateB2BCustomerComponent],
|
||||
declarations: [CreateB2BCustomerComponent],
|
||||
})
|
||||
export class CreateB2BCustomerModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './create-b2b-customer.component';
|
||||
export * from './create-b2b-customer.module';
|
||||
@@ -0,0 +1,27 @@
|
||||
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 { 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';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CreateB2BCustomerModule,
|
||||
CreateGuestCustomerModule,
|
||||
CreateStoreCustomerModule,
|
||||
CreateWebshopCustomerModule,
|
||||
CreateP4MCustomerModule,
|
||||
UpdateP4MWebshopCustomerModule,
|
||||
],
|
||||
exports: [
|
||||
CreateB2BCustomerModule,
|
||||
CreateGuestCustomerModule,
|
||||
CreateStoreCustomerModule,
|
||||
CreateWebshopCustomerModule,
|
||||
CreateP4MCustomerModule,
|
||||
UpdateP4MWebshopCustomerModule,
|
||||
],
|
||||
})
|
||||
export class CreateCustomerModule {}
|
||||
@@ -0,0 +1,42 @@
|
||||
:host {
|
||||
@apply block bg-white rounded-card px-20 py-10;
|
||||
}
|
||||
|
||||
h1.title {
|
||||
@apply text-2xl font-bold text-center mb-6;
|
||||
}
|
||||
|
||||
p.description {
|
||||
@apply text-xl text-center;
|
||||
}
|
||||
|
||||
p.info {
|
||||
@apply mt-8 font-semibold;
|
||||
}
|
||||
|
||||
form {
|
||||
@apply relative pb-4;
|
||||
}
|
||||
|
||||
button.cta-submit {
|
||||
@apply sticky left-1/2 bottom-8 text-center bg-brand text-cta-l text-white font-bold px-7 py-3 rounded-full transform -translate-x-1/2 transition-all duration-200 ease-in-out;
|
||||
|
||||
&:disabled {
|
||||
@apply bg-active-branch cursor-not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
app-newsletter-form-block,
|
||||
app-accept-agb-form-block,
|
||||
app-interests-form-block,
|
||||
app-deviating-address-form-block {
|
||||
@apply mt-8;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
@apply w-full h-8;
|
||||
}
|
||||
|
||||
app-customer-type-selector {
|
||||
@apply mt-8;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service <br />
|
||||
zu ermöglichen, legen wir Ihnen <br />
|
||||
gerne ein Kundenkonto an. <br />
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="guest"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
>
|
||||
</app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
[requiredMark]="true"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
>
|
||||
</app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexStart + 1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
appearence="compact"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
>
|
||||
</app-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
>
|
||||
</app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
>
|
||||
</app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
[data]="data.deviationgDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviationgDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviationgDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { DeviatingAddressFormBlockComponent } from '../../form-blocks';
|
||||
import { AddressFormBlockComponent } from '../../form-blocks/address';
|
||||
import { NameFormBlockData } from '../../form-blocks/name/name-form-block-data';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-guest-customer',
|
||||
templateUrl: 'create-guest-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-guest-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
|
||||
nameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
title: [],
|
||||
};
|
||||
|
||||
addressRequiredMarks = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
addressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
|
||||
emailFormBlockValidators = [Validators.email, validateEmail, Validators.required];
|
||||
|
||||
deviatingNameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
gender: [Validators.required],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
|
||||
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
|
||||
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
return await this.customerService
|
||||
.createGuestCustomer(customer)
|
||||
.pipe(map((r) => r.result))
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CreateGuestCustomerComponent } from './create-guest-customer.component';
|
||||
import { OrganisationFormBlockModule } from '../../form-blocks/organisation';
|
||||
import { NameFormBlockModule } from '../../form-blocks/name';
|
||||
import { AddressFormBlockModule } from '../../form-blocks/address';
|
||||
import { CustomerTypeSelectorModule } from '../../shared/customer-type-selector';
|
||||
import { BirthDateFormBlockModule, DeviatingAddressFormBlockComponentModule, EmailFormBlockModule } from '../../form-blocks';
|
||||
import { PhoneNumbersFormBlockModule } from '../../form-blocks/phone-numbers/phone-numbers-form-block.module';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
OrganisationFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
CustomerTypeSelectorModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [CreateGuestCustomerComponent],
|
||||
declarations: [CreateGuestCustomerComponent],
|
||||
})
|
||||
export class CreateGuestCustomerModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './create-guest-customer.component';
|
||||
export * from './create-guest-customer.module';
|
||||
@@ -0,0 +1,142 @@
|
||||
<form *ngIf="formData$ | async; let data" (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> -->
|
||||
</h1>
|
||||
|
||||
<p class="description">Haben Sie eine Kundenkarte?</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="true"
|
||||
[customerType]="customerType"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
[p4mReadonly]="data?._meta?.p4mRequired"
|
||||
>
|
||||
</app-customer-type-selector>
|
||||
|
||||
<app-p4m-number-form-block
|
||||
#p4mBlock
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('p4m', $event)"
|
||||
[data]="data.p4m"
|
||||
(dataChanges)="patchFormData('p4m', $event)"
|
||||
[readonly]="data?._meta?.p4mRequired"
|
||||
[focusAfterInit]="!data?._meta?.p4mRequired"
|
||||
>
|
||||
</app-p4m-number-form-block>
|
||||
|
||||
<app-newsletter-form-block
|
||||
#newsletterBlock
|
||||
[tabIndexStart]="p4mBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('newsletter', $event)"
|
||||
[data]="data.newsletter"
|
||||
(dataChanges)="patchFormData('newsletter', $event)"
|
||||
[focusAfterInit]="data?._meta?.p4mRequired"
|
||||
>
|
||||
</app-newsletter-form-block>
|
||||
|
||||
<app-name-form-block
|
||||
#nameBlock
|
||||
[tabIndexStart]="newsletterBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[data]="data.name"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
class="flex-grow"
|
||||
#email
|
||||
[tabIndexStart]="nameBlock.tabIndexEnd + 1"
|
||||
[requiredMark]="emailRequiredMark"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailValidatorFn"
|
||||
[asyncValidatorFns]="asyncEmailVlaidtorFn"
|
||||
>
|
||||
</app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orgBlock
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="compact"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
#addressBlock
|
||||
[tabIndexStart]="orgBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
>
|
||||
</app-address-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
#ddaBlock
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[tabIndexStart]="addressBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="shippingAddressRequiredMarks"
|
||||
[addressValidatorFns]="shippingAddressValidators"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="ddaBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#bdBlock
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="birthDateValidatorFns"
|
||||
>
|
||||
</app-birth-date-form-block>
|
||||
|
||||
<app-interests-form-block
|
||||
#inBlock
|
||||
[tabIndexStart]="bdBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('interests', $event)"
|
||||
[data]="data.interests"
|
||||
(dataChanges)="patchFormData('interests', $event)"
|
||||
></app-interests-form-block>
|
||||
|
||||
<app-accept-agb-form-block
|
||||
[tabIndexStart]="inBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('agb', $event)"
|
||||
[data]="data.agb"
|
||||
(dataChanges)="patchFormData('agb', $event)"
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="agbValidatorFns"
|
||||
>
|
||||
</app-accept-agb-form-block>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,255 @@
|
||||
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 '@swagger/crm';
|
||||
import { UiErrorModalComponent, UiModalResult } from '@ui/modal';
|
||||
import { NEVER, Observable, of } from 'rxjs';
|
||||
import { catchError, distinctUntilChanged, first, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { AddressFormBlockComponent, DeviatingAddressFormBlockComponent } from '../../form-blocks';
|
||||
import { NameFormBlockData } from '../../form-blocks/name/name-form-block-data';
|
||||
import { WebshopCustomnerAlreadyExistsModalComponent } from '../../modals';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
import { encodeFormData, mapCustomerDtoToCustomerCreateFormData } from '../customer-create-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-p4m-customer',
|
||||
templateUrl: 'create-p4m-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-p4m-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateP4MCustomerComponent extends AbstractCreateCustomer implements OnInit {
|
||||
validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
|
||||
get customerType() {
|
||||
return this.activatedRoute.snapshot.data.customerType;
|
||||
}
|
||||
|
||||
nameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
title: [],
|
||||
};
|
||||
|
||||
emailRequiredMark: boolean;
|
||||
|
||||
emailValidatorFn: ValidatorFn[];
|
||||
|
||||
asyncEmailVlaidtorFn: AsyncValidatorFn[];
|
||||
|
||||
shippingAddressRequiredMarks = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
shippingAddressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
|
||||
addressRequiredMarks: string[];
|
||||
|
||||
addressValidatorFns: Record<string, ValidatorFn[]>;
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
|
||||
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
|
||||
agbValidatorFns = [Validators.requiredTrue];
|
||||
|
||||
birthDateValidatorFns = [Validators.required];
|
||||
|
||||
existingCustomer$: Observable<CustomerInfoDTO | 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;
|
||||
})
|
||||
)
|
||||
.subscribe((customer) => {
|
||||
if (customer) {
|
||||
this.modal
|
||||
.open({
|
||||
content: WebshopCustomnerAlreadyExistsModalComponent,
|
||||
data: customer,
|
||||
title: 'Es existiert bereits ein Onlinekonto mit dieser E-Mail-Adresse',
|
||||
})
|
||||
.afterClosed$.subscribe(async (result: UiModalResult<boolean>) => {
|
||||
if (result.data) {
|
||||
this.navigateToUpdatePage(customer);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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() {
|
||||
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];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getInterests(): KeyValueDTOOfStringAndString[] {
|
||||
const interests: KeyValueDTOOfStringAndString[] = [];
|
||||
|
||||
for (const key in this.formData.interests) {
|
||||
if (this.formData.interests[key]) {
|
||||
interests.push({ key, group: 'KUBI_INTERESSEN' });
|
||||
}
|
||||
}
|
||||
|
||||
return interests;
|
||||
}
|
||||
|
||||
getNewsletter(): KeyValueDTOOfStringAndString | undefined {
|
||||
if (this.formData.newsletter) {
|
||||
return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
|
||||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
|
||||
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
|
||||
if (customerDto) {
|
||||
customer = { ...customerDto, ...customer };
|
||||
} else if (customerInfoDto) {
|
||||
customer = { ...this.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 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();
|
||||
}
|
||||
|
||||
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 (isWebshop) {
|
||||
res = await this.customerService.createOnlineCustomer(customer).toPromise();
|
||||
} else {
|
||||
res = await this.customerService.createStoreCustomer(customer).toPromise();
|
||||
}
|
||||
|
||||
return res.result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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 '../../form-blocks';
|
||||
import { CustomerTypeSelectorModule } from '../../shared/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 {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './create-p4m-customer.component';
|
||||
export * from './create-p4m-customer.module';
|
||||
@@ -0,0 +1,94 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service <br />
|
||||
zu ermöglichen, legen wir Ihnen <br />
|
||||
gerne ein Kundenkonto an.
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="store"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
>
|
||||
</app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
>
|
||||
</app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="name"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
>
|
||||
</app-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[organisation]="true"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-store-customer',
|
||||
templateUrl: 'create-store-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-store-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateStoreCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress = false;
|
||||
|
||||
validateShippingAddress = true;
|
||||
|
||||
addressRequiredMarks = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
addressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
|
||||
emailFormBlockValidators = [Validators.email, validateEmail];
|
||||
|
||||
nameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<string, ValidatorFn[]> = {
|
||||
gender: [Validators.required],
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
};
|
||||
|
||||
saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
customer.isGuestAccount = false;
|
||||
|
||||
return this.customerService
|
||||
.createStoreCustomer(customer)
|
||||
.pipe(map((res) => res.result))
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CreateStoreCustomerComponent } from './create-store-customer.component';
|
||||
import { AddressFormBlockModule } from '../../form-blocks/address';
|
||||
import { NameFormBlockModule } from '../../form-blocks/name';
|
||||
import { OrganisationFormBlockModule } from '../../form-blocks/organisation';
|
||||
import { BirthDateFormBlockModule } from '../../form-blocks/birth-date';
|
||||
import { CustomerTypeSelectorModule } from '../../shared/customer-type-selector';
|
||||
import { DeviatingAddressFormBlockComponentModule, EmailFormBlockModule, PhoneNumbersFormBlockModule } from '../../form-blocks';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
OrganisationFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
BirthDateFormBlockModule,
|
||||
CustomerTypeSelectorModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [CreateStoreCustomerComponent],
|
||||
declarations: [CreateStoreCustomerComponent],
|
||||
})
|
||||
export class CreateStoreCustomerModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './create-store-customer.component';
|
||||
export * from './create-store-customer.module';
|
||||
@@ -0,0 +1,98 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Wenn Sie möchten legen wir Ihnen <br />
|
||||
gerne ein Onlinekonto an. Dort können <br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="webshop"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
>
|
||||
</app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
>
|
||||
</app-name-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[requiredMark]="true"
|
||||
[asyncValidatorFns]="asyncEmailValidatorFns"
|
||||
[validatorFns]="emailValidatorFns"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#org
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="name"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="org.tabIndexEnd + 1"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
>
|
||||
</app-address-form-block>
|
||||
|
||||
<p class="info">Das Anlegen geht für Sie noch schneller, wenn wir Ihnen das initiale Passwort per SMS auf Ihr Mobilgerät schicken.</p>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
appearence="b2b"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressFormBlockComponent, DeviatingAddressFormBlockComponent } from '../../form-blocks';
|
||||
import { NameFormBlockData } from '../../form-blocks/name/name-form-block-data';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-webshop-customer',
|
||||
templateUrl: 'create-webshop-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-webshop-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress?: boolean;
|
||||
validateShippingAddress?: boolean;
|
||||
|
||||
nameRequiredMarks = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
title: [],
|
||||
};
|
||||
|
||||
addressRequiredMarks = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
addressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
|
||||
communicationDetailsValidators: Record<string, ValidatorFn[]> = {
|
||||
email: [Validators.required, Validators.email, validateEmail],
|
||||
};
|
||||
|
||||
emailValidatorFns = [Validators.required, Validators.email, validateEmail];
|
||||
|
||||
asyncEmailValidatorFns = [this.emailExistsValidator];
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
|
||||
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
|
||||
saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
return this.customerService
|
||||
.createOnlineCustomer(customer)
|
||||
.pipe(map((res) => res.result))
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CreateWebshopCustomerComponent } from './create-webshop-customer.component';
|
||||
import { AddressFormBlockModule } from '../../form-blocks/address';
|
||||
import { BirthDateFormBlockModule } from '../../form-blocks/birth-date';
|
||||
import { NameFormBlockModule } from '../../form-blocks/name';
|
||||
import { OrganisationFormBlockModule } from '../../form-blocks/organisation';
|
||||
import { CustomerTypeSelectorModule } from '../../shared/customer-type-selector';
|
||||
import { DeviatingAddressFormBlockComponentModule, EmailFormBlockModule, PhoneNumbersFormBlockModule } from '../../form-blocks';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
OrganisationFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
BirthDateFormBlockModule,
|
||||
CustomerTypeSelectorModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [CreateWebshopCustomerComponent],
|
||||
declarations: [CreateWebshopCustomerComponent],
|
||||
})
|
||||
export class CreateWebshopCustomerModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './create-webshop-customer.component';
|
||||
export * from './create-webshop-customer.module';
|
||||
@@ -0,0 +1,102 @@
|
||||
import { CustomerDTO, CustomerInfoDTO } from '@swagger/crm';
|
||||
import { AddressFormBlockData, DeviatingAddressFormBlockData, PhoneNumbersFormBlockData } from '../form-blocks';
|
||||
import { InterestsFormBlockData } from '../form-blocks/interests/interests-form-block-data';
|
||||
import { NameFormBlockData } from '../form-blocks/name/name-form-block-data';
|
||||
import { OrganisationFormBlockData } from '../form-blocks/organisation/organisation-form-block-data';
|
||||
|
||||
export interface CustomerCreateFormData {
|
||||
_meta?: {
|
||||
customerDto?: CustomerDTO;
|
||||
customerInfoDto?: CustomerInfoDTO;
|
||||
p4mRequired?: boolean;
|
||||
};
|
||||
agb?: boolean;
|
||||
address?: AddressFormBlockData;
|
||||
birthDate?: string;
|
||||
billingAddress?: DeviatingAddressFormBlockData;
|
||||
deviatingDeliveryAddress?: DeviatingAddressFormBlockData;
|
||||
email?: string;
|
||||
interests?: InterestsFormBlockData;
|
||||
name?: NameFormBlockData;
|
||||
newsletter?: boolean;
|
||||
organisation?: OrganisationFormBlockData;
|
||||
p4m?: string;
|
||||
phoneNumbers?: PhoneNumbersFormBlockData;
|
||||
}
|
||||
|
||||
export function mapCustomerDtoToCustomerCreateFormData(customerDto: CustomerDTO): CustomerCreateFormData {
|
||||
let p4m = customerDto.attributes?.find((attr) => attr.data.key === 'p4mUser')?.data.value;
|
||||
|
||||
if (!p4m) {
|
||||
p4m = customerDto.features?.find((f) => f.key === 'p4mUser')?.value;
|
||||
}
|
||||
|
||||
return {
|
||||
_meta: {
|
||||
customerDto,
|
||||
p4mRequired: !!p4m,
|
||||
},
|
||||
p4m,
|
||||
address: customerDto.address,
|
||||
birthDate: customerDto.dateOfBirth,
|
||||
email: customerDto?.communicationDetails?.email,
|
||||
name: {
|
||||
gender: customerDto?.gender,
|
||||
title: customerDto?.title,
|
||||
firstName: customerDto?.firstName,
|
||||
lastName: customerDto?.lastName,
|
||||
},
|
||||
organisation: customerDto.organisation,
|
||||
phoneNumbers: {
|
||||
mobile: customerDto?.communicationDetails?.mobile,
|
||||
phone: customerDto?.communicationDetails?.phone,
|
||||
},
|
||||
agb: !!p4m,
|
||||
};
|
||||
}
|
||||
|
||||
export function mapCustomerInfoDtoToCustomerCreateFormData(customerInfoDto: CustomerInfoDTO): CustomerCreateFormData {
|
||||
const p4m = customerInfoDto.features?.find((f) => f.key === 'p4mUser')?.value;
|
||||
|
||||
const interests = customerInfoDto.features
|
||||
?.filter((f) => f.group === 'KUBI_INTERESSEN')
|
||||
.reduce<Record<string, boolean>>((dic, f) => {
|
||||
dic[f.key] = true;
|
||||
return dic;
|
||||
}, {});
|
||||
|
||||
const newsletter = !!customerInfoDto.features?.find((f) => f.key === 'kubi_newsletter' && f.group === 'KUBI_NEWSLETTER');
|
||||
|
||||
return {
|
||||
_meta: {
|
||||
customerInfoDto,
|
||||
p4mRequired: !!p4m,
|
||||
},
|
||||
p4m,
|
||||
address: customerInfoDto.address,
|
||||
birthDate: customerInfoDto.dateOfBirth,
|
||||
email: customerInfoDto?.communicationDetails?.email,
|
||||
name: {
|
||||
gender: customerInfoDto?.gender,
|
||||
title: customerInfoDto?.title,
|
||||
firstName: customerInfoDto?.firstName,
|
||||
lastName: customerInfoDto?.lastName,
|
||||
},
|
||||
organisation: customerInfoDto.organisation,
|
||||
phoneNumbers: {
|
||||
mobile: customerInfoDto?.communicationDetails?.mobile,
|
||||
phone: customerInfoDto?.communicationDetails?.phone,
|
||||
},
|
||||
agb: !!p4m,
|
||||
interests,
|
||||
newsletter,
|
||||
};
|
||||
}
|
||||
|
||||
export function encodeFormData(data: CustomerCreateFormData): string {
|
||||
return encodeURIComponent(JSON.stringify(data));
|
||||
}
|
||||
|
||||
export function decodeFormData(data: string): CustomerCreateFormData {
|
||||
return JSON.parse(decodeURIComponent(data));
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { CustomerDTO, Gender } from '@swagger/crm';
|
||||
|
||||
export interface CreateCustomerQueryParams {
|
||||
p4mNumber?: string;
|
||||
customerId?: number;
|
||||
gender?: Gender;
|
||||
title?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
street?: string;
|
||||
streetNumber?: string;
|
||||
zipCode?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
info?: string;
|
||||
organisationName?: string;
|
||||
organisationDepartment?: string;
|
||||
organisationVatId?: string;
|
||||
}
|
||||
1
apps/page/customer/src/lib/create-customer/defs/index.ts
Normal file
1
apps/page/customer/src/lib/create-customer/defs/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './customer-query-params';
|
||||
9
apps/page/customer/src/lib/create-customer/index.ts
Normal file
9
apps/page/customer/src/lib/create-customer/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export * from './create-b2b-customer';
|
||||
export * from './create-guest-customer';
|
||||
export * from './create-p4m-customer';
|
||||
export * from './create-store-customer';
|
||||
export * from './create-webshop-customer';
|
||||
export * from './defs';
|
||||
export * from './abstract-create-customer';
|
||||
export * from './create-customer.module';
|
||||
export * from './customer-create-form-data';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './update-p4m-webshop-customer.component';
|
||||
export * from './update-p4m-webshop-customer.module';
|
||||
@@ -0,0 +1,87 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title flex flex-row items-center justify-center">Kundenkartendaten erfasen</h1>
|
||||
|
||||
<app-p4m-number-form-block
|
||||
#p4mBlock
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('p4m', $event)"
|
||||
[data]="data.p4m"
|
||||
(dataChanges)="patchFormData('p4m', $event)"
|
||||
[focusAfterInit]="!data?._meta?.p4mRequired"
|
||||
>
|
||||
</app-p4m-number-form-block>
|
||||
|
||||
<app-newsletter-form-block
|
||||
#newsletterBlock
|
||||
[tabIndexStart]="p4mBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('newsletter', $event)"
|
||||
[data]="data.newsletter"
|
||||
(dataChanges)="patchFormData('newsletter', $event)"
|
||||
[focusAfterInit]="data?._meta?.p4mRequired"
|
||||
>
|
||||
</app-newsletter-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
#dbaBlock
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[tabIndexStart]="newsletterBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('billingAddress', $event)"
|
||||
[data]="data.billingAddress"
|
||||
(dataChanges)="patchFormData('billingAddress', $event)"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="shippingAddressRequiredMarks"
|
||||
[addressValidatorFns]="shippingAddressValidators"
|
||||
>
|
||||
Abweichende Rechnugsaddresse anlegen
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
#ddaBlock
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[tabIndexStart]="dbaBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="shippingAddressRequiredMarks"
|
||||
[addressValidatorFns]="shippingAddressValidators"
|
||||
>
|
||||
Abweichende Lieferadresse anlegen
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#bdBlock
|
||||
[tabIndexStart]="ddaBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="birthDateValidatorFns"
|
||||
>
|
||||
</app-birth-date-form-block>
|
||||
|
||||
<app-interests-form-block
|
||||
#inBlock
|
||||
[tabIndexStart]="bdBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('interests', $event)"
|
||||
[data]="data.interests"
|
||||
(dataChanges)="patchFormData('interests', $event)"
|
||||
></app-interests-form-block>
|
||||
|
||||
<app-accept-agb-form-block
|
||||
[tabIndexStart]="inBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('agb', $event)"
|
||||
[data]="data.agb"
|
||||
(dataChanges)="patchFormData('agb', $event)"
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="agbValidatorFns"
|
||||
>
|
||||
</app-accept-agb-form-block>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</form>
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-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,
|
||||
})
|
||||
export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
|
||||
saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { UpdateP4MWebshopCustomerComponent } from './update-p4m-webshop-customer.component';
|
||||
|
||||
import {
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
} from '../../form-blocks';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
],
|
||||
exports: [UpdateP4MWebshopCustomerComponent],
|
||||
declarations: [UpdateP4MWebshopCustomerComponent],
|
||||
})
|
||||
export class UpdateP4MWebshopCustomerModule {}
|
||||
@@ -1,222 +0,0 @@
|
||||
<div class="card">
|
||||
<h1>Kundendaten erfassen</h1>
|
||||
<p>
|
||||
Für eine B2B Bestellung benötigen Sie<br />
|
||||
einen Firmenaccount. Wir legen diesen<br />
|
||||
gerne direkt für Sie an.
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="name" tabindex="1" />
|
||||
</ui-form-control>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="department" tabindex="2" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="3" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" [requiredMark]="control.value.lastName ? '*' : ''">
|
||||
<ui-select formControlName="gender" tabindex="4">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="5">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="6" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="7" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="8" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="9" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="10" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="11" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="12" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="13">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<ui-form-control label="E-Mail" [clearable]="false">
|
||||
<input uiInput type="mail" formControlName="email" tabindex="14" />
|
||||
</ui-form-control>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Festnetznummer">
|
||||
<input uiInput type="tel" formControlName="phone" tabindex="15" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer" [clearable]="false">
|
||||
<input uiInput type="tel" formControlName="mobile" tabindex="16" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="17" />
|
||||
</ui-form-control>
|
||||
|
||||
<div *ngIf="control.value.differentShippingAddress" formGroupName="shippingAddress">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control
|
||||
label="Firmenname"
|
||||
[requiredMark]="
|
||||
control.get('shippingAddress').value.lastName &&
|
||||
control.get('shippingAddress').value.firstName &&
|
||||
control.get('shippingAddress').value.gender
|
||||
? ''
|
||||
: '*'
|
||||
"
|
||||
>
|
||||
<input uiInput type="text" formControlName="name" tabindex="18" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="19" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="20" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control
|
||||
label="Anrede"
|
||||
[clearable]="true"
|
||||
[requiredMark]="control.get('shippingAddress').get('organisation').value.name ? '' : '*'"
|
||||
>
|
||||
<ui-select formControlName="gender" tabindex="21">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="22">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" [requiredMark]="control.get('shippingAddress').get('organisation').value.name ? '' : '*'">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="23" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" [requiredMark]="control.get('shippingAddress').get('organisation').value.name ? '' : '*'">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="24" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="25" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="26" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="27" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="28" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="29" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="30">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<ui-form-control label="E-Mail">
|
||||
<input uiInput type="text" formControlName="email" tabindex="31" />
|
||||
</ui-form-control>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Festnetznummer">
|
||||
<input uiInput type="tel" formControlName="phone" tabindex="32" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer">
|
||||
<input uiInput type="tel" formControlName="mobile" tabindex="33" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,229 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { genderLastNameValidator } from '../validators/gender-b2b-validator';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { organisationB2bDeliveryValidator } from '../validators/organisation-delivery-b2b-validator';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-b2b',
|
||||
templateUrl: 'customer-create-b2b.component.html',
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateB2BComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
subscription: Subscription;
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
private fb: FormBuilder,
|
||||
public modal: UiModalService,
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: DomainCheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
const fb = this.fb;
|
||||
|
||||
return fb.group(
|
||||
{
|
||||
organisation: fb.group({
|
||||
name: fb.control(customer?.organisation?.name, [Validators.required]),
|
||||
department: fb.control(customer?.organisation?.department),
|
||||
vatId: fb.control(customer?.organisation?.vatId),
|
||||
}),
|
||||
gender: fb.control(customer?.gender),
|
||||
title: fb.control(customer?.title),
|
||||
lastName: fb.control(customer?.lastName),
|
||||
firstName: fb.control(customer?.firstName),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
|
||||
city: fb.control(customer?.address?.city, [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(''),
|
||||
phone: fb.control(''),
|
||||
mobile: fb.control(''),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{ validators: [genderLastNameValidator(true), organisationB2bDeliveryValidator()] }
|
||||
);
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
try {
|
||||
const address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
if (typeof address === 'string') {
|
||||
if (address === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
if (typeof shippingAddress === 'string') {
|
||||
if (shippingAddress === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('shippingAddress').get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.customerService.createB2BCustomer(this.control.value).toPromise();
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
try {
|
||||
const shippingResponse = await this.customerService
|
||||
.createShippingAddress(response.result.id, this.control.value.shippingAddress, true)
|
||||
.toPromise();
|
||||
if (shippingResponse.error) {
|
||||
throw new Error(shippingResponse.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
this.enableControl();
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
this.removeBreadcrumb();
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]);
|
||||
}
|
||||
} catch (error) {
|
||||
this.setValidationError(error.error?.invalidProperties, this.control);
|
||||
}
|
||||
}
|
||||
|
||||
enableControl() {
|
||||
this.control.enable();
|
||||
|
||||
if (!this.control.get('differentShippingAddress').value) {
|
||||
this.control.get('differentShippingAddress').disable();
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
<div class="card">
|
||||
<h1>Kundendaten erfassen</h1>
|
||||
<p>
|
||||
Um Ihnen den ausgewählten Service <br />
|
||||
zu ermöglichen, legen wir Ihnen <br />
|
||||
gerne ein Kundenkonto an.
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="2">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="3" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="4" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<ui-form-control label="E-Mail" [clearable]="false">
|
||||
<input uiInput type="mail" formControlName="email" tabindex="5" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="6" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße">
|
||||
<input uiInput type="text" formControlName="street" tabindex="7" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="8" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="9" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort">
|
||||
<input uiInput type="text" formControlName="city" tabindex="10" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="11" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true">
|
||||
<ui-select formControlName="country" tabindex="12">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Festnetznummer">
|
||||
<input uiInput type="tel" formControlName="phone" tabindex="13" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer" [clearable]="false">
|
||||
<input uiInput type="tel" formControlName="mobile" tabindex="14" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)">
|
||||
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ng-container formGroupName="organisation" *ngIf="!!control.value?.organisation?.name">
|
||||
<h1>Geschäftliche Angaben</h1>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="16" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="17" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="18" />
|
||||
</ui-form-control>
|
||||
|
||||
<div *ngIf="control.value.differentShippingAddress" formGroupName="shippingAddress">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="19" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="20" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="21" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="22">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="23">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="28" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="29" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="30" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="31">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,275 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CustomerDTO } from '@swagger/crm';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isStringEmpty } from '@utils/common';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-branch',
|
||||
templateUrl: 'customer-create-branch.component.html',
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateBranchComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
shippingAddressSubscription: Subscription;
|
||||
organisationSubscription: Subscription;
|
||||
addressSubscription: Subscription;
|
||||
|
||||
get addressGroup(): AbstractControl {
|
||||
return this.control?.get('address');
|
||||
}
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
private fb: FormBuilder,
|
||||
public modal: UiModalService,
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: DomainCheckoutService,
|
||||
public cdr: ChangeDetectorRef,
|
||||
private _modal: UiModalService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
|
||||
if (!!this.p4mUser) {
|
||||
this.cusotmers$ = this.cusotmers$.pipe(
|
||||
map((data) => ({
|
||||
values: data.values.filter((f) => f.value === 'store'),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
|
||||
this.organisationSubscription = this.organisationGroup.get('name').valueChanges.subscribe((value: string) => {
|
||||
if (!value) {
|
||||
this.organisationGroup.get('department').reset('', { onlySelf: true });
|
||||
this.organisationGroup.get('vatId').reset('', { onlySelf: true });
|
||||
}
|
||||
});
|
||||
|
||||
this.addressSubscription = this.addressGroup.valueChanges.subscribe((value: AddressDTO) => {
|
||||
if (value) {
|
||||
const country = this.addressGroup.get('country');
|
||||
if (!country.value) {
|
||||
country.setValue('DEU');
|
||||
} else if (this.isAddressEmpty(value)) {
|
||||
country.setValue('', { onlySelf: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isAddressEmpty(value: AddressDTO): boolean {
|
||||
return (
|
||||
isStringEmpty(value?.street) && isStringEmpty(value?.streetNumber) && isStringEmpty(value?.zipCode) && isStringEmpty(value?.city)
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.shippingAddressSubscription) {
|
||||
this.shippingAddressSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.organisationSubscription) {
|
||||
this.organisationSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.addressSubscription) {
|
||||
this.addressSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
const fb = this.fb;
|
||||
|
||||
return fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(customer?.organisation?.name),
|
||||
department: fb.control(customer?.organisation?.department),
|
||||
vatId: fb.control(customer?.organisation?.vatId),
|
||||
}),
|
||||
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber),
|
||||
zipCode: fb.control(customer?.address?.zipCode),
|
||||
city: fb.control(customer?.address?.city),
|
||||
country: fb.control(!this.isAddressEmpty(customer?.address) ? customer?.address?.country : ''),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
try {
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
if (typeof address === 'string') {
|
||||
if (address === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
if (typeof shippingAddress === 'string') {
|
||||
if (shippingAddress === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('shippingAddress').get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let newCustomer: CustomerDTO = this.control.value;
|
||||
|
||||
if (!!this.p4mUser) {
|
||||
newCustomer.features = newCustomer.features ? [...newCustomer.features, this.p4mUser] : [this.p4mUser];
|
||||
}
|
||||
const response = await this.customerService.createBranchCustomer(newCustomer).toPromise();
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
try {
|
||||
const shippingResponse = await this.customerService
|
||||
.createShippingAddress(response.result.id, this.control.value.shippingAddress, true)
|
||||
.toPromise();
|
||||
if (shippingResponse.error) {
|
||||
throw new Error(shippingResponse.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
this.enableControl();
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
this.removeBreadcrumb();
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]);
|
||||
}
|
||||
} catch (error) {
|
||||
this.setValidationError(error.error?.invalidProperties, this.control);
|
||||
this._modal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim Anlegen eines Filialkunden' });
|
||||
}
|
||||
}
|
||||
|
||||
enableControl() {
|
||||
this.control.enable();
|
||||
|
||||
if (!this.control.get('differentShippingAddress').value) {
|
||||
this.control.get('differentShippingAddress').disable();
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
<div class="card">
|
||||
<h1>Kundendaten erfassen</h1>
|
||||
<p>
|
||||
Wenn Sie möchten legen wir Ihnen <br />
|
||||
gerne ein Onlinekonto an. Dort können <br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="2">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="3" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="4" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<p class="bold">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere
|
||||
als die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<ui-form-control label="E-Mail" requiredMark="*">
|
||||
<input uiInput type="mail" formControlName="email" tabindex="5" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="6" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="7" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="8" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="9" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="10" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="11" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control [clearable]="true" label="Land" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="12">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Festnetznummer">
|
||||
<input uiInput type="tel" formControlName="phone" tabindex="13" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer" [clearable]="false">
|
||||
<input uiInput type="tel" formControlName="mobile" tabindex="14" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)">
|
||||
<input type="text" formControlName="dateOfBirth" uiDateInput tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ng-container formGroupName="organisation" *ngIf="!!control.value?.organisation?.name">
|
||||
<h1>Geschäftliche Angaben</h1>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="16" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="17" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="18" />
|
||||
</ui-form-control>
|
||||
|
||||
<div *ngIf="control.value.differentShippingAddress" formGroupName="shippingAddress">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="19" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="20" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="21" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="22">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Titel" [clearable]="true">
|
||||
<ui-select formControlName="title" tabindex="23">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="28" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="29" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="30" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="31">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,245 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-guest',
|
||||
templateUrl: 'customer-create-guest.component.html',
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateGuestComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
shippingAddressSubscription: Subscription;
|
||||
organisationSubscription: Subscription;
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
private fb: FormBuilder,
|
||||
public modal: UiModalService,
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: DomainCheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
this.countries$ = this.countries$.pipe(map((countries) => countries.filter((country) => country.name === 'Deutschland')));
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
|
||||
this.organisationSubscription = this.organisationGroup.get('name').valueChanges.subscribe((value: string) => {
|
||||
if (!value) {
|
||||
this.organisationGroup.get('department').reset('', { onlySelf: true });
|
||||
this.organisationGroup.get('vatId').reset('', { onlySelf: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.shippingAddressSubscription) {
|
||||
this.shippingAddressSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.organisationSubscription) {
|
||||
this.organisationSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
const { fb } = this;
|
||||
|
||||
return fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(customer?.organisation?.name),
|
||||
department: fb.control(customer?.organisation?.department),
|
||||
vatId: fb.control(customer?.organisation?.vatId),
|
||||
}),
|
||||
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [Validators.required, validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
|
||||
city: fb.control(customer?.address?.city, [Validators.required]),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
try {
|
||||
const address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
if (typeof address === 'string') {
|
||||
if (address === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
if (typeof shippingAddress === 'string') {
|
||||
if (shippingAddress === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('shippingAddress').get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let newCustomer: CustomerDTO = this.control.value;
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
newCustomer.shippingAddresses = [
|
||||
{
|
||||
data: {
|
||||
...this.control.value.shippingAddress,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const response = await this.customerService.createGuestCustomer(newCustomer).toPromise();
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
try {
|
||||
const shippingResponse = await this.customerService
|
||||
.createShippingAddress(response.result.id, this.control.value.shippingAddress, true)
|
||||
.toPromise();
|
||||
if (shippingResponse.error) {
|
||||
throw new Error(shippingResponse.message);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
this.enableControl();
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
this.removeBreadcrumb();
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]);
|
||||
}
|
||||
} catch (error) {
|
||||
this.setValidationError(error.error?.invalidProperties, this.control);
|
||||
}
|
||||
}
|
||||
|
||||
enableControl() {
|
||||
this.control.enable();
|
||||
|
||||
if (!this.control.get('differentShippingAddress').value) {
|
||||
this.control.get('differentShippingAddress').disable();
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
<div class="card">
|
||||
<h1>Kundendaten erfassen</h1>
|
||||
<p>
|
||||
Wenn Sie möchten legen wir Ihnen <br />
|
||||
gerne ein Onlinekonto an. Dort können <br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control [clearable]="true" label="Titel">
|
||||
<ui-select formControlName="title" tabindex="2">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="3" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="4" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<ui-form-control label="E-Mail" requiredMark="*">
|
||||
<input uiInput type="mail" formControlName="email" tabindex="5" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="6" />
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="7" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="8" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="9" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="10" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="11" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="12">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
|
||||
<p class="bold">Das Anlegen geht für Sie noch schneller, wenn wir Ihnen das initiale Passwort per SMS auf Ihr Mobilgerät schicken.</p>
|
||||
|
||||
<ng-container formGroupName="communicationDetails">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Festnetznummer">
|
||||
<input uiInput type="tel" formControlName="phone" tabindex="13" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Mobilnummer" [clearable]="false">
|
||||
<input uiInput type="tel" formControlName="mobile" tabindex="14" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)" [requiredMark]="!!p4mUser ? '*' : ''">
|
||||
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ng-container formGroupName="organisation" *ngIf="!!control.value?.organisation?.name">
|
||||
<h1>Geschäftliche Angaben</h1>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="16" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="17" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="18" />
|
||||
</ui-form-control>
|
||||
|
||||
<div *ngIf="control.value.differentShippingAddress" formGroupName="shippingAddress">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname">
|
||||
<input uiInput type="text" formControlName="name" tabindex="19" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="20" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="21" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="22">
|
||||
<ui-select-option [value]="2" label="Herr"></ui-select-option>
|
||||
<ui-select-option [value]="4" label="Frau"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Titel" [clearable]="true">
|
||||
<ui-select formControlName="title" tabindex="23">
|
||||
<ui-select-option value="Dipl.-Ing." label="Dipl.-Ing."></ui-select-option>
|
||||
<ui-select-option value="Dr." label="Dr."></ui-select-option>
|
||||
<ui-select-option value="Dr. med." label="Dr. med."></ui-select-option>
|
||||
<ui-select-option value="Prof." label="Prof."></ui-select-option>
|
||||
<ui-select-option value="Prof. Dr." label="Prof. Dr."></ui-select-option>
|
||||
<ui-select-option value="RA" label="RA"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="address">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Straße" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="street" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="28" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="29" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="30" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="31">
|
||||
<ui-select-option
|
||||
*ngFor="let country of countries$ | async"
|
||||
[label]="country.name"
|
||||
[value]="country.isO3166_A_3"
|
||||
></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,294 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'customer-create-online',
|
||||
templateUrl: 'customer-create-online.component.html',
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
upgradeCustomerId: number;
|
||||
upgradeCustomer: CustomerDTO;
|
||||
shippingAddressSubscription: Subscription;
|
||||
organisationSubscription: Subscription;
|
||||
|
||||
invalidDomainEmails: string[] = [];
|
||||
emailDomainErrorValidator = (control: AbstractControl): ValidationErrors => {
|
||||
const controlValue: string = control.value || '';
|
||||
const emailDomain = this.invalidDomainEmails.find((s) => controlValue.endsWith(s));
|
||||
if (controlValue && emailDomain) {
|
||||
return { emailDomain: `Die Email-Domain ${emailDomain} ist ungültig.` };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
private fb: FormBuilder,
|
||||
public modal: UiModalService,
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: DomainCheckoutService,
|
||||
public cdr: ChangeDetectorRef,
|
||||
private _modal: UiModalService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
|
||||
this.countries$ = this.countries$.pipe(map((countries) => countries.filter((country) => country.name === 'Deutschland')));
|
||||
|
||||
this.upgradeCustomerId = Number(this.activatedRoute.snapshot.queryParams.upgradeCustomerId);
|
||||
if (!!this.upgradeCustomerId || !!this.p4mUser) {
|
||||
this.cusotmers$ = this.cusotmers$.pipe(
|
||||
map((data) => ({
|
||||
...data,
|
||||
values: data.values.filter((f) => f.value === 'webshop'),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
if (!isNaN(this.upgradeCustomerId)) {
|
||||
this.customerService
|
||||
.getCustomer(this.upgradeCustomerId, 2)
|
||||
.pipe(map((response) => response.result))
|
||||
.subscribe((customer) => {
|
||||
this.upgradeCustomer = customer;
|
||||
this.control.patchValue(customer);
|
||||
this.control.updateValueAndValidity();
|
||||
this.control.markAllAsTouched();
|
||||
});
|
||||
}
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
|
||||
this.organisationSubscription = this.organisationGroup.get('name').valueChanges.subscribe((value: string) => {
|
||||
if (!value) {
|
||||
this.organisationGroup.get('department').reset('', { onlySelf: true });
|
||||
this.organisationGroup.get('vatId').reset('', { onlySelf: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.shippingAddressSubscription) {
|
||||
this.shippingAddressSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.organisationSubscription) {
|
||||
this.organisationSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
const { fb } = this;
|
||||
|
||||
return fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(customer?.organisation?.name),
|
||||
department: fb.control(customer?.organisation?.department),
|
||||
vatId: fb.control(customer?.organisation?.vatId),
|
||||
}),
|
||||
gender: fb.control(customer?.gender || undefined, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [!!this.p4mUser ? Validators.required : () => null, UiValidators.date]),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(
|
||||
customer?.communicationDetails?.email,
|
||||
[Validators.required, validateEmail, this.emailDomainErrorValidator],
|
||||
[this.emailExistsValidator()]
|
||||
),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
|
||||
city: fb.control(customer?.address?.city, [Validators.required]),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
try {
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
if (typeof address === 'string') {
|
||||
if (address === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
if (typeof shippingAddress === 'string') {
|
||||
if (shippingAddress === 'close') {
|
||||
this.enableControl();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.enableControl();
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error.error.invalidProperties) {
|
||||
this.setValidateAddressError(error.error?.invalidProperties, this.control.get('shippingAddress').get('address'));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let newCustomer: CustomerDTO = this.control.value;
|
||||
if (this.upgradeCustomer) {
|
||||
newCustomer = { ...this.upgradeCustomer, ...newCustomer };
|
||||
}
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
newCustomer.shippingAddresses = [
|
||||
{
|
||||
data: {
|
||||
...this.control.value.shippingAddress,
|
||||
isDefault: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (!!this.p4mUser) {
|
||||
newCustomer.features = newCustomer.features ? [...newCustomer.features, this.p4mUser] : [this.p4mUser];
|
||||
}
|
||||
|
||||
const response = await this.customerService.createOnlineCustomer(newCustomer).toPromise();
|
||||
|
||||
if (response.error) {
|
||||
this.enableControl();
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
this.removeBreadcrumb();
|
||||
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error?.error?.invalidProperties?.Email || error?.error?.invalidProperties?.email) {
|
||||
this.addInvalidDomain(this.control.value.communicationDetails?.email);
|
||||
this.enableControl();
|
||||
} else {
|
||||
this.setValidationError(error?.error?.invalidProperties, this.control);
|
||||
}
|
||||
this._modal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim Anlegen eines Onlinekunden' });
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
|
||||
addInvalidDomain(email: string) {
|
||||
const indexOfAt = email.indexOf('@');
|
||||
this.invalidDomainEmails.push(email.substring(indexOfAt));
|
||||
}
|
||||
|
||||
enableControl() {
|
||||
this.control.enable();
|
||||
|
||||
if (!this.control.get('differentShippingAddress').value) {
|
||||
this.control.get('differentShippingAddress').disable();
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
:host {
|
||||
@apply flex flex-col box-border;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply bg-white rounded-card p-card;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply m-0 text-center font-bold;
|
||||
font-size: 26px;
|
||||
margin-top: 27px;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply m-0;
|
||||
font-size: 22px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card > h1,
|
||||
.card > p {
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
page-customer-type-selector {
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
.router-outlet-wrapper {
|
||||
max-width: 650px;
|
||||
@apply mx-auto;
|
||||
|
||||
p {
|
||||
@apply text-regular mt-8;
|
||||
}
|
||||
}
|
||||
|
||||
.control-row {
|
||||
@apply flex flex-row gap-8;
|
||||
|
||||
ui-form-control {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
.sticky-bottom {
|
||||
@apply sticky bottom-0;
|
||||
}
|
||||
|
||||
.create-customer-submit {
|
||||
@apply border-none outline-none bg-brand text-white font-bold text-cta-l px-px-25 py-px-15 rounded-full my-8;
|
||||
|
||||
&:disabled {
|
||||
@apply bg-inactive-branch;
|
||||
}
|
||||
}
|
||||
|
||||
.bold {
|
||||
@apply font-semibold;
|
||||
}
|
||||
|
||||
.spin {
|
||||
@apply animate-spin;
|
||||
}
|
||||
|
||||
.different-shipping-address {
|
||||
@apply flex-col justify-around text-cta-l mt-8;
|
||||
|
||||
input {
|
||||
@apply text-cta-l;
|
||||
}
|
||||
}
|
||||
|
||||
.organisation-header {
|
||||
}
|
||||
|
||||
::ng-deep.spin {
|
||||
@apply text-white;
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
import { AbstractControl, AsyncValidatorFn, FormArray, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO, CustomerDTO, InputOptionsDTO, KeyValueDTOOfStringAndString } from '@swagger/crm';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, debounceTime, distinctUntilChanged, first, map, switchMap } from 'rxjs/operators';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||
import { isBoolean } from '@utils/common';
|
||||
import { setInvalidPropertyErrors } from '@ui/validators';
|
||||
import { camelCase } from 'lodash';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
export abstract class CustomerCreateComponentBase {
|
||||
emailExistsValidator(): AsyncValidatorFn {
|
||||
return (control) =>
|
||||
control.valueChanges.pipe(
|
||||
debounceTime(500),
|
||||
distinctUntilChanged(),
|
||||
switchMap((value) => this.customerService.emailExists(value)),
|
||||
map((result) => {
|
||||
if (result?.result) {
|
||||
return { exists: result?.message ? result.message : 'E-Mail existiert bereits' };
|
||||
}
|
||||
}),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error?.error?.invalidProperties?.email) {
|
||||
return of({ invalid: error.error.invalidProperties.email });
|
||||
} else {
|
||||
return of({ invalid: 'E-Mail ist ungültig' });
|
||||
}
|
||||
}
|
||||
}),
|
||||
first()
|
||||
);
|
||||
}
|
||||
|
||||
type: string;
|
||||
control: FormGroup;
|
||||
|
||||
countries$: Observable<CountryDTO[]>;
|
||||
|
||||
abstract activatedRoute: ActivatedRoute;
|
||||
abstract router: Router;
|
||||
abstract modal: UiModalService;
|
||||
abstract customerService: CrmCustomerService;
|
||||
abstract breadcrumb: BreadcrumbService;
|
||||
abstract application: ApplicationService;
|
||||
abstract addressSelectionModal: AddressSelectionModalService;
|
||||
abstract checkoutService: DomainCheckoutService;
|
||||
abstract cdr: ChangeDetectorRef;
|
||||
|
||||
cusotmers$: Observable<InputOptionsDTO>;
|
||||
private _p4mUser: KeyValueDTOOfStringAndString;
|
||||
private _p4mUserFeature: 'webshop' | 'store';
|
||||
|
||||
get p4mUser() {
|
||||
return this._p4mUser;
|
||||
}
|
||||
|
||||
set p4mUser(data: KeyValueDTOOfStringAndString) {
|
||||
this._p4mUser = data;
|
||||
}
|
||||
|
||||
get p4mUserFeature() {
|
||||
return this._p4mUserFeature;
|
||||
}
|
||||
|
||||
set p4mUserFeature(data: 'webshop' | 'store') {
|
||||
this._p4mUserFeature = data;
|
||||
}
|
||||
|
||||
get organisationGroup(): AbstractControl {
|
||||
return this.control?.get('organisation');
|
||||
}
|
||||
|
||||
init() {
|
||||
this.type = this.activatedRoute.snapshot?.routeConfig?.path;
|
||||
this.control = this.createControl(this.parseCustomerDataQuery(this.activatedRoute.snapshot.queryParams));
|
||||
|
||||
this.countries$ = this.customerService.getCountries().pipe(map((response) => response.result));
|
||||
|
||||
this.breadcrumb.addBreadcrumbIfNotExists({
|
||||
key: this.application.activatedProcessId,
|
||||
name: 'Kundendaten erfassen',
|
||||
path: `/kunde/${this.application.activatedProcessId}/customer/create/${this.type}`,
|
||||
tags: ['customer', 'create'],
|
||||
section: 'customer',
|
||||
params: {},
|
||||
});
|
||||
|
||||
this.cusotmers$ = this.checkoutService
|
||||
.canSetCustomer({
|
||||
processId: this.application.activatedProcessId,
|
||||
})
|
||||
.pipe(
|
||||
map((r) => {
|
||||
const result = { ...r.create };
|
||||
result.options.values = [...result.options.values];
|
||||
result.options.values.forEach((f) => {
|
||||
f.selected = f.value === this.type;
|
||||
f.enabled = isBoolean(f.enabled) ? f.enabled : true;
|
||||
});
|
||||
|
||||
// Upgrade p4mUser if necessary - Only show available option and select it
|
||||
if (!!this.p4mUser && !!r?.filter?.customertype) {
|
||||
if (!r.filter.customertype.includes(this.p4mUserFeature)) {
|
||||
result.options.values = result.options.values.filter(
|
||||
(option) => option.value === (this.p4mUserFeature === 'store' ? 'webshop' : 'store')
|
||||
);
|
||||
result.options.values[0].selected = true;
|
||||
result.options.values[0].enabled = true;
|
||||
}
|
||||
}
|
||||
return result.options;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async setType(options: InputOptionsDTO) {
|
||||
let selected = options.values.find((f) => f.selected);
|
||||
|
||||
if (!selected) {
|
||||
selected = options.values.find((f) => f.enabled);
|
||||
}
|
||||
|
||||
const navigated = await this.router.navigate(['../', selected.value], {
|
||||
relativeTo: this.activatedRoute,
|
||||
queryParams: this.createCustomerDataQuery(this.control.value),
|
||||
});
|
||||
|
||||
// const navigated = await this.router.navigate(['../', type], {
|
||||
// relativeTo: this.activatedRoute,
|
||||
// queryParams: this.createCustomerDataQuery(this.control.value),
|
||||
// });
|
||||
// if (!navigated) {
|
||||
// this.type = this.activatedRoute.snapshot?.routeConfig?.path;
|
||||
// this.cdr.markForCheck();
|
||||
// }
|
||||
}
|
||||
|
||||
createCustomerDataQuery(customer: CustomerDTO): { [key: string]: string } {
|
||||
const query: { [key: string]: string } = {
|
||||
gender: String(customer.gender),
|
||||
title: customer.title,
|
||||
firstName: customer.firstName,
|
||||
lastName: customer.lastName,
|
||||
email: customer.communicationDetails.email,
|
||||
phone: customer.communicationDetails.phone,
|
||||
mobile: customer.communicationDetails.mobile,
|
||||
street: customer.address.street,
|
||||
streetNumber: customer.address.streetNumber,
|
||||
zipCode: customer.address.zipCode,
|
||||
city: customer.address.city,
|
||||
country: customer.address.country,
|
||||
info: customer.address.info,
|
||||
name: customer.organisation.name,
|
||||
department: customer.organisation.department,
|
||||
vatId: customer.organisation.vatId,
|
||||
dateOfBirth: customer.dateOfBirth,
|
||||
};
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
parseCustomerDataQuery(query: Params): CustomerDTO {
|
||||
if (Object.keys(query).length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (query.card && query.cardFeature) {
|
||||
this.p4mUser = JSON.parse(decodeURIComponent(query.card));
|
||||
this.p4mUserFeature = decodeURIComponent(query.cardFeature) as 'webshop' | 'store';
|
||||
}
|
||||
|
||||
return {
|
||||
gender: (+query.gender || 0) as any,
|
||||
title: query.title,
|
||||
firstName: query.firstName,
|
||||
lastName: query.lastName,
|
||||
communicationDetails: {
|
||||
email: query.email,
|
||||
phone: query.phone,
|
||||
mobile: query.mobile,
|
||||
},
|
||||
address: {
|
||||
street: query.street,
|
||||
streetNumber: query.streetNumber,
|
||||
zipCode: query.zipCode,
|
||||
city: query.city,
|
||||
country: query.country,
|
||||
info: query.info,
|
||||
},
|
||||
organisation: {
|
||||
name: query.name,
|
||||
department: query.department,
|
||||
vatId: query.vatId,
|
||||
},
|
||||
dateOfBirth: query.dateOfBirth,
|
||||
};
|
||||
}
|
||||
|
||||
setShippingAddressValidators(group: AbstractControl) {
|
||||
group.get('gender')?.setValidators([Validators.required]);
|
||||
group.get('firstName')?.setValidators([Validators.required]);
|
||||
group.get('lastName')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('street')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('streetNumber')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('zipCode')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('city')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('country')?.setValidators([Validators.required]);
|
||||
group.get('communicationDetails')?.get('email')?.setValidators([validateEmail]);
|
||||
}
|
||||
|
||||
clearShippingAddressValidators(group: AbstractControl) {
|
||||
group.get('gender')?.clearValidators();
|
||||
group.get('firstName')?.clearValidators();
|
||||
group.get('lastName')?.clearValidators();
|
||||
group.get('address')?.clearValidators();
|
||||
group.get('communicationDetails')?.clearValidators();
|
||||
}
|
||||
|
||||
setValidationError(invalidProperties: { [key: string]: string }, formGroup: AbstractControl) {
|
||||
this.control.enable();
|
||||
this.control.reset(this.control.value);
|
||||
|
||||
if (invalidProperties) {
|
||||
setInvalidPropertyErrors({ invalidProperties, formGroup });
|
||||
}
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
}
|
||||
|
||||
setValidateAddressError(invalidProperties: { [key: string]: string }, control: AbstractControl) {
|
||||
const keys = Object.keys(invalidProperties);
|
||||
for (const key of keys) {
|
||||
control?.get(camelCase(key))?.setErrors({ validateAddress: invalidProperties[key] });
|
||||
}
|
||||
}
|
||||
|
||||
focusFirstInvalidFormControl(formControlRef: ElementRef) {
|
||||
const invalidKeys = this.findInvalidControlsRecursive(this.control);
|
||||
const invalidControl = formControlRef.nativeElement.querySelector('[formcontrolname="' + invalidKeys?.find((_) => true) + '"]');
|
||||
invalidControl?.focus();
|
||||
}
|
||||
|
||||
findInvalidControlsRecursive(formToInvestigate: FormGroup | FormArray): string[] {
|
||||
const invalidControls: string[] = [];
|
||||
let recursiveFunc = (form: FormGroup | FormArray) => {
|
||||
Object.keys(form.controls).forEach((field) => {
|
||||
if (field === 'shippingAddress') {
|
||||
return;
|
||||
}
|
||||
|
||||
const control = form.get(field);
|
||||
if (control instanceof FormGroup) {
|
||||
recursiveFunc(control);
|
||||
} else if (control instanceof FormArray) {
|
||||
recursiveFunc(control);
|
||||
} else if (control.invalid) {
|
||||
invalidControls.push(field);
|
||||
}
|
||||
});
|
||||
};
|
||||
recursiveFunc(formToInvestigate);
|
||||
return invalidControls;
|
||||
}
|
||||
|
||||
abstract createControl(customer?: CustomerDTO): FormGroup;
|
||||
|
||||
abstract submit(): Promise<void>;
|
||||
|
||||
async removeBreadcrumb() {
|
||||
const bc = await this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['customer', 'create'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
if (bc.length > 0) {
|
||||
this.breadcrumb.removeBreadcrumb(bc[0].id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CustomerTypeSelectorComponent } from './customer-type-selector/customer-type-selector.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
import { CustomerCreateBranchComponent } from './customer-create-branch.component';
|
||||
import { CustomerCreateGuestComponent } from './customer-create-guest.component';
|
||||
import { CustomerCreateOnlineComponent } from './customer-create-online.component';
|
||||
import { CustomerCreateB2BComponent } from './customer-create-b2b.component';
|
||||
import { AddressSelectionModalModule } from '../modals/address-selection-modal/address-selection-modal.module';
|
||||
import { CantAddCustomerToCartModalModule } from '../modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.module';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
UiFormControlModule,
|
||||
UiInputModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule,
|
||||
UiSelectModule,
|
||||
UiIconModule,
|
||||
AddressSelectionModalModule,
|
||||
CantAddCustomerToCartModalModule,
|
||||
UiCheckboxModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [CustomerCreateBranchComponent, CustomerCreateGuestComponent, CustomerCreateOnlineComponent, CustomerCreateB2BComponent],
|
||||
declarations: [
|
||||
CustomerCreateBranchComponent,
|
||||
CustomerTypeSelectorComponent,
|
||||
CustomerCreateGuestComponent,
|
||||
CustomerCreateOnlineComponent,
|
||||
CustomerCreateB2BComponent,
|
||||
],
|
||||
})
|
||||
export class CustomerCreateModule {}
|
||||
@@ -1,39 +0,0 @@
|
||||
<!-- <ui-checkbox
|
||||
id="Gastkunde"
|
||||
[ngModel]="value"
|
||||
(ngModelChange)="setValue($event)"
|
||||
[disabled]="disabledOptions?.guest"
|
||||
name="customerType"
|
||||
value="guest"
|
||||
>
|
||||
Gastkunde</ui-checkbox
|
||||
>
|
||||
<ui-checkbox
|
||||
[ngModel]="value"
|
||||
(ngModelChange)="setValue($event)"
|
||||
[disabled]="disabledOptions?.webshop"
|
||||
id="Onlinekonto"
|
||||
value="webshop"
|
||||
name="customerType"
|
||||
>
|
||||
Onlinekonto
|
||||
</ui-checkbox>
|
||||
<ui-checkbox
|
||||
[ngModel]="value"
|
||||
(ngModelChange)="setValue($event)"
|
||||
[disabled]="disabledOptions?.b2b"
|
||||
id="B2B Kunde"
|
||||
value="b2b"
|
||||
name="customerType"
|
||||
>
|
||||
B2B Kunde
|
||||
</ui-checkbox> -->
|
||||
|
||||
<ui-checkbox
|
||||
*ngFor="let option of value?.values"
|
||||
[ngModel]="option.selected"
|
||||
(ngModelChange)="check($event, option)"
|
||||
[disabled]="!option.enabled || option.selected"
|
||||
>
|
||||
{{ option.label }}
|
||||
</ui-checkbox>
|
||||
@@ -1,7 +0,0 @@
|
||||
:host {
|
||||
@apply grid grid-flow-col flex-row text-card-sub justify-center gap-6;
|
||||
}
|
||||
|
||||
ui-checkbox.disabled.checked {
|
||||
@apply text-black;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ChangeDetectorRef,
|
||||
forwardRef,
|
||||
OnInit,
|
||||
ViewChildren,
|
||||
QueryList,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { InputDTO, InputOptionsDTO, OptionDTO } from '@swagger/crm';
|
||||
import { UiCheckboxComponent } from '@ui/checkbox';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-type-selector',
|
||||
templateUrl: 'customer-type-selector.component.html',
|
||||
styleUrls: ['customer-type-selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => CustomerTypeSelectorComponent),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CustomerTypeSelectorComponent implements OnInit, ControlValueAccessor {
|
||||
@ViewChildren(UiCheckboxComponent, { read: UiCheckboxComponent })
|
||||
checkboxes: QueryList<UiCheckboxComponent>;
|
||||
|
||||
@Input()
|
||||
value: InputOptionsDTO;
|
||||
|
||||
@Output()
|
||||
valueChange = new EventEmitter<InputOptionsDTO>();
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
private onChange = (value: InputOptionsDTO) => {};
|
||||
private onTouched = () => {};
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
this.value = obj;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
setValue(value: InputOptionsDTO) {
|
||||
this.onChange(value);
|
||||
this.onTouched();
|
||||
}
|
||||
|
||||
check(checked: boolean = false, option: OptionDTO) {
|
||||
this.value.values.forEach((o) => {
|
||||
if (o.value === option.value) {
|
||||
o.selected = checked;
|
||||
} else {
|
||||
o.selected = false;
|
||||
}
|
||||
});
|
||||
this.cdr.markForCheck();
|
||||
this.onChange(this.value);
|
||||
this.onTouched();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
<p class="info">Sind Ihre {{ (customerType$ | async) === 'b2b' ? 'Firmendaten' : 'Kundendaten' }} korrekt?</p>
|
||||
<div class="tags">
|
||||
<div class="tag" *ngFor="let feature of customerFeatures$ | async">
|
||||
<div class="tag" *ngFor="let feature of displayCustomerType$ | async">
|
||||
<ui-icon icon="check" size="15px"></ui-icon>
|
||||
{{ feature.description }}
|
||||
</div>
|
||||
@@ -61,8 +61,8 @@
|
||||
</div>
|
||||
|
||||
<ng-container [ngSwitch]="isB2b$ | async">
|
||||
<page-customer-data-b2b *ngSwitchCase="true" [customer]="customer" [customerType]="customerType$ | async"></page-customer-data-b2b>
|
||||
<page-customer-data-b2c *ngSwitchCase="false" [customer]="customer" [customerType]="customerType$ | async"></page-customer-data-b2c>
|
||||
<page-customer-data-b2b *ngSwitchCase="true" [customer]="customer" [customerType]="customerType$ | async"> </page-customer-data-b2b>
|
||||
<page-customer-data-b2c *ngSwitchCase="false" [customer]="customer" [customerType]="customerType$ | async"> </page-customer-data-b2c>
|
||||
</ng-container>
|
||||
|
||||
<div class="address-wrapper">
|
||||
|
||||
@@ -44,6 +44,9 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
shippingAddresses$: Observable<ShippingAddressDTO[]>;
|
||||
|
||||
customerFeatures$: Observable<KeyValueDTOOfStringAndString[]>;
|
||||
|
||||
displayCustomerType$: Observable<KeyValueDTOOfStringAndString[]>;
|
||||
|
||||
customerType$: Observable<string>;
|
||||
|
||||
canAdd$: Observable<boolean>;
|
||||
@@ -93,7 +96,7 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
this.customerId$ = this.activatedRoute.params.pipe(map((params) => Number(params['customerId'])));
|
||||
|
||||
this.customer$ = this.customerId$.pipe(
|
||||
switchMap((customerId) => this.customerDetailsService.getCustomer(customerId, 1)),
|
||||
switchMap((customerId) => this.customerDetailsService.getCustomer(customerId, 2)),
|
||||
map(({ result }) => result),
|
||||
map((customer) => {
|
||||
this.updateBreadcrumbName(customer);
|
||||
@@ -156,9 +159,12 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
this.customerFeatures$ = this.customer$.pipe(map((customer: CustomerDTO) => Object.values(customer.features).filter((f) => f.enabled)));
|
||||
this.customerFeatures$ = this.customer$.pipe(map((customer: CustomerDTO) => Object.values(customer.features)));
|
||||
|
||||
this.displayCustomerType$ = this.customerFeatures$.pipe(map((features) => features.filter((f) => f.group === 'd-customertype')));
|
||||
|
||||
this.hasCustomerCard$ = this.customerFeatures$.pipe(
|
||||
map((features) => !!features.find((feature) => feature.description === 'Kundenkarte'))
|
||||
map((features) => features.some((feature) => feature.key?.toLowerCase() === 'p4muser' || feature.key?.toLowerCase() === 'loyalty'))
|
||||
);
|
||||
this.customerType$ = this.customerFeatures$.pipe(
|
||||
map(
|
||||
@@ -237,9 +243,10 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
content: CantAddCustomerToCartModalComponent,
|
||||
data: {
|
||||
message: canSetCustomer.message,
|
||||
customerId: customer.id,
|
||||
customer: customer,
|
||||
required: canSetCustomer.create,
|
||||
upgradeableTo: canBeExtendedTo,
|
||||
attributes: customer.attributes.map((s) => s.data),
|
||||
} as CantAddCustomerToCartData,
|
||||
});
|
||||
this.showSpinner = false;
|
||||
@@ -290,9 +297,10 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
content: CantAddCustomerToCartModalComponent,
|
||||
data: {
|
||||
message: canSetCustomer.message,
|
||||
customerId: customer.id,
|
||||
customer: customer,
|
||||
required: canSetCustomer.create,
|
||||
upgradeableTo: undefined,
|
||||
attributes: customer.attributes.map((s) => s.data),
|
||||
} as CantAddCustomerToCartData,
|
||||
});
|
||||
|
||||
@@ -322,17 +330,17 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
|
||||
const currentBuyer = await this.checkoutService.getBuyer({ processId: this.application.activatedProcessId }).pipe(first()).toPromise();
|
||||
|
||||
this.checkoutService.setCustomer({
|
||||
processId: this.application.activatedProcessId,
|
||||
customerDto: customer,
|
||||
});
|
||||
|
||||
// Set Buyer For Process
|
||||
this.checkoutService.setBuyer({
|
||||
processId: this.application.activatedProcessId,
|
||||
buyer: this.getCheckoutBuyerDtoFromCrmCustomerDto(customer),
|
||||
});
|
||||
|
||||
this.checkoutService.setCustomerFeatures({
|
||||
processId: this.application.activatedProcessId,
|
||||
customerFeatures: this.getCusomterFeatures(customer),
|
||||
});
|
||||
|
||||
if (currentBuyer?.buyerNumber !== customer.customerNumber) {
|
||||
this.checkoutService.setNotificationChannels({
|
||||
processId: this.application.activatedProcessId,
|
||||
|
||||
@@ -39,6 +39,10 @@ ui-icon {
|
||||
@apply bg-white mt-px-10 box-border;
|
||||
width: 201px;
|
||||
height: 80px;
|
||||
|
||||
img {
|
||||
@apply mx-auto;
|
||||
}
|
||||
}
|
||||
|
||||
.barcode {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { UiFilterAutocompleteProvider } from '@ui/filter';
|
||||
import { CustomerSearchMainAutocompleteProvider } from './providers/customer-search-main-autocomplete.provider';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from '../create-customer/customer-create-form-data';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-search',
|
||||
@@ -94,6 +95,10 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
handleSearchCompleted(state: SearchState<CustomerInfoDTO>, processId: number) {
|
||||
const queryParams = state.filter?.getQueryParams();
|
||||
|
||||
if (state.latestResponse?.dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.hits === 1) {
|
||||
const customer = state.items[0];
|
||||
if (!customer) {
|
||||
@@ -129,32 +134,17 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToCreateCustomer(processId: number, customer: CustomerInfoDTO) {
|
||||
const feature = customer?.features?.find((feature) => feature.key === 'webshop') ? 'webshop' : 'store';
|
||||
const customerCreateQueryParams = {
|
||||
gender: customer?.gender,
|
||||
title: customer?.title,
|
||||
firstName: customer?.firstName,
|
||||
lastName: customer?.lastName,
|
||||
email: customer?.communicationDetails?.email,
|
||||
phone: customer?.communicationDetails?.phone,
|
||||
mobile: customer?.communicationDetails?.mobile,
|
||||
street: customer?.address?.street,
|
||||
streetNumber: customer?.address?.streetNumber,
|
||||
zipCode: customer?.address?.zipCode,
|
||||
city: customer?.address?.city,
|
||||
country: customer?.address?.country,
|
||||
info: customer?.address?.info,
|
||||
name: customer?.organisation?.name,
|
||||
department: customer?.organisation?.department,
|
||||
vatId: customer?.organisation?.vatId,
|
||||
dateOfBirth: customer?.dateOfBirth,
|
||||
card: encodeURIComponent(JSON.stringify(customer?.features[0])),
|
||||
cardFeature: feature,
|
||||
};
|
||||
const formData = encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(customer));
|
||||
this._router.navigate(
|
||||
['/kunde', processId, 'customer', 'create', customer?.features?.find((feature) => feature.key === 'webshop') ? 'webshop' : 'store'],
|
||||
[
|
||||
'/kunde',
|
||||
processId,
|
||||
'customer',
|
||||
'create',
|
||||
customer?.features?.find((feature) => feature.key === 'webshop') ? 'webshop-p4m' : 'store-p4m',
|
||||
],
|
||||
{
|
||||
queryParams: customerCreateQueryParams,
|
||||
queryParams: { formData },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="features">
|
||||
<ng-container>
|
||||
<ng-container *ngFor="let feature of customer?.features">
|
||||
<div class="feature" *ngIf="feature.enabled">
|
||||
<div class="feature" *ngIf="feature.group === 'd-customertype'">
|
||||
<ui-icon icon="check" size="15px"></ui-icon>
|
||||
<span>{{ feature.description }}</span>
|
||||
</div>
|
||||
|
||||
@@ -36,6 +36,7 @@ export class CustomerSearchStateSearchService implements SearchStateSearchServic
|
||||
message: response.message,
|
||||
hits: response.hits ?? 0,
|
||||
error: response.error ?? false,
|
||||
response: response,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-checkbox [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
AGBs sind unterschrieben{{ requiredMark ? '*' : '' }}
|
||||
</ui-checkbox>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid justify-center font-bold;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accept-agb-form-block',
|
||||
templateUrl: 'accept-agb-form-block.component.html',
|
||||
styleUrls: ['accept-agb-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
exportAs: 'appAcceptAGBFormBlock',
|
||||
})
|
||||
export class AcceptAGBFormBlockComponent extends FormBlockControl<boolean> {
|
||||
get tabIndexEnd(): number {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
initializeControl(data?: boolean): void {
|
||||
this.control = new FormControl(data ?? false, this.getValidatorFn(), this.getAsyncValidatorFn());
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: boolean; current: boolean }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators(this.getValidatorFn());
|
||||
}
|
||||
|
||||
setValue(value: boolean): void {
|
||||
this.control.setValue(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AcceptAGBFormBlockComponent } from './accept-agb-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, UiCheckboxModule, ReactiveFormsModule],
|
||||
exports: [AcceptAGBFormBlockComponent],
|
||||
declarations: [AcceptAGBFormBlockComponent],
|
||||
})
|
||||
export class AcceptAGBFormBlockModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './accept-agb-form-block.component';
|
||||
export * from './accept-agb-form-block.module';
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface AddressFormBlockData {
|
||||
street?: string;
|
||||
streetNumber?: string;
|
||||
zipCode?: string;
|
||||
city?: string;
|
||||
info?: string;
|
||||
country?: string;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<ng-container [formGroup]="control">
|
||||
<ui-form-control label="Straße" [requiredMark]="requiredMarks.includes('street') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="street" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" [requiredMark]="requiredMarks.includes('streetNumber') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="streetNumber" [tabindex]="tabIndexStart + 1" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="PLZ" [requiredMark]="requiredMarks.includes('zipCode') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="zipCode" [tabindex]="tabIndexStart + 2" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" [requiredMark]="requiredMarks.includes('city') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="city" [tabindex]="tabIndexStart + 3" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="col-span-2" label="Adresszusatz" [clearable]="false" [requiredMark]="requiredMarks.includes('info') ? '*' : ''">
|
||||
<input uiInput type="text" formControlName="info" [tabindex]="tabIndexStart + 4" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="col-span-2" label="Land" [clearable]="true" [requiredMark]="requiredMarks.includes('country') ? '*' : ''">
|
||||
<ui-select formControlName="country" [tabindex]="tabIndexStart + 5" [readonly]="readonly">
|
||||
<ui-select-option *ngFor="let country of countries || (countries$ | async)" [label]="country.name" [value]="country.isO3166_A_3">
|
||||
</ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-cols-2 gap-x-8;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO } from '@swagger/crm';
|
||||
import { camelCase } from 'lodash';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { AddressFormBlockData } from './address-form-block-data';
|
||||
|
||||
@Component({
|
||||
selector: 'app-address-form-block',
|
||||
templateUrl: 'address-form-block.component.html',
|
||||
styleUrls: ['address-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AddressFormBlockComponent extends FormBlockGroup<AddressFormBlockData> implements OnInit {
|
||||
@Input() countries: CountryDTO[];
|
||||
|
||||
@Input() defaults: Partial<AddressFormBlockData>;
|
||||
|
||||
get tabIndexEnd(): number {
|
||||
return this.tabIndexStart + 5;
|
||||
}
|
||||
|
||||
countries$: Observable<CountryDTO[]>;
|
||||
|
||||
constructor(private readonly _customerService: CrmCustomerService, private _cdr: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
|
||||
this.countries$ = this._customerService.getCountries().pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
initializeControl(data?: AddressFormBlockData): void {
|
||||
this.control = new FormGroup({
|
||||
country: new FormControl(data?.country ?? this.defaults?.country ?? '', this.getValidatorFn('country')),
|
||||
city: new FormControl(data?.city ?? this.defaults?.city ?? '', this.getValidatorFn('city')),
|
||||
street: new FormControl(data?.street ?? this.defaults?.street ?? '', this.getValidatorFn('street')),
|
||||
streetNumber: new FormControl(data?.streetNumber ?? this.defaults?.streetNumber ?? '', this.getValidatorFn('streetNumber')),
|
||||
zipCode: new FormControl(data?.zipCode ?? this.defaults?.zipCode ?? '', this.getValidatorFn('zipCode')),
|
||||
info: new FormControl(data?.info ?? this.defaults?.info ?? '', this.getValidatorFn('info')),
|
||||
});
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: AddressFormBlockData; current: AddressFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
country: update.current.country ?? '',
|
||||
city: update.current.city ?? '',
|
||||
street: update.current.street ?? '',
|
||||
streetNumber: update.current.streetNumber ?? '',
|
||||
zipCode: update.current.zipCode ?? '',
|
||||
info: update.current.info ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
setAddressValidationError(invalidProperties: Record<keyof AddressFormBlockData, string>) {
|
||||
const keys = Object.keys(invalidProperties);
|
||||
for (const key of keys) {
|
||||
this.control.get(camelCase(key))?.setErrors({ validateAddress: invalidProperties[key] });
|
||||
}
|
||||
this.control.markAllAsTouched();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.get('country')?.setValidators(this.getValidatorFn('country'));
|
||||
this.control.get('city')?.setValidators(this.getValidatorFn('city'));
|
||||
this.control.get('street')?.setValidators(this.getValidatorFn('street'));
|
||||
this.control.get('streetNumber')?.setValidators(this.getValidatorFn('streetNumber'));
|
||||
this.control.get('zipCode')?.setValidators(this.getValidatorFn('zipCode'));
|
||||
this.control.get('info')?.setValidators(this.getValidatorFn('info'));
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AddressFormBlockComponent } from './address-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, UiFormControlModule, UiInputModule, UiSelectModule, ReactiveFormsModule],
|
||||
exports: [AddressFormBlockComponent],
|
||||
declarations: [AddressFormBlockComponent],
|
||||
})
|
||||
export class AddressFormBlockModule {}
|
||||
3
apps/page/customer/src/lib/form-blocks/address/index.ts
Normal file
3
apps/page/customer/src/lib/form-blocks/address/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './address-form-block-data';
|
||||
export * from './address-form-block.component';
|
||||
export * from './address-form-block.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-form-control label="Geburtsdatum (TT.MM.JJJJ)" [requiredMark]="requiredMark ? '*' : ''">
|
||||
<input uiDateInput type="text" [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FormControl, Validators } from '@angular/forms';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-birth-date-form-block',
|
||||
templateUrl: 'birth-date-form-block.component.html',
|
||||
styleUrls: ['birth-date-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BirthDateFormBlockComponent extends FormBlockControl<Date> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
initializeControl(data?: Date): void {
|
||||
this.control = new FormControl(data, [...this.getValidatorFn(), UiValidators.date]);
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: Date; current: Date }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn(), UiValidators.date]);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { BirthDateFormBlockComponent } from './birth-date-form-block.component';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ReactiveFormsModule, UiFormControlModule, UiInputModule, UiCommonModule],
|
||||
exports: [BirthDateFormBlockComponent],
|
||||
declarations: [BirthDateFormBlockComponent],
|
||||
})
|
||||
export class BirthDateFormBlockModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './birth-date-form-block.component';
|
||||
export * from './birth-date-form-block.module';
|
||||
// end:ng42.barrel
|
||||
@@ -0,0 +1,12 @@
|
||||
import { AddressFormBlockData } from '../address';
|
||||
import { NameFormBlockData } from '../name/name-form-block-data';
|
||||
import { OrganisationFormBlockData } from '../organisation/organisation-form-block-data';
|
||||
|
||||
export interface DeviatingAddressFormBlockData {
|
||||
deviatingAddress?: boolean;
|
||||
organisation?: OrganisationFormBlockData;
|
||||
name?: NameFormBlockData;
|
||||
address?: AddressFormBlockData;
|
||||
email?: string;
|
||||
phoneNumbers?: { mobile?: string; phone?: string };
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<ui-checkbox [formControl]="control.get('deviatingAddress')" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly">
|
||||
<ng-content></ng-content>
|
||||
</ui-checkbox>
|
||||
<div class="address-block" *ngIf="control.value.deviatingAddress">
|
||||
<div class="wrapper">
|
||||
<app-organisation-form-block
|
||||
*ngIf="organisation"
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
#orgaBlock
|
||||
(onInit)="addOrganisationGroup($event)"
|
||||
(onDestroy)="removeOrganisationGroup()"
|
||||
[data]="data?.organisation"
|
||||
#nameFormBlock
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
[requiredMarks]="organisationRequiredMarks"
|
||||
[validatorFns]="organisationValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-organisation-form-block>
|
||||
<app-name-form-block
|
||||
(onInit)="addNameGroup($event)"
|
||||
(onDestroy)="removeNameGroup()"
|
||||
[data]="data?.name"
|
||||
#nameFormBlock
|
||||
[tabIndexStart]="orgaBlock ? orgaBlock?.tabIndexEnd + 1 : tabIndexStart + 1"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-name-form-block>
|
||||
<app-address-form-block
|
||||
#addressFormBlock
|
||||
[defaults]="defaults?.address"
|
||||
(onInit)="addAddressGroup($event)"
|
||||
(onDestroy)="removeAddressGroup()"
|
||||
[data]="data?.address"
|
||||
[tabIndexStart]="nameFormBlock?.tabIndexEnd + 1"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-address-form-block>
|
||||
<app-email-form-block
|
||||
*ngIf="email"
|
||||
#emailFormBlock
|
||||
[tabIndexStart]="addressFormBlock + 1"
|
||||
[defaults]="defaults?.email"
|
||||
(onInit)="addEmailGroup($event)"
|
||||
(onDestroy)="removeEmailGroup()"
|
||||
[data]="data?.email"
|
||||
[tabIndexStart]="nameFormBlock?.tabIndexEnd + 1"
|
||||
[requiredMark]="emailRequiredMark"
|
||||
[validatorFns]="emailValidationFns"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
</app-email-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
*ngIf="phoneNumbers"
|
||||
[tabIndexStart]="emailFormBlock ? emailFormBlock.tabIndexEnd + 1 : addressFormBlock.tabIndexEnd + 1"
|
||||
[defaults]="defaults?.phoneNumbers"
|
||||
(onInit)="addPhoneNumbersGroup($event)"
|
||||
(onDestroy)="removePhoneNumbersGroup()"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
[tabIndexStart]="emailFormBlock?.tabIndexEnd+1" [requiredMarks]="phoneNumbersRequiredMarks" [validatorFns]="phoneNumbersValidatorFns">
|
||||
</app-phone-numbers-form-block>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
ui-checkbox {
|
||||
@apply font-semibold;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild, ElementRef, Self, AfterViewInit, ChangeDetectorRef, Input } from '@angular/core';
|
||||
import { FormBuilder, ValidatorFn } from '@angular/forms';
|
||||
import { FormBlockGroup } from '../form-block';
|
||||
import { DeviatingAddressFormBlockData } from './deviating-address-form-block-data';
|
||||
import { NameFormBlockComponent } from '../name';
|
||||
import { AddressFormBlockComponent, AddressFormBlockData } from '../address';
|
||||
import { NameFormBlockData } from '../name/name-form-block-data';
|
||||
import { OrganisationFormBlockComponent } from '../organisation';
|
||||
import { OrganisationFormBlockData } from '../organisation/organisation-form-block-data';
|
||||
import { EmailFormBlockComponent } from '../email';
|
||||
import { PhoneNumbersFormBlockComponent, PhoneNumbersFormBlockData } from '../phone-numbers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-deviating-address-form-block',
|
||||
templateUrl: 'deviating-address-form-block.component.html',
|
||||
styleUrls: ['deviating-address-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DeviatingAddressFormBlockComponent extends FormBlockGroup<DeviatingAddressFormBlockData> implements AfterViewInit {
|
||||
@Input()
|
||||
organisation = false;
|
||||
|
||||
@Input()
|
||||
email = false;
|
||||
|
||||
@Input()
|
||||
phoneNumbers = false;
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
private readonly _addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(EmailFormBlockComponent, { static: false })
|
||||
private readonly _emailFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(PhoneNumbersFormBlockComponent, { static: false })
|
||||
private readonly _phoneNumbersFormBlock: AddressFormBlockComponent;
|
||||
|
||||
get tabIndexEnd() {
|
||||
let tabIndex = this.tabIndexStart;
|
||||
if (this.control.value.deviatingDeliveryAddress) {
|
||||
return (
|
||||
this._phoneNumbersFormBlock?.tabIndexEnd ?? this._emailFormBlock?.tabIndexEnd ?? this._addressFormBlock?.tabIndexEnd ?? tabIndex
|
||||
);
|
||||
}
|
||||
return tabIndex;
|
||||
}
|
||||
|
||||
@Input() defaults: Partial<DeviatingAddressFormBlockData>;
|
||||
|
||||
@Input()
|
||||
nameRequiredMarks: Array<keyof NameFormBlockData>;
|
||||
|
||||
@Input()
|
||||
nameValidatorFns: Record<keyof NameFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
addressRequiredMarks: Array<keyof AddressFormBlockData>;
|
||||
|
||||
@Input()
|
||||
addressValidatorFns: Record<keyof AddressFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
organisationRequiredMarks: Array<keyof OrganisationFormBlockData>;
|
||||
|
||||
@Input()
|
||||
organisationValidatorFns: Record<keyof OrganisationFormBlockData, ValidatorFn[]>;
|
||||
|
||||
@Input()
|
||||
emailRequiredMark = false;
|
||||
|
||||
@Input()
|
||||
emailValidationFns: ValidatorFn[] = [];
|
||||
|
||||
@Input()
|
||||
phoneNumbersRequiredMarks: Array<keyof PhoneNumbersFormBlockData>;
|
||||
|
||||
@Input()
|
||||
phoneNumbersValidatorFns: Record<keyof PhoneNumbersFormBlockData, ValidatorFn[]>;
|
||||
|
||||
constructor(private readonly _fb: FormBuilder, @Self() private _elementRef: ElementRef, private _cdr: ChangeDetectorRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
initializeControl(data?: DeviatingAddressFormBlockData): void {
|
||||
this.control = this._fb.group({
|
||||
deviatingAddress: this._fb.control(data?.deviatingAddress ?? false, this.getValidatorFn('deviatingAddress')),
|
||||
});
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: DeviatingAddressFormBlockData; current: DeviatingAddressFormBlockData }): void {
|
||||
this.control.patchValue({
|
||||
deviatingAddress: update.current?.deviatingAddress ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
addOrganisationGroup(cmp: OrganisationFormBlockComponent) {
|
||||
if (!this.control.contains('organisation')) {
|
||||
this.control.addControl('organisation', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeOrganisationGroup() {
|
||||
if (this.control.contains('organisation')) {
|
||||
this.control.removeControl('organisation');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addNameGroup(cmp: NameFormBlockComponent) {
|
||||
if (!this.control.contains('name')) {
|
||||
this.control.addControl('name', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeNameGroup() {
|
||||
if (this.control.contains('name')) {
|
||||
this.control.removeControl('name');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addAddressGroup(cmp: AddressFormBlockComponent) {
|
||||
if (!this.control.contains('address')) {
|
||||
this.control.addControl('address', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeAddressGroup() {
|
||||
if (this.control.contains('address')) {
|
||||
this.control.removeControl('address');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addEmailGroup(cmp: AddressFormBlockComponent) {
|
||||
if (!this.control.contains('email')) {
|
||||
this.control.addControl('email', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removeEmailGroup() {
|
||||
if (this.control.contains('email')) {
|
||||
this.control.removeControl('email');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
addPhoneNumbersGroup(cmp: AddressFormBlockComponent) {
|
||||
if (!this.control.contains('phoneNumbers')) {
|
||||
this.control.addControl('phoneNumbers', cmp.control);
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
removePhoneNumbersGroup() {
|
||||
if (this.control.contains('phoneNumbers')) {
|
||||
this.control.removeControl('phoneNumbers');
|
||||
}
|
||||
setTimeout(() => this._cdr.markForCheck(), 0);
|
||||
}
|
||||
|
||||
setAddressValidationError(invalidProperties: Record<keyof AddressFormBlockData, string>) {
|
||||
this._addressFormBlock?.setAddressValidationError(invalidProperties);
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { DeviatingAddressFormBlockComponent } from './deviating-address-form-block.component';
|
||||
import { UiCheckboxModule } from '@ui/checkbox';
|
||||
import { AddressFormBlockModule } from '../address';
|
||||
import { NameFormBlockModule } from '../name';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { OrganisationFormBlockModule } from '../organisation';
|
||||
import { EmailFormBlockModule } from '../email';
|
||||
import { PhoneNumbersFormBlockModule } from '../phone-numbers';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
UiCheckboxModule,
|
||||
NameFormBlockModule,
|
||||
AddressFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiCommonModule,
|
||||
],
|
||||
exports: [DeviatingAddressFormBlockComponent],
|
||||
declarations: [DeviatingAddressFormBlockComponent],
|
||||
})
|
||||
export class DeviatingAddressFormBlockComponentModule {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './deviating-address-form-block-data';
|
||||
export * from './deviating-address-form-block.component';
|
||||
export * from './deviating-address-form-block.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<ui-form-control label="E-Mail" [requiredMark]="requiredMark ? '*' : ''">
|
||||
<input uiInput type="mail" [formControl]="control" [tabindex]="tabIndexStart" [autofocus]="focusAfterInit" [readonly]="readonly" />
|
||||
</ui-form-control>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
|
||||
@Component({
|
||||
selector: 'app-email-form-block',
|
||||
templateUrl: 'email-form-block.component.html',
|
||||
styleUrls: ['email-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class EmailFormBlockComponent extends FormBlockControl<string> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn(), validateEmail]);
|
||||
this.control.setAsyncValidators(this.getAsyncValidatorFn());
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
|
||||
initializeControl(data?: string): void {
|
||||
this.control = new FormControl(data, [...this.getValidatorFn(), validateEmail], this.getAsyncValidatorFn());
|
||||
}
|
||||
|
||||
_patchValue(update: { previous: string; current: string }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user