Merged PR 925: #2265 Dummy Anlage Update

#2265 Dummy Anlage Update
This commit is contained in:
Nino Righi
2021-10-21 15:19:39 +00:00
committed by Lorenz Hilpert
parent a4a39f643c
commit 4c95bb8354
14 changed files with 404 additions and 132 deletions

View File

@@ -11,26 +11,29 @@
</div>
</div>
<form *ngIf="control" [formGroup]="control" (submit)="submit()">
<ui-searchbox
formControlName="ean"
placeholder="EAN/ISBN"
[query]="query$ | async"
(search)="search($event)"
(scan)="search($event)"
[loading]="loading$ | async"
[scanProvider]="scanProvider"
[hint]="message$ | async"
>
</ui-searchbox>
<ui-form-control class="searchbox-control" label="EAN/ISBN">
<ui-searchbox
formControlName="ean"
[query]="query$ | async"
(search)="search($event)"
(scan)="search($event)"
[loading]="loading$ | async"
[scanProvider]="scanProvider"
[hint]="message$ | async"
tabindex="0"
>
</ui-searchbox>
</ui-form-control>
<ui-form-control label="Titel" requiredMark="*">
<input uiInput formControlName="name" />
<input tabindex="0" uiInput formControlName="name" />
</ui-form-control>
<div class="control-row">
<ui-form-control label="Menge" requiredMark="*">
<input uiInput formControlName="quantity" />
<input tabindex="0" uiInput formControlName="quantity" />
</ui-form-control>
<ui-form-control class="datepicker" label="vsl. Lieferdatum">
<ui-form-control class="datepicker" label="vsl. Lieferdatum" requiredMark="*">
<button
tabindex="-1"
class="date-btn"
type="button"
[class.content-selected]="!!(estimatedShippingDate$ | async)"
@@ -57,22 +60,22 @@
</ui-form-control>
</div>
<ui-form-control label="Autor">
<input uiInput formControlName="contributors" />
<input tabindex="0" uiInput formControlName="contributors" />
</ui-form-control>
<ui-form-control label="Verlag">
<input uiInput formControlName="manufacturer" />
<input tabindex="0" uiInput formControlName="manufacturer" />
</ui-form-control>
<ui-form-control label="Lieferant" requiredMark="*">
<ui-select formControlName="supplier">
<ui-select tabindex="-1" formControlName="supplier">
<ui-select-option *ngFor="let supplier of suppliers$ | async" [label]="supplier.name" [value]="supplier.id"></ui-select-option>
</ui-select>
</ui-form-control>
<div class="control-row">
<ui-form-control class="price" label="Stückpreis" [suffix]="price.value ? '' : ''" requiredMark="*">
<input #price uiInput formControlName="price" />
<input tabindex="0" #price uiInput formControlName="price" />
</ui-form-control>
<ui-form-control label="MwSt">
<ui-select formControlName="vat">
<ui-form-control label="MwSt" requiredMark="*">
<ui-select tabindex="-1" 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>

View File

