Merged PR 1387: Kubi Kundenanlage und Kundenkarte

Related work items: #3230, #3233
This commit is contained in:
Lorenz Hilpert
2022-10-10 15:57:50 +00:00
parent 14d1bb6ac8
commit 82513b5dde
15 changed files with 312 additions and 142 deletions

View File

@@ -107,15 +107,23 @@ export class CrmCustomerService {
return this.customerService.CustomerPatchCustomer({ customerId, customer: { ...customer, notificationChannels } });
}
createB2BCustomer(customer: CustomerDTO) {
createB2BCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
communicationDetails: customer?.communicationDetails,
});
return this.customerService.CustomerCreateCustomer({
customer: { ...customer, customerType: 16, notificationChannels },
modifiers: [{ key: 'b2b', group: 'customertype' }],
});
const payload: CustomerDTO = { ...customer, customerType: 16, notificationChannels };
payload.shippingAddresses = payload.shippingAddresses ?? [];
payload.payers = payload.payers ?? [];
return this.customerService
.CustomerCreateCustomer({
customer: payload,
modifiers: [{ key: 'b2b', group: 'customertype' }],
})
.toPromise();
}
createOnlineCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
@@ -162,23 +170,109 @@ export class CrmCustomerService {
];
}
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
if (p4mUser) {
modifiers.push({ key: 'add-loyalty-card', value: p4mUser });
}
return this.customerService.CustomerCreateCustomer({
customer: { ...payload, notificationChannels },
modifiers: [{ key: 'webshop', group: 'customertype' }],
modifiers,
});
}
async updateOnlineCustomerP4M(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const billingAddressToAdd = customer.payers.find((address) => isNil(address.payer.id));
mapCustomerToPayer(customer: CustomerDTO): PayerDTO {
return {
address: customer.address,
communicationDetails: customer.communicationDetails,
firstName: customer.firstName,
lastName: customer.lastName,
organisation: customer.organisation,
title: customer.title,
payerType: 1,
gender: customer.gender,
};
}
if (billingAddressToAdd) {
customer.payers = customer.payers.filter((address) => !isNil(address.payer.id));
mapCustomerToShippingAddress(customer: CustomerDTO): ShippingAddressDTO {
return {
address: customer.address,
communicationDetails: customer.communicationDetails,
firstName: customer.firstName,
gender: customer.gender,
lastName: customer.lastName,
organisation: customer.organisation,
title: customer.title,
type: 1,
};
}
async updateToOnlineCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const payload: CustomerDTO = { shippingAddresses: [], payers: [], ...customer, customerType: 8, hasOnlineAccount: true };
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
communicationDetails: payload?.communicationDetails,
});
const shippingAddressesToAdd = payload.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
payload.shippingAddresses = payload.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
if (payload.shippingAddresses.length === 0) {
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(payload));
}
const shippingAddressToAdd = customer.shippingAddresses.find((address) => isNil(address.id));
const payersToAdd = payload.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
payload.payers = payload.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
if (shippingAddressToAdd) {
customer.shippingAddresses = customer.shippingAddresses.filter((address) => !isNil(address.id));
if (payload.payers.length === 0) {
payersToAdd.unshift({
...this.mapCustomerToPayer(payload),
payerType: payload.customerType,
});
}
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
const res = await this.customerService
.CustomerUpdateCustomer({
customerId: customer.id,
payload: {
modifiers,
customer: { ...payload, notificationChannels },
},
})
.toPromise();
for (let shippingAddress of shippingAddressesToAdd) {
await this.createShippingAddress(res.result.id, shippingAddress, true);
}
for (let payer of payersToAdd) {
await this.createPayer(res.result.id, payer, true);
}
return res;
}
async updateToP4MOnlineCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const shippingAddressesToAdd = customer.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
customer.shippingAddresses = customer.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
if (customer.shippingAddresses.length === 0) {
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(customer));
}
const payersToAdd = customer.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
customer.payers = customer.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
if (customer.payers.length === 0) {
payersToAdd.unshift({
...this.mapCustomerToPayer(customer),
payerType: customer.customerType,
});
}
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
@@ -199,72 +293,107 @@ export class CrmCustomerService {
})
.toPromise();
if (billingAddressToAdd) {
await this.createPayer(res.result.id, billingAddressToAdd.payer.data).toPromise();
for (let shippingAddress of shippingAddressesToAdd) {
await this.createShippingAddress(res.result.id, shippingAddress, true);
}
if (shippingAddressToAdd) {
await this.createShippingAddress(res.result.id, shippingAddressToAdd.data).toPromise();
for (let payer of payersToAdd) {
await this.createPayer(res.result.id, payer, true);
}
return res;
}
createGuestCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
async updateStoreP4MToWebshopP4M(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const shippingAddressesToAdd = customer.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
customer.shippingAddresses = customer.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
if (customer.shippingAddresses.length === 0) {
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(customer));
}
const payersToAdd = customer.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
customer.payers = customer.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
if (customer.payers.length === 0) {
payersToAdd.unshift({
...this.mapCustomerToPayer(customer),
payerType: customer.customerType,
});
}
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
const res = await this.customerService
.CustomerUpdateCustomer({
customerId: customer.id,
payload: {
customer,
modifiers,
},
})
.toPromise();
for (let shippingAddress of shippingAddressesToAdd) {
await this.createShippingAddress(res.result.id, shippingAddress, true);
}
for (let payer of payersToAdd) {
await this.createPayer(res.result.id, payer, true);
}
return res;
}
async createGuestCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
communicationDetails: customer?.communicationDetails,
});
const payload: CustomerDTO = { ...customer, customerType: 8, isGuestAccount: true, notificationChannels };
if (!(isArray(payload.shippingAddresses) && payload.shippingAddresses.length > 0)) {
payload.shippingAddresses = [
{
data: {
address: payload.address,
communicationDetails: payload.communicationDetails,
firstName: payload.firstName,
gender: payload.gender,
lastName: payload.lastName,
organisation: payload.organisation,
title: payload.title,
type: 1,
},
},
];
}
payload.shippingAddresses = customer.shippingAddresses ?? [];
if (!(isArray(payload.payers) && payload.payers.length > 0)) {
payload.payers = [
{
payer: {
data: {
address: payload.address,
communicationDetails: payload.communicationDetails,
firstName: payload.firstName,
gender: payload.gender,
lastName: payload.lastName,
organisation: payload.organisation,
title: payload.title,
payerType: payload.customerType,
},
},
},
];
}
return this.customerService.CustomerCreateCustomer({
customer: payload,
modifiers: [{ key: 'webshop', group: 'customertype' }],
payload.shippingAddresses.push({
data: this.mapCustomerToShippingAddress(customer),
});
payload.payers = customer.payers ?? [];
payload.payers.push({
payer: {
data: {
...this.mapCustomerToPayer(customer),
payerType: payload.customerType,
},
},
});
const res = await this.customerService
.CustomerCreateCustomer({
customer: payload,
modifiers: [{ key: 'webshop', group: 'customertype' }],
})
.toPromise();
return res;
}
createStoreCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
communicationDetails: customer?.communicationDetails,
});
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'store', group: 'customertype' }];
if (p4mUser) {
modifiers.push({ key: 'add-loyalty-card', value: p4mUser });
}
return this.customerService.CustomerCreateCustomer({
customer: { ...customer, customerType: 8, notificationChannels },
modifiers: [{ key: 'store', group: 'customertype' }],
modifiers,
});
}
@@ -346,11 +475,7 @@ export class CrmCustomerService {
return this.customerService.CustomerModifyPayerReference({ payerId, customerId, isDefault });
}
createShippingAddress(
customerId: number,
shippingAddress: ShippingAddressDTO,
isDefault?: boolean
): Observable<Result<ShippingAddressDTO>> {
createShippingAddress(customerId: number, shippingAddress: ShippingAddressDTO, isDefault?: boolean): Promise<Result<ShippingAddressDTO>> {
const data: ShippingAddressDTO = { ...shippingAddress };
if (isDefault) {
data.isDefault = new Date().toJSON();
@@ -358,7 +483,7 @@ export class CrmCustomerService {
delete data.isDefault;
}
return this.shippingAddressService.ShippingAddressCreateShippingAddress({ customerId, shippingAddress: data });
return this.shippingAddressService.ShippingAddressCreateShippingAddress({ customerId, shippingAddress: data }).toPromise();
}
updateShippingAddress(
@@ -366,7 +491,7 @@ export class CrmCustomerService {
shippingAddressId: number,
shippingAddress: ShippingAddressDTO,
isDefault?: boolean
): Observable<Result<ShippingAddressDTO>> {
): Promise<Result<ShippingAddressDTO>> {
const data: ShippingAddressDTO = { ...shippingAddress };
if (isDefault) {
@@ -375,7 +500,9 @@ export class CrmCustomerService {
delete data.isDefault;
}
return this.shippingAddressService.ShippingAddressUpdateShippingAddress({ shippingAddressId, shippingAddress: data, customerId });
return this.shippingAddressService
.ShippingAddressUpdateShippingAddress({ shippingAddressId, shippingAddress: data, customerId })
.toPromise();
}
getShippingAddress(shippingAddressId: number): Observable<Result<ShippingAddressDTO>> {

View File

@@ -379,7 +379,7 @@ export class PurchasingOptionsModalComponent {
});
} else {
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop'], {
queryParams: { upgradeCustomerId: customer?.id },
queryParams: { formData: encodeFormData(mapCustomerDtoToCustomerCreateFormData(customer)) },
});
}
}

