From cdcd41a884d7098fd9e1fc06972f35cd4846fb8c Mon Sep 17 00:00:00 2001 From: Lorenz Hilpert Date: Fri, 11 Apr 2025 10:21:18 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=201831:=20Kundendaten=20//=20B2B=20?= =?UTF-8?q?-=20nach=20"Bearbeiten"=20speichern=20nicht=20m=C3=B6glich?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(customer): Updated Validations for B2B Customer Conditional Validator - If Organisation Name is set First and Lastname is not required, If no first and last name organisation name is required Ref: #4996 --- ...-shipping-address-main-view.component.html | 115 +++++++++++++--- ...dd-shipping-address-main-view.component.ts | 28 +++- .../customer-data-edit-b2b.component.ts | 6 +- ...-shipping-address-main-view.component.html | 127 +++++++++++++++--- ...it-shipping-address-main-view.component.ts | 23 +++- .../validators/gender-b2b-validator.ts | 61 +++++++++ 6 files changed, 319 insertions(+), 41 deletions(-) diff --git a/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.html b/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.html index de3c45af8..ae3708f0f 100644 --- a/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.html +++ b/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.html @@ -12,22 +12,45 @@
- - + + - + - + - {{ gender.label }} + {{ + gender.label + }} @@ -42,50 +65,107 @@ - - + + - - + + - + - + - + - + - + - + - + {{ country.name }}
- Diese Lieferadresse als Standard Adresse festlegen + Diese Lieferadresse als Standard Adresse festlegen
+ diff --git a/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.ts b/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.ts index f767dd2a9..306b3dc86 100644 --- a/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.ts +++ b/apps/isa-app/src/page/customer/customer-search/add-shipping-address-main-view/add-shipping-address-main-view.component.ts @@ -15,6 +15,7 @@ import { RouterLink } from '@angular/router'; import { IconComponent } from '@shared/components/icon'; import { zipCodeValidator } from '../../validators/zip-code-validator'; import { GenderSettingsService } from '@shared/services/gender'; +import { validateCompanyOrPersonalInfoRequired } from '../../validators/gender-b2b-validator'; @Component({ selector: 'page-add-shipping-address-main-view', @@ -70,10 +71,30 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy { ngOnInit() { this._store.customer$.pipe(takeUntil(this._onDestroy)).subscribe(() => { + // Dynamic validation rules based on account type + // For business accounts (isBusinessKonto), we apply different validation rules + // than for personal accounts if (this._store.isBusinessKonto) { - this.formGroup.controls.organisation.setValidators([Validators.required]); + // For business accounts: + // - Clear individual validators from personal info fields + // - Add a form-level validator that requires either company OR personal info + // - Make address fields optional to support different business address formats + this.formGroup.controls.organisation.setValidators([]); + this.formGroup.controls.gender.setValidators([]); + this.formGroup.controls.firstName.setValidators([]); + this.formGroup.controls.lastName.setValidators([]); + this.formGroup.setValidators([validateCompanyOrPersonalInfoRequired]); + this.formGroup.controls.street.setValidators([]); + this.formGroup.controls.streetNumber.setValidators([]); } else { + // For personal accounts: + // - Organization info is optional + // - First and last name are required + // - No special form-level validators needed this.formGroup.controls.organisation.clearValidators(); + this.formGroup.controls.firstName.setValidators([Validators.required]); + this.formGroup.controls.lastName.setValidators([Validators.required]); + this.formGroup.setValidators([]); } }); } @@ -133,7 +154,10 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy { formData.isDefault, ); - this._navigation.navigateToDetails({ processId: this._store.processId, customerId: this._store.customerId }); + this._navigation.navigateToDetails({ + processId: this._store.processId, + customerId: this._store.customerId, + }); } catch (error) { this.formGroup.enable(); } diff --git a/apps/isa-app/src/page/customer/customer-search/edit-main-view/customer-data-edit-b2b.component.ts b/apps/isa-app/src/page/customer/customer-search/edit-main-view/customer-data-edit-b2b.component.ts index b906a17e9..aeee66fee 100644 --- a/apps/isa-app/src/page/customer/customer-search/edit-main-view/customer-data-edit-b2b.component.ts +++ b/apps/isa-app/src/page/customer/customer-search/edit-main-view/customer-data-edit-b2b.component.ts @@ -5,9 +5,10 @@ import { UiCommonModule } from '@ui/common'; import { UiFormControlModule } from '@ui/form-control'; import { UiSelectModule } from '@ui/select'; import { UiIconModule } from '@ui/icon'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; import { UiDateInputDirective } from '@ui/input'; +import { validateCompanyOrPersonalInfoRequired } from '../../validators/gender-b2b-validator'; @Component({ selector: 'page-customer-data-edit-b2b', @@ -26,8 +27,9 @@ import { UiDateInputDirective } from '@ui/input'; ], }) export class CustomerDataEditB2BComponent extends CustomerDataEditComponent { - afterInitForm = (control) => { + afterInitForm = (control: FormGroup) => { control.get('lastName').setValidators([]); control.get('firstName').setValidators([]); + control.setValidators([validateCompanyOrPersonalInfoRequired]); }; } diff --git a/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.html b/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.html index 9fcdd9424..ac97e0686 100644 --- a/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.html +++ b/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.html @@ -12,22 +12,49 @@
- - + + - + - + - {{ gender.label }} + {{ + gender.label + }} @@ -42,50 +69,115 @@ - - + + - - + + - + - + - + - + - + - + - + {{ country.name }}
- Diese Lieferadresse als Standard Adresse festlegen + Diese Lieferadresse als Standard Adresse festlegen
+ diff --git a/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.ts b/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.ts index d57996f33..6b62567fd 100644 --- a/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.ts +++ b/apps/isa-app/src/page/customer/customer-search/edit-shipping-address-main-view/edit-shipping-address-main-view.component.ts @@ -1,4 +1,11 @@ -import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, ChangeDetectorRef } from '@angular/core'; +import { + Component, + ChangeDetectionStrategy, + OnInit, + OnDestroy, + inject, + ChangeDetectorRef, +} from '@angular/core'; import { CheckboxComponent } from '@shared/components/checkbox'; import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'; import { SelectModule } from '@shared/components/select'; @@ -16,6 +23,7 @@ import { ActivatedRoute, RouterLink } from '@angular/router'; import { ComponentStore } from '@ngrx/component-store'; import { zipCodeValidator } from '../../validators/zip-code-validator'; import { GenderSettingsService } from '@shared/services/gender'; +import { validateCompanyOrPersonalInfoRequired } from '../../validators/gender-b2b-validator'; export interface EditShippingAddressMainViewState { shippingAddress?: ShippingAddressDTO; @@ -102,9 +110,15 @@ export class EditShippingAddressMainViewComponent this._store.customer$.pipe(takeUntil(this._onDestroy)).subscribe(() => { if (this._store.isBusinessKonto) { - this.formGroup.controls.organisation.setValidators([Validators.required]); + this.formGroup.controls.organisation.setValidators([]); + this.formGroup.controls.firstName.setValidators([]); + this.formGroup.controls.lastName.setValidators([]); + this.formGroup.setValidators([validateCompanyOrPersonalInfoRequired]); } else { this.formGroup.controls.organisation.clearValidators(); + this.formGroup.controls.firstName.setValidators([Validators.required]); + this.formGroup.controls.lastName.setValidators([Validators.required]); + this.formGroup.setValidators([]); } }); } @@ -185,7 +199,10 @@ export class EditShippingAddressMainViewComponent formData.isDefault, ); - this._navigation.navigateToDetails({ processId: this._store.processId, customerId: this._store.customerId }); + this._navigation.navigateToDetails({ + processId: this._store.processId, + customerId: this._store.customerId, + }); } catch (error) { this.formGroup.enable(); } diff --git a/apps/isa-app/src/page/customer/validators/gender-b2b-validator.ts b/apps/isa-app/src/page/customer/validators/gender-b2b-validator.ts index 4e5fc2b88..0272a4b2f 100644 --- a/apps/isa-app/src/page/customer/validators/gender-b2b-validator.ts +++ b/apps/isa-app/src/page/customer/validators/gender-b2b-validator.ts @@ -26,6 +26,8 @@ export function requireGenderWhenNameIsSet(control: UntypedFormGroup): Validatio const genderControl = control.get('gender'); const nameControl = control.get('lastName'); + genderControl?.setErrors(null); + if (nameControl && !Validators.required(nameControl)) { const errors = Validators.min(1)(genderControl) ? { required: true } : null; @@ -36,3 +38,62 @@ export function requireGenderWhenNameIsSet(control: UntypedFormGroup): Validatio } return null; } + +/** + * Angular form validator that implements mutual exclusivity between organisation name and personal details. + * Either: + * 1. Organisation name must be provided (company case), OR + * 2. Both first name and last name must be provided (individual case) + * + * @param control - The form group containing 'firstName', 'lastName', and nested 'organisation.name' controls + * @returns Validation errors if the mutual exclusivity rule is violated, null otherwise + */ +export function validateCompanyOrPersonalInfoRequired( + control: UntypedFormGroup, +): ValidationErrors | null { + const firstNameControl = control.get('firstName'); + const lastNameControl = control.get('lastName'); + const organisationControl = control.get('organisation'); + + // Safely access the nested company name, with null checks + const companyNameControl = organisationControl?.get('name') ?? organisationControl; + + if (!firstNameControl || !lastNameControl || !companyNameControl) { + // If any control is missing, we can't validate + return null; + } + + const hasCompanyName = !!companyNameControl.value; + const hasFirstName = !!firstNameControl.value; + const hasLastName = !!lastNameControl.value; + const hasPersonalInfo = hasFirstName || hasLastName; + + // Clear previous validation errors + firstNameControl.setErrors(null); + lastNameControl.setErrors(null); + companyNameControl.setErrors(null); + + // Case 1: Neither company name nor personal info provided - require one of them + if (!hasCompanyName && !hasPersonalInfo) { + const errors = { required: true }; + companyNameControl.setErrors(errors); + firstNameControl.setErrors(errors); + lastNameControl.setErrors(errors); + return { eitherCompanyOrPersonalRequired: true }; + } + + // Case 2: First name provided but last name missing + if (hasFirstName && !hasLastName) { + lastNameControl.setErrors({ required: true }); + return { lastNameRequired: true }; + } + + // Case 3: Last name provided but first name missing + if (!hasFirstName && hasLastName) { + firstNameControl.setErrors({ required: true }); + return { firstNameRequired: true }; + } + + // Valid: Either company name or complete personal info is provided + return null; +}