mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'feature/customer_pages'
Conflicts: apps/page/customer/src/lib/customer-details/customer-details.component.html apps/page/customer/src/lib/customer-details/customer-details.module.ts apps/page/customer/src/lib/page-customer-routing.module.ts
This commit is contained in:
@@ -17,11 +17,11 @@ export class CartService {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
|
||||
getRequiredCustomerTypes(processId: number): Observable<('branch' | 'guest' | 'online' | 'b2b')[]> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
// getRequiredCustomerTypes(processId: number): Observable<('branch' | 'guest' | 'online' | 'b2b')[]> {
|
||||
// throw new Error('Not Implemented');
|
||||
// }
|
||||
|
||||
canAddItem(processId: number, availability: OLAAvailabilityDTO): Promise<boolean> {
|
||||
canAddItem(processId: number, availability: OLAAvailabilityDTO): Promise<true | string> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PayerDTO, ShippingAddressDTO } from '@swagger/checkout';
|
||||
import { StringDictionary } from '@cmf/core';
|
||||
import { BuyerResult, PayerDTO, ShippingAddressDTO } from '@swagger/checkout';
|
||||
import { CustomerDTO, PayerDTO as CrmPayerDTO, ShippingAddressDTO as CrmShippingAddressDTO } from '@swagger/crm';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@@ -9,7 +10,7 @@ import { Observable } from 'rxjs';
|
||||
export class CheckoutService {
|
||||
constructor() {}
|
||||
|
||||
setCustomer(processId: number, customer: CustomerDTO): Promise<boolean> {
|
||||
setCustomer(processId: number, customer: CustomerDTO): Observable<boolean> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
|
||||
@@ -32,4 +33,15 @@ export class CheckoutService {
|
||||
getSelectedShippingAddress(processId: number): Observable<ShippingAddressDTO> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
|
||||
canSetCustomer(
|
||||
processId: number,
|
||||
customer: CustomerDTO
|
||||
): Observable<{ ok?: boolean; filter?: StringDictionary<string>; message?: string }> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
|
||||
getSetableCustomerTypes(processId: number): Observable<string[]> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
InputDTO,
|
||||
PayerDTO,
|
||||
PayerService,
|
||||
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
||||
ShippingAddressDTO,
|
||||
} from '@swagger/crm';
|
||||
import { PagedResult, Result } from 'apps/domain/defs/src/public-api';
|
||||
@@ -173,4 +174,8 @@ export class CrmCustomerService {
|
||||
getPayer(payerId: number): Observable<Result<PayerDTO>> {
|
||||
return this.payerService.PayerGetPayer(payerId);
|
||||
}
|
||||
|
||||
getCustomerCard(customerId: number): Observable<ResponseArgsOfIEnumerableOfBonusCardInfoDTO> {
|
||||
return this.customerService.CustomerGetBonuscards(customerId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
einen Firmenaccount. Wir legen diesen<br />
|
||||
gerne direkt für Sie an.
|
||||
</p>
|
||||
<page-customer-type-selector [(ngModel)]="type" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<page-customer-type-selector
|
||||
[(ngModel)]="type"
|
||||
(ngModelChange)="setType($event)"
|
||||
[disabledOptions]="disabledSelectOptions$ | async"
|
||||
></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<ng-container formGroupName="organisation">
|
||||
@@ -97,6 +101,100 @@
|
||||
</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">
|
||||
<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]="false" requiredMark="*">
|
||||
<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 label="Titel">
|
||||
<ui-select formControlName="title" tabindex="22">
|
||||
<ui-select-option value="Dr." label="Dr."></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>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="23" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<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]="false" 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.invalid || control.disabled" [ngSwitch]="control.enabled">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -6,9 +6,11 @@ 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.service';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-b2b',
|
||||
@@ -16,7 +18,9 @@ import { validateEmail } from '../validators/email-validator';
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateB2BComponent extends CustomerCreateComponentBase implements OnInit {
|
||||
export class CustomerCreateB2BComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -25,13 +29,33 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: CheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
@@ -60,6 +84,31 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
phone: fb.control(customer?.communicationDetails?.phone),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control('', [validateEmail]),
|
||||
phone: fb.control(''),
|
||||
mobile: fb.control(''),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,16 +117,38 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable();
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
const address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
this.control.enable();
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
<p>
|
||||
TEXT FÜR FILIALKUNDE(MINIMALKUNDE)
|
||||
</p>
|
||||
<page-customer-type-selector [(ngModel)]="type" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<page-customer-type-selector
|
||||
[(ngModel)]="type"
|
||||
(ngModelChange)="setType($event)"
|
||||
[disabledOptions]="disabledSelectOptions$ | async"
|
||||
></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
@@ -93,6 +97,85 @@
|
||||
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="16" />
|
||||
</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="17" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="18" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="19" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="20">
|
||||
<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">
|
||||
<ui-select formControlName="title" tabindex="21">
|
||||
<ui-select-option value="Dr." label="Dr."></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>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="22" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="23" />
|
||||
</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="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="28" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="29">
|
||||
<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.invalid || control.disabled" [ngSwitch]="control.enabled">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -7,9 +7,11 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiValidators } from '@ui/common';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal.service';
|
||||
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 { CheckoutService } from '@domain/checkout';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-branch',
|
||||
@@ -17,7 +19,9 @@ import { validateEmail } from '../validators/email-validator';
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateBranchComponent extends CustomerCreateComponentBase implements OnInit {
|
||||
export class CustomerCreateBranchComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -26,13 +30,33 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: CheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
@@ -60,6 +84,26 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
phone: fb.control(customer?.communicationDetails?.phone),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,16 +112,38 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable();
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.customerService.createBranchCustomer(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) {
|
||||
this.control.enable();
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
gerne ein Onlinekonto an. Dort können <br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
<page-customer-type-selector [(ngModel)]="type" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<page-customer-type-selector
|
||||
[(ngModel)]="type"
|
||||
(ngModelChange)="setType($event)"
|
||||
[disabledOptions]="disabledSelectOptions$ | async"
|
||||
></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
@@ -100,6 +104,85 @@
|
||||
<input type="text" formControlName="dateOfBirth" uiDateInput tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="16" />
|
||||
</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="17" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="18" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="19" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="20">
|
||||
<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">
|
||||
<ui-select formControlName="title" tabindex="21">
|
||||
<ui-select-option value="Dr." label="Dr."></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>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="22" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="23" />
|
||||
</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="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="28" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="29">
|
||||
<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.invalid || control.disabled" [ngSwitch]="control.enabled">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -7,9 +7,11 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiValidators } from '@ui/common';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal.service';
|
||||
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 { CheckoutService } from '@domain/checkout';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-create-guest',
|
||||
@@ -17,7 +19,9 @@ import { validateEmail } from '../validators/email-validator';
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateGuestComponent extends CustomerCreateComponentBase implements OnInit {
|
||||
export class CustomerCreateGuestComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -26,13 +30,33 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: CheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
@@ -60,6 +84,26 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
phone: fb.control(customer?.communicationDetails?.phone),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,16 +112,38 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable();
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.customerService.createGuestCustomer(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) {
|
||||
this.control.enable();
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
gerne ein Onlinekonto an. Dort können <br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
<page-customer-type-selector [(ngModel)]="type" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<page-customer-type-selector
|
||||
[(ngModel)]="type"
|
||||
(ngModelChange)="setType($event)"
|
||||
[disabledOptions]="disabledSelectOptions$ | async"
|
||||
></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
@@ -97,6 +101,85 @@
|
||||
<input uiDateInput type="text" formControlName="dateOfBirth" tabindex="15" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="different-shipping-address" label="Die Lieferadresse weicht von der Rechnungsadresse ab">
|
||||
<input uiInput type="checkbox" formControlName="differentShippingAddress" tabindex="16" />
|
||||
</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="17" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Abteilung">
|
||||
<input uiInput type="text" formControlName="department" tabindex="18" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="USt-ID">
|
||||
<input uiInput type="text" formControlName="vatId" tabindex="19" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="20">
|
||||
<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">
|
||||
<ui-select formControlName="title" tabindex="21">
|
||||
<ui-select-option value="Dr." label="Dr."></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>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Nachname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="lastName" tabindex="22" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Vorname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="firstName" tabindex="23" />
|
||||
</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="24" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Hausnummer" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="streetNumber" tabindex="25" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<div class="control-row">
|
||||
<ui-form-control label="PLZ" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="zipCode" tabindex="26" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Ort" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="city" tabindex="27" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
|
||||
<ui-form-control label="Adresszusatz" [clearable]="false">
|
||||
<input uiInput type="text" formControlName="info" tabindex="28" />
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control label="Land" [clearable]="false" requiredMark="*">
|
||||
<ui-select formControlName="country" tabindex="29">
|
||||
<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.invalid || control.disabled" [ngSwitch]="control.enabled">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiValidators } from '@ui/common';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal.service';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'customer-create-online',
|
||||
@@ -19,9 +20,10 @@ import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
styleUrls: ['customer-create.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase implements OnInit {
|
||||
export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
upgradeCustomerId: number;
|
||||
upgradeCustomer: CustomerDTO;
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
@@ -31,7 +33,9 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
public customerService: CrmCustomerService,
|
||||
public breadcrumb: BreadcrumbService,
|
||||
public application: ApplicationService,
|
||||
public addressSelectionModal: AddressSelectionModalService
|
||||
public addressSelectionModal: AddressSelectionModalService,
|
||||
public checkoutService: CheckoutService,
|
||||
public cdr: ChangeDetectorRef
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -45,13 +49,31 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
this.customerService
|
||||
.getCustomer(this.upgradeCustomerId, 2)
|
||||
.pipe(map((response) => response.result))
|
||||
.subscribe((custoemr) => {
|
||||
this.upgradeCustomer = custoemr;
|
||||
this.control.patchValue(custoemr);
|
||||
.subscribe((customer) => {
|
||||
this.upgradeCustomer = customer;
|
||||
this.control.patchValue(customer);
|
||||
this.control.updateValueAndValidity();
|
||||
this.control.markAllAsTouched();
|
||||
});
|
||||
}
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
createControl(customer?: CustomerDTO): FormGroup {
|
||||
@@ -79,6 +101,26 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
phone: fb.control(customer?.communicationDetails?.phone),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
name: fb.control(''),
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
async submit(): Promise<void> {
|
||||
@@ -86,13 +128,20 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
return;
|
||||
}
|
||||
|
||||
this.control.disable();
|
||||
this.control.disable({ emitEvent: false });
|
||||
|
||||
let address = await this.addressSelectionModal.validateAddress(this.control.value.address);
|
||||
if (address) {
|
||||
this.control.patchValue({ address });
|
||||
}
|
||||
|
||||
if (this.control.value.differentShippingAddress) {
|
||||
const shippingAddress = await this.addressSelectionModal.validateAddress(this.control.value.shippingAddress.address);
|
||||
if (shippingAddress) {
|
||||
this.control.patchValue({ shippingAddress });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let newCustomer: CustomerDTO = this.control.value;
|
||||
if (this.upgradeCustomer) {
|
||||
@@ -101,6 +150,21 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
|
||||
const response = await this.customerService.createOnlineCustomer(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) {
|
||||
this.control.enable();
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
} else {
|
||||
|
||||
@@ -67,3 +67,11 @@ page-customer-type-selector {
|
||||
.spin {
|
||||
@apply animate-spin;
|
||||
}
|
||||
|
||||
.different-shipping-address {
|
||||
@apply flex-col justify-around text-cta-l mt-8;
|
||||
|
||||
input {
|
||||
@apply text-cta-l;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import { UiModalService } from '@ui/modal';
|
||||
import { StringDictionary } from '@cmf/core';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal.service';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
export abstract class CustomerCreateComponentBase {
|
||||
emailExistsValidator: AsyncValidatorFn = async (control): Promise<ValidationErrors | null> => {
|
||||
@@ -33,6 +35,10 @@ export abstract class CustomerCreateComponentBase {
|
||||
abstract breadcrumb: BreadcrumbService;
|
||||
abstract application: ApplicationService;
|
||||
abstract addressSelectionModal: AddressSelectionModalService;
|
||||
abstract checkoutService: CheckoutService;
|
||||
abstract cdr: ChangeDetectorRef;
|
||||
|
||||
disabledSelectOptions$: Observable<StringDictionary<boolean>>;
|
||||
|
||||
init() {
|
||||
this.type = this.activatedRoute.snapshot?.routeConfig?.path;
|
||||
@@ -47,9 +53,28 @@ export abstract class CustomerCreateComponentBase {
|
||||
tags: ['customer', 'create'],
|
||||
params: {},
|
||||
});
|
||||
|
||||
this.disabledSelectOptions$ = this.checkoutService.getSetableCustomerTypes(this.application.activatedProcessId).pipe(
|
||||
map((setableTypes) => {
|
||||
const disabledTypes: StringDictionary<boolean> = {
|
||||
webshop: false,
|
||||
b2b: false,
|
||||
store: false,
|
||||
guest: false,
|
||||
};
|
||||
|
||||
for (const key in disabledTypes) {
|
||||
if (!setableTypes.includes(key)) {
|
||||
disabledTypes[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return disabledTypes;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
setType(type: string = 'branch') {
|
||||
setType(type: string = 'store') {
|
||||
this.router.navigate(['../', type], {
|
||||
relativeTo: this.activatedRoute,
|
||||
queryParams: this.createCustomerDataQuery(this.control.value),
|
||||
|
||||
@@ -11,7 +11,8 @@ import { CustomerCreateBranchComponent } from './customer-create-branch.componen
|
||||
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.module';
|
||||
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';
|
||||
|
||||
@@ -26,6 +27,7 @@ import { UiCheckboxModule } from '@ui/checkbox';
|
||||
UiSelectModule,
|
||||
UiIconModule,
|
||||
AddressSelectionModalModule,
|
||||
CantAddCustomerToCartModalModule,
|
||||
UiCheckboxModule,
|
||||
],
|
||||
exports: [CustomerCreateBranchComponent, CustomerCreateGuestComponent, CustomerCreateOnlineComponent, CustomerCreateB2BComponent],
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
<ui-checkbox id="Gastkunde" [ngModel]="value" (ngModelChange)="setValue($event)" [disabled]="true" name="customerType" value="guest">
|
||||
<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]="!(customerType$ | async).includes('online')"
|
||||
[disabled]="disabledOptions?.webshop"
|
||||
id="Onlinekonto"
|
||||
value="online"
|
||||
value="webshop"
|
||||
name="customerType"
|
||||
>
|
||||
Onlinekonto
|
||||
@@ -14,7 +21,7 @@
|
||||
<ui-checkbox
|
||||
[ngModel]="value"
|
||||
(ngModelChange)="setValue($event)"
|
||||
[disabled]="!(customerType$ | async).includes('b2b')"
|
||||
[disabled]="disabledOptions?.b2b"
|
||||
id="B2B Kunde"
|
||||
value="b2b"
|
||||
name="customerType"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, ChangeDetectorRef, forwardRef, OnInit } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CartService } from '@domain/cart';
|
||||
import { Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
|
||||
type AvailableTypes = 'guest' | 'online' | 'b2b' | 'branch';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-type-selector',
|
||||
@@ -20,24 +18,23 @@ import { take } from 'rxjs/operators';
|
||||
})
|
||||
export class CustomerTypeSelectorComponent implements OnInit, ControlValueAccessor {
|
||||
@Input()
|
||||
value: 'guest' | 'online' | 'b2b' = 'guest';
|
||||
value: AvailableTypes = 'guest';
|
||||
|
||||
@Output()
|
||||
valueChange = new EventEmitter<'guest' | 'online' | 'b2b'>();
|
||||
valueChange = new EventEmitter<AvailableTypes>();
|
||||
|
||||
@Input()
|
||||
disabled: boolean;
|
||||
|
||||
customerType$: Observable<('guest' | 'online' | 'b2b' | 'branch')[]>;
|
||||
@Input()
|
||||
disabledOptions: { [key: string]: boolean } = {};
|
||||
|
||||
private onChange = (value: 'guest' | 'online' | 'b2b') => {};
|
||||
private onChange = (value: AvailableTypes) => {};
|
||||
private onTouched = () => {};
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef, private cartService: CartService, private application: ApplicationService) {}
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.customerType$ = this.cartService.getRequiredCustomerTypes(this.application.activatedProcessId).pipe(take(1));
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
this.value = obj;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO } from '@swagger/crm';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal.service';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CountryDTO, PayerDTO } from '@swagger/crm';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal.service';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { AssignedPayerHelper } from '@domain/crm';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO } from '@swagger/crm';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal.service';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO, ShippingAddressDTO } from '@swagger/crm';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal.service';
|
||||
import { AddressSelectionModalService } from '../../../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ShippingAddressHelper } from '@domain/crm';
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<ng-container *ngIf="customer$ | async; let customer">
|
||||
<a [routerLink]="['/customer', customer?.id]" class="card-customer-details">
|
||||
<span class="title">Kundendetails</span>
|
||||
</a>
|
||||
<a class="card-customer-orders">
|
||||
<span class="title">Bestellungen</span>
|
||||
</a>
|
||||
<div class="card-customer-card">
|
||||
<div class="header-container">
|
||||
<h1 class="title">Kundenkarte</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="info">
|
||||
Alle Infos zu Ihrer Kundenkarte <br />
|
||||
und allen Partnerkarten.
|
||||
</p>
|
||||
<div class="customer-card">
|
||||
<page-card-template
|
||||
*ngIf="customerCard$ | async; let customerCard"
|
||||
[cardDetails]="customerCard"
|
||||
[isCustomerCard]="true"
|
||||
></page-card-template>
|
||||
</div>
|
||||
<div class="partner-cards">
|
||||
<p class="partner-cards-headline">Partnerkarten</p>
|
||||
<page-card-template
|
||||
*ngFor="let partnerCard of partnerCards$ | async"
|
||||
[cardDetails]="partnerCard"
|
||||
[isCustomerCard]="false"
|
||||
></page-card-template>
|
||||
</div>
|
||||
<!-- <div class="cancel-customer-card">
|
||||
<button (click)="onCancelCard()" class="cancel-message">Kundenkarte kündigen</button>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
:host {
|
||||
@apply flex flex-col box-border overflow-scroll;
|
||||
height: calc(100vh - 293px);
|
||||
}
|
||||
|
||||
.title {
|
||||
@apply text-page-heading font-bold;
|
||||
}
|
||||
|
||||
.card-customer-orders,
|
||||
.card-customer-card,
|
||||
.card-customer-details {
|
||||
@apply bg-white p-4 text-center;
|
||||
box-shadow: 0 -2px 24px 0 #dce2e9;
|
||||
}
|
||||
|
||||
.card-customer-card {
|
||||
@apply p-0 relative;
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply relative overflow-x-hidden overflow-y-scroll;
|
||||
scroll-behavior: smooth;
|
||||
max-height: calc(100vh - 400px);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
@apply bg-white w-full absolute z-sticky;
|
||||
left: 0;
|
||||
box-shadow: 0px 19px 25px -10px white;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
.info {
|
||||
@apply text-2xl mb-px-35;
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
.partner-cards-headline {
|
||||
@apply text-xl mb-px-15 font-bold;
|
||||
}
|
||||
|
||||
.card-customer-orders .title,
|
||||
.card-customer-details .title {
|
||||
@apply text-inactive-customer text-xl;
|
||||
}
|
||||
|
||||
.cancel-customer-card {
|
||||
@apply mb-px-50;
|
||||
}
|
||||
|
||||
.cancel-message {
|
||||
@apply text-card-sub bg-transparent text-brand border-none font-bold pr-0 no-underline;
|
||||
}
|
||||
|
||||
.customer-card,
|
||||
.partner-cards {
|
||||
@apply box-border flex flex-col items-center justify-items-center;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,43 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { BonusCardInfoDTO, CustomerDTO } from '@swagger/crm';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-card',
|
||||
templateUrl: 'customer-card.component.html',
|
||||
styleUrls: ['customer-card.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCardComponent implements OnInit {
|
||||
constructor() {}
|
||||
customer$: Observable<CustomerDTO>;
|
||||
customerId$: Observable<number>;
|
||||
|
||||
ngOnInit() {}
|
||||
customerCard$: Observable<BonusCardInfoDTO>;
|
||||
partnerCards$: Observable<BonusCardInfoDTO[]>;
|
||||
|
||||
constructor(private route: ActivatedRoute, private customerService: CrmCustomerService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.customerId$ = this.route.params.pipe(map((p) => Number(p['customerId'])));
|
||||
|
||||
this.customer$ = this.customerId$.pipe(
|
||||
switchMap((id) => this.customerService.getCustomer(id, 2)),
|
||||
map((cr) => cr.result)
|
||||
);
|
||||
|
||||
this.customerCard$ = this.customerId$.pipe(
|
||||
switchMap((id) => this.customerService.getCustomerCard(id)),
|
||||
map((response) => response.result?.find((card) => card.isPrimary))
|
||||
);
|
||||
|
||||
this.partnerCards$ = this.customerId$.pipe(
|
||||
switchMap((id) => this.customerService.getCustomerCard(id)),
|
||||
map((response) => response.result?.filter((card) => !card.isPrimary))
|
||||
);
|
||||
}
|
||||
|
||||
onCancelCard(): void {}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<a [routerLink]="['/customer', customer?.id, 'orders']" class="card-customer-orders">
|
||||
<span class="title">Bestellungen</span>
|
||||
</a>
|
||||
<a [routerLink]="['/customer', customer?.id, 'card']" class="card-customer-card">
|
||||
<a *ngIf="hasCustomerCard$ | async" [routerLink]="['/customer', customer?.id, 'card']" class="card-customer-card">
|
||||
<span class="title">Kundenkarte</span>
|
||||
</a>
|
||||
<div class="card-customer-details">
|
||||
|
||||
@@ -10,6 +10,8 @@ import { UiDebugModalComponent, UiModalService } from '@ui/modal';
|
||||
import { ShippingAddressHelper } from 'apps/domain/crm/src/lib/helpers/shipping-address.helper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { first, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { CantAddCustomerToCartModalComponent } from '../modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.component';
|
||||
import { CantAddCustomerToCartData } from '../modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.data';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-details',
|
||||
@@ -31,6 +33,7 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
|
||||
isB2b$: Observable<boolean>;
|
||||
cartExists$: Observable<boolean>;
|
||||
hasCustomerCard$: Observable<boolean>;
|
||||
|
||||
private currentBreadcrumb: Breadcrumb;
|
||||
|
||||
@@ -87,6 +90,10 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
|
||||
this.customerFeatures$ = this.customer$.pipe(map((customer: CustomerDTO) => Object.values(customer.features).filter((f) => f.enabled)));
|
||||
|
||||
this.hasCustomerCard$ = this.customerFeatures$.pipe(
|
||||
map((features) => !!features.find((feature) => feature.description === 'Kundenkarte'))
|
||||
);
|
||||
|
||||
this.customerType$ = this.customerFeatures$.pipe(
|
||||
map((features: KeyValueDTOOfStringAndString[]) => features[0].key as 'store' | 'onlineshop' | 'b2b' | 'guestaccount')
|
||||
);
|
||||
@@ -129,15 +136,24 @@ export class CustomerDetailsComponent implements OnInit {
|
||||
async continue() {
|
||||
const customer = await this.customer$.pipe(first()).toPromise();
|
||||
|
||||
// Check if customer can be added to the checkout
|
||||
const canSetCustomer = await this.checkoutService.canSetCustomer(this.application.activatedProcessId, customer).toPromise();
|
||||
if (!canSetCustomer.ok) {
|
||||
this.modal.open({
|
||||
content: CantAddCustomerToCartModalComponent,
|
||||
data: {
|
||||
filter: canSetCustomer.filter,
|
||||
message: canSetCustomer.message,
|
||||
} as CantAddCustomerToCartData,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Set Process Name
|
||||
this.process.updateName(this.application.activatedProcessId, customer.lastName);
|
||||
|
||||
// Set Customer For Process
|
||||
const canAdd = await this.checkoutService.setCustomer(this.application.activatedProcessId, customer);
|
||||
|
||||
if (!canAdd) {
|
||||
return;
|
||||
}
|
||||
await this.checkoutService.setCustomer(this.application.activatedProcessId, customer).toPromise();
|
||||
|
||||
// Set Invoice Address If Selected
|
||||
if (!!this.selectedPayer) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CustomerDataEditB2CComponent } from './customer-data-edit/customer-data
|
||||
import { CustomerDataEditB2BComponent } from './customer-data-edit/customer-data-edit-b2b.component';
|
||||
import { UiRadioModule } from '@ui/radio';
|
||||
import { CustomerCardComponent } from './customer-card/customer-card.component';
|
||||
import { CardTemplateComponent } from './shared/card-template/card-template.component';
|
||||
import { CustomerOrdersComponent } from './customer-orders/customer-orders.component';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
@NgModule({
|
||||
@@ -55,6 +56,7 @@ import { UiCommonModule } from '@ui/common';
|
||||
ShippingEditB2BComponent,
|
||||
ShippingEditB2CComponent,
|
||||
CustomerCardComponent,
|
||||
CardTemplateComponent,
|
||||
CustomerOrdersComponent,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CustomerCardGuard implements CanActivate {
|
||||
constructor(private router: Router, private customerService: CrmCustomerService) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const id = Number(route.params?.customerId);
|
||||
|
||||
return this.customerService.getCustomer(id, 2).pipe(
|
||||
take(1),
|
||||
map((customer) => {
|
||||
if (!customer.result.features.find((feature) => feature.description === 'Kundenkarte')) {
|
||||
this.router.navigate(['/customer', id]);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<div class="wrapper">
|
||||
<div *ngIf="cardDetails" class="card-main">
|
||||
<div class="icons">
|
||||
<div *ngIf="isCustomerCard && frontside" class="icon-barcode">
|
||||
<ui-icon (click)="frontside = !frontside" size="35px" icon="barcode"></ui-icon>
|
||||
</div>
|
||||
<div *ngIf="isCustomerCard && !frontside" class="icon-back">
|
||||
<ui-icon (click)="frontside = !frontside" size="25px" icon="refresh"></ui-icon>
|
||||
</div>
|
||||
<!-- <div *ngIf="!isCustomerCard" class="icon-delete"><ui-icon (click)="onDeletePartnerCard()" size="25px" icon="trash"></ui-icon></div> -->
|
||||
</div>
|
||||
<div class="headline">
|
||||
<p *ngIf="isCustomerCard && frontside">Ihre Lesepunkte</p>
|
||||
<p *ngIf="isCustomerCard && !frontside">Kartennummer</p>
|
||||
<p *ngIf="!isCustomerCard">Partnerkartennummer</p>
|
||||
</div>
|
||||
<div>
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="card-number">{{ cardDetails.code }}</div>
|
||||
<div *ngIf="isCustomerCard && frontside" class="points">{{ cardDetails.totalPoints | number }}</div>
|
||||
</div>
|
||||
<div class="barcode-button">
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="barcode-field">
|
||||
<img class="barcode" src="/assets/images/barcode.png" alt="Barcode" />
|
||||
</div>
|
||||
<div *ngIf="isCustomerCard && frontside">
|
||||
<button class="button" (click)="onRewardShop()">Zum Prämienshop</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-bottom">
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="customer-name">
|
||||
<p>{{ cardDetails.firstName }} {{ cardDetails.lastName }}</p>
|
||||
</div>
|
||||
<div *ngIf="isCustomerCard && frontside" class="logo">
|
||||
<img class="logo-picture" src="/assets/images/Hugendubel_Logo.png" alt="Hugendubel Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,82 @@
|
||||
.wrapper {
|
||||
@apply box-border rounded-customerCard relative flex flex-col;
|
||||
width: 421px;
|
||||
height: 266px;
|
||||
margin-bottom: 42px;
|
||||
box-shadow: 0px 21px 64px 0px #4e4e4e;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
ui-icon {
|
||||
color: #f70400;
|
||||
}
|
||||
|
||||
.icons {
|
||||
@apply absolute;
|
||||
right: 21px;
|
||||
top: 19px;
|
||||
}
|
||||
|
||||
.headline {
|
||||
@apply text-wild-blue-yonder mt-px-15 text-sm font-bold;
|
||||
}
|
||||
|
||||
.card-number,
|
||||
.points {
|
||||
@apply text-white -mt-px-15 font-bold;
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
|
||||
.card-number {
|
||||
@apply -mt-px-10 text-4xl;
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply border-none outline-none bg-brand text-white font-bold text-cta-l px-px-25 py-px-15 rounded-full mt-px-15;
|
||||
}
|
||||
|
||||
.barcode-field {
|
||||
@apply bg-white mt-px-10 box-border;
|
||||
width: 201px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.barcode {
|
||||
@apply mt-px-15;
|
||||
}
|
||||
|
||||
.barcode-button {
|
||||
@apply box-border w-full flex flex-col items-center justify-items-center;
|
||||
}
|
||||
|
||||
.customer-name {
|
||||
@apply w-full text-left font-bold ml-px-20;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@apply w-full text-right;
|
||||
margin-top: 13px;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.logo-picture {
|
||||
width: 101px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.card-main {
|
||||
@apply box-border rounded-t-customerCard flex flex-col bg-onyx;
|
||||
width: 421px;
|
||||
height: 217px;
|
||||
transition: transform 0.8s;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.card-bottom {
|
||||
@apply box-border rounded-b-customerCard flex flex-row items-center text-black;
|
||||
width: 421px;
|
||||
height: 49px;
|
||||
transition: transform 0.8s;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { BonusCardInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Component({
|
||||
selector: 'page-card-template',
|
||||
templateUrl: 'card-template.component.html',
|
||||
styleUrls: ['card-template.component.scss'],
|
||||
})
|
||||
export class CardTemplateComponent implements OnInit {
|
||||
@Input() cardDetails: BonusCardInfoDTO;
|
||||
@Input() isCustomerCard: boolean;
|
||||
frontside: boolean;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {
|
||||
this.frontside = true;
|
||||
}
|
||||
|
||||
switchBacksideFrontside(): void {
|
||||
this.frontside = !this.frontside;
|
||||
}
|
||||
|
||||
onRewardShop(): void {}
|
||||
|
||||
onDeletePartnerCard(): void {}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<page-customer-searchbox (searchStarted)="applyFilters(false)"></page-customer-searchbox>
|
||||
|
||||
<ui-selected-filter-options [(value)]="selectedFilters" (valueChange)="updateFilter($event)"> </ui-selected-filter-options>
|
||||
<ui-selected-filter-options [(value)]="selectedFilters" [initialFilter]="initialFilter" (valueChange)="updateFilter($event)">
|
||||
</ui-selected-filter-options>
|
||||
|
||||
<ui-filter-group [(value)]="selectedFilters" (valueChange)="updateFilter($event)"></ui-filter-group>
|
||||
|
||||
|
||||
@@ -1,71 +1,30 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CartService } from '@domain/cart';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CustomerCreateGuard implements CanActivateChild {
|
||||
constructor(private router: Router, private cartService: CartService, private application: ApplicationService) {}
|
||||
readonly typeMappings = {
|
||||
guest: 'guest',
|
||||
online: 'webshop',
|
||||
b2b: 'b2b',
|
||||
branch: 'store',
|
||||
};
|
||||
|
||||
constructor(private router: Router, private checkoutService: CheckoutService, private application: ApplicationService) {}
|
||||
|
||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const currentRoute = route.url[0].path as 'guest' | 'online' | 'b2b' | 'branch';
|
||||
return this.cartService.getRequiredCustomerTypes(this.application.activatedProcessId).pipe(
|
||||
take(1),
|
||||
map((types) => {
|
||||
// If current route is not available in types -> navigate to next available route
|
||||
// Cycle => 'branch' -> 'online' -> 'guest' -> 'b2b'
|
||||
if (!types.includes(currentRoute)) {
|
||||
if (currentRoute === 'branch' && types.includes('online')) {
|
||||
this.router.navigate(['/customer', 'create', 'online']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'branch' && types.includes('guest')) {
|
||||
this.router.navigate(['/customer', 'create', 'guest']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'branch' && types.includes('b2b')) {
|
||||
this.router.navigate(['/customer', 'create', 'b2b']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'online' && types.includes('guest')) {
|
||||
this.router.navigate(['/customer', 'create', 'guest']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'online' && types.includes('b2b')) {
|
||||
this.router.navigate(['/customer', 'create', 'b2b']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'online' && types.includes('branch')) {
|
||||
this.router.navigate(['/customer', 'create', 'branch']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'guest' && types.includes('b2b')) {
|
||||
this.router.navigate(['/customer', 'create', 'b2b']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'guest' && types.includes('branch')) {
|
||||
this.router.navigate(['/customer', 'create', 'branch']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'guest' && types.includes('online')) {
|
||||
this.router.navigate(['/customer', 'create', 'online']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'b2b' && types.includes('branch')) {
|
||||
this.router.navigate(['/customer', 'create', 'branch']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'b2b' && types.includes('guest')) {
|
||||
this.router.navigate(['/customer', 'create', 'guest']);
|
||||
return false;
|
||||
}
|
||||
if (currentRoute === 'b2b' && types.includes('online')) {
|
||||
this.router.navigate(['/customer', 'create', 'online']);
|
||||
return false;
|
||||
}
|
||||
const currentRoute = route.url[0].path;
|
||||
|
||||
return this.checkoutService.getSetableCustomerTypes(this.application.activatedProcessId).pipe(
|
||||
map((selectables) => {
|
||||
const result = selectables.includes(currentRoute);
|
||||
if (!result) {
|
||||
this.router.navigate(['/customer/create', selectables[0]], { queryParams: route.queryParams });
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { AddressDTO } from '@swagger/crm';
|
||||
import { UiModalRef } from 'apps/ui/modal/src/lib/defs';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
|
||||
@Component({
|
||||
selector: 'page-address-selection-modal',
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="wrapper">
|
||||
<div class="actions">
|
||||
<button class="close-btn" (click)="ref.close()">
|
||||
<ui-icon icon="close" size="21px"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2>Warenkorb kann dem Kunden nicht zugewiesen werden</h2>
|
||||
<p>
|
||||
<!-- Der Kundentyp ist mit der gewählten Kaufoption nicht kombinierbar. -->
|
||||
{{ ref.data.message }}
|
||||
</p>
|
||||
|
||||
<div class="cta-wrapper">
|
||||
<a class="search-btn" [routerLink]="['/customer/search']" [queryParams]="ref.data.filter" (click)="ref.close()">Zur Kundensuche</a>
|
||||
<a class="create-btn" [routerLink]="['/customer/create', createCustomerType]" (click)="ref.close()">Kundendaten erfassen</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
:host {
|
||||
@apply bg-white;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply flex flex-row justify-end;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@apply bg-transparent border-none text-ucla-blue;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply mt-0;
|
||||
}
|
||||
|
||||
h2,
|
||||
p {
|
||||
@apply text-center text-card-sub font-bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply font-bold text-card-sub;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-dark-goldenrod;
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
@apply flex flex-row gap-8 my-8 justify-center;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
@apply border-4 border-solid border-brand outline-none bg-white text-brand font-bold text-cta-l px-px-25 py-px-15 rounded-full no-underline;
|
||||
}
|
||||
|
||||
.create-btn {
|
||||
@apply border-none outline-none bg-brand text-white font-bold text-cta-l px-px-25 py-px-15 rounded-full no-underline;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { CantAddCustomerToCartData } from './cant-add-customer-to-cart.data';
|
||||
|
||||
@Component({
|
||||
selector: 'pages-cant-add-customer-to-cart-modal',
|
||||
templateUrl: 'cant-add-customer-to-cart.component.html',
|
||||
styleUrls: ['cant-add-customer-to-cart.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CantAddCustomerToCartModalComponent {
|
||||
get createCustomerType() {
|
||||
if (this.ref?.data?.filter) {
|
||||
return this.ref?.data?.filter?.customertype;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
constructor(public ref: UiModalRef<CantAddCustomerToCartData>) {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { StringDictionary } from '@cmf/core';
|
||||
|
||||
export interface CantAddCustomerToCartData {
|
||||
message: string;
|
||||
filter: StringDictionary<string>;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CantAddCustomerToCartModalComponent } from './cant-add-customer-to-cart.component';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiIconModule, RouterModule],
|
||||
exports: [CantAddCustomerToCartModalComponent],
|
||||
declarations: [CantAddCustomerToCartModalComponent],
|
||||
})
|
||||
export class CantAddCustomerToCartModalModule {}
|
||||
@@ -16,6 +16,7 @@ import { CustomerCardComponent } from './customer-details/customer-card/customer
|
||||
import { CustomerDataEditB2BComponent } from './customer-details/customer-data-edit/customer-data-edit-b2b.component';
|
||||
import { CustomerDataEditB2CComponent } from './customer-details/customer-data-edit/customer-data-edit-b2c.component';
|
||||
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
|
||||
import { CustomerCardGuard } from './customer-details/guards/customer-card.guard';
|
||||
import { CustomerOrdersComponent } from './customer-details/customer-orders/customer-orders.component';
|
||||
import { CustomerSearchComponent } from './customer-search/customer-search.component';
|
||||
import { CustomerSearchMainComponent } from './customer-search/search-main/search-main.component';
|
||||
@@ -41,11 +42,11 @@ const routes: Routes = [
|
||||
path: 'create',
|
||||
canActivateChild: [CustomerCreateGuard],
|
||||
children: [
|
||||
{ path: 'branch', component: CustomerCreateBranchComponent },
|
||||
{ path: 'online', component: CustomerCreateOnlineComponent },
|
||||
{ path: 'store', component: CustomerCreateBranchComponent },
|
||||
{ path: 'webshop', component: CustomerCreateOnlineComponent },
|
||||
{ path: 'b2b', component: CustomerCreateB2BComponent },
|
||||
{ path: 'guest', component: CustomerCreateGuestComponent },
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'branch' },
|
||||
{ path: '', pathMatch: 'full', redirectTo: 'store' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -54,6 +55,7 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: ':customerId/card',
|
||||
canActivate: [CustomerCardGuard],
|
||||
component: CustomerCardComponent,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ChangeDetectorRef, Pipe, PipeTransform } from '@angular/core';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CommunicationDetailsDTO, OrganisationDTO } from '@swagger/crm';
|
||||
|
||||
export interface AddressData {
|
||||
@@ -20,7 +19,7 @@ export class AddressPipe implements PipeTransform {
|
||||
private result: string;
|
||||
private data: AddressData;
|
||||
|
||||
constructor(private customerService: CrmCustomerService, private cdr: ChangeDetectorRef) {}
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
transform(data: AddressData): string {
|
||||
this.data = data;
|
||||
@@ -32,10 +31,15 @@ export class AddressPipe implements PipeTransform {
|
||||
async getResult() {
|
||||
let result = undefined;
|
||||
if (!!this.data?.address?.street && !!this.data?.address?.city && !!this.data?.address?.zipCode && !!this.data?.address?.country) {
|
||||
const countries = await this.customerService.getCountries().toPromise();
|
||||
const country = countries.result.find((c) => c.isO3166_A_3 === this.data?.address.country)?.name || this.data?.address.country;
|
||||
|
||||
result = `${this.data?.address?.street} ${this.data?.address?.streetNumber}, ${this.data?.address?.zipCode} ${this.data?.address?.city}, ${country}`;
|
||||
let parts = [
|
||||
this.data?.organisation?.name || '',
|
||||
[this.data?.lastName, this.data?.firstName].filter((f) => !!f).join(', '),
|
||||
`${this.data?.address?.street || ''} ${this.data?.address?.streetNumber || ''}`,
|
||||
`${this.data?.address?.zipCode || ''} ${this.data?.address?.city || ''}`,
|
||||
]
|
||||
.filter((value) => value != '')
|
||||
.filter((value) => value.trim());
|
||||
result = parts.join(' | ');
|
||||
}
|
||||
|
||||
if (result !== this.result) {
|
||||
|
||||
@@ -2,5 +2,5 @@ import { FormControl, ValidationErrors } from '@angular/forms';
|
||||
|
||||
const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;
|
||||
export function validateEmail(c: FormControl): ValidationErrors | null {
|
||||
return c.value === '' || EMAIL_REGEXP.test(c.value) ? null : { invalid: 'E-Mail ist ungültig' };
|
||||
return c.value == null || c.value === '' || EMAIL_REGEXP.test(c.value) ? null : { invalid: 'E-Mail ist ungültig' };
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import { CustomerFeatures } from 'apps/sales/src/app/core/models/customer-featur
|
||||
import { CartService } from '../services/cart.service';
|
||||
import { BranchInfoDTO } from '@swagger/isa';
|
||||
import { AppState } from 'apps/sales/src/app/core/store/state/app.state';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { ApplicationService } from '@core/application';
|
||||
|
||||
export interface CartReviewItem extends BookData {
|
||||
cartEntryId: number;
|
||||
@@ -121,7 +123,9 @@ export class CartReviewComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
private printer: PrinterService,
|
||||
private errorService: ErrorService,
|
||||
private cartService: CartService
|
||||
private cartService: CartService,
|
||||
private checkoutService: CheckoutService,
|
||||
private applicationService: ApplicationService
|
||||
) {
|
||||
this.deliveryCount$ = this.cartData$.pipe(
|
||||
startWith(-1),
|
||||
@@ -359,17 +363,20 @@ export class CartReviewComponent implements OnInit, OnDestroy {
|
||||
this.total.price = totalSum;
|
||||
}
|
||||
|
||||
next() {
|
||||
async next() {
|
||||
if (this.customer) {
|
||||
// Crete order
|
||||
this.continueBtn.startLoading();
|
||||
this.payMethodDialog.directOrder();
|
||||
} else {
|
||||
const response = await this.checkoutService.canSetCustomer(this.applicationService.activatedProcessId, undefined).toPromise();
|
||||
console.log(response);
|
||||
// No customer redirect to search
|
||||
let newBread: Breadcrumb;
|
||||
newBread = {
|
||||
name: 'Kundensuche',
|
||||
path: 'customer/search',
|
||||
queryParams: response.filter,
|
||||
};
|
||||
this.navigate(newBread, 'customer');
|
||||
}
|
||||
@@ -409,7 +416,7 @@ export class CartReviewComponent implements OnInit, OnDestroy {
|
||||
private navigate(bread: Breadcrumb, menu: string) {
|
||||
this.store.dispatch(new ChangeCurrentRoute(bread.path));
|
||||
this.store.dispatch(new AddBreadcrumb(bread, menu));
|
||||
this.router.navigate([bread.path]);
|
||||
this.router.navigate([bread.path], { queryParams: bread.queryParams });
|
||||
}
|
||||
|
||||
enableDisableOrder(disable: boolean) {
|
||||
|
||||
@@ -124,50 +124,60 @@
|
||||
|
||||
<div class="product-information">
|
||||
<span class="book-title">{{ book.product.contributors }} - {{ book.product.name }}</span>
|
||||
<span class="can-add-hint" *ngIf="!canAddItem"
|
||||
>Leider können wir den Service mit den bereits ausgewählten Services im Warenkorb nicht kombinieren.</span
|
||||
>
|
||||
<span class="book-format">
|
||||
<lib-icon class="order-book-icon" width="18px" height="18px" name="Icon_{{ book.product.format }}" alt="book-icon"></lib-icon>
|
||||
{{ book.product.formatDetail }}
|
||||
</span>
|
||||
<span class="can-add-hint" *ngIf="!canAddItem">{{ availabilityMessage }}</span>
|
||||
|
||||
<span class="price">{{ selectedPrice | bookPrice }} {{ currency }}</span>
|
||||
<div class="product-details">
|
||||
<div class="product-column">
|
||||
<span>
|
||||
<lib-icon
|
||||
class="order-book-icon"
|
||||
width="18px"
|
||||
height="18px"
|
||||
name="Icon_{{ book.product.format }}"
|
||||
alt="book-icon"
|
||||
></lib-icon>
|
||||
{{ book.product.formatDetail }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
class="order-details-delivery-info"
|
||||
*ngIf="
|
||||
deliveryType === 'Donwload' ||
|
||||
deliveryType === 'Rücklage' ||
|
||||
deliveryType === 'Versand' ||
|
||||
!currentDeliveryDate ||
|
||||
deliveryType === 'Abholung' ||
|
||||
!currentPickUpDate
|
||||
"
|
||||
></span>
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === deliveryOptions.delivery && currentDeliveryDate"
|
||||
>Versanddatum {{ currentDeliveryDate }}</span
|
||||
>
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === deliveryOptions.deliveryB2b"
|
||||
>Versanddatum {{ b2bDeliveryDate | date: 'dd.MM.yy' }}</span
|
||||
>
|
||||
<span class="price">{{ selectedPrice | bookPrice }} {{ currency }}</span>
|
||||
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === 'Abholung' && currentPickUpDate"
|
||||
>Abholung ab {{ currentPickUpDate }}</span
|
||||
>
|
||||
<span
|
||||
class="order-details-delivery-info"
|
||||
*ngIf="
|
||||
deliveryType === 'Donwload' ||
|
||||
deliveryType === 'Rücklage' ||
|
||||
deliveryType === 'Versand' ||
|
||||
!currentDeliveryDate ||
|
||||
deliveryType === 'Abholung' ||
|
||||
!currentPickUpDate
|
||||
"
|
||||
></span>
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === deliveryOptions.delivery && currentDeliveryDate"
|
||||
>Versanddatum {{ currentDeliveryDate }}</span
|
||||
>
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === deliveryOptions.deliveryB2b"
|
||||
>Versanddatum {{ b2bDeliveryDate | date: 'dd.MM.yy' }}</span
|
||||
>
|
||||
|
||||
<span class="order-details-delivery-info" *ngIf="deliveryType === 'Abholung' && currentPickUpDate"
|
||||
>Abholung ab {{ currentPickUpDate }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="product-column">
|
||||
<app-delete-dropdown
|
||||
[selected]="currentNumberOfItems"
|
||||
[options]="possibleItems"
|
||||
(valueChanges)="setNumberOfItems($event)"
|
||||
(delete)="deleteProduct()"
|
||||
(switch)="switchSteps(true)"
|
||||
[ddload]="true"
|
||||
[notUpdatable]="isDownload"
|
||||
#deleteDropdown
|
||||
></app-delete-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO: populate here with delivery info like DHL I Lieferung 18.01.-->
|
||||
|
||||
<app-delete-dropdown
|
||||
[selected]="currentNumberOfItems"
|
||||
[options]="possibleItems"
|
||||
(valueChanges)="setNumberOfItems($event)"
|
||||
(delete)="deleteProduct()"
|
||||
(switch)="switchSteps(true)"
|
||||
[ddload]="true"
|
||||
[notUpdatable]="isDownload"
|
||||
#deleteDropdown
|
||||
></app-delete-dropdown>
|
||||
</div>
|
||||
<div class="secondary-info">
|
||||
<div class="errors">
|
||||
@@ -196,12 +206,15 @@
|
||||
Artikel scannen
|
||||
</button>
|
||||
|
||||
<button class="btn-outline" (click)="updateCart()" [disabled]="(addItemsToCartDisabled || loading) && !isDownload">
|
||||
<button class="btn-outline" (click)="continueSearch()" [disabled]="(addItemsToCartDisabled || loading) && !isDownload">
|
||||
Weiter suchen
|
||||
</button>
|
||||
<app-button [primary]="true" [disabled]="(addItemsToCartDisabled || loading) && !isDownload" (action)="itemsConfirmed()">{{
|
||||
confirmationBtnText
|
||||
}}</app-button>
|
||||
<app-button
|
||||
[primary]="true"
|
||||
[disabled]="(addItemsToCartDisabled || loading) && !isDownload"
|
||||
(action)="canAddItem ? itemsConfirmed() : redirectRoute()"
|
||||
>{{ confirmationBtnText }}</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</app-modal>
|
||||
|
||||
@@ -239,15 +239,22 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.product-details {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.product-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.book-title {
|
||||
font-size: 16px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.book-format {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.can-add-hint {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actio
|
||||
import { SetCartEntry } from '../../../../core/store/actions/cart-entry.actions';
|
||||
import { Subject, Observable, of } from 'rxjs';
|
||||
import { tap, map, filter, take } from 'rxjs/operators';
|
||||
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { AddBreadcrumb, PopLastBreadcrumbs } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { BranchSelectors } from '../../../../core/store/selectors/branch.selector';
|
||||
import { ProductAvailability } from '../../../../core/models/product-availability.model';
|
||||
@@ -28,6 +28,8 @@ import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/pr
|
||||
import { OrderService } from '@swagger/oms';
|
||||
import { CartService } from '@domain/cart';
|
||||
import { UiDebugModalComponent, UiModalService } from '@ui/modal';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
|
||||
@Component({
|
||||
selector: 'app-checkout',
|
||||
@@ -89,6 +91,7 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
confirmationBtnText = 'Fortfahren';
|
||||
|
||||
canAddItem: boolean = true;
|
||||
availabilityMessage: string;
|
||||
|
||||
// Trigger other functionality if needed
|
||||
@Output() closed: EventEmitter<boolean> = new EventEmitter();
|
||||
@@ -226,7 +229,9 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
private datePipe: DatePipe,
|
||||
private shoppingCartService: CartService,
|
||||
private uiModal: UiModalService,
|
||||
private cdrf: ChangeDetectorRef
|
||||
private cdrf: ChangeDetectorRef,
|
||||
private applicationService: ApplicationService,
|
||||
private checkoutService: CheckoutService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -600,8 +605,21 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async checkCanAdd() {
|
||||
// TODO: Hindernissmeldung
|
||||
this.canAddItem = await this.shoppingCartService.canAddItem(undefined, this.currentAvailability);
|
||||
let result = await this.shoppingCartService.canAddItem(undefined, this.currentAvailability);
|
||||
if (typeof result === 'string') {
|
||||
this.canAddItem = false;
|
||||
this.confirmationBtnText = 'Ohne Artikel fortfahren';
|
||||
this.availabilityMessage = result;
|
||||
} else {
|
||||
this.canAddItem = true;
|
||||
this.availabilityMessage = undefined;
|
||||
this.confirmationBtnText = 'Fortfahren';
|
||||
}
|
||||
}
|
||||
|
||||
continueSearch() {
|
||||
this.store.dispatch(new PopLastBreadcrumbs(''));
|
||||
this.router.navigate(['/product/results']);
|
||||
}
|
||||
|
||||
updateCart() {
|
||||
@@ -639,15 +657,17 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.closeModal();
|
||||
}
|
||||
|
||||
private redirectRoute() {
|
||||
async redirectRoute() {
|
||||
if (this.customerAlredySet) {
|
||||
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true));
|
||||
this.store.dispatch(new ChangeCurrentRoute('/cart/review'));
|
||||
this.router.navigate(['/cart/review']);
|
||||
} else {
|
||||
const response = await this.checkoutService.canSetCustomer(this.applicationService.activatedProcessId, undefined).toPromise();
|
||||
|
||||
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true));
|
||||
this.store.dispatch(new ChangeCurrentRoute('/customer/search'));
|
||||
this.router.navigate(['/customer/search']);
|
||||
this.router.navigate(['/customer/search'], { queryParams: response.filter });
|
||||
}
|
||||
|
||||
this.closeModal();
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { CartService, CartItem } from '@domain/cart';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, switchMap, tap, first } from 'rxjs/operators';
|
||||
import { map, switchMap, first, filter } from 'rxjs/operators';
|
||||
import { SharedSelectors } from '../core/store/selectors/shared.selectors';
|
||||
import { ProcessSelectors } from '../core/store/selectors/process.selectors';
|
||||
import { OLAAvailabilityDTO, StoreCheckoutService } from '@swagger/checkout';
|
||||
@@ -11,7 +11,6 @@ import { StringDictionary } from '@cmf/core';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { UiDebugModalComponent, UiModalService } from '@ui/modal';
|
||||
|
||||
@Injectable()
|
||||
export class CartRefactImp implements CartService {
|
||||
@@ -19,8 +18,7 @@ export class CartRefactImp implements CartService {
|
||||
private store: Store,
|
||||
private checkoutService: StoreCheckoutService,
|
||||
private applicationService: ApplicationService,
|
||||
private customerService: CrmCustomerService,
|
||||
private uiModal: UiModalService
|
||||
private customerService: CrmCustomerService
|
||||
) {}
|
||||
|
||||
getItems(processId: number): Observable<CartItem[]> {
|
||||
@@ -52,58 +50,61 @@ export class CartRefactImp implements CartService {
|
||||
}
|
||||
|
||||
getCartId(processId: number): Observable<number> {
|
||||
return this.store.select(ProcessSelectors.getCurrentProcess).pipe(map((process) => process.cartId));
|
||||
}
|
||||
|
||||
getRequiredCustomerTypes(processId: number): Observable<any> {
|
||||
// return this.getCartId(processId).pipe(
|
||||
// switchMap((cartId) => {
|
||||
// return this.checkoutService
|
||||
// .StoreCheckoutCanAddBuyer({ shoppingCartId: cartId, payload: { customerFeatures: {} } })
|
||||
// .pipe(tap((res) => console.log(res)));
|
||||
// })
|
||||
// );
|
||||
return this.getItems(processId).pipe(
|
||||
map((items) => {
|
||||
const itemsSet = new Set(items.map((i) => i.target));
|
||||
const itemsArr = Array.from(itemsSet);
|
||||
if (itemsArr.length === 1) {
|
||||
switch (itemsArr[0]) {
|
||||
// TODO bei Einzelprodukt im Warenkorb: Versand Artikel wenn Abholung auch möglich
|
||||
case 'reserve':
|
||||
case 'pickup':
|
||||
return ['branch', 'online', 'b2b'];
|
||||
case 'shipping':
|
||||
return ['guest', 'online'];
|
||||
case 'download':
|
||||
return ['online'];
|
||||
case 'shippingB2B':
|
||||
return ['b2b'];
|
||||
}
|
||||
}
|
||||
// TODO bei Mischkörben: Versand Artikel wenn Abholung auch möglich
|
||||
// TODO Mischkörbe Sonderfall: reserve wird nicht gehandled
|
||||
if (itemsSet.has('shipping') && itemsSet.has('download')) {
|
||||
return ['online'];
|
||||
}
|
||||
|
||||
if (itemsSet.has('shipping') && itemsSet.has('shippingB2B')) {
|
||||
return ['b2b'];
|
||||
}
|
||||
|
||||
if (itemsSet.has('pickup') && itemsSet.has('shipping')) {
|
||||
return ['guest', 'online'];
|
||||
}
|
||||
|
||||
if (itemsSet.has('pickup') && itemsSet.has('shippingB2B')) {
|
||||
return ['b2b'];
|
||||
}
|
||||
return ['branch', 'online', 'b2b'];
|
||||
})
|
||||
return this.store.select(ProcessSelectors.getCurrentProcess).pipe(
|
||||
filter((f) => !!f),
|
||||
map((process) => process.cartId)
|
||||
);
|
||||
}
|
||||
|
||||
async canAddItem(processId: number, availability: OLAAvailabilityDTO): Promise<boolean> {
|
||||
// getRequiredCustomerTypes(processId: number): Observable<any> {
|
||||
// // return this.getCartId(processId).pipe(
|
||||
// // switchMap((cartId) => {
|
||||
// // return this.checkoutService
|
||||
// // .StoreCheckoutCanAddBuyer({ shoppingCartId: cartId, payload: { customerFeatures: {} } })
|
||||
// // .pipe(tap((res) => console.log(res)));
|
||||
// // })
|
||||
// // );
|
||||
// return this.getItems(processId).pipe(
|
||||
// map((items) => {
|
||||
// const itemsSet = new Set(items.map((i) => i.target));
|
||||
// const itemsArr = Array.from(itemsSet);
|
||||
// if (itemsArr.length === 1) {
|
||||
// switch (itemsArr[0]) {
|
||||
// // TODO bei Einzelprodukt im Warenkorb: Versand Artikel wenn Abholung auch möglich
|
||||
// case 'reserve':
|
||||
// case 'pickup':
|
||||
// return ['branch', 'online', 'b2b'];
|
||||
// case 'shipping':
|
||||
// return ['guest', 'online'];
|
||||
// case 'download':
|
||||
// return ['online'];
|
||||
// case 'shippingB2B':
|
||||
// return ['b2b'];
|
||||
// }
|
||||
// }
|
||||
// // TODO bei Mischkörben: Versand Artikel wenn Abholung auch möglich
|
||||
// // TODO Mischkörbe Sonderfall: reserve wird nicht gehandled
|
||||
// if (itemsSet.has('shipping') && itemsSet.has('download')) {
|
||||
// return ['online'];
|
||||
// }
|
||||
|
||||
// if (itemsSet.has('shipping') && itemsSet.has('shippingB2B')) {
|
||||
// return ['b2b'];
|
||||
// }
|
||||
|
||||
// if (itemsSet.has('pickup') && itemsSet.has('shipping')) {
|
||||
// return ['guest', 'online'];
|
||||
// }
|
||||
|
||||
// if (itemsSet.has('pickup') && itemsSet.has('shippingB2B')) {
|
||||
// return ['b2b'];
|
||||
// }
|
||||
// return ['branch', 'online', 'b2b'];
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
async canAddItem(processId: number, availability: OLAAvailabilityDTO): Promise<true | string> {
|
||||
const getCustomerIdByProcessIdFn = await this.store.select(CustomerSelectors.getActiveUser).pipe(first()).toPromise();
|
||||
|
||||
let customerId: number;
|
||||
@@ -135,8 +136,10 @@ export class CartRefactImp implements CartService {
|
||||
.StoreCheckoutCanAddItem({ shoppingCartId: cartId, payload: { customerFeatures, availabilities: [availability] } })
|
||||
.pipe(
|
||||
map((res) => {
|
||||
this.uiModal.open({ content: UiDebugModalComponent, data: res });
|
||||
return res.result.ok;
|
||||
if (res.result.ok) {
|
||||
return true;
|
||||
}
|
||||
return res.message;
|
||||
})
|
||||
);
|
||||
})
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Dictionary, StringDictionary } from '@cmf/core';
|
||||
import { StringDictionary } from '@cmf/core';
|
||||
import { CartService } from '@domain/cart';
|
||||
import { CheckoutService } from '@domain/checkout';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { PayerDTO, PayerType, ShippingAddressDTO, StoreCheckoutService } from '@swagger/checkout';
|
||||
import { CustomerDTO, PayerDTO as CrmPayerDTO, ShippingAddressDTO as CrmShippingAddressDTO } from '@swagger/crm';
|
||||
import { UiDebugModalComponent, UiModalService } from '@ui/modal';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { first, map, mergeMap } from 'rxjs/operators';
|
||||
import { first, map, mergeMap, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
RefactSetCustomer,
|
||||
RefactSetCustomerBillingAddress,
|
||||
@@ -19,38 +18,19 @@ import { CustomerSelectors } from '../core/store/selectors/customer.selectors';
|
||||
|
||||
@Injectable()
|
||||
export class CheckoutRefactImp implements CheckoutService {
|
||||
constructor(
|
||||
private store: Store,
|
||||
private checkoutService: StoreCheckoutService,
|
||||
private cartService: CartService,
|
||||
private uiModal: UiModalService
|
||||
) {}
|
||||
constructor(private store: Store, private checkoutService: StoreCheckoutService, private cartService: CartService) {}
|
||||
|
||||
async setCustomer(processId: number, customer: CustomerDTO): Promise<boolean> {
|
||||
const response = await this.cartService
|
||||
.getCartId(processId)
|
||||
.pipe(
|
||||
first(),
|
||||
mergeMap((shoppingCartId) => {
|
||||
const customerFeatures: StringDictionary<string> = {};
|
||||
for (const feature of customer.features) {
|
||||
customerFeatures[feature.key] = feature.key;
|
||||
}
|
||||
setCustomer(processId: number, customer: CustomerDTO): Observable<boolean> {
|
||||
return this.canSetCustomer(processId, customer).pipe(
|
||||
map((canSetCustomer) => {
|
||||
if (canSetCustomer.ok) {
|
||||
this.store.dispatch(new SetActiveCustomer(customer.id));
|
||||
this.store.dispatch(new RefactSetCustomer(customer));
|
||||
}
|
||||
|
||||
return this.checkoutService.StoreCheckoutCanAddBuyer({ shoppingCartId, payload: { customerFeatures } });
|
||||
})
|
||||
)
|
||||
.toPromise();
|
||||
if (response.result.ok) {
|
||||
this.store.dispatch(new SetActiveCustomer(customer.id));
|
||||
this.store.dispatch(new RefactSetCustomer(customer));
|
||||
}
|
||||
|
||||
if (!response.result.ok) {
|
||||
this.uiModal.open({ content: UiDebugModalComponent, data: response });
|
||||
}
|
||||
|
||||
return response.result.ok;
|
||||
return canSetCustomer.ok;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getCustomer(processId: number): Observable<CustomerDTO> {
|
||||
@@ -103,4 +83,54 @@ export class CheckoutRefactImp implements CheckoutService {
|
||||
getSelectedShippingAddress(processId: number): Observable<ShippingAddressDTO> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
canSetCustomer(
|
||||
processId: number,
|
||||
customer: CustomerDTO
|
||||
): Observable<{ ok?: boolean; filter?: StringDictionary<string>; message?: string }> {
|
||||
const cartId$ = this.cartService.getCartId(processId).pipe(first());
|
||||
|
||||
const customerFeatures: StringDictionary<string> = {};
|
||||
if (!!customer?.features) {
|
||||
for (const feature of customer?.features) {
|
||||
customerFeatures[feature.key] = feature.key;
|
||||
}
|
||||
}
|
||||
|
||||
return cartId$.pipe(
|
||||
switchMap((shoppingCartId) => this.checkoutService.StoreCheckoutCanAddBuyer({ shoppingCartId, payload: { customerFeatures } })),
|
||||
map(
|
||||
(response) =>
|
||||
({
|
||||
ok: response.result.ok,
|
||||
filter: response.result.queryToken?.filter || {},
|
||||
message: response.message,
|
||||
} as { ok?: boolean; filter?: StringDictionary<string>; message?: string })
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getSetableCustomerTypes(processId: number) {
|
||||
return this.canSetCustomer(processId, undefined).pipe(
|
||||
map((res) => {
|
||||
let setableTypes = [];
|
||||
|
||||
if (Object.keys(res.filter).length === 0) {
|
||||
return ['store', 'guest', 'webshop', 'b2b'];
|
||||
} else {
|
||||
const cusotmerType = res.filter.customertype;
|
||||
if (cusotmerType) {
|
||||
setableTypes = [...setableTypes, ...cusotmerType.split(';')];
|
||||
}
|
||||
|
||||
let customerAttributes = res.filter.customerattributes;
|
||||
if (customerAttributes) {
|
||||
setableTypes = [...setableTypes, ...cusotmerType.replace('store', 'branch').split(';')];
|
||||
}
|
||||
}
|
||||
|
||||
return setableTypes;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="container" *ngIf="value">
|
||||
<button class="filters-clear" *ngIf="selectedOptions?.length" (click)="unselectAllOptions()">
|
||||
Alle Filter Entfernen
|
||||
<button class="filters-clear" *ngIf="compareFilter()" (click)="resetFilter()">
|
||||
Filter zurücksetzen
|
||||
</button>
|
||||
|
||||
<div
|
||||
|
||||
@@ -26,6 +26,9 @@ export class UiSelectedFilterOptionsComponent {
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@Input()
|
||||
initialFilter: Filter[];
|
||||
|
||||
@Output()
|
||||
valueChange = new EventEmitter<Filter[]>();
|
||||
|
||||
@@ -37,18 +40,24 @@ export class UiSelectedFilterOptionsComponent {
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
unselectAllOptions() {
|
||||
function unselectOptions(options: FilterOption[]) {
|
||||
options?.forEach((option) => {
|
||||
option.selected = false;
|
||||
unselectOptions(option.options);
|
||||
});
|
||||
}
|
||||
|
||||
this.value?.forEach((filter) => unselectOptions(filter.options));
|
||||
resetFilter() {
|
||||
this.value = this.initialFilter;
|
||||
this.valueChange.emit(this.value);
|
||||
}
|
||||
|
||||
compareFilter(): boolean {
|
||||
let result = false;
|
||||
|
||||
this.value.forEach((val) => {
|
||||
let filter = this.initialFilter.find((f) => f.name === val.name);
|
||||
val.options.forEach((option) => {
|
||||
result = result || filter.options.find((o) => o.id === option.id).selected !== option.selected;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
unselectOption(option: FilterOption) {
|
||||
function unselectOptions(options: FilterOption[]) {
|
||||
options?.forEach((o) => {
|
||||
@@ -61,5 +70,6 @@ export class UiSelectedFilterOptionsComponent {
|
||||
|
||||
this.value.forEach((filter) => unselectOptions(filter.options));
|
||||
this.valueChange.emit(this.value);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './defs';
|
||||
export * from './modal.component';
|
||||
export * from './debug-modal.component';
|
||||
export * from './modal.module';
|
||||
|
||||
@@ -65,6 +65,7 @@ module.exports = {
|
||||
'cool-grey': '#89949e',
|
||||
glitter: '#e9f0f8',
|
||||
munsell: '#edeff0',
|
||||
onyx: '#171717',
|
||||
// active: '#1f466c',
|
||||
// inactive: '#3980c6',
|
||||
// disabled: '#b0cce8',
|
||||
@@ -87,6 +88,7 @@ module.exports = {
|
||||
},
|
||||
borderRadius: {
|
||||
card: '5px',
|
||||
customerCard: '10px',
|
||||
filter: '20px',
|
||||
autocomplete: '30px',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user