View File

@@ -260,7 +260,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
customer.payers = [
{
payer: { data: billingAddress },
isDefault: new Date().toJSON(),
isDefault: new Date().toISOString(),
},
];
}
@@ -283,7 +283,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
customer.shippingAddresses = [
{
data: shippingAddress,
data: { ...shippingAddress, isDefault: new Date().toISOString() },
},
];
}

View File

@@ -67,9 +67,9 @@
<app-deviating-address-form-block
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
[data]="data.deviationgDeliveryAddress"
(dataChanges)="patchFormData('deviationgDeliveryAddress', $event)"
(onInit)="addFormBlock('deviationgDeliveryAddress', $event)"
[data]="data.deviatingDeliveryAddress"
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
[nameRequiredMarks]="deviatingNameRequiredMarks"
[nameValidatorFns]="deviatingNameValidationFns"
[addressRequiredMarks]="addressRequiredMarks"

View File

@@ -43,9 +43,8 @@ export class CreateB2BCustomerComponent extends AbstractCreateCustomer {
};
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
return await this.customerService
.createB2BCustomer(customer)
.pipe(map((r) => r.result))
.toPromise();
const res = await this.customerService.createB2BCustomer(customer);
return res.result;
}
}

View File

@@ -84,9 +84,9 @@
<app-deviating-address-form-block
[tabIndexStart]="birthDate.tabIndexEnd + 1"
[data]="data.deviationgDeliveryAddress"
(dataChanges)="patchFormData('deviationgDeliveryAddress', $event)"
(onInit)="addFormBlock('deviationgDeliveryAddress', $event)"
[data]="data.deviatingDeliveryAddress"
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
[nameRequiredMarks]="deviatingNameRequiredMarks"
[nameValidatorFns]="deviatingNameValidationFns"
[addressRequiredMarks]="addressRequiredMarks"

