mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1555: #4074 Implemented Changes from ISA-Integration to ISA-Test
#4074 Implemented Changes from ISA-Integration to ISA-Test
This commit is contained in:
committed by
Andreas Schickinger
parent
74bf2133c6
commit
d86f595b1f
@@ -47,3 +47,10 @@
|
||||
.fancy-checkbox:checked::after {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
::ng-deep shared-purchase-options-list-item ui-select .ui-input-wrapper {
|
||||
@apply h-full;
|
||||
}
|
||||
::ng-deep shared-purchase-options-list-item ui-select .ui-select-toggle {
|
||||
@apply ml-2;
|
||||
}
|
||||
|
||||
@@ -64,8 +64,46 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__price text-right ml-4 flex flex-col items-end">
|
||||
<div class="shared-purchase-options-list-item__price-value font-bold text-xl" *ngIf="!(canEditPrice$ | async)">
|
||||
{{ priceValue$ | async | currency: 'EUR':'code' }}
|
||||
<div
|
||||
class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center"
|
||||
*ngIf="!(canEditPrice$ | async)"
|
||||
>
|
||||
<ng-container *ngIf="!(setManualPrice$ | async); else setManualPrice">
|
||||
{{ priceValue$ | async | currency: 'EUR':'code' }}
|
||||
</ng-container>
|
||||
<ng-template #setManualPrice>
|
||||
<div class="relative flex flex-row items-start">
|
||||
<ui-select
|
||||
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded border border-solid border-[#AEB7C1] mr-4"
|
||||
tabindex="-1"
|
||||
[formControl]="manualVatFormControl"
|
||||
[defaultLabel]="'MwSt'"
|
||||
>
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
</ui-select>
|
||||
|
||||
<shared-input-control [class.ml-8]="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty">
|
||||
<shared-input-control-indicator>
|
||||
<ui-svg-icon *ngIf="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty" icon="mat-info"></ui-svg-icon>
|
||||
</shared-input-control-indicator>
|
||||
<input
|
||||
triggerOn="init"
|
||||
#quantityInput
|
||||
sharedInputControlInput
|
||||
type="string"
|
||||
class="w-24"
|
||||
[formControl]="manualPriceFormControl"
|
||||
placeholder="00,00"
|
||||
(sharedOnInit)="quantityInput.focus()"
|
||||
sharedNumberValue
|
||||
/>
|
||||
<shared-input-control-suffix>EUR</shared-input-control-suffix>
|
||||
<shared-input-control-error error="required">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="pattern">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
|
||||
</shared-input-control>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__price-value font-bold text-xl" *ngIf="canEditPrice$ | async">
|
||||
<div class="relative flex flex-col">
|
||||
|
||||
@@ -21,9 +21,11 @@ import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { combineLatest, ReplaySubject, Subscription } from 'rxjs';
|
||||
import { map, startWith, switchMap } from 'rxjs/operators';
|
||||
import { map, take, shareReplay, startWith, switchMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { GIFT_CARD_MAX_PRICE, PRICE_PATTERN } from '../constants';
|
||||
import { Item, PurchaseOptionsStore } from '../store';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
|
||||
|
||||
@Component({
|
||||
selector: 'scale-content, [scaleContent]',
|
||||
@@ -78,6 +80,7 @@ export class ScaleContentComponent implements AfterContentInit {
|
||||
imports: [
|
||||
CommonModule,
|
||||
UiQuantityDropdownModule,
|
||||
UiSelectModule,
|
||||
ProductImageModule,
|
||||
UiIconModule,
|
||||
UiSpinnerModule,
|
||||
@@ -108,7 +111,15 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
|
||||
quantityFormControl = new FormControl<number>(null);
|
||||
|
||||
priceFormControl = new FormControl<string>(null, [Validators.required, Validators.min(1), Validators.max(GIFT_CARD_MAX_PRICE)]);
|
||||
priceFormControl = new FormControl<string>(null, [
|
||||
Validators.required,
|
||||
Validators.min(1),
|
||||
Validators.max(GIFT_CARD_MAX_PRICE),
|
||||
Validators.pattern(PRICE_PATTERN),
|
||||
]);
|
||||
|
||||
manualPriceFormControl = new FormControl<string>(null, [Validators.required, Validators.max(999.99), Validators.pattern(PRICE_PATTERN)]);
|
||||
manualVatFormControl = new FormControl<string>('', [Validators.required]);
|
||||
|
||||
selectedFormControl = new FormControl<boolean>(false);
|
||||
|
||||
@@ -123,7 +134,25 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
|
||||
priceValue$ = this.price$.pipe(map((price) => price?.value?.value));
|
||||
|
||||
priceVat$ = this.price$.pipe(map((price) => price?.vat?.value));
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// take(2) um die Response des Katalogpreises und danach um die Response der OLAs abzuwarten
|
||||
// Logik gilt ausschließlich für Archivartikel
|
||||
setManualPrice$ = this.price$.pipe(
|
||||
take(2),
|
||||
map((price) => {
|
||||
// Logik nur beim Hinzufügen über Kaufoptionen, da über Ändern im Warenkorb die Info fehlt ob das jeweilige ShoppingCartItem ein Archivartikel ist oder nicht
|
||||
const features = this.item?.features as KeyValueDTOOfStringAndString[];
|
||||
if (!!features && Array.isArray(features)) {
|
||||
const isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
|
||||
return isArchive ? !price?.value?.value || price?.vat === undefined : false;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
vats$ = this._store.vats$.pipe(shareReplay());
|
||||
|
||||
priceVat$ = this.price$.pipe(map((price) => price?.vat?.vatType));
|
||||
|
||||
canEditPrice$ = this.item$.pipe(switchMap((item) => this._store.getCanEditPrice$(item.id)));
|
||||
|
||||
@@ -174,7 +203,6 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
if (PRICE_PATTERN.test(value)) {
|
||||
return parseFloat(value.replace(',', '.'));
|
||||
}
|
||||
this.priceFormControl.setErrors({ pattern: true });
|
||||
}
|
||||
|
||||
stringifyPrice(value: number) {
|
||||
@@ -193,6 +221,7 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
this.initQuantitySubscription();
|
||||
this.initPriceSubscription();
|
||||
this.initSelectedSubscription();
|
||||
this.initManualPriceSubscriptions();
|
||||
}
|
||||
|
||||
ngOnChanges({ item }: SimpleChanges) {
|
||||
@@ -206,6 +235,19 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
this._subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// Logik gilt ausschließlich für Archivartikel und über die Kaufoptionen. Nicht über den Warenkorb
|
||||
initManualPriceSubscriptions() {
|
||||
const features = this.item?.features as KeyValueDTOOfStringAndString[];
|
||||
if (!!features && Array.isArray(features)) {
|
||||
const isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
|
||||
if (isArchive) {
|
||||
this.initManualPriceSubscription();
|
||||
this.initManualVatSubscription();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initQuantitySubscription() {
|
||||
const sub = this.item$.subscribe((item) => {
|
||||
if (this.quantityFormControl.value !== item.quantity) {
|
||||
@@ -250,6 +292,51 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
this._subscriptions.add(valueChangesSub);
|
||||
}
|
||||
|
||||
initManualPriceSubscription() {
|
||||
const sub = this.price$.subscribe((price) => {
|
||||
const priceStr = this.stringifyPrice(price?.value?.value);
|
||||
if (priceStr === '') return;
|
||||
|
||||
if (this.parsePrice(this.manualPriceFormControl.value) !== price?.value?.value) {
|
||||
this.manualPriceFormControl.setValue(priceStr);
|
||||
}
|
||||
});
|
||||
|
||||
const valueChangesSub = this.manualPriceFormControl.valueChanges.subscribe((value) => {
|
||||
const price = this._store.getPrice(this.item.id);
|
||||
const parsedPrice = this.parsePrice(value);
|
||||
|
||||
if (!parsedPrice) {
|
||||
this._store.setPrice(this.item.id, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (price[this.item.id] !== parsedPrice) {
|
||||
this._store.setPrice(this.item.id, this.parsePrice(value));
|
||||
}
|
||||
});
|
||||
this._subscriptions.add(sub);
|
||||
this._subscriptions.add(valueChangesSub);
|
||||
}
|
||||
|
||||
initManualVatSubscription() {
|
||||
const valueChangesSub = this.manualVatFormControl.valueChanges.pipe(withLatestFrom(this.vats$)).subscribe(([formVatType, vats]) => {
|
||||
const price = this._store.getPrice(this.item.id);
|
||||
|
||||
const vat = vats.find((vat) => vat?.vatType === Number(formVatType));
|
||||
|
||||
if (!vat) {
|
||||
this._store.setVat(this.item.id, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (price[this.item.id]?.vat?.vatType !== vat?.vatType) {
|
||||
this._store.setVat(this.item.id, vat);
|
||||
}
|
||||
});
|
||||
this._subscriptions.add(valueChangesSub);
|
||||
}
|
||||
|
||||
initSelectedSubscription() {
|
||||
const sub = this.item$
|
||||
.pipe(switchMap((item) => this._store.selectedItemIds$.pipe(map((ids) => ids.includes(item.id)))))
|
||||
|
||||
@@ -3,15 +3,6 @@
|
||||
Wie möchten Sie die Artikel erhalten?
|
||||
</p>
|
||||
<div class="rounded p-4 shadow-card mt-4 grid grid-flow-col gap-4 justify-center items-center relative">
|
||||
<!-- <ng-container *ngFor="let option of purchasingOptions$ | async">
|
||||
<ng-container [ngSwitch]="option">
|
||||
<app-delivery-purchase-options-tile *ngSwitchCase="'delivery'"> </app-delivery-purchase-options-tile>
|
||||
<app-in-store-purchase-options-tile *ngSwitchCase="'in-store'"> </app-in-store-purchase-options-tile>
|
||||
<app-pickup-purchase-options-tile *ngSwitchCase="'pickup'"> </app-pickup-purchase-options-tile>
|
||||
<app-download-purchase-options-tile *ngSwitchCase="'download'"> </app-download-purchase-options-tile>
|
||||
</ng-container>
|
||||
</ng-container> -->
|
||||
|
||||
<ng-container *ngIf="!(isDownloadOnly$ | async)">
|
||||
<ng-container *ngIf="!(isGiftCardOnly$ | async)">
|
||||
<app-in-store-purchase-options-tile> </app-in-store-purchase-options-tile>
|
||||
@@ -32,13 +23,18 @@
|
||||
</div>
|
||||
<div class="text-center -mx-4 border-t border-gray-200 p-4 border-solid">
|
||||
<ng-container *ngIf="type === 'add'">
|
||||
<button type="button" class="isa-cta-button" [disabled]="!(canContinue$ | async) || saving" (click)="save('continue-shopping')">
|
||||
<button
|
||||
type="button"
|
||||
class="isa-cta-button"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
(click)="save('continue-shopping')"
|
||||
>
|
||||
Weiter einkaufen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
Fortfahren
|
||||
@@ -48,7 +44,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
Fortfahren
|
||||
|
||||
@@ -5,7 +5,7 @@ import { PurchaseOptionsModalData } from './purchase-options-modal.data';
|
||||
import { PurchaseOptionsListHeaderComponent } from './purchase-options-list-header';
|
||||
import { PurchaseOptionsListItemComponent } from './purchase-options-list-item';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Subject, zip } from 'rxjs';
|
||||
import {
|
||||
DeliveryPurchaseOptionTileComponent,
|
||||
DownloadPurchaseOptionTileComponent,
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
PickupPurchaseOptionTileComponent,
|
||||
} from './purchase-options-tile';
|
||||
import { isGiftCard, Item, PurchaseOptionsStore } from './store';
|
||||
import { delay, map, shareReplay, skip, takeUntil } from 'rxjs/operators';
|
||||
import { delay, map, shareReplay, skip, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-purchase-options-modal',
|
||||
@@ -44,6 +45,39 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
|
||||
|
||||
items$ = this.store.items$;
|
||||
|
||||
hasPrice$ = this.items$.pipe(
|
||||
switchMap((items) =>
|
||||
items.map((item) => {
|
||||
let isArchive = false;
|
||||
const features = item?.features as KeyValueDTOOfStringAndString[];
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
|
||||
if (!!features && Array.isArray(features)) {
|
||||
isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
|
||||
}
|
||||
return zip(
|
||||
this.store
|
||||
?.getPrice$(item?.id)
|
||||
.pipe(
|
||||
map((price) =>
|
||||
isArchive
|
||||
? !!price?.value?.value &&
|
||||
price?.vat !== undefined &&
|
||||
price?.vat?.vatType !== undefined &&
|
||||
price?.vat?.value !== undefined
|
||||
: !!price?.value?.value
|
||||
)
|
||||
)
|
||||
);
|
||||
})
|
||||
),
|
||||
switchMap((hasPrices) => hasPrices),
|
||||
map((hasPrices) => {
|
||||
const containsItemWithNoPrice = hasPrices?.filter((hasPrice) => hasPrice === false) ?? [];
|
||||
return containsItemWithNoPrice?.length === 0;
|
||||
})
|
||||
);
|
||||
|
||||
purchasingOptions$ = this.store.getPurchaseOptionsInAvailabilities$;
|
||||
|
||||
isDownloadOnly$ = this.purchasingOptions$.pipe(
|
||||
|
||||
@@ -11,21 +11,27 @@ import {
|
||||
UpdateShoppingCartItemDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { map, shareReplay, take } from 'rxjs/operators';
|
||||
import { Branch, ItemData } from './purchase-options.types';
|
||||
import { memorize } from '@utils/common';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PurchaseOptionsService {
|
||||
constructor(
|
||||
private _availabilityService: DomainAvailabilityService,
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _auth: AuthService,
|
||||
private _app: ApplicationService
|
||||
) {}
|
||||
|
||||
getVats$() {
|
||||
return this._omsService.getVATs();
|
||||
}
|
||||
|
||||
getSelectedBranchForProcess(processId: number): Observable<Branch> {
|
||||
return this._app.getSelectedBranch$(processId).pipe(take(1), shareReplay(1));
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@ import {
|
||||
} from './purchase-options.helpers';
|
||||
import { Observable } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE } from '../constants';
|
||||
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE, DEFAULT_VAT_VALUE } from '../constants';
|
||||
import { AddToShoppingCartDTO, EntityDTOContainerOfDestinationDTO, UpdateShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { isEqual, uniqueId } from 'lodash';
|
||||
import { uniqueId } from 'lodash';
|
||||
import { VATDTO } from '@swagger/oms';
|
||||
|
||||
@Injectable()
|
||||
export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
|
||||
@@ -128,6 +129,10 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
|
||||
|
||||
fetchingAvailabilities$ = this.select(Selectors.getFetchingAvailabilities);
|
||||
|
||||
get vats$() {
|
||||
return this._service.getVats$();
|
||||
}
|
||||
|
||||
constructor(private _service: PurchaseOptionsService) {
|
||||
super({
|
||||
defaultBranch: undefined,
|
||||
@@ -641,9 +646,23 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
|
||||
let price = prices[itemId];
|
||||
if (price?.value?.value !== value) {
|
||||
if (!price) {
|
||||
price = { ...DEFAULT_PRICE_DTO, value: { ...DEFAULT_PRICE_VALUE, value } };
|
||||
price = { ...DEFAULT_PRICE_DTO, value: { ...DEFAULT_PRICE_VALUE, value }, vat: { ...DEFAULT_VAT_VALUE, ...price?.vat } };
|
||||
} else {
|
||||
price = { ...price, value: { ...price.value, value } };
|
||||
price = { ...price, value: { ...price.value, value }, vat: price?.vat };
|
||||
}
|
||||
|
||||
this.patchState({ prices: { ...prices, [itemId]: price } });
|
||||
}
|
||||
}
|
||||
|
||||
setVat(itemId: number, vat: VATDTO) {
|
||||
const prices = this.prices;
|
||||
let price = prices[itemId];
|
||||
if (price?.vat?.vatType !== vat?.vatType) {
|
||||
if (!price) {
|
||||
price = { ...DEFAULT_PRICE_DTO, value: { ...DEFAULT_PRICE_VALUE, ...price?.value }, vat: { ...DEFAULT_VAT_VALUE, ...vat } };
|
||||
} else {
|
||||
price = { ...price, value: price.value, vat: { ...price.vat, ...vat } };
|
||||
}
|
||||
|
||||
this.patchState({ prices: { ...prices, [itemId]: price } });
|
||||
|
||||
Reference in New Issue
Block a user