@@ -97,10 +97,14 @@
@apply pl-0 !important;
}
::ng-deep page-checkout-dummy ui-searchbox .search-button {
@apply w-auto !important;
::ng-deep page-checkout-dummy ui-searchbox .hint {
@apply mr-10 !important;
}
::ng-deep page-checkout-dummy .price .suffix {
margin-top: 7.5px;
}
::ng-deep page-checkout-dummy .searchbox-control .input-wrapper {
@apply block;
}

View File

@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, Optional } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { ItemDTO } from '@swagger/cat';
@@ -9,6 +9,7 @@ import { UiFilterScanProvider } from '@ui/filter';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { Subject } from 'rxjs';
import { first, shareReplay, takeUntil } from 'rxjs/operators';
import { threadId } from 'worker_threads';
import { CheckoutDummyScanProvider } from './checkout-dummy-scan.provider';
import { CheckoutDummyStore } from './checkout-dummy.store';
@@ -48,11 +49,14 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
vats$ = this._store.vats$;
suppliers$ = this._store.suppliers$;
params: Params;
_onDestroy$ = new Subject<void>();
constructor(
@Inject(UiFilterScanProvider) @Optional() private scanProviders: UiFilterScanProvider[],
private _router: Router,
private _route: ActivatedRoute,
private _application: ApplicationService,
private _breadcrumb: BreadcrumbService,
private _fb: FormBuilder,
@@ -72,6 +76,23 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
this.populateForm(item);
}
});
const params = this._route.snapshot.queryParams;
if (Object.keys(params).length !== 0) {
this.params = params;
const item = {
ean: params.ean || '',
name: params.name || '',
quantity: params.quantity || '',
estimatedShippingDate: params.estimatedShippingDate || this._dateAdapter.today().toISOString(),
contributors: params.contributors || '',
manufacturer: params.manufacturer || '',
supplier: params.supplier || 5,
price: params.price || '',
vat: params.vat || '',
};
this.populateFormFromParams(item);
}
}
ngOnDestroy() {
@@ -90,12 +111,8 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
}
async search(ean: string) {
if (!ean || (ean.length !== 8 && ean.length !== 13)) {
this._store.message = 'EAN/ISBN Ungültig';
} else {
this._store.query = ean;
this._store.search();
}
this._store.query = ean;
this._store.search();
}
changeEstimatedShippingDate(date: Date) {
@@ -108,23 +125,23 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
initForm() {
const fb = this._fb;
this.control = fb.group({
ean: fb.control(''),
ean: fb.control('', Validators.pattern('^.{8,13}$')),
name: fb.control('', Validators.required),
quantity: fb.control('', [Validators.required, Validators.pattern('^[0-9]*$'), Validators.min(1)]),
estimatedShippingDate: fb.control(''),
estimatedShippingDate: fb.control(this._dateAdapter.today().toISOString(), Validators.required),
contributors: fb.control(''),
manufacturer: fb.control(''),
supplier: fb.control('', Validators.required),
supplier: fb.control(5, Validators.required), // 5 === Dummy
price: fb.control('', [Validators.required, Validators.pattern(/^\d+([\,]\d{1,2})?$/)]),
vat: fb.control(''),
vat: fb.control('', Validators.required),
});
this.changeEstimatedShippingDate(this._dateAdapter.today()); // Update View
}
populateForm(item: ItemDTO) {
this.control.get('name').setValue(item?.product?.name);
this.control.get('contributors').setValue(item?.product?.contributors);
this.control.get('manufacturer').setValue(item?.product?.manufacturer);
this.control.get('supplier').setValue(5); // Dummy
this.control
.get('price')
.setValue(
@@ -133,6 +150,19 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
this.control.get('vat').setValue(item?.catalogAvailability?.price?.vat?.vatType);
}
populateFormFromParams(item: any) {
this.control.get('name').setValue(item.name);
this.control.get('contributors').setValue(item.contributors);
this.control.get('manufacturer').setValue(item.manufacturer);
this.control.get('price').setValue(item.price ? String(item.price).replace('.', ',') : '');
this.control.get('vat').setValue(Number(item.vat));
this.control.get('quantity').setValue(item.quantity);
this.control.get('ean').setValue(item.ean);
this.control.get('supplier').setValue(Number(item.supplier));
this.control.get('estimatedShippingDate').setValue(item.estimatedShippingDate);
this.changeEstimatedShippingDate(new Date(item.estimatedShippingDate)); // Update View
}
clearForm(withEan?: boolean) {
if (withEan) {
this.control.reset();
@@ -143,6 +173,10 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
}
});
}
this.control.get('supplier').setValue(5);
this.control.get('estimatedShippingDate').setValue(this._dateAdapter.today().toISOString());
this.changeEstimatedShippingDate(this._dateAdapter.today());
this.control.markAsUntouched();
}
async nextItem() {
@@ -154,8 +188,13 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
this._store.createAddToCartItem(this.control, branch);
this._store.addToCart();
if (!this.params) {
await this._store.createAddToCartItem(this.control, branch);
this._store.addToCart(() => {});
} else {
await this._store.createAddToCartItem(this.control, branch, true);
this._store.updateCart(() => {});
}
} catch (error) {
this._modal.open({
title: 'Bestellung konnte nicht angelegt werden',
@@ -178,18 +217,34 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
this._store.createAddToCartItem(this.control, branch);
this._store.addToCart();
// Set filter for navigation to customer search if customer is not set
const customer = await this._store.customer$.pipe(first()).toPromise();
const customerFilter = await this._store.customerFilter$.pipe(first()).toPromise();
let filter: { [key: string]: string };
if (!customer) {
filter = customerFilter;
this._router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } });
if (!this.params) {
await this._store.createAddToCartItem(this.control, branch);
this._store.addToCart(async () => {
// Set filter for navigation to customer search if customer is not set
const customer = await this._store.customer$.pipe(first()).toPromise();
const customerFilter = await this._store.customerFilter$.pipe(first()).toPromise();
let filter: { [key: string]: string };
if (!customer) {
filter = customerFilter;
this._router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } });
} else {
this._router.navigate(['/cart', 'review']);
}
});
} else {
this._router.navigate(['/cart', 'review']);
await this._store.createAddToCartItem(this.control, branch, true);
this._store.updateCart(async () => {
// Set filter for navigation to customer search if customer is not set
const customer = await this._store.customer$.pipe(first()).toPromise();
const customerFilter = await this._store.customerFilter$.pipe(first()).toPromise();
let filter: { [key: string]: string };
if (!customer) {
filter = customerFilter;
this._router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } });
} else {
this._router.navigate(['/cart', 'review']);
}
});
}
} catch (error) {
this._modal.open({

View File

@@ -7,12 +7,14 @@ import { DomainCheckoutService } from '@domain/checkout';
import { DomainOmsService } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ItemDTO } from '@swagger/cat';
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO, PriceDTO, ProductDTO, PromotionDTO } from '@swagger/checkout';
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO, DestinationDTO, PriceDTO, ProductDTO, PromotionDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { first, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
interface CheckoutDummyState {
item: ItemDTO;
shoppingCartItemId: number;
addToCartItem: AddToShoppingCartDTO;
estimatedShippingDate: string;
message: string;
@@ -34,6 +36,18 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
readonly item$ = this.select((s) => s.item);
get shoppingCartItemId() {
return this.get((s) => s.shoppingCartItemId);
}
set shoppingCartItemId(shoppingCartItemId: number) {
if (this.shoppingCartItemId !== shoppingCartItemId) {
this.patchState({ shoppingCartItemId });
}
}
readonly shoppingCartItemId$ = this.select((s) => s.shoppingCartItemId);
get addToCartItem() {
return this.get((s) => s.addToCartItem);
}
@@ -101,7 +115,14 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
readonly estimatedShippingDate$ = this.select((s) => s.estimatedShippingDate);
readonly vats$ = this._omsService.getVATs();
readonly suppliers$ = this._availabilityService.getSuppliers();
readonly suppliers$ = this._availabilityService.getSuppliers().pipe(
map((suppliers) =>
suppliers.filter((supplier) => {
const displaySupplierIds = [2, 3, 4, 5, 6, 8, 10, 13, 15, 16];
return displaySupplierIds.find((id) => id === supplier.id);
})
)
);
readonly currentBranch$ = this._availabilityService.getCurrentBranch();
constructor(
@@ -114,6 +135,7 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
) {
super({
item: undefined,
shoppingCartItemId: undefined,
addToCartItem: undefined,
estimatedShippingDate: '',
query: '',
@@ -131,16 +153,16 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
tapResponse(
(res) => {
const item = res.result[0];
if (!item) {
if (!!item && item?.product?.format !== 'EB' && item?.product?.format !== 'DL') {
this.patchState({
item: undefined,
message: 'Keine Suchergebnisse',
item: res.result[0],
message: '',
fetching: false,
});
} else {
this.patchState({
item: res.result[0],
message: '',
item: undefined,
message: 'Keine Suchergebnisse',
fetching: false,
});
}
@@ -160,17 +182,18 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
)
);
addToCart = this.effect(($) =>
$.pipe(
addToCart = this.effect((cb$: Observable<Function>) =>
cb$.pipe(
tap((_) => this.patchState({ fetching: true })),
withLatestFrom(this.processId$, this.addToCartItem$),
switchMap(([_, processId, newItem]) =>
switchMap(([cb, processId, newItem]) =>
this.addToCartRequest(processId, newItem).pipe(
tapResponse(
(res) => {
this.patchState({
fetching: false,
});
cb?.call(undefined);
},
(error: Error) => {
this._modal.open({
@@ -187,6 +210,37 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
)
);
updateCart = this.effect((cb$: Observable<Function>) =>
cb$.pipe(
tap((_) => this.patchState({ fetching: true })),
withLatestFrom(this.processId$, this.addToCartItem$, this.shoppingCartItemId$),
switchMap(([cb, processId, newItem, shoppingCartItemId]) => {
const availability = newItem.availability;
const quantity = newItem.quantity;
const destination = newItem.destination;
return this.updateCartRequest({ processId, shoppingCartItemId, availability, quantity, destination }).pipe(
tapResponse(
(res) => {
this.patchState({
fetching: false,
});
cb?.call(undefined);
},
(error: Error) => {
this._modal.open({
title: 'Fehler beim Updaten des Warenkorbs',
content: UiErrorModalComponent,
data: error,
});
this.patchState({ fetching: false });
console.error('CheckoutDummyStore.updateCart()', error);
}
)
);
})
)
);
searchRequest(ean: string) {
return this._catalogService.searchByEans({ eans: [ean] });
}
@@ -195,9 +249,98 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
return this._checkoutService.addItemToShoppingCart({ processId, items: [newItem] });
}
createAddToCartItem(control: FormGroup, branch: BranchDTO) {
const quantity = control.get('quantity').value;
const price: PriceDTO = {
updateCartRequest({
processId,
shoppingCartItemId,
availability,
quantity,
destination,
}: {
processId: number;
shoppingCartItemId: number;
availability: AvailabilityDTO;
quantity: number;
destination: DestinationDTO;
}) {
return this._checkoutService.updateItemInShoppingCart({
processId,
shoppingCartItemId,
update: {
availability,
quantity,
destination,
},
});
}
async createAddToCartItem(control: FormGroup, branch: BranchDTO, update?: boolean) {
let item: ItemDTO;
const quantity = Number(control.get('quantity').value);
const price = this._createPriceDTO(control);
let promoPoints: number;
// Check if item exists or ean inside the control changed in the meantime
if (!!this.item && this.item.product.ean === control.get('ean').value) {
item = this.item;
promoPoints = await this._getPromoPoints({ itemId: item.id, quantity, price: price.value.value });
} else {
item = undefined;
}
const availability = this._createAvailabilityDTO({ price, control });
const product = this._createProductDTO({ item, control });
const newItem: AddToShoppingCartDTO = {
quantity,
availability,
product,
promotion: !!item ? { points: promoPoints } : undefined,
destination: {
data: { target: 1, targetBranch: { id: branch.id } },
},
};
if (update) {
const shoppingCart = await this._checkoutService
.getShoppingCart({ processId: this._application.activatedProcessId })
.pipe(first())
.toPromise();
const existingItem = shoppingCart?.items?.find(
(i) => i?.data?.product?.ean === i?.data?.product?.ean && i?.data?.features['orderType'] === 'Abholung'
);
this.patchState({ addToCartItem: newItem, shoppingCartItemId: existingItem?.id });
}
this.patchState({ addToCartItem: newItem });
}
private async _getPromoPoints({ itemId, quantity, price }: { itemId: number; quantity: number; price: number }) {
let points: number;
try {
points = await this._catalogService
.getPromotionPoints({
items: [
{
id: itemId,
quantity,
price,
},
],
})
.pipe(
first(),
map((response) => response.result.itemId)
)
.toPromise();
} catch (error) {
this._modal.open({
title: 'Fehler beim Abfragen der Lesepunkte',
content: UiErrorModalComponent,
data: error,
});
console.error('CheckoutDummyStore._getPromoPoints()', error);
}
return points;
}
private _createPriceDTO(control: FormGroup): PriceDTO {
return {
value: {
value: Number(String(control.get('price').value).replace(',', '.')),
currency: 'EUR',
@@ -206,7 +349,10 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
vatType: control.get('vat').value || null,
},
};
const availability: AvailabilityDTO = {
}
private _createAvailabilityDTO({ price, control }: { price: PriceDTO; control: FormGroup }): AvailabilityDTO {
return {
availabilityType: 1024,
supplier: {
id: control.get('supplier').value,
@@ -215,42 +361,24 @@ export class CheckoutDummyStore extends ComponentStore<CheckoutDummyState> {
estimatedShippingDate: control.get('estimatedShippingDate').value || null,
supplyChannel: 'MANUALLY',
};
}
let item: ItemDTO;
let product: ProductDTO;
let promotion: PromotionDTO;
// Check if item exists or ean inside the control changed in the meantime
if (!!this.item && this.item.product.ean === control.get('ean').value) {
item = this.item;
} else {
item = undefined;
}
if (item) {
// Dummy Anlage
product = {
catalogProductNumber: String(item.id),
...item.product,
};
promotion = { points: item.promoPoints };
} else {
// Manuelle Anlage
product = {
catalogProductNumber: '', // was soll passieren wenn manuelle anlage?
// ...item.product // was soll passieren wenn manuelle anlage?
};
// promotion = { points: item.promoPoints } // was soll passieren wenn manuelle anlage?
}
const newItem: AddToShoppingCartDTO = {
quantity,
availability,
product,
promotion,
destination: {
data: { target: 1, targetBranch: { id: branch.id } },
},
private _createProductDTO({ item, control }: { item?: ItemDTO; control: FormGroup }): ProductDTO {
const formValues: Partial<ProductDTO> = {
ean: control.get('ean').value,
name: control.get('name').value,
contributors: control.get('contributors').value,
manufacturer: control.get('manufacturer').value,
};
this.patchState({ addToCartItem: newItem });
return !!item
? {
catalogProductNumber: String(item.id),
...item.product,
...formValues,
}
: {
catalogProductNumber: '',
...formValues,
};
}
}

View File

@@ -79,7 +79,8 @@
"
></ui-icon>
<div class="label" [class.dummy]="group.orderType === 'Dummy'">
{{ group.orderType !== 'Dummy' ? group.orderType : 'manuelle Anlage / Dummy Bestellung' }}
{{ group.orderType !== 'Dummy' ? group.orderType : 'Manuelle Anlage / Dummy Bestellung' }}
<a *ngIf="group.orderType === 'Dummy'" class="cta-secondary" [routerLink]="['/cart', 'dummy']">Hinzufügen</a>
</div>
</div>
<ng-container *ngIf="group.orderType === 'Versand' || group.orderType === 'B2B-Versand' || group.orderType === 'DIG-Versand'">
@@ -145,15 +146,32 @@
</div>
<div class="product-quantity">
<ui-quantity-dropdown
*ngIf="group.orderType !== 'Dummy'"
;
else
quantityDummy
[ngModel]="item?.quantity"
(ngModelChange)="updateItemQuantity(item, $event)"
[showSpinner]="showQuantityControlSpinnerItemId === item.id"
>
</ui-quantity-dropdown>
<ng-template #quantityDummy>
{{ item?.quantity }}
</ng-template>
</div>
<div class="product-actions">
<button
*ngIf="group.orderType !== 'Download'"
*ngIf="group.orderType === 'Dummy'"
class="cta-edit"
(click)="changeDummyItem(item)"
[disabled]="showChangeButtonSpinnerItemId"
>
<ui-spinner [show]="showChangeButtonSpinnerItemId === item.id">
Ändern
</ui-spinner>
</button>
<button
*ngIf="group.orderType !== 'Download' && group.orderType !== 'Dummy'"
class="cta-edit"
(click)="changeItem(item)"
[disabled]="showChangeButtonSpinnerItemId"

View File

@@ -105,7 +105,12 @@ hr {
}
.dummy {
width: 450px;
@apply w-full flex justify-between items-center;
.cta-secondary {
@apply mt-0 pr-0;
text-decoration: none;
}
}
.icon-order-type {

View File

@@ -52,13 +52,12 @@ export class CheckoutReviewComponent implements OnInit {
// let index = grouped.findIndex((g) => g?.orderType === item?.features?.orderType && g.destination?.id === item?.destination?.id);
let index = grouped.findIndex((g) => g?.orderType === item?.features?.orderType);
let group = index !== -1 ? grouped[index] : undefined;
if (item?.availability?.supplyChannel === 'MANUALLY') {
group = { orderType: 'Dummy', destination: item.destination?.data, items: [] };
} else {
if (!group) {
group = { orderType: item.features.orderType, destination: item.destination?.data, items: [] };
}
}
group = {
orderType: item?.availability?.supplyChannel === 'MANUALLY' ? 'Dummy' : item.features.orderType,
destination: item.destination?.data,
items: [],
};
group.items = [...group.items, item]?.sort((a, b) => a.destination?.data?.targetBranch?.id - b.destination?.data?.targetBranch?.id);
if (index !== -1) {
@@ -91,15 +90,26 @@ export class CheckoutReviewComponent implements OnInit {
if (displayOrders.length === 0) {
return NEVER;
}
return this.domainCatalogService
.getPromotionPoints({
items: displayOrders.map((i) => ({
id: Number(i.product?.catalogProductNumber),
quantity: i.quantity,
price: i.unitPrice?.value?.value,
})),
const items = displayOrders
.map((i) => {
if (i?.product?.catalogProductNumber) {
return {
id: Number(i.product?.catalogProductNumber),
quantity: i.quantity,
price: i.unitPrice?.value?.value,
};
}
})
.pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)));
.filter((item) => item !== undefined);
if (items.length !== 0) {
return this.domainCatalogService
.getPromotionPoints({
items,
})
.pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)));
} else {
return NEVER;
}
})
);
@@ -155,6 +165,22 @@ export class CheckoutReviewComponent implements OnInit {
});
}
changeDummyItem(shoppingCartItem: ShoppingCartItemDTO) {
this.router.navigate(['/cart', 'dummy'], {
queryParams: {
price: shoppingCartItem?.availability?.price?.value?.value,
vat: shoppingCartItem?.availability?.price?.vat?.vatType,
supplier: shoppingCartItem?.availability?.supplier?.id,
estimatedShippingDate: shoppingCartItem?.estimatedShippingDate,
manufacturer: shoppingCartItem?.product?.manufacturer,
name: shoppingCartItem?.product?.name,
contributors: shoppingCartItem?.product?.contributors,
ean: shoppingCartItem?.product?.ean,
quantity: shoppingCartItem?.quantity,
},
});
}
async changeItem(shoppingCartItem: ShoppingCartItemDTO) {
this.showChangeButtonSpinnerItemId = shoppingCartItem.id;

View File

@@ -105,7 +105,9 @@
<div class="footer">
<div class="overview">
<span class="promotion-points">{{ totalItemCount$ | async }} Artikel | {{ totalReadingPoints$ | async }} Lesepunkte</span>
<span *ngIf="totalReadingPoints$ | async; let totalReadingPoints" class="promotion-points"
>{{ totalItemCount$ | async }} Artikel | {{ totalReadingPoints }} Lesepunkte</span
>
<div class="price-wrapper">
<div class="total-price">Gesamtsumme {{ totalPrice$ | async | currency: ' ' }} {{ totalPriceCurrency$ | async }}</div>

View File

@@ -140,12 +140,12 @@
box-shadow: 0px -2px 24px 0px #dce2e9;
.overview {
@apply flex flex-row items-center justify-between;
@apply flex flex-row items-center justify-end;
width: 95%;
}
.promotion-points {
@apply text-ucla-blue text-regular font-bold;
@apply text-ucla-blue text-regular font-bold flex-grow;
}
.total-price {

View File

@@ -11,6 +11,7 @@ import { DisplayOrderItemDTO } from '@swagger/oms';
import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { DomainPrinterService } from '@domain/printer';
import { NEVER } from 'rxjs';
@Component({
selector: 'page-checkout-summary',
@@ -31,19 +32,27 @@ export class CheckoutSummaryComponent {
);
totalReadingPoints$ = this.displayOrders$.pipe(
switchMap((displayOrders) =>
this.domainCatalogService
.getPromotionPoints({
items: displayOrders
.reduce<DisplayOrderItemDTO[]>((items, order) => [...items, ...order.items], [])
.map((i) => ({
switchMap((displayOrders) => {
const items = displayOrders
.reduce<DisplayOrderItemDTO[]>((items, order) => [...items, ...order.items], [])
.map((i) => {
if (i?.product?.catalogProductNumber) {
return {
id: Number(i.product?.catalogProductNumber),
quantity: i.quantity,
price: i.price?.value?.value,
})),
};
}
})
.pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)))
)
.filter((item) => item !== undefined);
if (items.length !== 0) {
return this.domainCatalogService
.getPromotionPoints({ items })
.pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)));
} else {
return NEVER;
}
})
);
totalPrice$ = this.displayOrders$.pipe(

View File

@@ -58,7 +58,7 @@
<div *ngSwitchCase="'Abholung'" class="order-category">
<ng-container *ngIf="hasDummyItems(orderItem.data); else abholung">
<div class="order-category-dummy">
<h2>manuelle Anlage / Dummy Bestellung</h2>
<h2>Manuelle Anlage / Dummy Bestellung</h2>
</div>
</ng-container>
<ng-template #abholung>

View File

@@ -1,7 +1,7 @@
<div class="input-wrapper" [class.empty]="!ngControl?.value" [class.focused]="uiControl?.focused | async">
<!-- suffix and prefix order changed due to flex-row-reverse -->
<span class="suffix">{{ suffix }}</span>
<ng-content select="input, ui-select, ui-datepicker, button"></ng-content>
<ng-content select="input, ui-select, ui-datepicker, button, ui-searchbox"></ng-content>
<span class="prefix">{{ prefix }}</span>
<label *ngIf="label" [for]="uiControl?.id">{{ label }}{{ requiredMark }}</label>
</div>

View File

@@ -7,7 +7,8 @@
[placeholder]="placeholder"
[(ngModel)]="query"
(ngModelChange)="setQuery($event, true, true)"
(focus)="clearHint()"
(focus)="clearHint(); focused.emit(true)"
(blur)="focused.emit(false)"
(keyup)="onKeyup($event)"
/>
<div *ngIf="showHint" class="hint" (click)="focus()">{{ hint }}</div>

View File

@@ -17,6 +17,7 @@ import {
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { UiAutocompleteComponent } from '@ui/autocomplete';
import { UiFormControlDirective } from '@ui/form-control';
import { containsElement } from '@utils/common';
import { NativeContainerService } from 'native-container';
import { Subscription } from 'rxjs';
@@ -31,8 +32,10 @@ import { UiSearchboxScanProvider } from './providers';
providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => UiSearchboxNextComponent), multi: true }],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UiSearchboxNextComponent implements AfterViewInit, OnDestroy, AfterContentInit, ControlValueAccessor {
export class UiSearchboxNextComponent extends UiFormControlDirective<any>
implements AfterViewInit, OnDestroy, AfterContentInit, ControlValueAccessor {
disabled: boolean;
type = 'text';
@ViewChild('input', { read: ElementRef, static: true })
input: ElementRef;
@@ -73,6 +76,14 @@ export class UiSearchboxNextComponent implements AfterViewInit, OnDestroy, After
@Input()
autocompleteValueSelector: (item: any) => string = (item: any) => item;
get valueEmpty(): boolean {
return !!this.query;
}
clear(): void {
this.setQuery('');
}
@HostBinding('class.autocomplete-opend')
get autocompleteOpen() {
return this.autocomplete?.opend;
@@ -100,7 +111,9 @@ export class UiSearchboxNextComponent implements AfterViewInit, OnDestroy, After
private nativeContainer: NativeContainerService,
private cdr: ChangeDetectorRef,
private elementRef: ElementRef<HTMLElement>
) {}
) {
super();
}
writeValue(obj: any): void {
this.setQuery(obj, false);
@@ -171,6 +184,7 @@ export class UiSearchboxNextComponent implements AfterViewInit, OnDestroy, After
clearHint() {
this.hint = '';
this.focused.emit(true);
this.cdr.markForCheck();
}
@@ -202,4 +216,11 @@ export class UiSearchboxNextComponent implements AfterViewInit, OnDestroy, After
this.autocomplete?.close();
}
}
@HostListener('focusout', ['$event'])
onBlur() {
this.onTouched();
this.focused.emit(false);
this.cdr.markForCheck();
}
}