Merge branch 'release/3.0' into feature/3968-Artikeldetails-Mehrwertsteuer

This commit is contained in:
Lorenz Hilpert
2023-08-31 14:11:06 +02:00
14 changed files with 270 additions and 206 deletions

View File

@@ -23,6 +23,7 @@ import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockS
import { PriceDTO } from '@swagger/availability';
import { AvailabilityByBranchDTO, ItemData, Ssc } from './defs';
import { Availability } from './defs/availability';
import { isEmpty } from 'lodash';
@Injectable()
export class DomainAvailabilityService {
@@ -483,6 +484,10 @@ export class DomainAvailabilityService {
};
}
private _priceIsEmpty(price: PriceDTO) {
return isEmpty(price?.value) || isEmpty(price?.vat);
}
private _mapToTakeAwayAvailability({
response,
supplier,
@@ -503,7 +508,7 @@ export class DomainAvailabilityService {
inStock: inStock,
supplierSSC: quantity <= inStock ? '999' : '',
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
price: price ?? stockInfo?.retailPrice,
price: this._priceIsEmpty(price) ? stockInfo?.retailPrice : price,
supplier: { id: supplier?.id },
// TODO: Change after API Update
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice

View File

@@ -103,29 +103,13 @@
<div class="page-article-details__product-publication">{{ publicationDate$ | async }}</div>
</div>
<div class="page-article-details__product-price-info flex flex-col mb-4 flex-nowrap self-end">
<ng-container *ngIf="item.catalogAvailability?.price?.value?.value; else retailPrice">
<div class="page-article-details__product-price font-bold text-xl self-end">
{{ item.catalogAvailability?.price?.value?.value | currency: item.catalogAvailability?.price?.value?.currency:'code' }}
</div>
<div *ngIf="item.catalogAvailability?.price?.vat?.vatType" class="page-article-details__product-price-bound self-end">
{{ item.catalogAvailability?.price?.vat?.vatType | vat: item.catalogAvailability?.priceMaintained }}
</div>
</ng-container>
<ng-template #retailPrice>
<ng-container *ngIf="store.takeAwayAvailability$ | async; let takeAwayAvailability">
<div class="page-article-details__product-price font-bold text-xl self-end">
{{ takeAwayAvailability?.price?.value?.value | currency: takeAwayAvailability?.price?.value?.currency:'code' }}
</div>
<div *ngIf="takeAwayAvailability?.price?.vat?.vatType" class="page-article-details__product-price-bound self-end">
{{ takeAwayAvailability?.price?.vat?.vatType | vat: takeAwayAvailability?.priceMaintained }}
</div>
</ng-container>
</ng-template>
<div class="page-article-details__product-price-info flex flex-col mb-4">
<div class="page-article-details__product-price font-bold text-xl self-end" *ngIf="price$ | async; let price">
{{ price?.value?.value | currency: price?.value?.currency:'code' }}
</div>
<div *ngIf="price$ | async; let price" class="page-article-details__product-price-bound self-end">
{{ price?.vat?.vatType | vat: (priceMaintained$ | async) }}
</div>
<div class="page-article-details__product-points self-end" *ngIf="store.promotionPoints$ | async; let promotionPoints">
{{ promotionPoints }} Lesepunkte
</div>

View File

@@ -127,6 +127,50 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
})
);
priceMaintained$ = combineLatest([
this.store.item$,
this.store.takeAwayAvailability$,
this.store.deliveryAvailability$,
this.store.deliveryDigAvailability$,
this.store.deliveryB2BAvailability$,
]).pipe(
map(([item, takeAway, delivery, deliveryDig, deliveryB2B]) =>
[item, takeAway, delivery, deliveryDig, deliveryB2B].some((i) => i?.priceMaintained)
)
);
price$ = combineLatest([
this.store.item$,
this.store.takeAwayAvailability$,
this.store.deliveryAvailability$,
this.store.deliveryDigAvailability$,
this.store.deliveryB2BAvailability$,
]).pipe(
map(([item, takeAway, delivery, deliveryDig, deliveryB2B]) => {
if (item?.catalogAvailability?.price?.value?.value) {
return item?.catalogAvailability?.price;
}
if (takeAway?.price?.value?.value) {
return takeAway.price;
}
if (delivery?.price?.value?.value) {
return delivery.price;
}
if (deliveryDig?.price?.value?.value) {
return deliveryDig.price;
}
if (deliveryB2B?.price?.value?.value) {
return deliveryB2B.price;
}
return null;
})
);
constructor(
public readonly applicationService: ApplicationService,
private activatedRoute: ActivatedRoute,

View File

@@ -0,0 +1,47 @@
import { AfterContentInit, ChangeDetectionStrategy, Component, ElementRef, Renderer2 } from '@angular/core';
@Component({
selector: 'shared-scale-content, [sharedScaleContent]',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
styles: [
`
:host {
overflow-y: hidden;
}
`,
],
})
export class ScaleContentComponent implements AfterContentInit {
// TODO: Bessere Lösung finden? Falls keine bessere Lösung gefunden wird, dann muss die Komponente auslagen
fontSizeInEm = 1;
adjustmentSteps = 0.05;
constructor(private _elementRef: ElementRef<HTMLElement>, private _renderer: Renderer2) {}
ngAfterContentInit(): void {
this.adjustFontSize();
}
adjustFontSize() {
const element = this._elementRef.nativeElement;
const clientRect = element?.getClientRects();
const scrollHeight = element?.scrollHeight;
const domRect = clientRect && clientRect[0];
if (domRect && Math.ceil(domRect?.height) < scrollHeight) {
this.fontSizeInEm -= this.adjustmentSteps;
} else {
return;
}
this._renderer.setStyle(element, 'font-size', `${this.fontSizeInEm}em`);
setTimeout(() => this.adjustFontSize(), 1);
}
}

View File

@@ -0,0 +1 @@
export * from './lib/scale-content.component';

View File

@@ -6,7 +6,7 @@
<div class="shared-purchase-options-list-item__contributors font-bold">
{{ product?.contributors }}
</div>
<div class="shared-purchase-options-list-item__name font-bold h-12" scaleContent>
<div class="shared-purchase-options-list-item__name font-bold h-12" sharedScaleContent>
{{ product?.name }}
</div>
<div class="shared-purchase-options-list-item__format flex flex-row items-center">
@@ -82,67 +82,40 @@
</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 flex flex-row items-center"
*ngIf="!(canEditPrice$ | async)"
>
<div class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center">
<ui-svg-icon class="mr-3" [uiOverlayTrigger]="tooltip" icon="mat-info" *ngIf="priceMaintained$ | async"></ui-svg-icon>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [xOffset]="5" [closeable]="true">
Günstigerer Preis aus Hugendubel Katalog wird übernommen
</ui-tooltip>
<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 manual-price">
<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>
<shared-icon *ngIf="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty" icon="mat-info"></shared-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">
<shared-input-control>
<div class="relative flex flex-row justify-end items-start">
<ui-select
*ngIf="canEditVat$ | async"
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded-card 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-6]="priceFormControl?.invalid && priceFormControl?.dirty"
*ngIf="canEditPrice$ | async; else priceTmpl"
>
<shared-input-control-indicator>
<shared-icon *ngIf="priceFormControl?.invalid && priceFormControl?.dirty" icon="mat-info"></shared-icon>
</shared-input-control-indicator>
<input
[uiOverlayTrigger]="tooltip"
triggerOn="init"
[uiOverlayTrigger]="giftCardTooltip"
triggerOn="none"
#quantityInput
#priceOverlayTrigger="uiOverlayTrigger"
sharedInputControlInput
type="string"
class="w-24"
[formControl]="priceFormControl"
placeholder="00,00"
(sharedOnInit)="quantityInput.focus()"
(sharedOnInit)="onPriceInputInit(quantityInput, priceOverlayTrigger)"
sharedNumberValue
/>
<shared-input-control-suffix>EUR</shared-input-control-suffix>
@@ -152,11 +125,14 @@
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
</shared-input-control>
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #tooltip>
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #giftCardTooltip>
Tragen Sie hier den <br />
Gutscheinbetrag ein.
</ui-tooltip>
</div>
<ng-template #priceTmpl>
{{ priceValue$ | async | currency: 'EUR':'code' }}
</ng-template>
</div>
<ui-quantity-dropdown class="mt-2" [formControl]="quantityFormControl" [range]="maxSelectableQuantity$ | async"> </ui-quantity-dropdown>
<div class="pt-7">

