Compare commits

..

1 Commits

Author SHA1 Message Date
Nino
17a63da1c6 #4537 Relocated Component Store provision 2023-12-21 16:08:38 +01:00
183 changed files with 795 additions and 1567 deletions

View File

@@ -458,6 +458,10 @@ export class DomainAvailabilityService {
return [2, 32, 256, 1024, 2048, 4096].some((code) => availability?.availabilityType === code);
}
private _priceIsEmpty(price: PriceDTO) {
return isEmpty(price?.value) || isEmpty(price?.vat);
}
private _mapToTakeAwayAvailability({
response,
supplier,
@@ -478,7 +482,7 @@ export class DomainAvailabilityService {
inStock: inStock,
supplierSSC: quantity <= inStock ? '999' : '',
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
price: stockInfo?.retailPrice ?? price, // #4553 Es soll nun immer der retailPrice aus der InStock Abfrage verwendet werden, egal ob "price" empty ist oder nicht
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

@@ -1021,11 +1021,7 @@ export class DomainCheckoutService {
//#region Common
// Fix für Ticket #4619 Versand Artikel im Warenkob -> keine Änderung bei Kundendaten erfassen
// Auskommentiert, da dieser Aufruf oftmals mit gleichen Parametern aufgerufen wird (ohne ausgewählten Kunden nur ein leeres Objekt bei customerFeatures)
// memorize macht keinen deepCompare von Objekten und denkt hier, dass immer der gleiche Return Wert zurückkommt, allerdings ist das hier oft nicht der Fall
// und der Decorator memorized dann fälschlicherweise
// @memorize()
@memorize()
canSetCustomer({
processId,
customerFeatures,

View File

@@ -1,60 +1,37 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { DomainPrinterService, Printer } from '@domain/printer';
import { DomainPrinterService } from '@domain/printer';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { UiModalService } from '@ui/modal';
import { NativeContainerService } from 'native-container';
import { OrderItemsContext } from './order-items.context';
import { EnvironmentService } from '@core/environment';
@Injectable()
export class PrintCompartmentLabelActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private domainPrinterService: DomainPrinterService,
private nativeContainerService: NativeContainerService,
private _environmentSerivce: EnvironmentService
private nativeContainerService: NativeContainerService
) {
super('PRINT_COMPARTMENTLABEL');
}
printCompartmentLabelHelper(printer: string, orderItemSubsetIds: number[]) {
return this.domainPrinterService
.printCompartmentLabel({
printer,
orderItemSubsetIds,
})
.toPromise();
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
const printerList = await this.domainPrinterService.getAvailableLabelPrinters().toPromise();
let printer: Printer;
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: (printer) =>
this.domainPrinterService
.printCompartmentLabel({ printer, orderItemSubsetIds: data.items.map((item) => item.orderItemSubsetId) })
.toPromise(),
} as PrintModalData,
})
.afterClosed$.toPromise();
if (Array.isArray(printerList)) {
printer = printerList.find((printer) => printer.selected === true);
}
if (!printer || this._environmentSerivce.matchTablet()) {
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this._environmentSerivce.matchTablet(),
printerType: 'Label',
print: (printer) =>
this.printCompartmentLabelHelper(
printer,
data.items.map((item) => item.orderItemSubsetId)
),
} as PrintModalData,
})
.afterClosed$.toPromise();
} else {
await this.printCompartmentLabelHelper(
printer.key,
data.items.map((item) => item.orderItemSubsetId)
);
}
return data;
}
}

View File

@@ -6,60 +6,45 @@ import { UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { groupBy } from '@ui/common';
import { NativeContainerService } from 'native-container';
import { ReceiptDTO } from '@swagger/oms';
import { EnvironmentService } from '@core/environment';
@Injectable()
export class PrintShippingNoteActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private domainPrinterService: DomainPrinterService,
private nativeContainerService: NativeContainerService,
private _environmentSerivce: EnvironmentService
private nativeContainerService: NativeContainerService
) {
super('PRINT_SHIPPINGNOTE');
}
async printShippingNoteHelper(printer: string, receipts: ReceiptDTO[]) {
try {
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.domainPrinterService.printShippingNote({ printer, receipts: group?.items?.map((r) => r?.id) }).toPromise();
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
const printerList = await this.domainPrinterService.getAvailableLabelPrinters().toPromise();
const receipts = data?.receipts?.filter((r) => r?.receiptType & 1);
let printer: Printer;
if (Array.isArray(printerList)) {
printer = printerList.find((printer) => printer.selected === true);
}
if (!printer || this._environmentSerivce.matchTablet()) {
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => await this.printShippingNoteHelper(printer, receipts),
} as PrintModalData,
})
.afterClosed$.toPromise();
} else {
await this.printShippingNoteHelper(printer.key, receipts);
}
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => {
try {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 1);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.domainPrinterService.printShippingNote({ printer, receipts: group?.items?.map((r) => r?.id) }).toPromise();
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
},
} as PrintModalData,
})
.afterClosed$.toPromise();
return data;
}

View File

@@ -35,7 +35,7 @@ export class PickupShelfOutService extends PickupShelfIOService {
);
}
const { orderdate, supplier_id } = args.filter?.getQueryToken()?.filter ?? {};
const { orderdate } = args.filter?.getQueryToken()?.filter ?? {};
return this._abholfachService.AbholfachWarenausgabe({
input: {
@@ -45,7 +45,6 @@ export class PickupShelfOutService extends PickupShelfIOService {
archive: String(true),
all_branches: String(true),
orderdate,
supplier_id,
},
});
}

View File

