mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 918: #2244 WK Preis kann bei Archivartikeln vergeben werden
#2244 WK Preis kann bei Archivartikeln vergeben werden Related work items: #2244
This commit is contained in:
committed by
Lorenz Hilpert
parent
0115e235c2
commit
27a9d446ef
@@ -63,7 +63,7 @@
|
||||
<div class="price">
|
||||
{{ item.catalogAvailability?.price?.value?.value | currency: item.catalogAvailability?.price?.value?.currency:'code' }}
|
||||
</div>
|
||||
<div>{{ store.promotionPoints$ | async }} Lesepunkte</div>
|
||||
<div *ngIf="store.promotionPoints$ | async; let promotionPoints">{{ promotionPoints }} Lesepunkte</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<form *ngIf="control" [formGroup]="control">
|
||||
<ui-form-control label="MwSt" variant="default">
|
||||
<ui-select formControlName="vat">
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
|
||||
<ui-form-control class="price" label="Preis" variant="default">
|
||||
<input uiInput formControlName="price" />
|
||||
</ui-form-control>
|
||||
</form>
|
||||
@@ -0,0 +1,11 @@
|
||||
form {
|
||||
@apply grid grid-flow-col items-center justify-end gap-4 mb-2;
|
||||
}
|
||||
|
||||
ui-form-control {
|
||||
@apply w-32;
|
||||
}
|
||||
|
||||
::ng-deep page-purchasing-options-modal-price-input ui-form-control .input-wrapper input {
|
||||
@apply w-20;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { VATDTO } from '@swagger/oms';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-purchasing-options-modal-price-input',
|
||||
templateUrl: 'purchasing-options-modal-price-input.component.html',
|
||||
styleUrls: ['purchasing-options-modal-price-input.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PurchasingOptionsModalPriceInputComponent implements OnInit {
|
||||
control: FormGroup;
|
||||
|
||||
vats$: Observable<VATDTO[]> = this._omsService.getVATs().pipe(shareReplay());
|
||||
|
||||
@Output()
|
||||
priceChanged = new EventEmitter<number>();
|
||||
|
||||
@Output()
|
||||
vatChanged = new EventEmitter<VATDTO>();
|
||||
|
||||
private _subscriptions = new Subscription();
|
||||
|
||||
constructor(private _omsService: DomainOmsService, private _fb: FormBuilder) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.initForm();
|
||||
}
|
||||
|
||||
initForm() {
|
||||
const fb = this._fb;
|
||||
this.control = fb.group({
|
||||
price: fb.control(undefined, [Validators.required, Validators.pattern(/^\d+([\,]\d{1,2})?$/), Validators.max(99999)]),
|
||||
vat: fb.control(undefined, [Validators.required]),
|
||||
});
|
||||
|
||||
this._subscriptions.add(
|
||||
this.control.get('price').valueChanges.subscribe((price) => this.priceChanged.emit(Number(String(price).replace(',', '.'))))
|
||||
);
|
||||
this._subscriptions.add(this.control.get('vat').valueChanges.subscribe(this.vatChanged));
|
||||
|
||||
this.control.markAllAsTouched();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
|
||||
import { PurchasingOptionsModalPriceInputComponent } from './purchasing-options-modal-price-input.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiFormControlModule, UiInputModule, UiSelectModule, FormsModule, ReactiveFormsModule],
|
||||
exports: [PurchasingOptionsModalPriceInputComponent],
|
||||
declarations: [PurchasingOptionsModalPriceInputComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class PurchasingOptionsModalPriceInputModule {}
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="grow"></div>
|
||||
<div class="format">{{ item?.product?.formatDetail }}</div>
|
||||
<div class="price">
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: item?.catalogAvailability?.price?.value?.currency:'code' }}
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
|
||||
</div>
|
||||
<div class="date" *ngIf="option$ | async; let option">
|
||||
<ng-container *ngIf="option === 'pick-up'">Abholung ab</ng-container>
|
||||
@@ -60,12 +60,21 @@
|
||||
<div class="quantity-error" *ngIf="quantityError$ | async; let message">{{ message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="custom-price" *ngIf="showCustomPrice$ | async">
|
||||
<page-purchasing-options-modal-price-input
|
||||
(priceChanged)="changeCustomPrice($event)"
|
||||
(vatChanged)="changeCustomVat($event)"
|
||||
></page-purchasing-options-modal-price-input>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="summary-row" *ngIf="quantity$ | async; let quantity">
|
||||
<div class="reading-points">{{ quantity }} Artikel | {{ promoPoints$ | async }} Lesepunkte</div>
|
||||
<div class="reading-points">
|
||||
{{ quantity }} Artikel
|
||||
<ng-container *ngIf="promoPoints$ | async; let promoPoints"> | {{ promoPoints }} Lesepunkte </ng-container>
|
||||
</div>
|
||||
<div class="subtotal">
|
||||
Zwischensumme
|
||||
{{ item?.catalogAvailability?.price?.value?.value * quantity | currency: item?.catalogAvailability?.price?.value?.currency:'code' }}
|
||||
{{ (price$ | async) * quantity | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
|
||||
<div class="shipping-cost" *ngIf="showDeliveryInfo$ | async">
|
||||
ohne Versandkosten
|
||||
</div>
|
||||
@@ -76,7 +85,7 @@
|
||||
<button
|
||||
*ngIf="canContinueShopping$ | async"
|
||||
class="cta-continue-shopping"
|
||||
[disabled]="(fetching$ | async) || (canContinueShopping$ | async) === false"
|
||||
[disabled]="(fetching$ | async) || (canContinueShopping$ | async) === false || (customPriceInvalid$ | async) === true"
|
||||
(click)="continue('continue-shopping')"
|
||||
>
|
||||
Weiter einkaufen
|
||||
@@ -86,7 +95,7 @@
|
||||
</button>
|
||||
<button
|
||||
*ngIf="showTakeAwayButton$ | async"
|
||||
[disabled]="(fetching$ | async) || (canAdd$ | async) === false"
|
||||
[disabled]="(fetching$ | async) || (canAdd$ | async) === false || (customPriceInvalid$ | async) === true"
|
||||
class="cta-continue"
|
||||
(click)="continue()"
|
||||
>
|
||||
@@ -96,7 +105,7 @@
|
||||
*ngIf="showDefaultContinueButton$ | async"
|
||||
class="cta-continue"
|
||||
(click)="continue()"
|
||||
[disabled]="(fetching$ | async) || (canAdd$ | async) === false"
|
||||
[disabled]="(fetching$ | async) || (canAdd$ | async) === false || (customPriceInvalid$ | async) === true"
|
||||
>
|
||||
Fortfahren
|
||||
</button>
|
||||
|
||||
@@ -88,6 +88,10 @@ img.thumbnail {
|
||||
::ng-deep.spin {
|
||||
@apply text-brand;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply text-inactive-branch border-inactive-branch;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-continue,
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { AddToShoppingCartDTO, AvailabilityDTO } from '@swagger/checkout';
|
||||
import { AddToShoppingCartDTO, AvailabilityDTO, VATType } from '@swagger/checkout';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { first, map, switchMap } from 'rxjs/operators';
|
||||
import { debounceTime, first, map, switchMap } from 'rxjs/operators';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { PurchasingOptionsModalData } from './purchasing-options-modal.data';
|
||||
import { PurchasingOptions, PurchasingOptionsModalStore } from './purchasing-options-modal.store';
|
||||
@@ -45,6 +45,14 @@ export class PurchasingOptionsModalComponent {
|
||||
|
||||
readonly quantityError$ = this.purchasingOptionsModalStore.selectQuantityError;
|
||||
|
||||
readonly showCustomPrice$ = this.item$.pipe(map((item) => !item?.catalogAvailability?.price?.value?.value));
|
||||
|
||||
readonly customPriceInvalid$ = combineLatest([
|
||||
this.showCustomPrice$,
|
||||
this.purchasingOptionsModalStore.selectCustomPrice,
|
||||
this.purchasingOptionsModalStore.selectCustomVat,
|
||||
]).pipe(map(([showCustomPrice, customPrice, customVat]) => showCustomPrice && (!customPrice || !customVat)));
|
||||
|
||||
readonly showTakeAwayButton$ = combineLatest([
|
||||
this.option$,
|
||||
this.purchasingOptionsModalStore.selectFetchingAvailability,
|
||||
@@ -105,15 +113,24 @@ export class PurchasingOptionsModalComponent {
|
||||
map((buyer) => buyer.source)
|
||||
);
|
||||
|
||||
readonly promoPoints$ = combineLatest([this.item$, this.quantity$]).pipe(
|
||||
switchMap(([item, quantity]) =>
|
||||
price$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomPrice]).pipe(
|
||||
map(([item, customPrice]) => item?.catalogAvailability?.price?.value?.value ?? customPrice ?? 0)
|
||||
);
|
||||
|
||||
vat$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomVat]).pipe(
|
||||
map(([item, customVat]) => item?.catalogAvailability?.price.vat ?? customVat)
|
||||
);
|
||||
|
||||
readonly promoPoints$ = combineLatest([this.item$, this.quantity$, this.price$]).pipe(
|
||||
debounceTime(250),
|
||||
switchMap(([item, quantity, price]) =>
|
||||
this.domainCatalogService
|
||||
.getPromotionPoints({
|
||||
items: [
|
||||
{
|
||||
id: item.id,
|
||||
quantity: quantity,
|
||||
price: item.catalogAvailability.price?.value?.value,
|
||||
price: price,
|
||||
},
|
||||
],
|
||||
})
|
||||
@@ -145,6 +162,14 @@ export class PurchasingOptionsModalComponent {
|
||||
}
|
||||
}
|
||||
|
||||
changeCustomVat(vat: VATType) {
|
||||
this.purchasingOptionsModalStore.setCustomVat(vat);
|
||||
}
|
||||
|
||||
changeCustomPrice(price: number) {
|
||||
this.purchasingOptionsModalStore.setCustomPrice(price);
|
||||
}
|
||||
|
||||
backToSetOptions() {
|
||||
this.purchasingOptionsModalStore.setOption(undefined);
|
||||
}
|
||||
@@ -173,6 +198,8 @@ export class PurchasingOptionsModalComponent {
|
||||
const branch = await this.branch$.pipe(first()).toPromise();
|
||||
const shoppingCartItem = await this.purchasingOptionsModalStore.selectShoppingCartItem.pipe(first()).toPromise();
|
||||
const canAdd = await this.canAdd$.pipe(first()).toPromise();
|
||||
const customPrice = await this.purchasingOptionsModalStore.selectCustomPrice.pipe(first()).toPromise();
|
||||
const customVat = await this.purchasingOptionsModalStore.selectCustomVat.pipe(first()).toPromise();
|
||||
|
||||
if (canAdd || navigate === 'add-customer-data') {
|
||||
const newItem: AddToShoppingCartDTO = {
|
||||
@@ -187,6 +214,18 @@ export class PurchasingOptionsModalComponent {
|
||||
|
||||
newItem.product.catalogProductNumber = String(item.id);
|
||||
|
||||
if (!!customPrice && !!customVat) {
|
||||
newItem.availability.price = {
|
||||
value: {
|
||||
value: customPrice,
|
||||
currency: 'EUR',
|
||||
},
|
||||
vat: {
|
||||
vatType: customVat,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case 'take-away':
|
||||
case 'pick-up':
|
||||
|
||||
@@ -21,6 +21,7 @@ import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
|
||||
import { KeyNavigationModule } from '../../shared/key-navigation/key-navigation.module';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
import { PurchasingOptionsModalPriceInputModule } from './price-input/purchasing-options-modal-price-input.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -34,6 +35,7 @@ import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
UiSpinnerModule,
|
||||
KeyNavigationModule,
|
||||
RouterModule,
|
||||
PurchasingOptionsModalPriceInputModule,
|
||||
],
|
||||
exports: [PurchasingOptionsModalComponent],
|
||||
declarations: [
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, ShoppingCartItemDTO, VATType } from '@swagger/checkout';
|
||||
import { isBoolean, isNullOrUndefined, isString } from '@utils/common';
|
||||
import { NEVER, Observable } from 'rxjs';
|
||||
import { delay, filter, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
@@ -32,6 +32,8 @@ interface PurchasingOptionsModalState {
|
||||
// TODO: FilterBranch in der UI Component sortieren und filtern
|
||||
filterResult?: BranchDTO[];
|
||||
availabilities: { [key: string]: AvailabilityDTO };
|
||||
customPrice?: number;
|
||||
customVat?: VATType;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -67,6 +69,10 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
|
||||
|
||||
readonly selectQuantity = this.select((s) => s.quantity);
|
||||
|
||||
readonly selectCustomPrice = this.select((s) => s.customPrice);
|
||||
|
||||
readonly selectCustomVat = this.select((s) => s.customVat);
|
||||
|
||||
readonly selectAvailabilities = this.select((s) => s.availabilities);
|
||||
|
||||
readonly selectAvailability = this.select((s) => s.availabilities[s.option]);
|
||||
@@ -266,6 +272,20 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
|
||||
};
|
||||
});
|
||||
|
||||
readonly setCustomPrice = this.updater((state, customPrice: number) => {
|
||||
return {
|
||||
...state,
|
||||
customPrice,
|
||||
};
|
||||
});
|
||||
|
||||
readonly setCustomVat = this.updater((state, customVat: VATType) => {
|
||||
return {
|
||||
...state,
|
||||
customVat,
|
||||
};
|
||||
});
|
||||
|
||||
readonly setAvailableOptions = this.updater((state, availableOptions: PurchasingOptions[]) => {
|
||||
let option = state.option;
|
||||
if (availableOptions?.length === 1 && availableOptions[0] === 'download') {
|
||||
|
||||
@@ -11,6 +11,8 @@ export class UiFormControlFirstErrorPipe implements PipeTransform {
|
||||
switch (error) {
|
||||
case 'min':
|
||||
return `${label} wird benötigt`; // gender validation for create (upgrade) online customer with gender min value of 2
|
||||
case 'max':
|
||||
return `${label} ist ungültig`;
|
||||
case 'required':
|
||||
return `${label} wird benötigt`;
|
||||
case 'email':
|
||||
|
||||
Reference in New Issue
Block a user