View File

@@ -55,9 +55,8 @@ export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
return await this.customerService
.createGuestCustomer(customer)
.pipe(map((r) => r.result))
.toPromise();
const res = await this.customerService.createGuestCustomer(customer);
return res.result;
}
}

View File

@@ -245,7 +245,15 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
}
if (isWebshop) {
res = await this.customerService.createOnlineCustomer(customer).toPromise();
if (customer.id > 0) {
if (this.formData?._meta?.hasLocalityCard) {
res = await this.customerService.updateStoreP4MToWebshopP4M(customer);
} else {
res = await this.customerService.updateToP4MOnlineCustomer(customer);
}
} else {
res = await this.customerService.createOnlineCustomer(customer).toPromise();
}
} else {
res = await this.customerService.createStoreCustomer(customer).toPromise();
}

View File

@@ -6,6 +6,7 @@ import { AddressFormBlockComponent, DeviatingAddressFormBlockComponent } from '.
import { NameFormBlockData } from '../../form-blocks/name/name-form-block-data';
import { validateEmail } from '../../validators/email-validator';
import { AbstractCreateCustomer } from '../abstract-create-customer';
import { CreateP4MCustomerComponent } from '../create-p4m-customer';
@Component({
selector: 'app-create-webshop-customer',
@@ -50,10 +51,25 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
return this.customerService
.createOnlineCustomer(customer)
.pipe(map((res) => res.result))
.toPromise();
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
const isUpgrade = !!(customerDto || customerInfoDto);
if (isUpgrade) {
if (customerDto) {
customer = { ...customerDto, ...customer };
} else if (customerInfoDto) {
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
}
const res = await this.customerService.updateToOnlineCustomer(customer);
return res.result;
} else {
return this.customerService
.createOnlineCustomer(customer)
.pipe(map((res) => res.result))
.toPromise();
}
}
}