@@ -2,26 +2,23 @@ import { Injectable } from '@angular/core';
import { Logger, LogLevel } from '@core/logger';
import { Store } from '@ngrx/store';
import { UserStateService } from '@swagger/isa';
import { debounceTime, switchMap, takeUntil } from 'rxjs/operators';
import { debounceTime, switchMap } from 'rxjs/operators';
import { RootState } from './root.state';
import packageInfo from 'package';
import { environment } from '../../environments/environment';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class RootStateService {
static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE';
private _cancelSave = new Subject<void>();
constructor(private readonly _userStateService: UserStateService, private _logger: Logger, private _store: Store) {
if (!environment.production) {
console.log('Die UserState kann in der Konsole mit der Funktion "clearUserState()" geleert werden.');
}
window['clearUserState'] = () => {
this.clear();
};
window['clearUserState'] = () => {
this.clear();
};
}
}
async init() {
@@ -34,8 +31,7 @@ export class RootStateService {
this._store
.select((state) => state)
.pipe(
takeUntil(this._cancelSave),
debounceTime(1000),
debounceTime(500),
switchMap((state) => {
const raw = JSON.stringify({ ...state, version: packageInfo.version });
RootStateService.SaveToLocalStorageRaw(raw);
@@ -68,17 +64,13 @@ export class RootStateService {
return false;
}
async clear() {
try {
this._cancelSave.next();
await this._userStateService.UserStateResetUserState().toPromise();
await new Promise((resolve) => setTimeout(resolve, 100));
RootStateService.RemoveFromLocalStorage();
await new Promise((resolve) => setTimeout(resolve, 100));
window.location.reload();
} catch (error) {
this._logger.log(LogLevel.ERROR, error);
}
clear() {
this._userStateService
.UserStateResetUserState()
.toPromise()
.catch((error) => this._logger.log(LogLevel.ERROR, error));
RootStateService.RemoveFromLocalStorage();
window.location.reload();
}
static SaveToLocalStorage(state: RootState) {

View File

@@ -280,11 +280,6 @@
"name": "apps",
"data": "M226-160q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19ZM226-414q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19ZM226-668q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Zm254 0q-28 0-47-19t-19-47q0-28 19-47t47-19q28 0 47 19t19 47q0 28-19 47t-47 19Z",
"viewBox": "0 -960 960 960"
},
{
"name": "gift",
"data": "M2 21V10H0V4H5.2C5.11667 3.85 5.0625 3.69167 5.0375 3.525C5.0125 3.35833 5 3.18333 5 3C5 2.16667 5.29167 1.45833 5.875 0.875C6.45833 0.291667 7.16667 0 8 0C8.38333 0 8.74167 0.0708333 9.075 0.2125C9.40833 0.354167 9.71667 0.55 10 0.8C10.2833 0.533333 10.5917 0.333333 10.925 0.2C11.2583 0.0666667 11.6167 0 12 0C12.8333 0 13.5417 0.291667 14.125 0.875C14.7083 1.45833 15 2.16667 15 3C15 3.18333 14.9833 3.35417 14.95 3.5125C14.9167 3.67083 14.8667 3.83333 14.8 4H20V10H18V21H2ZM12 2C11.7167 2 11.4792 2.09583 11.2875 2.2875C11.0958 2.47917 11 2.71667 11 3C11 3.28333 11.0958 3.52083 11.2875 3.7125C11.4792 3.90417 11.7167 4 12 4C12.2833 4 12.5208 3.90417 12.7125 3.7125C12.9042 3.52083 13 3.28333 13 3C13 2.71667 12.9042 2.47917 12.7125 2.2875C12.5208 2.09583 12.2833 2 12 2ZM7 3C7 3.28333 7.09583 3.52083 7.2875 3.7125C7.47917 3.90417 7.71667 4 8 4C8.28333 4 8.52083 3.90417 8.7125 3.7125C8.90417 3.52083 9 3.28333 9 3C9 2.71667 8.90417 2.47917 8.7125 2.2875C8.52083 2.09583 8.28333 2 8 2C7.71667 2 7.47917 2.09583 7.2875 2.2875C7.09583 2.47917 7 2.71667 7 3ZM2 6V8H9V6H2ZM9 19V10H4V19H9ZM11 19H16V10H11V19ZM18 8V6H11V8H18Z",
"viewBox": "0 0 20 21"
}
],

View File

@@ -1,5 +0,0 @@
<svg width="48" height="51" viewBox="0 0 48 51" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4.47368C8 2.01878 9.99009 0 12.445 0L43.555 0C46.0099 0 48 2.01878 48 4.47368H8Z" fill="#172062"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.445C0 1.99009 1.99009 0 4.445 0L42.7807 0V43.4808C42.7807 46.7878 39.2981 48.9368 36.3423 47.4537L23.376 40.948C22.1212 40.3183 20.6426 40.3186 19.3879 40.9486L6.4397 47.4505C3.48377 48.9348 0 46.7859 0 43.4782L0 4.445Z" fill="#0556B4"/>
<rect x="19" y="19" width="18" height="17" fill="#0556B4"/>
</svg>

Before

Width:  |  Height:  |  Size: 606 B

View File

@@ -75,12 +75,6 @@
"licence": {
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -74,11 +74,5 @@
"licence": {
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -76,11 +76,5 @@
"licence": {
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -75,11 +75,5 @@
"licence": {
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -75,11 +75,5 @@
"licence": {
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -76,11 +76,5 @@
"licence": {
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -10,7 +10,7 @@
<div class="page-price-update-item__item-card p-5 h-[212px] bg-white">
<div class="page-price-update-item__item-thumbnail text-center mr-4 w-[47px] h-[73px]">
<img
class="page-price-update-item__item-image w-[47px] max-h-[73px]"
class="page-price-update-item__item-image w-[47px] h-[73px]"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[src]="productImage"

View File

@@ -1,7 +1,7 @@
<div class="page-article-details__wrapper">
<div #detailsContainer class="page-article-details__container px-5" *ngIf="store.item$ | async; let item">
<div class="page-article-details__product-details mb-3">
<div class="page-article-details__product-bookmark flex fixed justify-self-end">
<div class="page-article-details__product-bookmark justify-self-end">
<div *ngIf="showArchivBadge$ | async" class="archiv-badge">
<button [uiOverlayTrigger]="archivTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
<img src="/assets/images/bookmark_benachrichtigung_archiv.svg" alt="Archiv Badge" />
@@ -30,9 +30,9 @@
</div>
<div *ngIf="showPromotionBadge$ | async" class="promotion-badge">
<button [uiOverlayTrigger]="promotionTooltip" class="p-0 m-0 outline-none border-none bg-transparent relative -top-[0.3125rem]">
<shared-icon-badge icon="gift" alt="Prämienkatalog Badge"></shared-icon-badge>
<ui-icon-badge icon="gift" alt="Prämienkatalog Badge"></ui-icon-badge>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #promotionTooltip [closeable]="true">
Der Artikel ist als Prämie für {{ promotionPoints$ | async }} Punkte erhältlich.
Dieser Artikel befindet sich im Prämienkatalog.
</ui-tooltip>
</button>
</div>

View File

@@ -76,12 +76,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
showSubscriptionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'PFO')));
hasPromotionFeature$ = this.store.item$.pipe(map((item) => !!item?.features?.find((i) => i.key === 'Promotion')));
promotionPoints$ = this.store.item$.pipe(map((item) => item?.redemptionPoints));
showPromotionBadge$ = combineLatest([this.hasPromotionFeature$, this.promotionPoints$]).pipe(
map(([hasPromotionFeature, promotionPoints]) => hasPromotionFeature && promotionPoints > 0)
);
showPromotionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'Promotion')));
showArchivBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'ARC')));
@@ -343,7 +338,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
const item = await this.store.item$.pipe(first()).toPromise();
const modal = this.uiModal.open<BranchDTO>({
content: ModalAvailabilitiesComponent,
title: 'Bestände in anderen Filialen',
title: 'Weitere Verfügbarkeiten',
data: {
item,
},

View File

@@ -13,7 +13,6 @@ import { UiCommonModule } from '@ui/common';
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { IconModule } from '@shared/components/icon';
import { ArticleDetailsTextComponent } from './article-details-text/article-details-text.component';
import { IconBadgeComponent } from 'apps/shared/components/icon/src/lib/badge/icon-badge.component';
@NgModule({
imports: [
@@ -29,7 +28,6 @@ import { IconBadgeComponent } from 'apps/shared/components/icon/src/lib/badge/ic
PipesModule,
OrderDeadlinePipeModule,
ArticleDetailsTextComponent,
IconBadgeComponent,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],

View File

@@ -9,7 +9,7 @@
<p>Neben dem Titel "{{ item.product?.name }}" gibt es noch andere Artikel, die Sie interessieren könnten.</p>
<div class="articles">
<span class="label mb-2">
<span class="label">
<ui-icon icon="recommendation" size="20px"></ui-icon>
Artikel
</span>
@@ -28,10 +28,9 @@
(click)="close.emit()"
>
<img [src]="recommendation.product?.ean | productImage: 195:315:true" alt="product-image" />
<div class="flex flex-col">
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
</div>
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
</a>
</ui-slider>
</ng-container>

View File

@@ -29,12 +29,12 @@ p {
}
.article {
@apply flex flex-col mr-7 mt-4 no-underline text-black h-full min-w-[11rem] justify-between;
@apply flex flex-col mr-7 mt-4 no-underline text-black;
img {
@apply rounded-xl;
max-height: 19.6875rem;
max-width: 11rem;
height: 315px;
max-width: 195px;
box-shadow: 0 0 15px #949393;
}

View File

@@ -5,7 +5,7 @@
>
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
<img
class="page-search-result-item__item-image w-[3.125rem] max-h-[4.9375rem]"
class="page-search-result-item__item-image w-[3.125rem] h-[4.9375rem]"
loading="lazy"
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
[src]="thumbnailUrl"

View File

@@ -40,9 +40,8 @@
<div class="page-search-results__order-by mb-[0.125rem]" [class.page-search-results__order-by-primary]="primaryOutletActive$ | async">
<shared-order-by-filter
*ngIf="filter$ | async; let filter"
[orderBy]="filter?.orderBy"
(selectedOrderByChange)="search({ filter, clear: true, orderBy: true }); updateBreadcrumbs()"
[orderBy]="(filter$ | async)?.orderBy"
(selectedOrderByChange)="search({ clear: true, orderBy: true }); updateBreadcrumbs()"
>
</shared-order-by-filter>
</div>

View File

@@ -11,7 +11,7 @@ import {
AfterViewInit,
inject,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { EnvironmentService } from '@core/environment';
@@ -20,7 +20,7 @@ import { ItemDTO } from '@swagger/cat';
import { AddToShoppingCartDTO } from '@swagger/checkout';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { CacheService } from 'apps/core/cache/src/public-api';
import { isEqual } from 'lodash';
import { debounce, isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
import { debounceTime, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
import { ArticleSearchService } from '../article-search.store';
@@ -115,8 +115,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
private _checkoutService: DomainCheckoutService,
private _environment: EnvironmentService,
private _navigationService: ProductCatalogNavigationService,
private _availability: DomainAvailabilityService,
private _router: Router
private _availability: DomainAvailabilityService
) {}
ngOnInit() {
@@ -155,7 +154,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
const cleanQueryParams = this.cleanupQueryParams(queryParams);
if (this.route.outlet === 'primary' || processChanged) {
if (this.route.outlet === 'primary' && processChanged) {
this.scrollToItem(this._getScrollIndexFromCache());
}
@@ -199,9 +198,9 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
.subscribe(async ([searchCompleted, processId]) => {
const params = searchCompleted.state.filter.getQueryParams();
if (searchCompleted.state.searchState === '') {
// Ticket 4524 Korrekte Navigation bei orderBy mit aktuellen queryParams
// Keine Navigation bei OrderBy
if (searchCompleted?.orderBy) {
return await this._router.navigate([], { queryParams: params });
return;
}
// Navigation auf Details bzw. Results | Details wenn hits 1
@@ -209,6 +208,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
if (searchCompleted.state.hits === 1) {
const item = searchCompleted.state.items.find((f) => f);
const ean = this.route?.snapshot?.params?.ean;
const itemId = this.route?.snapshot?.params?.id ? Number(this.route?.snapshot?.params?.id) : item.id; // Nicht zum ersten Item der Liste springen wenn bereits eines selektiert ist
// Navigation from Cart uses ean
if (!!ean) {
@@ -223,7 +223,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
await this._navigationService
.getArticleDetailsPath({
processId,
itemId: item.id,
itemId,
extras: { queryParams: params },
})
.navigate();

View File

@@ -1,5 +1,3 @@
import { ShoppingCartItemDTO } from '@swagger/checkout';
export interface CheckoutDummyData extends ShoppingCartItemDTO {
changeDataFromCart?: boolean;
}
export interface CheckoutDummyData extends ShoppingCartItemDTO {}

View File

@@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { ItemDTO } from '@swagger/cat';
import { DateAdapter } from '@ui/common';
@@ -8,8 +9,7 @@ import { Subject } from 'rxjs';
import { first, shareReplay, takeUntil } from 'rxjs/operators';
import { CheckoutDummyData } from './checkout-dummy-data';
import { CheckoutDummyStore } from './checkout-dummy.store';
import { CheckoutNavigationService, CustomerSearchNavigation } from '@shared/services';
import { Router } from '@angular/router';
import { CheckoutNavigationService } from '@shared/services';
@Component({
selector: 'page-checkout-dummy',
@@ -38,15 +38,14 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
_onDestroy$ = new Subject<void>();
constructor(
private _router: Router,
private _fb: UntypedFormBuilder,
private _dateAdapter: DateAdapter,
private _modal: UiModalService,
private _store: CheckoutDummyStore,
private _ref: UiModalRef<any, CheckoutDummyData>,
private readonly _applicationService: ApplicationService,
private readonly _checkoutNavigationService: CheckoutNavigationService,
private readonly _customerNavigationService: CustomerSearchNavigation,
private _router: Router
private readonly _checkoutNavigationService: CheckoutNavigationService
) {}
ngOnInit() {
@@ -59,7 +58,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
}
});
if (this.hasShoppingCartItemToUpdate()) {
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
const data = this._ref?.data;
this._store.patchState({ shoppingCartItem: data });
this.populateFormFromModalData(data);
@@ -150,14 +149,6 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
this.control.markAsUntouched();
}
hasShoppingCartItemToUpdate(): boolean {
const hasShoppingCartItem = !!this._ref.data?.id;
if (!!this._ref?.data && hasShoppingCartItem) {
return true;
}
return false;
}
async nextItem() {
if (this.control.invalid || this.control.disabled) {
this.control.enable();
@@ -167,7 +158,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
if (this.hasShoppingCartItemToUpdate()) {
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
await this._store.createAddToCartItem(this.control, branch, true);
this._store.updateCart(() => {});
} else {
@@ -196,17 +187,16 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
if (this.hasShoppingCartItemToUpdate()) {
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
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 && !this._ref?.data?.changeDataFromCart) {
if (!customer) {
filter = customerFilter;
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
await this._router.navigate(path, {
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
queryParams: { customertype: filter.customertype },
});
} else {
@@ -221,10 +211,9 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
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 && !this._ref?.data?.changeDataFromCart) {
if (!customer) {
filter = customerFilter;
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
await this._router.navigate(path, {
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
queryParams: { customertype: filter.customertype },
});
} else {

View File

@@ -14,7 +14,7 @@
<div class="btn-wrapper">
<a class="cta-primary" [routerLink]="productSearchBasePath">Artikel suchen</a>
<button class="cta-secondary" (click)="openDummyModal({})">Neuanlage</button>
<button class="cta-secondary" (click)="openDummyModal()">Neuanlage</button>
</div>
</div>
</div>
@@ -54,7 +54,7 @@
<button
*ngIf="group.orderType === 'Dummy'"
class="text-brand border-none font-bold text-p1 outline-none pl-4"
(click)="openDummyModal({ changeDataFromCart: true })"
(click)="openDummyModal()"
>
Hinzufügen
</button>
@@ -81,11 +81,8 @@
*ngIf="group?.orderType !== undefined && (item.features?.orderType === 'Abholung' || item.features?.orderType === 'Rücklage')"
>
<ng-container *ngIf="item?.destination?.data?.targetBranch?.data; let targetBranch">
<ng-container *ngIf="i === 0 || checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)">
<div
class="flex flex-row items-center px-5 pt-0 pb-[0.875rem] -mt-2 bg-[#F5F7FA]"
[class.multiple-destinations]="checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)"
>
<ng-container *ngIf="i === 0 || targetBranch.id !== group.items[i - 1].destination?.data?.targetBranch?.data.id">
<div class="flex flex-row items-center px-5 pt-0 pb-[0.875rem] -mt-2 bg-[#F5F7FA]">
<span class="branch-name">{{ targetBranch?.name }} | {{ targetBranch | branchAddress }}</span>
</div>
<hr />

View File

@@ -105,10 +105,6 @@ h1 {
}
}
.multiple-destinations {
@apply py-[0.875rem] mt-0;
}
.icon-order-type {
@apply text-black mr-2;
}

View File

@@ -14,7 +14,7 @@ import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
import { AvailabilityDTO, BranchDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
import { AvailabilityDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { PrintModalData, PrintModalComponent } from '@modal/printer';
import { delay, first, map, switchMap, takeUntil, tap } from 'rxjs/operators';
@@ -254,10 +254,6 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy, AfterViewInit
});
}
checkIfMultipleDestinationsForOrderTypeExist(targetBranch: BranchDTO, group: { items: ShoppingCartItemDTO[] }, i: number) {
return i === 0 ? false : targetBranch.id !== group.items[i - 1].destination?.data?.targetBranch?.data.id;
}
async refreshAvailabilities() {
this.checkingOla$.next(true);
@@ -292,15 +288,15 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy, AfterViewInit
this._store.notificationsControl = undefined;
}
openDummyModal({ data, changeDataFromCart = false }: { data?: CheckoutDummyData; changeDataFromCart?: boolean }) {
openDummyModal(data?: CheckoutDummyData) {
this.uiModal.open({
content: CheckoutDummyComponent,
data: { ...data, changeDataFromCart },
data,
});
}
changeDummyItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
this.openDummyModal({ data: shoppingCartItem, changeDataFromCart: true });
this.openDummyModal(shoppingCartItem);
}
async changeItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {

View File

@@ -5,10 +5,14 @@
<ng-container *ngIf="buyer$ | async; let buyer">
<div *ngIf="!(showAddresses$ | async)" class="flex flex-row items-start justify-between p-5">
<div class="flex flex-row flex-wrap pr-4">
<ng-container *ngIf="getNameFromBuyer(buyer); let name">
<div class="mr-3">{{ name.label }}</div>
<div class="font-bold">{{ name.value }}</div>
<ng-container *ngIf="!!buyer?.lastName && !!buyer?.firstName; else organisation">
<div class="mr-3">Nachname, Vorname</div>
<div class="font-bold">{{ buyer?.lastName }}, {{ buyer?.firstName }}</div>
</ng-container>
<ng-template #organisation>
<div class="mr-3">Firmenname</div>
<div class="font-bold">{{ buyer?.organisation?.name }}</div>
</ng-template>
</div>
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { emailNotificationValidator, mobileNotificationValidator } from '@shared/components/notification-channel-control';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { combineLatest } from 'rxjs';
@@ -7,8 +7,7 @@ import { first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators
import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { Router } from '@angular/router';
import { BuyerDTO, NotificationChannel } from '@swagger/checkout';
import { CustomerSearchNavigation } from '@shared/services';
import { NotificationChannel } from '@swagger/checkout';
@Component({
selector: 'page-checkout-review-details',
@@ -17,8 +16,6 @@ import { CustomerSearchNavigation } from '@shared/services';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckoutReviewDetailsComponent implements OnInit {
customerNavigation = inject(CustomerSearchNavigation);
control: UntypedFormGroup;
customerFeatures$ = this._store.customerFeatures$;
@@ -26,6 +23,20 @@ export class CheckoutReviewDetailsComponent implements OnInit {
payer$ = this._store.payer$;
buyer$ = this._store.buyer$;
showAddresses$ = this._store.shoppingCartItems$.pipe(
takeUntil(this._store.orderCompleted),
withLatestFrom(this.customerFeatures$),
map(
([items, customerFeatures]) =>
items.some(
(item) =>
item.features?.orderType === 'Versand' ||
item.features?.orderType === 'B2B-Versand' ||
item.features?.orderType === 'DIG-Versand'
) || !!customerFeatures?.b2b
)
);
showNotificationChannels$ = combineLatest([this._store.shoppingCartItems$, this.payer$, this.buyer$]).pipe(
takeUntil(this._store.orderCompleted),
map(
@@ -54,22 +65,6 @@ export class CheckoutReviewDetailsComponent implements OnInit {
switchMap((processId) => this._domainCheckoutService.getShippingAddress({ processId }))
);
showAddresses$ = this._store.shoppingCartItems$.pipe(
takeUntil(this._store.orderCompleted),
withLatestFrom(this.customerFeatures$, this.payer$, this.shippingAddress$),
map(([items, customerFeatures, payer, shippingAddress]) => {
const hasShippingOrBillingAddresses = !!payer?.address || !!shippingAddress;
const hasShippingFeature = items.some(
(item) =>
item.features?.orderType === 'Versand' || item.features?.orderType === 'B2B-Versand' || item.features?.orderType === 'DIG-Versand'
);
const isB2bCustomer = !!customerFeatures?.b2b;
return hasShippingOrBillingAddresses && (hasShippingFeature || isB2bCustomer);
})
);
notificationChannelLoading$ = this._store.notificationChannelLoading$;
constructor(
@@ -123,20 +118,6 @@ export class CheckoutReviewDetailsComponent implements OnInit {
this._store.onNotificationChange(notificationChannels);
}
getNameFromBuyer(buyer: BuyerDTO): { value: string; label: string } {
if (buyer?.lastName && buyer?.firstName) {
return { value: `${buyer?.lastName}, ${buyer?.firstName}`, label: 'Nachname, Vorname' };
} else if (buyer?.lastName) {
return { value: buyer?.lastName, label: 'Nachname, Vorname' };
} else if (buyer?.firstName) {
return { value: buyer?.firstName, label: 'Nachname, Vorname' };
} else if (buyer?.organisation?.name) {
return { value: buyer?.organisation?.name, label: 'Firmenname' };
} else {
return;
}
}
async changeAddress() {
const processId = this._application.activatedProcessId;
const customer = await this._domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();
@@ -145,8 +126,7 @@ export class CheckoutReviewDetailsComponent implements OnInit {
return;
}
const customerId = customer.source;
await this.customerNavigation.navigateToDetails({ processId, customerId, customer: { customerNumber: customer.buyerNumber } });
// this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'search', `${customerId}`]);
this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'search', `${customerId}`]);
}
async navigateToCustomerSearch(processId: number) {

View File

@@ -17,7 +17,7 @@ button {
grid-area: item-thumbnail;
@apply mr-8 w-[3.75rem] h-[5.9375rem];
img {
@apply w-[3.75rem] max-h-[5.9375rem] rounded shadow-cta;
@apply w-[3.75rem] h-[5.9375rem] rounded shadow-cta;
}
}

View File

@@ -55,8 +55,6 @@
<span class="w-32">Vorgangs-ID</span>
<ng-container *ngIf="customer$ | async; let customer">
<a
data-which="Vorgangs-ID"
data-what="link"
*ngIf="customer$ | async; let customer"
class="font-bold text-[#0556B4] no-underline"
[routerLink]="['/kunde', processId, 'customer', 'search', customer?.id, 'orders', displayOrder.id]"
@@ -104,7 +102,7 @@
>
<div class="page-checkout-summary__items-thumbnail flex flex-row">
<a [routerLink]="getProductSearchDetailsPath(order?.product?.ean)" [queryParams]="getProductSearchDetailsQueryParams(order)">
<img class="w-[3.125rem] max-h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
<img class="w-[3.125rem] h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
</a>
</div>
@@ -228,12 +226,11 @@
<div class="absolute left-1/2 bottom-10 inline-grid grid-flow-col gap-4 justify-center transform -translate-x-1/2">
<button
*ifRole="'Store'"
[disabled]="isPrinting$ | async"
type="button"
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14 flex flex-row items-center justify-center print-button"
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
(click)="printOrderConfirmation()"
>
<ui-spinner class="min-h-4 min-w-4" [show]="isPrinting$ | async"> Bestellbestätigung drucken </ui-spinner>
Bestellbestätigung drucken
</button>
<button

View File

@@ -96,12 +96,6 @@ hr {
}
}
.print-button {
&:disabled {
@apply bg-inactive-branch border-solid border-inactive-branch text-white cursor-not-allowed;
}
}
.last {
@apply pb-5;
}

View File

@@ -16,6 +16,7 @@ import { DateAdapter } from '@ui/common';
import { CheckoutNavigationService, PickUpShelfOutNavigationService, ProductCatalogNavigationService } from '@shared/services';
import { EnvironmentService } from '@core/environment';
import { SendOrderConfirmationModalService } from '@shared/modals/send-order-confirmation-modal';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ToasterService } from '@shared/shell';
@Component({
@@ -135,8 +136,6 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
)
);
isPrinting$ = new BehaviorSubject(false);
totalPriceCurrency$ = this.displayOrders$.pipe(map((displayOrders) => displayOrders[0]?.items[0]?.price?.value?.currency));
containsDeliveryOrder$ = this.displayOrders$.pipe(
@@ -162,10 +161,6 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
return this._environmentService.matchDesktopLarge$;
}
get isTablet() {
return this._environmentService.matchTablet();
}
expanded: boolean[] = [];
constructor(
@@ -296,9 +291,22 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
if (takeNowOrders.length != 1) return;
try {
await this.router.navigate(this._shelfOutNavigationService.listRoute({ processId: Date.now() }).path, {
queryParams: { main_qs: takeNowOrders[0].orderNumber, filter_supplier_id: '16' },
});
for (const takeNowOrder of takeNowOrders) {
for (const orderItem of takeNowOrder.items.filter((item) => item.features?.orderType === 'Rücklage')) {
await this.omsService
.changeOrderStatus(takeNowOrder.id, orderItem.id, orderItem.subsetItems[0]?.id, {
processingStatus: 128,
})
.toPromise();
}
}
await this.router.navigate(
this._shelfOutNavigationService.detailRoute({
processId: Date.now(),
item: { orderId: takeNowOrders[0].id, orderNumber: takeNowOrders[0].orderNumber, processingStatus: 128 },
}).path
);
} catch (e) {
console.error(e);
}
@@ -311,57 +319,27 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
}
async printOrderConfirmation() {
this.isPrinting$.next(true);
const orders = await this.displayOrders$.pipe(first()).toPromise();
const selectedPrinter = await this.domainPrinterService
.getAvailableLabelPrinters()
.pipe(
first(),
map((printers) => {
if (Array.isArray(printers)) return printers.find((printer) => printer.selected === true);
})
)
.toPromise();
console.log(selectedPrinter);
if (!selectedPrinter || this.isTablet) {
await this.uiModal
.open({
content: PrintModalComponent,
data: {
printerType: 'Label',
printImmediately: !this.isTablet,
print: async (printer) => {
try {
const result = await this.domainPrinterService.printOrder({ orderIds: orders.map((o) => o.id), printer }).toPromise();
this._toaster.open({ type: 'success', message: 'Bestellbestätigung wurde gedruckt' });
return result;
} catch (error) {
this._toaster.open({ type: 'danger', message: 'Fehler beim Drucken der Bestellbestätigung' });
} finally {
this.isPrinting$.next(false);
}
},
} as PrintModalData,
config: {
panelClass: [],
showScrollbarY: false,
await this.uiModal
.open({
content: PrintModalComponent,
data: {
printerType: 'Label',
print: async (printer) => {
try {
const result = await this.domainPrinterService.printOrder({ orderIds: orders.map((o) => o.id), printer }).toPromise();
this._toaster.open({ type: 'success', message: 'Bestellbestätigung wurde gedruckt' });
return result;
} catch (error) {
this._toaster.open({ type: 'danger', message: 'Fehler beim Drucken der Bestellbestätigung' });
}
},
})
.afterClosed$.toPromise();
this.isPrinting$.next(false);
} else {
try {
const result = await this.domainPrinterService
.printOrder({ orderIds: orders.map((o) => o.id), printer: selectedPrinter.key })
.toPromise();
this._toaster.open({ type: 'success', message: 'Bestellbestätigung wurde gedruckt' });
return result;
} catch (error) {
this._toaster.open({ type: 'danger', message: 'Fehler beim Drucken der Bestellbestätigung' });
} finally {
this.isPrinting$.next(false);
}
}
} as PrintModalData,
config: {
panelClass: [],
showScrollbarY: false,
},
})
.afterClosed$.toPromise();
}
}

View File

@@ -140,19 +140,6 @@
</ng-container>
</ng-template>
</div>
<div class="page-customer-order-details-item__tracking-details" *ngIf="getOrderItemTrackingData(orderItem); let trackingData">
<div class="label">{{ trackingData.length > 1 ? 'Sendungsnummern' : 'Sendungsnummer' }}</div>
<ng-container *ngFor="let tracking of trackingData">
<ng-container *ngIf="tracking.trackingProvider === 'DHL' && !isNative; else noTrackingLink">
<a class="value text-[#0556B4]" [href]="getTrackingNumberLink(tracking.trackingNumber)" target="_blank"
>{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}</a
>
</ng-container>
<ng-template #noTrackingLink>
<p class="value">{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}</p>
</ng-template>
</ng-container>
</div>
<hr class="border-[#EDEFF0] border-t-2 my-4" />

View File

@@ -22,7 +22,7 @@ button {
.page-customer-order-details-item__thumbnail {
img {
@apply rounded shadow-cta w-[3.625rem] max-h-[5.9375rem];
@apply rounded shadow-cta w-[3.625rem] h-[5.9375rem];
}
}
@@ -55,18 +55,6 @@ button {
}
}
.page-customer-order-details-item__tracking-details {
@apply flex gap-x-7;
.label {
@apply w-[8.125rem];
}
.value {
@apply flex flex-row items-center font-bold;
}
}
.page-customer-order-details-item__comment {
textarea {
@apply w-full flex-grow rounded bg-[#EDEFF0] border-[#AEB7C1] border border-solid outline-none text-p2 p-4;

View File

@@ -18,7 +18,6 @@ import { isEqual } from 'lodash';
import { combineLatest, NEVER, Subject, Observable } from 'rxjs';
import { catchError, filter, first, map, switchMap, withLatestFrom } from 'rxjs/operators';
import { CustomerOrderDetailsStore } from '../customer-order-details.store';
import { EnvironmentService } from '@core/environment';
export interface CustomerOrderDetailsItemComponentState {
orderItem?: OrderItemListItemDTO;
@@ -143,16 +142,11 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
private _onDestroy$ = new Subject();
get isNative() {
return this._environment.isNative();
}
constructor(
private _store: CustomerOrderDetailsStore,
private _domainReceiptService: DomainReceiptService,
private _omsService: DomainOmsService,
private _cdr: ChangeDetectorRef,
private _environment: EnvironmentService
private _cdr: ChangeDetectorRef
) {
super({
more: false,
@@ -237,35 +231,6 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
return orderItems?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features?.orderType;
}
getOrderItemTrackingData(orderItemListItem: OrderItemListItemDTO): Array<{ trackingProvider: string; trackingNumber: string }> {
const orderItems = this.order?.items;
const completeTrackingInformation = orderItems
?.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)
?.data?.subsetItems?.find((subsetItem) => subsetItem.id === orderItemListItem.orderItemSubsetId)?.data?.trackingNumber;
if (!completeTrackingInformation) {
return;
}
// Beispielnummer: 'DHL: 124124' - Bei mehreren Tracking-Informationen muss noch ein Splitter eingebaut werden, je nach dem welcher Trenner verwendet wird
const trackingInformationPairs = completeTrackingInformation.split(':').map((obj) => obj.trim());
return this._trackingTransformationHelper(trackingInformationPairs);
}
// Macht aus einem String Array ein Array von Objekten mit den keys trackingProvider und trackingNumber
private _trackingTransformationHelper(trackingInformationPairs: string[]): Array<{ trackingProvider: string; trackingNumber: string }> {
return trackingInformationPairs.reduce((acc, current, index, array) => {
if (index % 2 === 0) {
acc.push({ trackingProvider: current, trackingNumber: array[index + 1] });
}
return acc;
}, [] as { trackingProvider: string; trackingNumber: string }[]);
}
getTrackingNumberLink(trackingNumber: string) {
return `https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${trackingNumber}`;
}
triggerResize() {
this.autosize.reset();
}

View File

@@ -345,7 +345,7 @@ export class CustomerOrderDetailsComponent implements OnInit, AfterViewInit, OnD
if (action.command.includes('ARRIVED')) {
navigateTo = await this.arrivedActionNavigation();
}
if (action.command.includes('PRINT_PRICEDIFFQRCODELABEL') || action.command.includes('BACKTOSTOCK')) {
if (action.command.includes('PRINT_PRICEDIFFQRCODELABEL')) {
navigateTo = 'main';
}

View File

@@ -7,7 +7,7 @@ import { Filter } from '@shared/components/filter';
import { BranchDTO, ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QuerySettingsDTO } from '@swagger/oms';
import { isResponseArgs } from '@utils/object';
import { Observable, Subject } from 'rxjs';
import { switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { switchMap, tap, withLatestFrom } from 'rxjs/operators';
export interface CustomerOrderSearchState {
defaultSettings?: QuerySettingsDTO;
@@ -125,8 +125,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
searchStarted = new Subject<{ clear?: boolean; silentReload?: boolean }>();
cancelSearch$ = new Subject<void>();
constructor(private _domainGoodsInService: DomainCustomerOrderService, private _cache: CacheService) {
super({
fetching: false,
@@ -194,11 +192,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
});
}
cancelSearchRequest() {
this.cancelSearch$.next();
this.patchState({ fetching: false, silentFetching: false });
}
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
options$.pipe(
tap((_) => {
@@ -245,7 +238,6 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
}
return this._domainGoodsInService.search(queryToken).pipe(
takeUntil(this.cancelSearch$),
tapResponse(
(res) => {
let _results: OrderItemListItemDTO[] = [];

View File

@@ -17,7 +17,10 @@ import { ApplicationService } from '@core/application';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
filter$ = this._customerOrderSearchStore.filter$.pipe(filter((f) => !!f));
filter$ = this._customerOrderSearchStore.filter$.pipe(
filter((f) => !!f),
take(1)
);
loading$ = this._customerOrderSearchStore.fetching$;

View File

@@ -12,7 +12,7 @@
>
<div class="page-customer-order-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
<img
class="page-customer-order-item__item-image w-[3.125rem] max-h-[4.9375rem]"
class="page-customer-order-item__item-image w-[3.125rem] h-[4.9375rem]"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[src]="productImage"

View File

@@ -170,9 +170,10 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
);
this._searchResultSubscription.add(
combineLatest([this.processId$, this._activatedRoute.queryParams])
this.processId$
.pipe(
debounceTime(150),
withLatestFrom(this._activatedRoute.queryParams),
switchMap(([processId, params]) =>
this._application.getSelectedBranch$(processId).pipe(map((selectedBranch) => ({ processId, params, selectedBranch })))
)
@@ -184,6 +185,16 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
if (processChanged) {
if (!!this._customerOrderSearchStore.processId && this._customerOrderSearchStore.filter instanceof Filter) {
const queryToken = {
...this._customerOrderSearchStore.filter?.getQueryParams(),
processId,
branchId: String(selectedBranch?.id),
};
this._customerOrderSearchStore.setCache({
queryToken,
hits: this._customerOrderSearchStore.hits,
results: this._customerOrderSearchStore.results,
});
await this.updateBreadcrumb(processId, this._customerOrderSearchStore.filter?.getQueryParams());
}
this._customerOrderSearchStore.patchState({ processId });
@@ -201,7 +212,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this._customerOrderSearchStore.filter.getQueryParams()))) {
this._customerOrderSearchStore.setQueryParams(params);
const queryToken = {
...this._customerOrderSearchStore.filter.getQueryParams(),
processId,
@@ -236,8 +246,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
const process = await this._application.getProcessById$(processId).pipe(first()).toPromise();
if (!!process) {
await this.createBreadcrumb(processId, params);
await this.updateBreadcrumb(processId, params);
await this.createBreadcrumb(processId, params);
}
if (this._activatedRoute?.outlet === 'primary') {
@@ -278,13 +288,13 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
...this.cleanupQueryParams(this._customerOrderSearchStore?.filter?.getQueryParams()),
main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
};
this._customerOrderSearchStore?.setQueryParams(queryParams);
})
);
this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
if (event instanceof NavigationStart) {
this.cacheResults();
this._addScrollPositionToCache();
}
});
@@ -325,6 +335,18 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
this._onDestroy$.complete();
this._searchResultSubscription.unsubscribe();
const queryToken = {
...this._customerOrderSearchStore.filter?.getQueryParams(),
processId: this._customerOrderSearchStore.processId,
branchId: String(this._customerOrderSearchStore.selectedBranch?.id),
};
this._customerOrderSearchStore.setCache({
queryToken,
hits: this._customerOrderSearchStore.hits,
results: this._customerOrderSearchStore.results,
});
await this.updateBreadcrumb(this._customerOrderSearchStore.processId, this._customerOrderSearchStore.filter?.getQueryParams());
}
@@ -342,6 +364,25 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
return clean;
}
async removeBreadcrumbs(processId: number) {
const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'edit']).pipe(first()).toPromise();
const historyCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'history'])
.pipe(first())
.toPromise();
editCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
historyCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
await this.removeDetailsBreadcrumb(processId);
}
async removeDetailsBreadcrumb(processId: number) {
const detailsCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'details'])
@@ -353,19 +394,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
});
}
async createMainBreadcrumb(processId: number, params: Record<string, string>) {
await this._breadcrumb.addBreadcrumbIfNotExists({
key: processId,
name: 'Kundenbestellung',
path: this._navigationService.getCustomerOrdersBasePath(processId).path,
params,
tags: ['customer-order', 'main', 'filter'],
section: 'customer',
});
}
async createBreadcrumb(processId: number, params: Record<string, string>) {
await this.createMainBreadcrumb(processId, params);
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
key: processId,
name: this.getBreadcrumbName(params),
@@ -394,20 +423,6 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
}
}
cacheResults() {
const queryToken = {
...this._customerOrderSearchStore.filter?.getQueryParams(),
processId: this._customerOrderSearchStore.processId,
branchId: String(this._customerOrderSearchStore.selectedBranch?.id),
};
this._customerOrderSearchStore.setCache({
queryToken,
hits: this._customerOrderSearchStore.hits,
results: this._customerOrderSearchStore.results,
});
}
getBreadcrumbName(params: Record<string, string>) {
const input = params?.main_qs;
@@ -427,8 +442,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
search({ filter, clear = false }: { filter?: Filter; clear?: boolean }) {
if (!!filter) {
this.sharedFilterInputGroupMain.cancelAutocomplete();
this._customerOrderSearchStore.setQueryParams(filter?.getQueryParams());
}
this._customerOrderSearchStore.search({ clear });
}

View File

@@ -42,24 +42,13 @@ export class CustomerOrderComponent implements OnInit {
private _uiModal: UiModalService,
private _renderer: Renderer2,
private _environmentService: EnvironmentService,
public auth: AuthService,
private _store: CustomerOrderSearchStore
public auth: AuthService
) {}
ngOnInit(): void {
this.selectedBranch$ = this.application.activatedProcessId$.pipe(
switchMap((processId) => this.application.getSelectedBranch$(Number(processId)))
);
/* Ticket #4544 - Suchrequest abbrechen bei Prozesswechsel
/ um zu verhindern, dass die Suche in einen anderen Kundenbestellungen Prozess übernommen wird
/ bei Prozesswechsel zwischen 2 Kundenbestellungen Prozessen
*/
this.processId$.pipe(takeUntil(this._onDestroy$)).subscribe((processId) => {
if (Number(processId) !== this._store.processId) {
this._store.cancelSearchRequest();
}
});
}
ngAfterViewInit(): void {

View File

@@ -7,7 +7,8 @@
[tabindex]="tabIndexStart"
[autofocus]="focusAfterInit"
>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
</shared-select>
</shared-form-control>

View File

@@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { FormBlockGroup } from '../form-block';
import { NameFormBlockData } from './name-form-block-data';
import { GenderSettingsService } from '@shared/services';
import { Gender } from '@swagger/crm';
@Component({
selector: 'app-name-form-block',
@@ -15,7 +15,18 @@ export class NameFormBlockComponent extends FormBlockGroup<NameFormBlockData> {
return this.tabIndexStart + 3;
}
constructor(public genderSettings: GenderSettingsService) {
displayGenderNameFn = (gender: Gender) => {
if (gender == 2) {
return 'Herr';
}
if (gender == 4) {
return 'Frau';
}
return undefined;
};
constructor() {
super();
}

View File

@@ -4,7 +4,7 @@ import { AbstractControl, AsyncValidatorFn, UntypedFormControl, UntypedFormGroup
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { CrmCustomerService } from '@domain/crm';
import { AddressDTO, CustomerDTO, PayerDTO, ShippingAddressDTO } from '@swagger/crm';
import { AddressDTO, CustomerDTO, CustomerInfoDTO, PayerDTO, ShippingAddressDTO } from '@swagger/crm';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiValidators } from '@ui/validators';
import { isNull } from 'lodash';
@@ -106,8 +106,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
}
ngOnDestroy(): void {
// Fix für #4676 - Breadcrumb wurde beim Schließen des Prozesses neu erstellt und nicht korrekt gelöscht
// this.updateBreadcrumb(this.latestProcessId, this.formData);
this.updateBreadcrumb(this.latestProcessId, this.formData);
this.onDestroy$.next();
this.onDestroy$.complete();
this.busy$.complete();
@@ -230,33 +229,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
map((response) => {
if (response.error) {
throw response.message;
}
/**
* #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
* Fall1: Kundenkarte hat Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
* Fall2: Kundenkarte hat keine Daten in point4more:
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
*/
if (response.result && response.result.customer) {
const customer = response.result.customer;
const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
if (data.name.firstName && data.name.lastName) {
// Fall1
this._formData.next(data);
} else {
// Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
const current = this.formData;
current._meta = data._meta;
current.p4m = data.p4m;
}
}
return null;
return !response?.error && (response as any)?.result === 1 ? null : { invalid: 'Kundenkartencode ist ungültig' };
}),
catchError((error) => {
if (error instanceof HttpErrorResponse) {
@@ -269,13 +242,31 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
})
);
}),
tap(() => {
tap(async (result) => {
control.markAsTouched();
this.cdr.markForCheck();
if (result === null) {
const customerInfoDto = await this.getAnonymousCustomerForCode(control.value);
if (customerInfoDto) {
const data = mapCustomerInfoDtoToCustomerCreateFormData(customerInfoDto);
this._formData.next(data);
}
}
})
);
};
async getAnonymousCustomerForCode(code: string): Promise<CustomerInfoDTO | undefined> {
try {
const res = await this.customerService.getCustomers(code).toPromise();
if (res.result.length > 0 && res.result[0].id < 0) {
return res.result[0];
}
} catch (error) {}
}
async navigateToCustomerDetails(customer: CustomerDTO) {
const processId = await this.processId$.pipe(first()).toPromise();
const route = this.customerSearchNavigation.detailsRoute({ processId, customerId: customer.id, customer });

View File

@@ -41,7 +41,7 @@ export class CreateB2BCustomerComponent extends AbstractCreateCustomer {
deviatingNameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
firstName: [Validators.required],
lastName: [Validators.required],
};

View File

@@ -25,7 +25,7 @@ export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
title: [],
};
@@ -44,7 +44,7 @@ export class CreateGuestCustomerComponent extends AbstractCreateCustomer {
deviatingNameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
deviatingNameValidationFns: Record<string, ValidatorFn[]> = {
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
firstName: [Validators.required],
lastName: [Validators.required],
};

View File

@@ -64,12 +64,6 @@
>
</app-name-form-block>
<p class="info" *ngIf="customerType === 'webshop-p4m'">
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
die Übermittlungskosten nach den Basistarifen entstehen.
</p>
<app-email-form-block
class="flex-grow"
#email

View File

@@ -36,7 +36,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
title: [],
};

View File

@@ -36,7 +36,7 @@ export class CreateStoreCustomerComponent extends AbstractCreateCustomer {
nameValidationFns: Record<string, ValidatorFn[]> = {
title: [],
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
firstName: [Validators.required],
lastName: [Validators.required],
};

View File

@@ -25,7 +25,7 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
title: [],
};

View File

@@ -29,7 +29,7 @@ export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer im
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
firstName: [Validators.required],
lastName: [Validators.required],
gender: [Validators.required],
gender: [Validators.required, Validators.min(1)],
title: [],
};

View File

@@ -13,7 +13,8 @@
<form [formGroup]="formGroup" (ngSubmit)="save()">
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
</shared-select>
</shared-form-control>

View File

@@ -9,7 +9,7 @@ import { map } from 'rxjs/operators';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchNavigation } from '@shared/services';
import { IconComponent } from '@shared/components/icon';
import { combineLatest } from 'rxjs';
import { RouterLink } from '@angular/router';
@@ -35,7 +35,7 @@ import { RouterLink } from '@angular/router';
})
export class AddBillingAddressMainViewComponent {
formGroup = new FormGroup({
gender: new FormControl<Gender>(undefined, [Validators.required]),
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -59,8 +59,7 @@ export class AddBillingAddressMainViewComponent {
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
private _navigation: CustomerSearchNavigation
) {}
async save() {

View File

@@ -27,7 +27,8 @@
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
</shared-select>
</shared-form-control>

View File

@@ -9,7 +9,7 @@ import { map, takeUntil } from 'rxjs/operators';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchNavigation } from '@shared/services';
import { Subject, combineLatest } from 'rxjs';
import { RouterLink } from '@angular/router';
import { IconComponent } from '@shared/components/icon';
@@ -41,7 +41,7 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
);
formGroup = new FormGroup({
gender: new FormControl<Gender>(undefined, [Validators.required]),
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -65,8 +65,7 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
private _navigation: CustomerSearchNavigation
) {}
ngOnInit() {

View File

@@ -39,9 +39,7 @@
<div class="customer-details-customer-main-data px-5 py-3 grid grid-flow-row gap-3">
<div class="flex flex-row">
<div class="data-label">Erstellungsdatum</div>
<div *ngIf="created$ | async; let created" class="data-value">
{{ created | date: 'dd.MM.yyyy' }} | {{ created | date: 'HH:mm' }} Uhr
</div>
<div class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'HH:mm' }} Uhr</div>
</div>
<div class="flex flex-row">
<div class="data-label">Kundennummer</div>
@@ -113,7 +111,7 @@
</div>
<div class="customer-details-customer-main-row">
<div class="data-label">Land</div>
<div *ngIf="country$ | async; let country" class="data-value">{{ country | country }}</div>
<div class="data-value">{{ country$ | async | country }}</div>
</div>
<div class="customer-details-customer-main-row">
<div class="data-label">Festnetznr.</div>
@@ -125,16 +123,6 @@
</div>
<ng-container *ngIf="!(isBusinessKonto$ | async)">
<div class="customer-details-customer-main-row">
<div class="data-label">Geburtstag</div>
<div class="data-value">{{ dateOfBirth$ | async | date: 'dd.MM.yyyy' }}</div>
</div>
</ng-container>
<ng-container *ngIf="!(isBusinessKonto$ | async) && (organisationName$ | async)">
<div class="customer-details-customer-main-row">
<div class="data-label">Firmenname</div>
<div class="data-value">{{ organisationName$ | async }}</div>
</div>
<div class="customer-details-customer-main-row">
<div class="data-label">Abteilung</div>
<div class="data-value">{{ department$ | async }}</div>

View File

@@ -1,7 +1,7 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
import { Subject, combineLatest } from 'rxjs';
import { first, map, switchMap, takeUntil } from 'rxjs/operators';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchNavigation } from '@shared/services';
import { CustomerSearchStore } from '../store';
import { ShippingAddressDTO, NotificationChannel, ShoppingCartDTO, PayerDTO, BuyerDTO } from '@swagger/checkout';
import { DomainCheckoutService } from '@domain/checkout';
@@ -10,11 +10,16 @@ import { UiModalService } from '@ui/modal';
import { ComponentStore } from '@ngrx/component-store';
import { ApplicationService } from '@core/application';
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
import { ActivatedRoute, Router } from '@angular/router';
import { Router } from '@angular/router';
import { log, logAsync } from '@utils/common';
import { CrmCustomerService } from '@domain/crm';
import { MessageModalComponent, MessageModalData } from '@shared/modals/message-modal';
const GENDER_MAP = {
2: 'Herr',
4: 'Frau',
};
export interface CustomerDetailsViewMainState {
isBusy: boolean;
shoppingCart: ShoppingCartDTO;
@@ -33,9 +38,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
customerService = inject(CrmCustomerService);
fetching$ = combineLatest([this._store.fetchingCustomer$, this._store.fetchingCustomerList$]).pipe(
map(([fetchingCustomer, fetchingList]) => fetchingCustomer || fetchingList)
);
fetching$ = this._store.fetchingCustomer$;
isBusy$ = this.select((s) => s.isBusy);
@@ -83,7 +86,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
customerNumberBeeline$ = this._store.select((s) => s.customer?.linkedRecords?.find((r) => r.repository === 'beeline')?.number);
gender$ = this._store.select((s) => this._genderSettings.getGenderByValue(s.customer?.gender));
gender$ = this._store.select((s) => GENDER_MAP[s.customer?.gender]);
title$ = this._store.select((s) => s.customer?.title);
@@ -121,8 +124,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
shoppingCartHasNoItems$ = this.shoppingCartHasItems$.pipe(map((hasItems) => !hasItems));
dateOfBirth$ = this._store.select((s) => s.customer?.dateOfBirth);
get isBusy() {
return this.get((s) => s.isBusy);
}
@@ -180,10 +181,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
return this.get((s) => s.payer);
}
get snapshot() {
return this._activatedRoute.snapshot;
}
constructor(
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
@@ -192,9 +189,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
private _application: ApplicationService,
private _catalogNavigation: ProductCatalogNavigationService,
private _checkoutNavigation: CheckoutNavigationService,
private _router: Router,
private _activatedRoute: ActivatedRoute,
private _genderSettings: GenderSettingsService
private _router: Router
) {
super({ isBusy: false, shoppingCart: undefined, shippingAddress: undefined, payer: undefined });
}
@@ -227,21 +222,6 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
.subscribe((shoppingCart) => {
this.patchState({ shoppingCart });
});
// #4564 Fix - Da der Kunde bei einer erneuten Suche auf undefined gesetzt wird, muss man diesen erneut nach erfolgreicher Suche setzen.
// Dies geschieht bereits in der customer-search.component.ts immer wenn eine Navigation ausgeführt wird (sprich für Fälle in denen ein neuer Kunde gesetzt wird).
// Falls aber nach exakt dem gleichen Kunden gesucht wird, muss man diesen hier manuell erneut setzen, ansonsten bleibt dieser im Store auf undefined.
// Da dies nur für die Detailansicht relevant ist, wird hier auch auf hits 1 überprüft, ansonsten befindet man sich sowieso in der Trefferliste.
this._store.customerListResponse$.pipe(takeUntil(this._onDestroy$)).subscribe(([result]) => {
if (result.hits === 1) {
const customerId = result?.result[0].id;
const selectedCustomerId = +this.snapshot.params.customerId;
if (customerId === selectedCustomerId) {
this._store.selectCustomer({ customerId });
}
}
});
}
ngOnDestroy() {
@@ -429,12 +409,12 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
@log
_patchProcessName() {
let name = `${this.customer.firstName ?? ''} ${this.customer.lastName ?? ''}`;
let name = `${this.customer.firstName} ${this.customer.lastName}`;
// Ticket #4458 Es kann vorkommen, dass B2B Konten keinen Firmennamen hinterlegt haben
// zusätzlich kanne es bei Mitarbeiter Konten vorkommen, dass die Namen in der Organisation statt im Kundennamen hinterlegt sind
if ((this._store.isBusinessKonto && this.customer.organisation?.name) || (!this.customer.firstName && !this.customer.lastName)) {
name = `${this.customer.organisation?.name ?? ''}`;
name = `${this.customer.organisation?.name}`;
}
this._application.patchProcess(this.processId, {

View File

@@ -13,7 +13,8 @@
<form [formGroup]="formGroup" (ngSubmit)="save()">
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
</shared-select>
</shared-form-control>

View File

@@ -9,7 +9,7 @@ import { map, switchMap, takeUntil } from 'rxjs/operators';
import { AsyncPipe, NgForOf } from '@angular/common';
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchNavigation } from '@shared/services';
import { ComponentStore } from '@ngrx/component-store';
import { Subject, combineLatest } from 'rxjs';
import { ActivatedRoute, RouterLink } from '@angular/router';
@@ -39,7 +39,7 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
private _cdr = inject(ChangeDetectorRef);
formGroup = new FormGroup({
gender: new FormControl<Gender>(undefined, [Validators.required]),
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -74,8 +74,7 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
private _activatedRoute: ActivatedRoute,
private _modal: UiModalService,
public genderSettings: GenderSettingsService
private _modal: UiModalService
) {
super({ payer: undefined });
}

View File

@@ -28,7 +28,8 @@
<ui-form-control [clearable]="true" label="Anrede" variant="inline">
<ui-select formControlName="gender" tabindex="4">
<ui-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value" [label]="gender.label"></ui-select-option>
<ui-select-option [value]="2" label="Herr"></ui-select-option>
<ui-select-option [value]="4" label="Frau"></ui-select-option>
</ui-select>
</ui-form-control>
<ui-form-control [clearable]="true" label="Titel" variant="inline">

View File

@@ -16,7 +16,8 @@
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<ui-form-control [clearable]="true" label="Anrede" variant="inline">
<ui-select formControlName="gender" tabindex="1">
<ui-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value" [label]="gender.label"></ui-select-option>
<ui-select-option [value]="2" label="Herr"></ui-select-option>
<ui-select-option [value]="4" label="Frau"></ui-select-option>
</ui-select>
</ui-form-control>
<ui-form-control [clearable]="true" label="Titel" variant="inline">

View File

@@ -12,7 +12,7 @@ import { validateEmail } from '../../validators/email-validator';
import { genderLastNameValidator } from '../../validators/gender-b2b-validator';
import { camelCase } from 'lodash';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, GenderSettingsService, NavigationRoute } from '@shared/services';
import { CustomerSearchNavigation, NavigationRoute } from '@shared/services';
@Component({ template: '' })
export abstract class CustomerDataEditComponent implements OnInit {
@@ -41,8 +41,7 @@ export abstract class CustomerDataEditComponent implements OnInit {
private cdr: ChangeDetectorRef,
private location: Location,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
private _navigation: CustomerSearchNavigation
) {}
ngOnInit() {
@@ -91,7 +90,7 @@ export abstract class CustomerDataEditComponent implements OnInit {
this.control = fb.group(
{
gender: fb.control(customerDTO?.gender, [isBranch ? Validators.required : () => null]),
gender: fb.control(customerDTO?.gender, [isBranch ? Validators.min(1) : () => null, isBranch ? Validators.required : () => null]),
title: fb.control(customerDTO?.title),
lastName: fb.control(customerDTO?.lastName, [Validators.required]),
firstName: fb.control(customerDTO?.firstName, [Validators.required]),

View File

@@ -27,7 +27,8 @@
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
</shared-select>
</shared-form-control>

View File

@@ -9,7 +9,7 @@ import { map, switchMap, takeUntil } from 'rxjs/operators';
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
import { AddressSelectionModalService } from '@shared/modals/address-selection-modal';
import { CustomerSearchStore } from '../store';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchNavigation } from '@shared/services';
import { Subject, combineLatest } from 'rxjs';
import { IconComponent } from '@shared/components/icon';
import { ActivatedRoute, RouterLink } from '@angular/router';
@@ -50,7 +50,7 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
);
formGroup = new FormGroup({
gender: new FormControl<Gender>(undefined, [Validators.required]),
gender: new FormControl<Gender>(0, [Validators.required, Validators.min(1)]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -83,8 +83,7 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
private _navigation: CustomerSearchNavigation
) {
super({ shippingAddress: undefined });
}

View File

@@ -8,10 +8,10 @@ import { CrmCustomerService } from '@domain/crm';
import { Result } from '@domain/defs';
import { CustomerDTO, ListResponseArgsOfCustomerInfoDTO, QuerySettingsDTO } from '@swagger/crm';
import { Filter } from '@shared/components/filter';
import { isEmpty } from 'lodash';
import { DomainOmsService } from '@domain/oms';
import { OrderDTO, OrderListItemDTO } from '@swagger/oms';
import { hash } from '@utils/common';
import { UiModalService } from '@ui/modal';
@Injectable()
export class CustomerSearchStore extends ComponentStore<CustomerSearchState> implements OnStoreInit, OnDestroy {
@@ -163,7 +163,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService, private _modal: UiModalService) {
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService) {
super({ customerListCount: 0 });
}
@@ -205,8 +205,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSelectCustomerError = (err: any) => {
this._modal.error('Fehler beim Auswählen des Kundens', err);
this.patchState({ fetchingCustomer: false });
console.error(err);
};
handleSelectCustomerComplete = () => {
@@ -231,8 +230,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSelectOrderError = (err: any) => {
this._modal.error('Fehler beim Auswählen der Bestellung', err);
this.patchState({ fetchingOrder: false });
console.error(err);
};
handleSelectOrderComplete = () => {
@@ -261,8 +259,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleFetchCustomerOrdersError = (err: any) => {
this._modal.error('Fehler beim Laden der Kundenbestellungen', err);
this.patchState({ fetchingCustomerOrders: false });
console.error(err);
};
handleFetchCustomerOrdersComplete = () => {
@@ -285,8 +282,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleFetchFilterError = (err: any) => {
this._modal.error('Fehler beim Laden der Filter', err);
this.patchState({ fetchingFilter: false });
console.error(err);
};
handleFetchFilterComplete = () => {
@@ -303,10 +299,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
),
withLatestFrom(this.filter$, this.processId$),
map(([a1, a2, a3]) => {
// #4564 Setze "customer" undefined immer wenn neu gesucht wird,
// da sonst Änderungen einer zweiten ISA am Kunden, selbst nach erneuter Suche, nicht geupdated werden,
// da noch der alte Kundendatensatz im Store gespeichert ist
this.patchState({ fetchingCustomerList: true, customerList: [], customer: undefined, customerListCount: 0, message: '' });
this.patchState({ fetchingCustomerList: true, customerList: [], customerListCount: 0, message: '' });
let retored = false;
if (a1.ignoreRestore) {
@@ -348,8 +341,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
};
handleSearchError = (err: any) => {
this._modal.error('Fehler beim Laden der Liste', err);
this.patchState({ fetchingCustomerList: false });
console.error(err);
};
handleSearchComplete = () => {

View File

@@ -5,8 +5,8 @@ export function genderLastNameValidator(isB2b: boolean): ValidatorFn | null {
return (control: UntypedFormGroup): ValidationErrors | null => {
const gender = control.get('gender').value;
const lastName = control.get('lastName').value;
if (!!lastName) {
control.get('gender').setValidators([Validators.required]);
if (!!lastName && gender === 0) {
control.get('gender').setValidators([Validators.required, Validators.min(1)]);
return { genderNotSet: true };
} else {
control.get('gender').setValidators(null);

View File

@@ -16,7 +16,7 @@ export function organisationB2bDeliveryValidator(): ValidatorFn | null {
lastName = control.get('shippingAddress').get('lastName');
organisation = control.get('shippingAddress').get('organisation').get('name');
isOrganisationSet = !!organisation.value;
isNameSet = !!firstName.value && !!lastName.value;
isNameSet = gender.value !== 0 && !!firstName.value && !!lastName.value;
if (control.get('differentShippingAddress').value === true) {
return validate(gender, firstName, lastName, organisation, isOrganisationSet, isNameSet);
@@ -28,14 +28,14 @@ export function organisationB2bDeliveryValidator(): ValidatorFn | null {
lastName = control.get('lastName');
organisation = control.get('organisation').get('name');
isOrganisationSet = !!organisation.value;
isNameSet = !!firstName.value && !!lastName.value;
isNameSet = gender.value !== 0 && !!firstName.value && !!lastName.value;
return validate(gender, firstName, lastName, organisation, isOrganisationSet, isNameSet);
}
};
}
function setNameValidation(gender: AbstractControl, firstName: AbstractControl, lastName: AbstractControl) {
gender.setValidators([Validators.required]);
gender.setValidators([Validators.required, Validators.min(1)]);
firstName.setValidators([Validators.required]);
lastName.setValidators([Validators.required]);
}

View File

@@ -16,7 +16,7 @@
ui-slider {
img {
@apply rounded cursor-pointer;
max-height: 160px;
height: 160px;
// max-width: aut;
}

View File

@@ -22,7 +22,6 @@ import { debounceTime, first, map, shareReplay, takeUntil, tap } from 'rxjs/oper
import { GoodsInListItemComponent } from './goods-in-list-item/goods-in-list-item.component';
import { GoodsInListStore } from './goods-in-list.store';
import { PickupShelfInNavigationService } from '@shared/services';
import { CacheService } from '@core/cache';
@Component({
selector: 'page-goods-in-list',
@@ -61,21 +60,20 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
private _onDestroy$ = new Subject();
private readonly SCROLL_POSITION_TOKEN = 'GOODS_IN_LIST_SCROLL_POSITION';
constructor(
private _breadcrumb: BreadcrumbService,
private _domainOmsService: DomainOmsService,
public store: GoodsInListStore,
private _router: Router,
private _route: ActivatedRoute,
private readonly _config: Config,
private _cache: CacheService
private readonly _config: Config
) {}
ngOnInit() {
this.store.setTake(Number(this._route.snapshot.queryParams.take ?? 25));
this._route.queryParams.pipe(takeUntil(this._onDestroy$), debounceTime(0)).subscribe(async (params) => {
const scrollPos = Number(params.scroll_position ?? 0);
// Initial Search - Always Search If No Params Are Set
if (
(Object.keys(params).length === 0 || this.store.results.length === 0) &&
@@ -84,8 +82,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
this.store.search({
cb: () => {
setTimeout(() => {
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
this._removeScrollPositionFromCache();
this.scrollContainer?.scrollTo(scrollPos);
}, 0);
},
});
@@ -104,8 +101,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
this.store.search({
cb: () => {
setTimeout(() => {
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache());
this._removeScrollPositionFromCache();
this.scrollContainer?.scrollTo(scrollPos);
}, 0);
},
});
@@ -121,13 +117,12 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
this._onDestroy$.next();
this._onDestroy$.complete();
this._addScrollPositionToCache();
this.updateBreadcrumb(this.store.filter.getQueryParams());
}
cleanupQueryParams(params: Record<string, string> = {}) {
const clean = { ...params };
delete clean['scroll_position'];
delete clean['take'];
delete clean['view'];
@@ -148,21 +143,6 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
this.listItems.changes.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.registerEditSscDisabled());
}
private _removeScrollPositionFromCache(): void {
this._cache.delete({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
}
private _addScrollPositionToCache(): void {
this._cache.set<number>(
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
this.scrollContainer?.scrollPos
);
}
private _getScrollPositionFromCache(): number {
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
}
navigateToDetails(orderItem: OrderItemListItemDTO) {
if (this.editSsc) {
return;
@@ -204,6 +184,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
}
async updateBreadcrumb(queryParams: Record<string, string> | Params = this.store.filter?.getQueryParams()) {
const scroll_position = this.scrollContainer?.scrollPos;
const take = this._route?.snapshot?.queryParams?.take;
if (queryParams) {
@@ -211,7 +192,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list'])
.pipe(first())
.toPromise();
const params = { ...queryParams, take };
const params = { ...queryParams, scroll_position, take };
for (const crumb of crumbs) {
this._breadcrumb.patchBreadcrumb(crumb.id, {

View File

@@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
@@ -10,7 +10,6 @@ import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store
import { Config } from '@core/config';
import { ToasterService } from '@shared/shell';
import { PickupShelfInNavigationService } from '@shared/services';
import { CacheService } from '@core/cache';
@Component({
selector: 'page-goods-in-remission-preview',
@@ -23,6 +22,8 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
private _scrollPosition: number;
items$ = this._store.results$;
itemLength$ = this.items$.pipe(map((items) => items?.length));
@@ -51,16 +52,14 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
!!item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
private readonly SCROLL_POSITION_TOKEN = 'REMISSION_PREVIEW_SCROLL_POSITION';
constructor(
private _breadcrumb: BreadcrumbService,
private _store: GoodsInRemissionPreviewStore,
private _route: ActivatedRoute,
private _router: Router,
private _modal: UiModalService,
private _config: Config,
private _toast: ToasterService,
private _cache: CacheService
private _toast: ToasterService
) {}
ngOnInit(): void {
@@ -72,25 +71,9 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this._onDestroy$.next();
this._onDestroy$.complete();
this._addScrollPositionToCache();
this.updateBreadcrumb();
}
private _removeScrollPositionFromCache(): void {
this._cache.delete({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
}
private _addScrollPositionToCache(): void {
this._cache.set<number>(
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
this.scrollContainer?.scrollPos
);
}
private _getScrollPositionFromCache(): number {
return this._cache.get<number>({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
}
async createBreadcrumb() {
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
key: this._config.get('process.ids.goodsIn'),
@@ -110,6 +93,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
for (const crumb of crumbs) {
this._breadcrumb.patchBreadcrumb(crumb.id, {
name: crumb.name,
params: { scroll_position: this.scrollContainer?.scrollPos },
});
}
}
@@ -149,11 +133,14 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
await this.createBreadcrumb();
this.scrollContainer?.scrollTo(this._getScrollPositionFromCache() ?? 0);
this._removeScrollPositionFromCache();
if (this._scrollPosition) {
this.scrollContainer?.scrollTo(Number(this._scrollPosition ?? 0));
this._scrollPosition = undefined;
}
});
}
this._scrollPosition = this._route.snapshot.queryParams?.scroll_position;
this._store.search();
}

View File

@@ -11,7 +11,7 @@
}
.package-details-list-item__product-image {
@apply w-12 shadow max-h-full;
@apply w-12 h-auto shadow;
}
@screen desktop {
@@ -20,7 +20,7 @@
}
.package-details-list-item__product-image {
@apply w-auto max-h-full shadow mx-auto;
@apply w-auto h-full shadow mx-auto;
}
}

View File

@@ -75,10 +75,7 @@
>Zur erneuten Prüfung 7 Tage nach Avisierung.</ui-tooltip
>
</ng-container>
<div
class="isa-label text-white font-bold page-package-details__arrival-status"
[class]="packageDetails.package.arrivalStatus | arrivalStatusColorClass"
>
<div class="isa-label text-white font-bold" [class]="packageDetails.package.arrivalStatus | arrivalStatusColorClass">
{{ packageDetails.package.arrivalStatus | arrivalStatus }}
</div>
</div>
@@ -97,7 +94,7 @@
</ng-container>
</div>
<div class="text-right">
<span class="font-bold">{{ packageDetails?.package?.items ?? '-' }}</span> Exemplare
<span class="font-bold">{{ packageDetails.package.items }}</span> Exemplare
</div>
</div>
</div>

View File

@@ -5,7 +5,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { take } from 'rxjs/operators';
import { NavigationRoute } from '@shared/services';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, ListResponseArgsOfDBHOrderItemListItemDTO } from '@swagger/oms';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
import { isEmpty } from 'lodash';
import { Observable } from 'rxjs';
import { RunCheckTrigger } from './trigger';
@@ -64,11 +64,6 @@ export abstract class PickupShelfBaseComponent implements OnInit {
this._runChecks();
}
// der debounce soll verhindern, dass die breadcrumb zu oft aktualisiert,
// besonders bei asynchronen calls kommt es sonst zu fehlern
// @debounce(500)
// Auskommentiert, da es zu anderen Problemen führt, siehe z.B. Ticket #4538 oder #4540
// Ursprungsproblem des Tickets #4533 konnte anders gelöst werden, somit wird debounce hier nicht mehr benötigt
private _runChecks() {
const processId = this._checkAndUpdateProcessId();
const queryParams = this._checkAndUpdateQueryParams();
@@ -116,8 +111,31 @@ export abstract class PickupShelfBaseComponent implements OnInit {
return queryParams;
}
// Fix Ticket #4688 Navigation behaves different based on section PickUpShelfOut and PickUpShelfIn
abstract regsiterFetchListResponseHandler(): void | Promise<void>;
regsiterFetchListResponseHandler() {
this.listStore.fetchListResponse$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(async ({ processId, queryParams, response }) => {
/**
* Wenn die Suche erfolgreich war, wird der Benutzer auf die Liste oder Detailseite des gefundenen Artikels weitergeleitet.
*/
const filterQueryParams = this.listStore.filter.getQueryParams();
// Only Update QueryParams if the user is already on the details, edit or history page
const view: string = this.activatedRoute.snapshot.data.view;
if (['details', 'edit', 'history'].includes(view)) {
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams }, skipLocationChange: true });
return;
}
if (response.hits === 1) {
const detailsPath = await this.getPathForDetail(response.result[0]).pipe(take(1)).toPromise();
await this.router.navigate(detailsPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...detailsPath.queryParams } });
} else if (response.hits > 1) {
const listPath = await this.getPathFoListBreadcrumb({ processId, queryParams });
await this.router.navigate(listPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...listPath.queryParams } });
} else {
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams } });
}
});
}
/**
* Sucht die Breadcrumb anhand des Tags.

View File

@@ -43,7 +43,6 @@ export abstract class PickupShelfDetailsBaseComponent {
this.store.resetCoverItems();
}
this.store.fetchOrder({ orderId: Number(params.orderId) });
this.store.fetchOrderItems({
orderNumber: params.orderNumber ? decodeURIComponent(params.orderNumber) : undefined,
compartmentCode: params.compartmentCode ? decodeURIComponent(params.compartmentCode) : undefined,
@@ -125,7 +124,6 @@ export abstract class PickupShelfDetailsBaseComponent {
processingStatus: updatedItem.processingStatus,
compartmentCode: updatedItem.compartmentCode,
compartmentInfo: updatedItem.compartmentInfo,
quantity: updatedItem.quantity,
},
});
});

View File

@@ -17,25 +17,17 @@
(updateDate)="updateDate($event)"
(editClick)="navigateToEditPage($event)"
></page-pickup-shelf-details-header>
<ng-container *ngIf="selectedItem$ | async; let item">
<page-pickup-shelf-details-items-group
[orderType]="selectedItemOrderType$ | async"
[groupedItems]="[item]"
></page-pickup-shelf-details-items-group>
<page-pickup-shelf-details-item
class="mb-px-2"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
</ng-container>
<page-pickup-shelf-details-item
*ngIf="selectedItem$ | async; let item"
class="mb-px-2"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
<page-pickup-shelf-details-tags class="mb-px-2" *ngIf="showTagsComponent$ | async"></page-pickup-shelf-details-tags>
<page-pickup-shelf-details-covers
*ngIf="(coverOrderItems$ | async)?.length > 0"
[coverItems]="coverOrderItems$ | async"

View File

@@ -10,14 +10,12 @@ import { UiSpinnerModule } from '@ui/spinner';
import { OnInitDirective } from '@shared/directives/element-lifecycle';
import { PickupShelfInNavigationService } from '@shared/services';
import { BehaviorSubject, asapScheduler, combineLatest } from 'rxjs';
import { distinctUntilChanged, map, shareReplay } from 'rxjs/operators';
import { map, shareReplay } from 'rxjs/operators';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { ActivatedRoute } from '@angular/router';
import { RunCheckTrigger } from '../../trigger';
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
import { isEqual } from 'lodash';
@Component({
selector: 'page-pickup-shelf-in-details',
@@ -34,7 +32,6 @@ import { isEqual } from 'lodash';
PickUpShelfDetailsItemComponent,
PickUpShelfDetailsTagsComponent,
PickUpShelfDetailsCoversComponent,
PickUpShelfDetailsItemsGroupComponent,
PickupShelfAddToPreviousCompartmentCodeLabelPipe,
UiSpinnerModule,
OnInitDirective,
@@ -50,7 +47,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
order$ = this.store.order$;
orderItems$ = this.store.orderItems$.pipe(shareReplay(1));
orderItems$ = this.store.orderItems$.pipe(shareReplay());
noOrderItemsFound$ = this.store.noOrderItemsFound$;
@@ -77,8 +74,6 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
selectedItem$ = this.store.selectedOrderItem$;
selectedItemOrderType$ = this.selectedItem$.pipe(map((item) => item?.features?.orderType));
coverOrderItems$ = this.store.coverOrderItems$;
displayedCompartmentInfo$ = this.store.compartmentInfo$;
@@ -103,7 +98,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
}
ngOnInit() {
combineLatest([this._activatedRoute.params.pipe(distinctUntilChanged(isEqual)), this.orderItems$.pipe(distinctUntilChanged(isEqual))])
combineLatest([this._activatedRoute.params, this.orderItems$])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([params, items]) => {
const orderItemSubsetId = +params?.orderItemSubsetId;
@@ -142,14 +137,11 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
try {
this.changeActionLoader$.next(action.command);
this.store.setDisableHeaderStatusDropdown(true);
const context = await this.execAction({ action, latestCompartmentCode, latestCompartmentInfo });
if (!!context) {
if (
action.command.includes('ARRIVED') ||
action.command.includes('PRINT_PRICEDIFFQRCODELABEL') ||
action.command.includes('BACKTOSTOCK')
) {
if (action.command.includes('ARRIVED') || action.command.includes('PRINT_PRICEDIFFQRCODELABEL')) {
asapScheduler.schedule(async () => {
await this.navigateBasedOnCurrentView();
}, 100);
@@ -214,17 +206,17 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
switch (type) {
case 'delivery':
this.store.selectedOrderItems.forEach((item) =>
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } })
);
break;
case 'pickup':
this.store.selectedOrderItems.forEach((item) =>
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } })
);
break;
case 'preferred':
this.store.selectedOrderItems.forEach((item) =>
this.store.orderItems.forEach((item) =>
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } })
);
break;

View File

@@ -1,7 +1,5 @@
<ng-container *ngIf="store.selectedOrderItem$ | async; let item">
<shared-goods-in-out-order-edit
*ngIf="item"
(navigation)="navigateToShelfInDetailsPage($event)"
[items]="[item]"
></shared-goods-in-out-order-edit>
</ng-container>
<shared-goods-in-out-order-edit
*ngIf="store.orderItems$ | async; let items"
(navigation)="navigateToShelfInDetailsPage($event)"
[items]="items"
></shared-goods-in-out-order-edit>

View File

@@ -20,23 +20,15 @@ export class PickupShelfInEditComponent extends PickupShelfDetailsBaseComponent
constructor() {
super();
}
this.listStore;
}
async navigateToShelfInDetailsPage(changes?: Partial<DBHOrderItemListItemDTO>) {
const orderId = (await this.store.orderItems$.pipe(first()).toPromise())?.find((_) => true)?.orderId;
const orderNumber = decodeURIComponent(this.activatedRoute?.snapshot?.params?.orderNumber);
let compartmentCode = changes?.compartmentCode ?? this.activatedRoute?.snapshot?.params?.compartmentCode;
const orderNumber = this.activatedRoute?.snapshot?.params?.orderNumber;
const compartmentCode = changes?.compartmentCode ?? this.activatedRoute?.snapshot?.params?.compartmentCode;
const processingStatus = changes?.processingStatus ?? this.activatedRoute.snapshot.params.orderItemProcessingStatus;
let compartmentInfo = changes?.compartmentInfo ?? this.activatedRoute.snapshot.params.compartmentInfo;
const item = this.store?.selectedOrderItem;
if (compartmentCode) {
compartmentCode = decodeURIComponent(compartmentCode);
}
if (compartmentInfo) {
compartmentInfo = decodeURIComponent(compartmentInfo);
}
const compartmentInfo = changes?.compartmentInfo ?? this.activatedRoute.snapshot.params.compartmentInfo;
await this.router.navigate(
this.shelfInNavigation.detailRoute({
@@ -46,7 +38,7 @@ export class PickupShelfInEditComponent extends PickupShelfDetailsBaseComponent
compartmentCode,
processingStatus,
compartmentInfo,
orderItemSubsetId: item?.orderItemSubsetId,
orderItemSubsetId: this.store?.selectPreviousSelectedOrderItemSubsetId,
},
side: this.side,
}).path,

View File

@@ -18,7 +18,7 @@ import { PickUpShelfListItemComponent } from '../../shared/pickup-shelf-list-ite
import { Group, GroupByPipe } from '@ui/common';
import { UiSpinnerModule } from '@ui/spinner';
import { PickupShelfInNavigationService } from '@shared/services';
import { map } from 'rxjs/operators';
import { debounceTime, map } from 'rxjs/operators';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
import { Observable, combineLatest, of } from 'rxjs';
import { PickupShelfDetailsStore, PickupShelfStore } from '../../store';
@@ -79,7 +79,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
})
);
fetching$: Observable<boolean> = this.store.fetchingList$;
fetching$: Observable<boolean> = this.store.fetching$;
searchboxHint$ = this.store.searchboxHint$;
@@ -124,9 +124,8 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
combineLatest([this.store.processId$, this._activatedRoute.queryParams])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([_, queryParams]) => {
if (!this.store.list.length || !isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))) {
this.store.setQueryParams(queryParams);
this.store.fetchList({ emitFetchListResponse: false });
if (!this.store.list.length || !isEqual(queryParams, this.store.filter.getQueryParams())) {
this.store.fetchList();
}
const scrollPos = this._getScrollPositionFromCache();
@@ -149,20 +148,6 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
this.scrollItemIntoView();
}
cleanupQueryParams(params: Record<string, string> = {}) {
const clean = { ...params };
for (const key in clean) {
if (Object.prototype.hasOwnProperty.call(clean, key)) {
if (clean[key] == undefined) {
delete clean[key];
}
}
}
return clean;
}
private _removeScrollPositionFromCache(): void {
this._cache.delete({ processId: this.store.processId, token: this.SCROLL_POSITION_TOKEN });
}

View File

@@ -13,6 +13,7 @@
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
<shared-filter-input-group-main
class="block w-full mr-3 desktop-large:mx-auto"
*ngIf="filter$ | async; let filter"
[inputGroup]="filter?.input | group: 'main'"
[hint]="searchboxHint$ | async"
[loading]="fetching$ | async"

View File

@@ -38,7 +38,7 @@ export class PickUpShelfInMainSideViewComponent implements OnInit {
})
);
fetching$: Observable<boolean> = this.store.fetchingList$;
fetching$: Observable<boolean> = this.store.fetching$;
searchboxHint$ = this.store.searchboxHint$;

View File

@@ -16,7 +16,6 @@ import { map, take } from 'rxjs/operators';
import { ApplicationService } from '@core/application';
import { FilterAutocompleteProvider } from '@shared/components/filter';
import { PickUpShelfInAutocompleteProvider } from './providers/pickup-shelf-in-autocomplete.provider';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'page-pickup-shelf-in',
@@ -206,23 +205,4 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
async getNameForHistoryBreadcrumb(data: GetNameForBreadcrumbData): Promise<string> {
return 'Historie';
}
async regsiterFetchListResponseHandler() {
this.listStore.fetchListResponse$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(async ({ processId, queryParams, response }) => {
/**
* Wenn die Suche erfolgreich war, wird der Benutzer auf die Liste oder Detailseite des gefundenen Artikels weitergeleitet.
*/
const filterQueryParams = this.listStore.filter.getQueryParams();
if (response.hits === 1) {
const detailsPath = await this.getPathForDetail(response.result[0]).pipe(take(1)).toPromise();
await this.router.navigate(detailsPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...detailsPath.queryParams } });
} else if (response.hits > 1) {
const listPath = await this.getPathFoListBreadcrumb({ processId, queryParams });
await this.router.navigate(listPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...listPath.queryParams } });
} else {
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams } });
}
});
}
}

View File

@@ -14,24 +14,16 @@
(updateDate)="updateDate($event)"
(editClick)="navigateToEditPage($event)"
></page-pickup-shelf-details-header>
<ng-container *ngFor="let group of groupedItems$ | async; trackBy: trackByFnGroupDBHOrderItemListItemDTO">
<page-pickup-shelf-details-items-group
[orderType]="group.type"
[groupedItems]="group.items"
></page-pickup-shelf-details-items-group>
<page-pickup-shelf-details-item
class="mb-px-2"
*ngFor="let item of group.items; trackBy: trackByFnDBHOrderItemListItemDTO"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
</ng-container>
<page-pickup-shelf-details-item
class="mb-px-2"
*ngFor="let item of orderItems$ | async; trackBy: trackByFnDBHOrderItemListItemDTO"
[orderItem]="item"
[selected]="true"
(historyClick)="navigateToHistoryPage($event)"
[order]="order$ | async"
(specialCommentChanged)="updateSpecialComment(item, $event)"
(sharedOnInit)="fetchNotifications(item)"
></page-pickup-shelf-details-item>
<page-pickup-shelf-details-tags
*ngIf="showTagsComponent$ | async"
[ngModel]="selectedCompartmentInfo$ | async"

View File

@@ -3,9 +3,9 @@ import { PickupShelfDetailsBaseComponent } from '../../pickup-shelf-details-base
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
import { PickUpShelfDetailsHeaderComponent } from '../../shared/pickup-shelf-details-header/pickup-shelf-details-header.component';
import { PickUpShelfDetailsItemComponent } from '../../shared/pickup-shelf-details-item/pickup-shelf-details-item.component';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString } from '@swagger/oms';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, OrderItemProcessingStatusValue } from '@swagger/oms';
import { PickUpShelfOutNavigationService } from '@shared/services';
import { BehaviorSubject, Observable, asapScheduler, combineLatest } from 'rxjs';
import { BehaviorSubject, asapScheduler, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { PickUpShelfDetailsTagsComponent } from '../../shared/pickup-shelf-details-tags/pickup-shelf-details-tags.component';
import { UiSpinnerModule } from '@ui/spinner';
@@ -13,7 +13,6 @@ import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { OnInitDirective } from '@shared/directives/element-lifecycle';
import { FormsModule } from '@angular/forms';
import { RunCheckTrigger } from '../../trigger';
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
@Component({
selector: 'page-pickup-shelf-out-details',
@@ -29,7 +28,6 @@ import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf
PickUpShelfDetailsHeaderComponent,
PickUpShelfDetailsItemComponent,
PickUpShelfDetailsTagsComponent,
PickUpShelfDetailsItemsGroupComponent,
UiSpinnerModule,
OnInitDirective,
FormsModule,
@@ -46,25 +44,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
order$ = this.store.order$;
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.store.orderItems$.pipe(
map((items) => {
const groups: Array<{ type: string; items: DBHOrderItemListItemDTO[] }> = [];
// New Set to remove duplicates
const types = Array.from(new Set(items.map((item) => item?.features?.orderType)));
for (let type of types) {
const filteredItemsByType = items.filter((item) => item?.features?.orderType === type);
if (!!type && filteredItemsByType.length > 0) {
// Add items to matching orderType group
groups.push({ type, items: filteredItemsByType });
}
}
return groups;
})
);
orderItems$ = this.store.orderItems$;
fetching$ = this.store.fetchingOrder$;
fetchingItems$ = this.store.fetchingOrderItems$;
@@ -84,9 +64,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
addToPreviousCompartmentAction$ = this.store.addToPreviousCompartmentAction$;
mainActions$ = this.store.mainShelfOutActions$;
trackByFnGroupDBHOrderItemListItemDTO = (index: number, group: { type: string; items: DBHOrderItemListItemDTO[] }) => group.type;
mainActions$ = this.store.mainActions$;
trackByFnDBHOrderItemListItemDTO = (index: number, item: DBHOrderItemListItemDTO) => item.orderItemSubsetId;
@@ -105,11 +83,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
const context = await this.execAction({ action });
if (!!context) {
if (
action.command.includes('ARRIVED') ||
action.command.includes('PRINT_PRICEDIFFQRCODELABEL') ||
action.command.includes('BACKTOSTOCK')
) {
if (action.command.includes('ARRIVED') || action.command.includes('PRINT_PRICEDIFFQRCODELABEL')) {
const nav = this._pickupShelfOutNavigationService.defaultRoute({ processId: this.processId });
await this.router.navigate(nav.path, { queryParams: nav.queryParams, queryParamsHandling: 'preserve' });
} else {

View File

@@ -26,18 +26,10 @@ export class PickupShelfOutEditComponent extends PickupShelfDetailsBaseComponent
async navigateToShelfOutDetailsPage(changes: Partial<DBHOrderItemListItemDTO>) {
const orderId = (await this.store.orderItems$.pipe(first()).toPromise())?.find((_) => true)?.orderId;
const orderNumber = decodeURIComponent(this.activatedRoute?.snapshot?.params?.orderNumber);
let compartmentCode = changes?.compartmentCode ?? this.activatedRoute?.snapshot?.params?.compartmentCode;
const orderNumber = this.activatedRoute?.snapshot?.params?.orderNumber;
const compartmentCode = changes?.compartmentCode ?? this.activatedRoute?.snapshot?.params?.compartmentCode;
const processingStatus = changes?.processingStatus ?? this.activatedRoute.snapshot.params.orderItemProcessingStatus;
let compartmentInfo = changes?.compartmentInfo ?? this.activatedRoute.snapshot.params.compartmentInfo;
if (compartmentCode) {
compartmentCode = decodeURIComponent(compartmentCode);
}
if (compartmentInfo) {
compartmentInfo = decodeURIComponent(compartmentInfo);
}
const compartmentInfo = changes?.compartmentInfo ?? this.activatedRoute.snapshot.params.compartmentInfo;
await this.router.navigate(
this.shelfOutNavigation.detailRoute({

View File

@@ -83,7 +83,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
})
);
fetching$: Observable<boolean> = this.store.fetchingList$;
fetching$: Observable<boolean> = this.store.fetching$;
searchboxHint$ = this.store.searchboxHint$;
@@ -245,7 +245,7 @@ export class PickupShelfOutListComponent implements OnInit, AfterViewInit {
action,
items: selectedItems,
});
this.store.fetchList({ emitFetchListResponse: false });
this.store.fetchList();
this.store.resetSelectedListItems();
this.loadingFetchedActionButton$.next(false);

View File

@@ -16,6 +16,7 @@
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
<shared-filter-input-group-main
class="block w-full mr-3 desktop-large:mx-auto"
*ngIf="filter$ | async; let filter"
[inputGroup]="filter?.input | group: 'main'"
[hint]="searchboxHint$ | async"
[loading]="fetching$ | async"

View File

@@ -38,7 +38,7 @@ export class PickupShelfOutMainSideViewComponent implements OnInit {
})
);
fetching$: Observable<boolean> = this.store.fetchingList$;
fetching$: Observable<boolean> = this.store.fetching$;
searchboxHint$ = this.store.searchboxHint$;

View File

@@ -7,7 +7,7 @@ import { PickupShelfIOService, PickupShelfOutService } from '@domain/pickup-shel
import { GetNameForBreadcrumbData, GetPathForBreadcrumbData, PickupShelfBaseComponent } from '../pickup-shelf-base.component';
import { NavigationRoute, PickUpShelfOutNavigationService } from '@shared/services';
import { AsyncPipe } from '@angular/common';
import { DBHOrderItemListItemDTO, ListResponseArgsOfDBHOrderItemListItemDTO } from '@swagger/oms';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
import { Observable, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { provideActionHandlers } from '@core/command';
@@ -15,7 +15,6 @@ import { ActionHandlerServices } from '@domain/oms';
import { ActionHandlerService } from '../services/action-handler.service';
import { FilterAutocompleteProvider } from '@shared/components/filter';
import { PickUpShelfOutAutocompleteProvider } from './providers/pickup-shelf-out-autocomplete.provider';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'page-pickup-shelf-out',
@@ -204,30 +203,4 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
async getNameForHistoryBreadcrumb(data: GetNameForBreadcrumbData): Promise<string> {
return 'Historie';
}
async regsiterFetchListResponseHandler() {
this.listStore.fetchListResponse$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(async ({ processId, queryParams, response }) => {
/**
* Wenn die Suche erfolgreich war, wird der Benutzer auf die Liste oder Detailseite des gefundenen Artikels weitergeleitet.
*/
const filterQueryParams = this.listStore.filter.getQueryParams();
if (response.hits === 1 || this._hasSameOrderNumber(response)) {
const detailsPath = await this.getPathForDetail(response.result[0]).pipe(take(1)).toPromise();
await this.router.navigate(detailsPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...detailsPath.queryParams } });
} else if (response.hits > 1) {
const listPath = await this.getPathFoListBreadcrumb({ processId, queryParams });
await this.router.navigate(listPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...listPath.queryParams } });
} else {
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams } });
}
});
}
// Fix Ticket #4684 Navigate on Details if items contain same OrderNumber
private _hasSameOrderNumber(response: ListResponseArgsOfDBHOrderItemListItemDTO) {
if (response.hits === 0) return false;
const orderNumbers = new Set(response.result.map((item) => item.orderNumber));
return orderNumbers.size === 1;
}
}

View File

@@ -11,7 +11,7 @@ ui-slider {
@apply mr-5;
.image {
@apply relative border-none outline-none bg-transparent flex;
@apply relative border-none outline-none bg-transparent;
width: 53px;
height: 85px;
}
@@ -27,7 +27,7 @@ ui-slider {
.thumbnail {
@apply rounded-md shadow-lg;
max-height: 5.3125rem;
height: 85px;
}
.faded {

View File

@@ -1,7 +1,6 @@
<button
type="button"
[disabled]="!(customer$ | async)"
class="page-pickup-shelf-details-header-nav-menu__nav-button px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
class="px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
[cdkMenuTriggerFor]="navMenu"
#menuTrigger="cdkMenuTriggerFor"
[class.open]="menuTrigger.isOpen()"

View File

@@ -1,7 +1,3 @@
:host {
@apply inline-block;
}
.page-pickup-shelf-details-header-nav-menu__nav-button:disabled {
@apply cursor-not-allowed text-white;
}

View File

@@ -7,12 +7,11 @@ import { ComponentStore } from '@ngrx/component-store';
import { IconComponent } from '@shared/components/icon';
import { SharedMenuModule } from '@shared/components/menu';
import { CustomerSearchNavigation } from '@shared/services';
import { CustomerInfoDTO } from '@swagger/crm';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
export interface PickUpShelfDetailsHeaderNavMenuComponentState {
customer?: CustomerInfoDTO;
customerId?: number;
showCustomerDetails: boolean;
}
@@ -26,26 +25,26 @@ export interface PickUpShelfDetailsHeaderNavMenuComponentState {
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
})
export class PickUpShelfDetailsHeaderNavMenuComponent extends ComponentStore<PickUpShelfDetailsHeaderNavMenuComponentState> {
@Input() set customer(value: CustomerInfoDTO) {
this.patchState({ customer: value });
@Input() set customerId(value: NumberInput) {
this.patchState({ customerId: coerceNumberProperty(value) });
}
@Input() set showCustomerDetails(value: BooleanInput) {
this.patchState({ showCustomerDetails: coerceBooleanProperty(value) });
}
readonly customer$ = this.select((state) => state.customer);
readonly customerId$ = this.select((state) => state.customerId);
readonly showCustomerDetails$ = this.select((state) => state.showCustomerDetails);
ordersRoute$ = this.customer$.pipe(
map((customer) => !!customer && this._navigation.ordersRoute({ processId: Date.now(), customerId: customer?.id, customer }))
ordersRoute$ = this.customerId$.pipe(
map((customerId) => customerId && this._navigation.ordersRoute({ processId: Date.now(), customerId }))
);
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customer$]).pipe(
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customerId$]).pipe(
map(
([showCustomerDetails, customer]) =>
showCustomerDetails && !!customer && this._navigation.detailsRoute({ processId: Date.now(), customerId: customer?.id, customer })
([showCustomerDetails, customerId]) =>
showCustomerDetails && customerId && this._navigation.detailsRoute({ processId: Date.now(), customerId })
)
);

View File

@@ -20,7 +20,10 @@
<div class="page-pickup-shelf-details-header__details bg-white px-4 pt-4 pb-5">
<div class="flex flex-row items-center" [class.mb-8]="!orderItem?.features?.paid && !isKulturpass">
<page-pickup-shelf-details-header-nav-menu class="mr-2" [customer]="customer$ | async"></page-pickup-shelf-details-header-nav-menu>
<page-pickup-shelf-details-header-nav-menu
class="mr-2"
[customerId]="customerId$ | async"
></page-pickup-shelf-details-header-nav-menu>
<h2 class="page-pickup-shelf-details-header__details-header items-center">
<div class="text-h2">
{{ orderItem?.organisation }}
@@ -142,6 +145,96 @@
</div>
</div>
</div>
<div class="flex flex-row items-center relative bg-[#F5F7FA] p-4 rounded-t">
<div *ngIf="showFeature" class="flex flex-row items-center mr-3">
<ng-container [ngSwitch]="order.features.orderType">
<ng-container *ngSwitchCase="'Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'DIG-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-truck"></shared-icon>
</div>
<p class="font-bold text-p1">Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'B2B-Versand'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-b2b-truck"></shared-icon>
</div>
<p class="font-bold text-p1">B2B-Versand</p>
</ng-container>
<ng-container *ngSwitchCase="'Abholung'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-box-out"></shared-icon>
</div>
<p class="font-bold text-p1 mr-3">Abholung</p>
{{ orderItem.targetBranch }}
</ng-container>
<ng-container *ngSwitchCase="'Rücklage'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-shopping-bag"></shared-icon>
</div>
<p class="font-bold text-p1">Rücklage</p>
</ng-container>
<ng-container *ngSwitchCase="'Download'">
<div class="flex items-center justify-center bg-[#D8DFE5] w-[2.25rem] h-[2.25rem] rounded rounded-br-none mr-2">
<shared-icon [size]="24" icon="isa-download"></shared-icon>
</div>
<p class="font-bold text-p1">Download</p>
</ng-container>
</ng-container>
</div>
<div class="page-pickup-shelf-details-header__additional-addresses" *ngIf="showAddresses">
<button (click)="openAddresses = !openAddresses" class="text-[#0556B4]">
Lieferadresse / Rechnungsadresse {{ openAddresses ? 'ausblenden' : 'anzeigen' }}
</button>
<div class="page-pickup-shelf-details-header__addresses-popover" *ngIf="openAddresses">
<button (click)="openAddresses = !openAddresses" class="close">
<shared-icon icon="close" [size]="24"></shared-icon>
</button>
<div class="page-pickup-shelf-details-header__addresses-popover-data">
<div *ngIf="order.shipping" class="page-pickup-shelf-details-header__addresses-popover-delivery">
<p>Lieferadresse</p>
<div class="page-pickup-shelf-details-header__addresses-popover-delivery-data">
<ng-container *ngIf="order.shipping?.data?.organisation">
<p>{{ order.shipping?.data?.organisation?.name }}</p>
<p>{{ order.shipping?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.shipping?.data?.firstName }} {{ order.shipping?.data?.lastName }}</p>
<p>{{ order.shipping?.data?.address?.info }}</p>
<p>{{ order.shipping?.data?.address?.street }} {{ order.shipping?.data?.address?.streetNumber }}</p>
<p>{{ order.shipping?.data?.address?.zipCode }} {{ order.shipping?.data?.address?.city }}</p>
</div>
</div>
<div *ngIf="order.billing" class="page-pickup-shelf-details-header__addresses-popover-billing">
<p>Rechnungsadresse</p>
<div class="page-pickup-shelf-details-header__addresses-popover-billing-data">
<ng-container *ngIf="order.billing?.data?.organisation">
<p>{{ order.billing?.data?.organisation?.name }}</p>
<p>{{ order.billing?.data?.organisation?.department }}</p>
</ng-container>
<p>{{ order.billing?.data?.firstName }} {{ order.billing?.data?.lastName }}</p>
<p>{{ order.billing?.data?.address?.info }}</p>
<p>{{ order.billing?.data?.address?.street }} {{ order.billing?.data?.address?.streetNumber }}</p>
<p>{{ order.billing?.data?.address?.zipCode }} {{ order.billing?.data?.address?.city }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="page-pickup-shelf-details-header__select grow" *ngIf="showMultiselect$ | async">
<button class="cta-select-all" (click)="selectAll()">Alle auswählen</button>
{{ selectedOrderItemCount$ | async }} von {{ orderItemCount$ | async }} Titeln
</div>
</div>
</div>
<ng-template #featureLoading>

View File

@@ -81,6 +81,51 @@
}
}
.page-pickup-shelf-details-header__select {
@apply flex flex-col items-end;
}
.page-pickup-shelf-details-header__additional-addresses {
.page-pickup-shelf-details-header__addresses-popover {
@apply absolute inset-x-0 top-16 bottom-0 z-popover;
.close {
@apply bg-white absolute right-0 p-6;
}
.page-pickup-shelf-details-header__addresses-popover-data {
@apply flex flex-col bg-white p-6 z-popover min-h-[200px];
box-shadow: 0px 6px 24px rgba(206, 212, 219, 0.8);
.page-pickup-shelf-details-header__addresses-popover-delivery {
@apply grid mb-6;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-header__addresses-popover-delivery-data {
p {
@apply font-bold;
}
}
}
.page-pickup-shelf-details-header__addresses-popover-billing {
@apply grid;
grid-template-columns: 9.5625rem auto;
.page-pickup-shelf-details-header__addresses-popover-billing-data {
p {
@apply font-bold;
}
}
}
}
}
}
.cta-select-all {
@apply text-brand bg-transparent text-p2 font-bold outline-none border-none;
}
.fetch-wrapper {
@apply grid grid-flow-col gap-4;
}

View File

@@ -2,7 +2,7 @@ import { AsyncPipe, DatePipe, NgFor, NgIf, NgSwitch, NgSwitchCase, NgTemplateOut
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
import { DBHOrderItemListItemDTO, KeyValueDTOOfStringAndString, NotificationChannel, OrderDTO } from '@swagger/oms';
import { PickupShelfDetailsStore } from '../../store';
import { map, shareReplay, tap, withLatestFrom } from 'rxjs/operators';
import { map, shareReplay, withLatestFrom } from 'rxjs/operators';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
import { DateAdapter, UiCommonModule } from '@ui/common';
import { IconModule } from '@shared/components/icon';
@@ -92,11 +92,11 @@ export class PickUpShelfDetailsHeaderComponent {
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
today = this.dateAdapter.today();
// Daten die im Header Angezeigt werden sollen
orderItem$ = combineLatest([
this._store.selectedOrderItem$, // Wenn man im Abholfach ist muss das ausgewählte OrderItem genommen werden
this._store.selectedOrderItems$.pipe(map((orderItems) => orderItems?.find((_) => true))), // Wenn man in der Warenausgabe ist muss man das erste OrderItem nehmen
]).pipe(map(([selectedOrderItem, selectedOrderItems]) => selectedOrderItem || selectedOrderItems));
selectedOrderItemCount$ = this._store.selectedOrderItemIds$.pipe(map((ids) => ids?.length ?? 0));
orderItemCount$ = this._store.orderItems$.pipe(map((items) => items?.length ?? 0));
orderItem$ = this._store.orderItems$.pipe(map((orderItems) => orderItems?.find((_) => true)));
changeDateLoader$ = new BehaviorSubject<boolean>(false);
changePreferredDateLoader$ = new BehaviorSubject<boolean>(false);
@@ -105,9 +105,9 @@ export class PickUpShelfDetailsHeaderComponent {
changeDateDisabled$ = this.changeStatusDisabled$;
customer$ = this._store.customer$;
customerId$ = this._store.customer$.pipe(map((customer) => customer?.id));
features$ = this.customer$.pipe(
features$ = this._store.customer$.pipe(
map((customer) => customer?.features || []),
map((features) => features.filter((f) => f.enabled && !!f.description)),
shareReplay()
@@ -115,6 +115,14 @@ export class PickUpShelfDetailsHeaderComponent {
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
get isItemSelectable() {
return this._store.orderItems?.some((item) => !!item?.actions && item?.actions?.length > 0);
}
showMultiselect$ = combineLatest([this._store.orderItems$, this._store.fetchPartial$]).pipe(
map(([orderItems, fetchPartial]) => this.isItemSelectable && fetchPartial && orderItems?.length > 1)
);
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));
editButtonDisabled$ = combineLatest([this.changeStatusLoader$, this.crudaUpdate$]).pipe(
@@ -125,6 +133,20 @@ export class PickUpShelfDetailsHeaderComponent {
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate)
);
openAddresses: boolean = false;
get digOrderNumber(): string {
return this.order?.linkedRecords?.find((_) => true)?.number;
}
get showAddresses(): boolean {
return (this.order?.orderType === 2 || this.order?.orderType === 4) && (!!this.order?.shipping || !!this.order?.billing);
}
get showFeature(): boolean {
return !!this.order?.features && !!this.order?.features?.orderType;
}
constructor(private dateAdapter: DateAdapter, private cdr: ChangeDetectorRef) {}
async handleActionClick(action?: KeyValueDTOOfStringAndString) {
@@ -134,6 +156,10 @@ export class PickUpShelfDetailsHeaderComponent {
this.cdr.markForCheck();
}
selectAll() {
this._store.selectAllOrderItemIds();
}
updatePickupDeadline(date: Date) {
this.updateDate.emit({ date, type: 'pickup' });
}

View File

@@ -22,7 +22,7 @@ button {
.page-pickup-shelf-details-item__thumbnail {
img {
@apply rounded shadow-cta w-[3.625rem] max-h-[5.9375rem];
@apply rounded shadow-cta w-[3.625rem] h-[5.9375rem];
}
}

View File

@@ -77,10 +77,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
set orderItem(orderItem: DBHOrderItemListItemDTO) {
if (!isEqual(this.orderItem, orderItem)) {
// Remove Prev OrderItem from selected list
if (this.orderItem?.orderItemSubsetId !== orderItem?.orderItemSubsetId) {
this._store.selectOrderItem(this.orderItem, false);
}
this._store.selectOrderItem(this.orderItem, false);
this.patchState({ orderItem, quantity: orderItem?.quantity, more: false });
this.specialCommentControl.reset(orderItem?.specialComment);
// Add New OrderItem to selected list if selected was set to true by its input

Some files were not shown because too many files have changed in this diff Show More