mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
committed by
Lorenz Hilpert
parent
a4a39f643c
commit
4c95bb8354
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user