View File

@@ -9,6 +9,7 @@ export interface CustomerCreateFormData {
customerDto?: CustomerDTO;
customerInfoDto?: CustomerInfoDTO;
p4mRequired?: boolean;
hasLocalityCard?: boolean;
};
agb?: boolean;
address?: AddressFormBlockData;
@@ -31,10 +32,13 @@ export function mapCustomerDtoToCustomerCreateFormData(customerDto: CustomerDTO)
p4m = customerDto.features?.find((f) => f.key === 'p4mUser')?.value;
}
const hasLocalityCard = !!p4m;
return {
_meta: {
customerDto,
p4mRequired: !!p4m,
hasLocalityCard,
},
p4m,
address: customerDto.address,
@@ -67,10 +71,13 @@ export function mapCustomerInfoDtoToCustomerCreateFormData(customerInfoDto: Cust
const newsletter = !!customerInfoDto.features?.find((f) => f.key === 'kubi_newsletter' && f.group === 'KUBI_NEWSLETTER');
const hasLocalityCard = !!p4m;
return {
_meta: {
customerInfoDto,
p4mRequired: !!p4m,
hasLocalityCard,
},
p4m,
address: customerInfoDto.address,

View File

@@ -139,7 +139,7 @@ export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer im
customer.features = customer.features.filter((feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER');
}
res = await this.customerService.updateOnlineCustomerP4M(customer);
res = await this.customerService.updateToP4MOnlineCustomer(customer);
return res.result;
}

View File

@@ -103,7 +103,7 @@ export abstract class ShippingCreateComponent implements OnInit {
}
try {
await this.customerService.createShippingAddress(this.customerId, this.control.value, this.control.value.isDefault).toPromise();
await this.customerService.createShippingAddress(this.customerId, this.control.value, this.control.value.isDefault);
this.location.back();
} catch (err) {
this.control.enable();

View File

@@ -123,9 +123,12 @@ export abstract class ShippingEditComponent implements OnInit {
}
try {
await this.customerService
.updateShippingAddress(this.customerId, this.shippingAddressId, this.control.value, this.control.value.isDefault)
.toPromise();
await this.customerService.updateShippingAddress(
this.customerId,
this.shippingAddressId,
this.control.value,
this.control.value.isDefault
);
this.location.back();
} catch (err) {
this.control.enable();

View File

@@ -1,58 +1,68 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router';
import { ApplicationService } from '@core/application';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router';
import { DomainCheckoutService } from '@domain/checkout';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CustomerCreateGuard implements CanActivateChild {
readonly typeMappings = {
guest: 'guest',
online: 'webshop',
b2b: 'b2b',
branch: 'store',
};
export class CustomerCreateGuard implements CanActivateChild, CanActivate {
constructor(private router: Router, private checkoutService: DomainCheckoutService) {}
constructor(private router: Router, private checkoutService: DomainCheckoutService, private application: ApplicationService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
// exit with true if canActivateChild will be called
if (route.firstChild) {
return true;
}
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const currentRoute = route.url[0].path;
const processId = this.getProcessId(route);
const canActivateCustomerType = await this.canActivateCustomerType(processId, 'store');
return this.checkoutService.getSetableCustomerTypes(this.application.activatedProcessId).pipe(
map((selectables) => {
if (selectables[currentRoute]) {
return true;
}
if (canActivateCustomerType === true) {
return true;
}
// If Upgrade for p4mUser is necessary - select the correct route
// cardFeature is either 'webshop' or 'store' for p4mUsers
if (!!route?.queryParams?.card && !!route?.queryParams?.cardFeature) {
this.router.navigate(
[
'/customer/create',
selectables[route?.queryParams?.cardFeature] === true
? route?.queryParams?.cardFeature
: route?.queryParams?.cardFeature === 'webshop'
? 'store'
: 'webshop',
],
{ queryParams: route.queryParams }
);
return false;
}
await this.navigate(processId, canActivateCustomerType[0]);
for (const key in selectables) {
if (Object.prototype.hasOwnProperty.call(selectables, key)) {
if (selectables[key]) {
this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', key], {
queryParams: route.queryParams,
});
return false;
}
}
}
return false;
})
);
return true;
}
async canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
const processId = this.getProcessId(route);
const customerType = route.routeConfig.path?.replace('/update', '');
const canActivateCustomerType = await this.canActivateCustomerType(processId, customerType);
if (canActivateCustomerType === true) {
return true;
}
await this.navigate(processId, canActivateCustomerType[0]);
return true;
}
async canActivateCustomerType(processId: number, customerType: string): Promise<true | string[]> {
const res = await this.checkoutService.getSetableCustomerTypes(processId).toPromise();
customerType = customerType.toLowerCase().replace('-p4m', '');
if (res[customerType]) {
return true;
}
return Object.keys(res).filter((key) => res[key]);
}
getProcessId(snapshot: ActivatedRouteSnapshot): number | undefined {
if (snapshot.params['processId']) {
return +snapshot.params['processId'];
} else if (snapshot.parent) {
return this.getProcessId(snapshot.parent);
}
return undefined;
}
navigate(processId: number, customerType: string): Promise<boolean> {
const url = `/kunde/${processId}/customer/create/${customerType}`;
return this.router.navigateByUrl(url);
}
}

View File

@@ -27,6 +27,7 @@ import {
} from './create-customer';
import { CreateP4MCustomerComponent } from './create-customer/create-p4m-customer';
import { UpdateP4MWebshopCustomerComponent } from './create-customer/update-p4m-webshop-customer';
import { CustomerCreateGuard } from './guards/customer-create.guard';
const routes: Routes = [
{
@@ -44,7 +45,8 @@ const routes: Routes = [
},
{
path: 'create',
// canActivateChild: [CustomerCreateGuard],
canActivate: [CustomerCreateGuard],
canActivateChild: [CustomerCreateGuard],
children: [
{ path: 'store', component: CreateStoreCustomerComponent },
{ path: 'webshop', component: CreateWebshopCustomerComponent },
@@ -53,7 +55,6 @@ const routes: Routes = [
{ path: 'webshop-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'webshop' } },
{ path: 'store-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'store' } },
{ path: 'webshop-p4m/update', component: UpdateP4MWebshopCustomerComponent, data: { customerType: 'webshop' } },
{ path: '', pathMatch: 'full', redirectTo: 'store' },
],
},
{