View File

@@ -1,78 +1,22 @@
import { CommonModule, NgIf } from '@angular/common';
import {
Component,
ChangeDetectionStrategy,
Input,
OnInit,
OnDestroy,
OnChanges,
SimpleChanges,
AfterContentInit,
ElementRef,
Renderer2,
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, OnInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { ProductImageModule } from '@cdn/product-image';
import { InputControlModule } from '@shared/components/input-control';
import { ElementLifecycleModule } from '@shared/directives/element-lifecycle';
import { UiCommonModule } from '@ui/common';
import { UiCommonModule, UiOverlayTriggerDirective } from '@ui/common';
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { UiSpinnerModule } from '@ui/spinner';
import { UiTooltipModule } from '@ui/tooltip';
import { combineLatest, ReplaySubject, Subscription } from 'rxjs';
import { IconComponent } from '@shared/components/icon';
import { map, take, shareReplay, startWith, switchMap, withLatestFrom, last } 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 { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { UiSelectModule } from '@ui/select';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
@Component({
selector: 'scale-content, [scaleContent]',
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
styles: [
`
:host {
overflow-y: hidden;
}
`,
],
})
export class ScaleContentComponent implements AfterContentInit {
// TODO: Bessere Lösung finden? Falls keine bessere Lösung gefunden wird, dann muss die Komponente auslagen
fontSizeInEm = 1;
adjustmentSteps = 0.05;
constructor(private _elementRef: ElementRef<HTMLElement>, private _renderer: Renderer2) {}
ngAfterContentInit(): void {
this.adjustFontSize();
}
adjustFontSize() {
const element = this._elementRef.nativeElement;
const clientRect = element?.getClientRects();
const scrollHeight = element?.scrollHeight;
const domRect = clientRect && clientRect[0];
if (domRect && Math.ceil(domRect?.height) < scrollHeight) {
this.fontSizeInEm -= this.adjustmentSteps;
} else {
return;
}
this._renderer.setStyle(element, 'font-size', `${this.fontSizeInEm}em`);
setTimeout(() => this.adjustFontSize(), 1);
}
}
import { ScaleContentComponent } from '@shared/components/scale-content';
import { IconComponent } from '@shared/components/icon';
@Component({
selector: 'shared-purchase-options-list-item',
@@ -115,14 +59,22 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
quantityFormControl = new FormControl<number>(null);
priceFormControl = new FormControl<string>(null, [
private readonly _giftCardValidators = [
Validators.required,
Validators.min(1),
Validators.max(GIFT_CARD_MAX_PRICE),
Validators.pattern(PRICE_PATTERN),
]);
];
private readonly _defaultValidators = [
Validators.required,
Validators.min(0.01),
Validators.max(999.99),
Validators.pattern(PRICE_PATTERN),
];
priceFormControl = new FormControl<string>(null);
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);
@@ -160,10 +112,20 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
priceVat$ = this.price$.pipe(map((price) => price?.vat?.vatType));
canEditPrice$ = this.item$.pipe(switchMap((item) => this._store.getCanEditPrice$(item.id)));
canAddResult$ = this.item$.pipe(switchMap((item) => this._store.getCanAddResultForItemAndCurrentPurchaseOption$(item.id)));
canEditPrice$ = this.item$.pipe(
switchMap((item) => combineLatest([this.canAddResult$, this._store.getCanEditPrice$(item.id)])),
map(([canAddResult, canEditPrice]) => canAddResult?.canAdd && canEditPrice)
);
canEditVat$ = this.item$.pipe(
switchMap((item) => combineLatest([this.canAddResult$, this._store.getCanEditVat$(item.id)])),
map(([canAddResult, canEditVat]) => canAddResult?.canAdd && canEditVat)
);
isGiftCard$ = this.item$.pipe(switchMap((item) => this._store.getIsGiftCard$(item.id)));
maxSelectableQuantity$ = combineLatest([this._store.purchaseOption$, this.availability$]).pipe(
map(([purchaseOption, availability]) => {
if (purchaseOption === 'in-store') {
@@ -205,6 +167,14 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
constructor(private _store: PurchaseOptionsStore) {}
onPriceInputInit(target: HTMLElement, overlayTrigger: UiOverlayTriggerDirective) {
if (this._store.getIsGiftCard(this.item.id)) {
overlayTrigger.open();
}
target?.focus();
}
// Wichtig für das korrekte Setzen des Preises an das Item für den Endpoint request
parsePrice(value: string) {
if (PRICE_PATTERN.test(value)) {
@@ -225,10 +195,11 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
}
ngOnInit(): void {
this.initPriceValidatorSubscription();
this.initQuantitySubscription();
this.initPriceSubscription();
this.initVatSubscription();
this.initSelectedSubscription();
this.initManualPriceSubscriptions();
}
ngOnChanges({ item }: SimpleChanges) {
@@ -242,14 +213,16 @@ 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
async initManualPriceSubscriptions() {
const isManualPrice = await this.setManualPrice$.pipe(last()).toPromise();
if (!!isManualPrice) {
this.initManualPriceSubscription();
this.initManualVatSubscription();
}
initPriceValidatorSubscription() {
const sub = this.item$.pipe(switchMap((item) => this._store.getIsGiftCard$(item.id))).subscribe((isGiftCard) => {
if (isGiftCard) {
this.priceFormControl.setValidators(this._giftCardValidators);
} else {
this.priceFormControl.setValidators(this._defaultValidators);
}
});
this._subscriptions.add(sub);
}
initQuantitySubscription() {
@@ -279,7 +252,6 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
if (priceStr === '') return;
if (this.parsePrice(this.priceFormControl.value) !== price?.value?.value) {
debugger;
this.priceFormControl.setValue(priceStr);
}
});
@@ -305,34 +277,7 @@ 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, true);
return;
}
if (price?.value?.value !== parsedPrice) {
this._store.setPrice(this.item.id, this.parsePrice(value), true);
}
});
this._subscriptions.add(sub);
this._subscriptions.add(valueChangesSub);
}
initManualVatSubscription() {
initVatSubscription() {
const valueChangesSub = this.manualVatFormControl.valueChanges.pipe(withLatestFrom(this.vats$)).subscribe(([formVatType, vats]) => {
const price = this._store.getPrice(this.item.id);

View File

@@ -25,18 +25,13 @@
</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 || !(hasPrice$ | async)"
(click)="save('continue-shopping')"
>
<button type="button" class="isa-cta-button" [disabled]="!(canContinue$ | async) || saving" (click)="save('continue-shopping')">
Weiter einkaufen
</button>
<button
type="button"
class="ml-4 isa-cta-button isa-button-primary"
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
[disabled]="!(canContinue$ | async) || saving"
(click)="save('continue')"
>
Fortfahren
@@ -46,7 +41,7 @@
<button
type="button"
class="ml-4 isa-cta-button isa-button-primary"
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
[disabled]="!(canContinue$ | async) || saving"
(click)="save('continue')"
>
Fortfahren

View File

@@ -83,7 +83,7 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
hasDownload$ = this.purchasingOptions$.pipe(map((purchasingOptions) => purchasingOptions.includes('download')));
canContinue$ = this.store.canContinue$.pipe(tap((canContinue) => console.log('canContinue', canContinue)));
canContinue$ = this.store.canContinue$;
private _onDestroy$ = new Subject<void>();

View File

@@ -1,5 +1,4 @@
export * from './purchase-options.helpers';
export * from './purchase-options.selectors';
export * from './purchase-options.service';
export * from './purchase-options.state';
export * from './purchase-options.store';

View File

@@ -60,6 +60,14 @@ export function isGiftCard(item: Item, type: ActionType): boolean {
}
}
export function isArchive(item: Item, type: ActionType): boolean {
if (isItemDTO(item, type)) {
return item?.features?.some((f) => f.key === 'ARC');
} else {
return !!item?.features?.['ARC'];
}
}
export function mapToItemPayload({
item,
quantity,

View File

@@ -1,5 +1,5 @@
import { PriceDTO, PriceValueDTO } from '@swagger/checkout';
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE, GIFT_CARD_MAX_PRICE, GIFT_CARD_TYPE, PURCHASE_OPTIONS } from '../constants';
import { PriceDTO } from '@swagger/checkout';
import { DEFAULT_PRICE_DTO, GIFT_CARD_MAX_PRICE, PURCHASE_OPTIONS } from '../constants';
import { isGiftCard, isItemDTO } from './purchase-options.helpers';
import { PurchaseOptionsState } from './purchase-options.state';
import { ActionType, Availability, Branch, CanAdd, FetchingAvailability, Item, PurchaseOption } from './purchase-options.types';
@@ -207,24 +207,49 @@ export function isArchive(item: Item, type: ActionType): boolean {
}
export function getCanEditPrice(itemId: number): (state: PurchaseOptionsState) => boolean {
return (state) => {
const item = getItems(state).find((item) => item.id === itemId);
if (isGiftCard(item, getType(state))) {
return true;
}
const purchaseOption = getPurchaseOption(state);
if (isArchive(item, getType(state)) && !getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state)) {
return true;
}
return false;
};
}
export function getCanEditVat(itemId: number): (state: PurchaseOptionsState) => boolean {
return (state) => {
const item = getItems(state).find((item) => item.id === itemId);
const purchaseOption = getPurchaseOption(state);
if (isArchive(item, getType(state)) && !getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state)) {
return true;
}
return false;
};
}
export function getIsGiftCard(itemId: number): (state: PurchaseOptionsState) => boolean {
return (state) => {
const item = getItems(state).find((item) => item.id === itemId);
return isGiftCard(item, getType(state));
};
}
export function getPriceForPurchaseOption(
export function getAvailabilityPriceForPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
): (state: PurchaseOptionsState) => (PriceDTO & { fromCatalogue?: boolean }) | undefined {
return (state) => {
if (
getCanEditPrice(itemId)(state) ||
isArchive(
getItems(state).find((item) => item.id === itemId),
getType(state)
)
) {
if (getCanEditPrice(itemId)(state)) {
const price = getPrices(state)[itemId];
if (price) {
return price;
@@ -232,9 +257,7 @@ export function getPriceForPurchaseOption(
}
const item = getItems(state).find((item) => item.id === itemId);
const type = getType(state);
let availabilities = getAvailabilitiesForItem(itemId)(state);
let availability = availabilities.find((availability) => availability.purchaseOption === purchaseOption);
@@ -271,13 +294,29 @@ export function getPriceForPurchaseOption(
}
if (isItemDTO(item, type)) {
return item?.catalogAvailability?.price ?? DEFAULT_PRICE_DTO;
return item?.catalogAvailability?.price;
} else {
return item?.unitPrice ?? DEFAULT_PRICE_DTO;
return item?.unitPrice;
}
};
}
export function getPriceForPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
return (state) => {
if (getCanEditPrice(itemId)(state)) {
const price = getPrices(state)[itemId];
if (price) {
return price;
}
}
return getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state) ?? DEFAULT_PRICE_DTO;
};
}
export function getQuantityForItem(itemId: number): (state: PurchaseOptionsState) => number {
return (state) => {
const item = getItems(state).find((item) => item.id === itemId);
@@ -364,12 +403,21 @@ export function canContinue(state: PurchaseOptionsState): boolean {
return false;
}
const actionType = getType(state);
for (let item of items) {
if (isGiftCard(item, getType(state))) {
if (isGiftCard(item, actionType)) {
const price = getPriceForPurchaseOption(item.id, purchaseOption)(state);
if (!(price?.value?.value > 0 && price?.value?.value <= GIFT_CARD_MAX_PRICE)) {
return false;
}
} else if (isArchive(item, actionType) && !getAvailabilityPriceForPurchaseOption(item.id, purchaseOption)(state)) {
const price = getPriceForPurchaseOption(item.id, purchaseOption)(state);
const hasPrice = price?.value?.value > 0;
const hasVat = price?.vat?.vatType > 0;
if (!(hasPrice && hasVat)) {
return false;
}
}
}

View File

@@ -612,8 +612,8 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
});
}
isGiftcard(itemId: number) {
return this._service;
getIsGiftCard(itemId: number) {
return this.get(Selectors.getIsGiftCard(itemId));
}
getPrice(itemId: number) {
@@ -640,6 +640,18 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
return this.select(Selectors.getCanEditPrice(itemId));
}
getCanEditVat(itemId: number) {
return this.get(Selectors.getCanEditVat(itemId));
}
getCanEditVat$(itemId: number) {
return this.select(Selectors.getCanEditVat(itemId));
}
getIsGiftCard$(itemId: number) {
return this.select(Selectors.getIsGiftCard(itemId));
}
setPrice(itemId: number, value: number, manually: boolean = false) {
const prices = this.prices;
let price = prices[itemId];

View File

@@ -25,7 +25,7 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
component: UiOverlayTrigger;
@Input()
triggerOn: 'click' | 'hover' | 'init' = 'click';
triggerOn: 'click' | 'hover' | 'init' | 'none' = 'click';
@Input()
overlayTriggerDisabled: boolean;