Compare commits

...

52 Commits

Author SHA1 Message Date
Nino
94af2f6b76 #4712 Removed isShippingEnabled check from availabilities modal
(cherry picked from commit d5b79dcf6f)
2024-03-20 14:08:25 +01:00
Nino Righi
8f47163627 Merged PR 1762: #4692 Hotfix Undefined Values in Route Url
#4692 Hotfix Undefined Values in Route Url
2024-03-07 14:33:36 +00:00
Nino Righi
a209d59ea9 Merged PR 1760: #4532 Fallback Route if Url contains Undefined or Null values
#4532 Fallback Route if Url contains Undefined or Null values
2024-03-06 12:13:13 +00:00
Lorenz Hilpert
b838f4c475 Merge branch 'develop' into release/3.0 2024-02-01 18:33:25 +01:00
Lorenz Hilpert
2bc97ee574 #4633 WK // nach klick auf "Ändern" ("Name, Vorname" oder Adressen) im Warenkorb -> Suche nicht korrekt 2024-01-31 19:29:21 +01:00
Lorenz Hilpert
f054614cfe clearUserState() ist immer zugänglich. 2024-01-31 10:58:00 +01:00
Lorenz Hilpert
0aa1cddf72 Merge branch 'release/3.0' into develop 2024-01-29 17:37:07 +01:00
Lorenz Hilpert
d39521b9f2 Merge branch 'develop' into release/3.0 2024-01-29 17:36:25 +01:00
Lorenz Hilpert
f5468d7f8e #4624 #4623 Fallback wenn keine Versandoption für den Kundentypen vorhanden ist 2024-01-29 16:01:42 +01:00
Lorenz Hilpert
4ad99270bd #4622 AHF // Filter schließt sich nicht 2024-01-29 15:49:45 +01:00
Lorenz Hilpert
4e098ae962 #4620 Suche nach EAN wirft Fehlermeldung 2024-01-29 14:25:08 +01:00
Lorenz Hilpert
8e00e646fb #4621 Preis von Preisgebundenen Artikel wird nach Kundenauswahl nicht richtig übernommen 2024-01-29 14:19:31 +01:00
Lorenz Hilpert
4fad5a7c2f Merge branch 'develop' into release/3.0 2024-01-26 15:39:53 +01:00
Lorenz Hilpert
5ece030ec8 #2062 Fachbodenbeschriftung 2024-01-25 19:56:33 +01:00
Lorenz Hilpert
54d7c525a9 Null Check PriceMaintained und fallback für delivery kaufoptionen 2024-01-24 14:48:51 +01:00
Lorenz Hilpert
41d4dc4663 #4611 Preisunterschiede im Warenkorb 2024-01-24 13:48:52 +01:00
Lorenz Hilpert
c266c51572 #4611 Deadcode entfernt 2024-01-24 11:09:59 +01:00
Lorenz Hilpert
4ea50f68d1 #4611 Preisunterschiede im Warenkorb 2024-01-24 11:09:02 +01:00
Lorenz Hilpert
e5d61c8622 #Preis // Preisunterschiede im Warenkorb 2024-01-23 17:44:28 +01:00
Lorenz Hilpert
d06c64c08a #4613 IPad 6 Breite angepasst - wegen overflow-x 2024-01-22 10:11:12 +01:00
Lorenz Hilpert
91ebc3e27f #2062 text-right hinzugefügt 2024-01-19 16:19:08 +01:00
Lorenz Hilpert
d643c19642 Merged PR 1737: #4611 Preisunterschiede im Warenkorb
#4611 Preisunterschiede im Warenkorb
2024-01-19 13:57:36 +00:00
Nino Righi
afd1f5e302 Merged PR 1736: #2527 Improved Code, fixed navigation error when adding a new item
#2527 Improved Code, fixed navigation error when adding a new item
2024-01-19 13:09:11 +00:00
Lorenz Hilpert
4099aa0a57 #2062 #4613 Remi Liste Anpassungen 2024-01-19 13:51:49 +01:00
Nino Righi
ebe11b75d1 Merged PR 1735: #2527 Navigation Improvements Dummy Modal
#2527 Navigation Improvements Dummy Modal
2024-01-18 15:06:49 +00:00
Lorenz Hilpert
81f7270cf7 #4611 Preisunterschiede im Warenkorb 2024-01-18 15:50:46 +01:00
Nino Righi
570a8800a0 Merged PR 1734: #4010 Removed Cheaper Price Tooltip From Purchasing Options Modal
#4010 Removed Cheaper Price Tooltip From Purchasing Options Modal
2024-01-18 12:47:31 +00:00
Lorenz Hilpert
25aecffafc Merge branch 'release/3.0' into develop 2024-01-17 17:31:21 +01:00
Lorenz Hilpert
7c48c63584 Merge branch 'develop' into release/3.0 2024-01-17 17:30:50 +01:00
Nino Righi
5bf32b2e72 Merged PR 1733: #4603 Select no default gender
#4603 Select no default gender
2024-01-17 16:12:31 +00:00
Lorenz Hilpert
f44fbe3fdb #4598 ngIf angepasst Kundendetails 2024-01-17 13:21:07 +01:00
Lorenz Hilpert
5df075f448 Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2024-01-17 13:18:52 +01:00
Lorenz Hilpert
9cee33e286 #4598 Firma soll angezeigt werden wenn eingetragn 2024-01-17 13:18:33 +01:00
Nino Righi
42bf7e4120 Merged PR 1731: #4601 WA, AHF Navigation Dropdown Fix
#4601 WA, AHF Navigation Dropdown Fix
2024-01-17 12:15:25 +00:00
Nino Righi
77ff7ca1a8 Merged PR 1732: #4603 Changed Genders Order
#4603 Changed Genders Order
2024-01-17 12:14:59 +00:00
Lorenz Hilpert
7f195ee627 Merged PR 1730: #4598 Kundendetails // "Geb.-tags"-Feld wird nicht angezeigt, "USt-ID" und "A...
#4598 Kundendetails // "Geb.-tags"-Feld wird nicht angezeigt, "USt-ID" und "Abteilung" dafür immer
2024-01-16 15:12:53 +00:00
Nino Righi
79bec55818 Merged PR 1729: #4600 Fix Product Images Height
#4600 Fix Product Images Height
2024-01-16 14:05:48 +00:00
Nino Righi
35093afaff Merged PR 1728: #4597 Only Print Order Confirmation If Not On Tablet
#4597 Only Print Order Confirmation If Not On Tablet
2024-01-15 16:07:13 +00:00
Nino Righi
358ba3963c Merged PR 1727: #3462 Fix Checkout Review Details Display Name Section if Customer has no shi...
#3462 Fix Checkout Review Details Display Name Section if Customer has no shipping or billing addresses but orderType is shipping
2024-01-15 16:06:49 +00:00
Anastasiia Chetverykova
d47e617f8c Merged PR 1726: #4419 - Pop Up - Wording Änderung
#4419 - Pop Up - Wording Änderung

Related work items: #4419
2024-01-15 12:38:18 +00:00
Nino Righi
55bd001146 Merged PR 1725: #4597 Fix Print Order Confirmation Immediately
#4597 Fix Print Order Confirmation Immediately
2024-01-12 19:50:00 +00:00
Lorenz Hilpert
a9f11426a7 Merge branch 'develop' into release/3.0 2024-01-11 18:47:50 +01:00
Nino Righi
10b86756d2 Merged PR 1724: #4564 Fix customer page empty details view
#4564 Fix customer page empty details view
2024-01-11 16:19:24 +00:00
Lorenz Hilpert
262dd084c1 Merged PR 1723: #4571 - Kundendaten erfassen // Bei Kundenkarten Formular mit Onlinekonto feh...
#4571 - Kundendaten erfassen // Bei Kundenkarten Formular mit Onlinekonto fehlt die Aufklärung über die Konditionen
2024-01-11 10:27:02 +00:00
Nino Righi
abc58c8a78 Merged PR 1722: #4569 Fix Pickup Shelf display searchboxHint correctly
#4569 Fix Pickup Shelf display searchboxHint correctly
2024-01-10 15:39:08 +00:00
Nino Righi
866cd23e41 Merged PR 1721: #4564 Always keep customer updated and check name properties correctly
#4564 Always keep customer updated and check name properties correctly
2024-01-09 19:12:45 +00:00
Lorenz Hilpert
fdcf12c022 Element naming - Vorgangs-ID 2024-01-09 13:54:31 +01:00
Nino Righi
432f1161af Merged PR 1720: #4553 Always take retailPrice from instock request for take away availability
#4553 Always take retailPrice from instock request for take away availability
2024-01-08 12:59:14 +00:00
Nino Righi
82dbce5744 Merged PR 1713: #4291 Gender Refactoring
#4291 Gender Refactoring
2024-01-08 11:46:24 +00:00
Nino
a4b9f5fcf1 Merge branch 'develop' into release/3.0 2024-01-05 14:17:29 +01:00
Nino Righi
7ea9359c30 Merged PR 1719: #4514 Added regex pattern to compartmentCode field inside goods in out order...
#4514 Added regex pattern to compartmentCode field inside goods in out order edit page
2024-01-05 12:09:43 +00:00
Nino Righi
b9a4b0d315 Merged PR 1718: #4552 Added whitespace
#4552 Added whitespace
2024-01-05 09:19:08 +00:00
119 changed files with 615 additions and 308 deletions

View File

@@ -458,10 +458,6 @@ 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,
@@ -482,7 +478,7 @@ export class DomainAvailabilityService {
inStock: inStock,
supplierSSC: quantity <= inStock ? '999' : '',
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
price: this._priceIsEmpty(price) ? stockInfo?.retailPrice : price,
price: stockInfo?.retailPrice ?? price, // #4553 Es soll nun immer der retailPrice aus der InStock Abfrage verwendet werden, egal ob "price" empty ist oder nicht
supplier: { id: supplier?.id },
// TODO: Change after API Update
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice

View File

@@ -2,23 +2,26 @@ import { Injectable } from '@angular/core';
import { Logger, LogLevel } from '@core/logger';
import { Store } from '@ngrx/store';
import { UserStateService } from '@swagger/isa';
import { debounceTime, switchMap } from 'rxjs/operators';
import { debounceTime, switchMap, takeUntil } 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() {
@@ -31,7 +34,8 @@ export class RootStateService {
this._store
.select((state) => state)
.pipe(
debounceTime(500),
takeUntil(this._cancelSave),
debounceTime(1000),
switchMap((state) => {
const raw = JSON.stringify({ ...state, version: packageInfo.version });
RootStateService.SaveToLocalStorageRaw(raw);
@@ -64,13 +68,17 @@ export class RootStateService {
return false;
}
clear() {
this._userStateService
.UserStateResetUserState()
.toPromise()
.catch((error) => this._logger.log(LogLevel.ERROR, error));
RootStateService.RemoveFromLocalStorage();
window.location.reload();
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);
}
}
static SaveToLocalStorage(state: RootState) {

View File

@@ -75,6 +75,12 @@
"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,5 +74,11 @@
"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,5 +76,11 @@
"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,5 +75,11 @@
"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,5 +75,11 @@
"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,5 +76,11 @@
"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

@@ -35,7 +35,11 @@
</div>
<div class="branch-actions">
<button *ngIf="(branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0" class="cta-reserve" (click)="reserve(branch)">
<button
*ngIf="(branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0 && branch?.isShippingEnabled"
class="cta-reserve"
(click)="reserve(branch)"
>
Reservieren
</button>

View File

@@ -37,7 +37,7 @@ export class ModalAvailabilitiesComponent {
(branch) =>
branch &&
branch?.isOnline &&
branch?.isShippingEnabled &&
// branch?.isShippingEnabled && ------ Rausgenommen aufgrund des Tickets #4712
branch?.isOrderingEnabled &&
branch?.id !== userbranch?.id &&
branch?.branchType === 1

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] h-[73px]"
class="page-price-update-item__item-image w-[47px] max-h-[73px]"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[src]="productImage"

View File

@@ -338,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: 'Weitere Verfügbarkeiten',
title: 'Bestände in anderen Filialen',
data: {
item,
},

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">
<span class="label mb-2">
<ui-icon icon="recommendation" size="20px"></ui-icon>
Artikel
</span>
@@ -28,9 +28,10 @@
(click)="close.emit()"
>
<img [src]="recommendation.product?.ean | productImage: 195:315:true" alt="product-image" />
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
<div class="flex flex-col">
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
</div>
</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;
@apply flex flex-col mr-7 mt-4 no-underline text-black h-full min-w-[11rem] justify-between;
img {
@apply rounded-xl;
height: 315px;
max-width: 195px;
max-height: 19.6875rem;
max-width: 11rem;
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] h-[4.9375rem]"
class="page-search-result-item__item-image w-[3.125rem] max-h-[4.9375rem]"
loading="lazy"
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
[src]="thumbnailUrl"

View File

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

View File

@@ -1,6 +1,5 @@
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';
@@ -9,7 +8,8 @@ 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 } from '@shared/services';
import { CheckoutNavigationService, CustomerSearchNavigation } from '@shared/services';
import { Router } from '@angular/router';
@Component({
selector: 'page-checkout-dummy',
@@ -38,14 +38,15 @@ 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 _checkoutNavigationService: CheckoutNavigationService,
private readonly _customerNavigationService: CustomerSearchNavigation,
private _router: Router
) {}
ngOnInit() {
@@ -58,7 +59,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
}
});
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
if (this.hasShoppingCartItemToUpdate()) {
const data = this._ref?.data;
this._store.patchState({ shoppingCartItem: data });
this.populateFormFromModalData(data);
@@ -149,6 +150,14 @@ 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();
@@ -158,7 +167,7 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
if (this.hasShoppingCartItemToUpdate()) {
await this._store.createAddToCartItem(this.control, branch, true);
this._store.updateCart(() => {});
} else {
@@ -187,16 +196,17 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy {
try {
const branch = await this._store.currentBranch$.pipe(first()).toPromise();
if (!!this._ref?.data && Object.keys(this._ref?.data).length !== 0) {
if (this.hasShoppingCartItemToUpdate()) {
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) {
if (!customer && !this._ref?.data?.changeDataFromCart) {
filter = customerFilter;
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
await this._router.navigate(path, {
queryParams: { customertype: filter.customertype },
});
} else {
@@ -211,9 +221,10 @@ 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) {
if (!customer && !this._ref?.data?.changeDataFromCart) {
filter = customerFilter;
this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], {
const path = this._customerNavigationService.defaultRoute({ processId: this._applicationService.activatedProcessId }).path;
await this._router.navigate(path, {
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()"
(click)="openDummyModal({ changeDataFromCart: true })"
>
Hinzufügen
</button>

View File

@@ -292,15 +292,15 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy, AfterViewInit
this._store.notificationsControl = undefined;
}
openDummyModal(data?: CheckoutDummyData) {
openDummyModal({ data, changeDataFromCart = false }: { data?: CheckoutDummyData; changeDataFromCart?: boolean }) {
this.uiModal.open({
content: CheckoutDummyComponent,
data,
data: { ...data, changeDataFromCart },
});
}
changeDummyItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {
this.openDummyModal(shoppingCartItem);
this.openDummyModal({ data: shoppingCartItem, changeDataFromCart: true });
}
async changeItem({ shoppingCartItem }: { shoppingCartItem: ShoppingCartItemDTO }) {

View File

@@ -5,14 +5,10 @@
<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="!!buyer?.lastName && !!buyer?.firstName; else organisation">
<div class="mr-3">Nachname, Vorname</div>
<div class="font-bold">{{ buyer?.lastName }}, {{ buyer?.firstName }}</div>
<ng-container *ngIf="getNameFromBuyer(buyer); let name">
<div class="mr-3">{{ name.label }}</div>
<div class="font-bold">{{ name.value }}</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 } from '@angular/core';
import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core';
import { emailNotificationValidator, mobileNotificationValidator } from '@shared/components/notification-channel-control';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { combineLatest } from 'rxjs';
@@ -7,7 +7,8 @@ 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 { NotificationChannel } from '@swagger/checkout';
import { BuyerDTO, NotificationChannel } from '@swagger/checkout';
import { CustomerSearchNavigation } from '@shared/services';
@Component({
selector: 'page-checkout-review-details',
@@ -16,6 +17,8 @@ import { NotificationChannel } from '@swagger/checkout';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckoutReviewDetailsComponent implements OnInit {
customerNavigation = inject(CustomerSearchNavigation);
control: UntypedFormGroup;
customerFeatures$ = this._store.customerFeatures$;
@@ -23,20 +26,6 @@ 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(
@@ -65,6 +54,22 @@ 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(
@@ -118,6 +123,20 @@ 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();
@@ -126,7 +145,8 @@ export class CheckoutReviewDetailsComponent implements OnInit {
return;
}
const customerId = customer.source;
this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'search', `${customerId}`]);
await this.customerNavigation.navigateToDetails({ processId, customerId, customer: { customerNumber: customer.buyerNumber } });
// 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] h-[5.9375rem] rounded shadow-cta;
@apply w-[3.75rem] max-h-[5.9375rem] rounded shadow-cta;
}
}

View File

@@ -55,6 +55,8 @@
<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]"
@@ -102,7 +104,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] h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
<img class="w-[3.125rem] max-h-20 mr-2" [src]="order.product?.ean | productImage: 195:315:true" />
</a>
</div>

View File

@@ -16,7 +16,6 @@ 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({
@@ -161,6 +160,10 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
return this._environmentService.matchDesktopLarge$;
}
get isTablet() {
return this._environmentService.matchTablet();
}
expanded: boolean[] = [];
constructor(
@@ -325,6 +328,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
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();

View File

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

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] h-[4.9375rem]"
class="page-customer-order-item__item-image w-[3.125rem] max-h-[4.9375rem]"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[src]="productImage"

View File

@@ -7,8 +7,7 @@
[tabindex]="tabIndexStart"
[autofocus]="focusAfterInit"
>
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</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 { Gender } from '@swagger/crm';
import { GenderSettingsService } from '@shared/services';
@Component({
selector: 'app-name-form-block',
@@ -15,18 +15,7 @@ export class NameFormBlockComponent extends FormBlockGroup<NameFormBlockData> {
return this.tabIndexStart + 3;
}
displayGenderNameFn = (gender: Gender) => {
if (gender == 2) {
return 'Herr';
}
if (gender == 4) {
return 'Frau';
}
return undefined;
};
constructor() {
constructor(public genderSettings: GenderSettingsService) {
super();
}

View File

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

View File

@@ -64,6 +64,12 @@
>
</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, Validators.min(1)],
gender: [Validators.required],
title: [],
};

View File

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

View File

@@ -13,8 +13,7 @@
<form [formGroup]="formGroup" (ngSubmit)="save()">
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</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 } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService } 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>(0, [Validators.required, Validators.min(1)]),
gender: new FormControl<Gender>(undefined, [Validators.required]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -59,7 +59,8 @@ export class AddBillingAddressMainViewComponent {
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
) {}
async save() {

View File

@@ -27,8 +27,7 @@
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</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 } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService } 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>(0, [Validators.required, Validators.min(1)]),
gender: new FormControl<Gender>(undefined, [Validators.required]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -65,7 +65,8 @@ export class AddShippingAddressMainViewComponent implements OnInit, OnDestroy {
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
) {}
ngOnInit() {

View File

@@ -39,7 +39,9 @@
<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 class="data-value">{{ created$ | async | date: 'dd.MM.yyyy' }} | {{ created$ | async | date: 'HH:mm' }} Uhr</div>
<div *ngIf="created$ | async; let created" class="data-value">
{{ created | date: 'dd.MM.yyyy' }} | {{ created | date: 'HH:mm' }} Uhr
</div>
</div>
<div class="flex flex-row">
<div class="data-label">Kundennummer</div>
@@ -111,7 +113,7 @@
</div>
<div class="customer-details-customer-main-row">
<div class="data-label">Land</div>
<div class="data-value">{{ country$ | async | country }}</div>
<div *ngIf="country$ | async; let country" class="data-value">{{ country | country }}</div>
</div>
<div class="customer-details-customer-main-row">
<div class="data-label">Festnetznr.</div>
@@ -123,6 +125,16 @@
</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 } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService } from '@shared/services';
import { CustomerSearchStore } from '../store';
import { ShippingAddressDTO, NotificationChannel, ShoppingCartDTO, PayerDTO, BuyerDTO } from '@swagger/checkout';
import { DomainCheckoutService } from '@domain/checkout';
@@ -10,16 +10,11 @@ import { UiModalService } from '@ui/modal';
import { ComponentStore } from '@ngrx/component-store';
import { ApplicationService } from '@core/application';
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
import { Router } from '@angular/router';
import { ActivatedRoute, 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;
@@ -38,7 +33,9 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
customerService = inject(CrmCustomerService);
fetching$ = this._store.fetchingCustomer$;
fetching$ = combineLatest([this._store.fetchingCustomer$, this._store.fetchingCustomerList$]).pipe(
map(([fetchingCustomer, fetchingList]) => fetchingCustomer || fetchingList)
);
isBusy$ = this.select((s) => s.isBusy);
@@ -86,7 +83,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) => GENDER_MAP[s.customer?.gender]);
gender$ = this._store.select((s) => this._genderSettings.getGenderByValue(s.customer?.gender));
title$ = this._store.select((s) => s.customer?.title);
@@ -124,6 +121,8 @@ 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);
}
@@ -181,6 +180,10 @@ 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,
@@ -189,7 +192,9 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
private _application: ApplicationService,
private _catalogNavigation: ProductCatalogNavigationService,
private _checkoutNavigation: CheckoutNavigationService,
private _router: Router
private _router: Router,
private _activatedRoute: ActivatedRoute,
private _genderSettings: GenderSettingsService
) {
super({ isBusy: false, shoppingCart: undefined, shippingAddress: undefined, payer: undefined });
}
@@ -222,6 +227,21 @@ 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() {
@@ -409,12 +429,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,8 +13,7 @@
<form [formGroup]="formGroup" (ngSubmit)="save()">
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="1" [autofocus]="true">
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</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 } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService } 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>(0, [Validators.required, Validators.min(1)]),
gender: new FormControl<Gender>(undefined, [Validators.required]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -74,7 +74,8 @@ export class EditBillingAddressMainViewComponent extends ComponentStore<EditBill
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation,
private _activatedRoute: ActivatedRoute,
private _modal: UiModalService
private _modal: UiModalService,
public genderSettings: GenderSettingsService
) {
super({ payer: undefined });
}

View File

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

View File

@@ -16,8 +16,7 @@
<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 [value]="2" label="Herr"></ui-select-option>
<ui-select-option [value]="4" label="Frau"></ui-select-option>
<ui-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value" [label]="gender.label"></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, NavigationRoute } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService, NavigationRoute } from '@shared/services';
@Component({ template: '' })
export abstract class CustomerDataEditComponent implements OnInit {
@@ -41,7 +41,8 @@ export abstract class CustomerDataEditComponent implements OnInit {
private cdr: ChangeDetectorRef,
private location: Location,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
) {}
ngOnInit() {
@@ -90,7 +91,7 @@ export abstract class CustomerDataEditComponent implements OnInit {
this.control = fb.group(
{
gender: fb.control(customerDTO?.gender, [isBranch ? Validators.min(1) : () => null, isBranch ? Validators.required : () => null]),
gender: fb.control(customerDTO?.gender, [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,8 +27,7 @@
<shared-form-control label="Anrede">
<shared-select formControlName="gender" placeholder="Anrede" tabindex="4" [autofocus]="true">
<shared-select-option [value]="2">Herr</shared-select-option>
<shared-select-option [value]="4">Frau</shared-select-option>
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</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 } from '@shared/services';
import { CustomerSearchNavigation, GenderSettingsService } 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>(0, [Validators.required, Validators.min(1)]),
gender: new FormControl<Gender>(undefined, [Validators.required]),
title: new FormControl<string>(undefined),
firstName: new FormControl<string>(undefined, [Validators.required]),
lastName: new FormControl<string>(undefined, [Validators.required]),
@@ -83,7 +83,8 @@ export class EditShippingAddressMainViewComponent extends ComponentStore<EditShi
private _customerService: CrmCustomerService,
private _addressSelection: AddressSelectionModalService,
private _store: CustomerSearchStore,
private _navigation: CustomerSearchNavigation
private _navigation: CustomerSearchNavigation,
public genderSettings: GenderSettingsService
) {
super({ shippingAddress: undefined });
}

View File

@@ -8,7 +8,6 @@ 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';
@@ -304,7 +303,10 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
),
withLatestFrom(this.filter$, this.processId$),
map(([a1, a2, a3]) => {
this.patchState({ fetchingCustomerList: true, customerList: [], customerListCount: 0, message: '' });
// #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: '' });
let retored = false;
if (a1.ignoreRestore) {

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 && gender === 0) {
control.get('gender').setValidators([Validators.required, Validators.min(1)]);
if (!!lastName) {
control.get('gender').setValidators([Validators.required]);
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 = gender.value !== 0 && !!firstName.value && !!lastName.value;
isNameSet = !!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 = gender.value !== 0 && !!firstName.value && !!lastName.value;
isNameSet = !!firstName.value && !!lastName.value;
return validate(gender, firstName, lastName, organisation, isOrganisationSet, isNameSet);
}
};
}
function setNameValidation(gender: AbstractControl, firstName: AbstractControl, lastName: AbstractControl) {
gender.setValidators([Validators.required, Validators.min(1)]);
gender.setValidators([Validators.required]);
firstName.setValidators([Validators.required]);
lastName.setValidators([Validators.required]);
}

View File

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

View File

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

View File

@@ -125,11 +125,11 @@ export abstract class PickupShelfBaseComponent implements OnInit {
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 (['filter', 'details', 'edit', 'history'].includes(view)) {
await this.router.navigate([], { queryParams: { ...queryParams, ...filterQueryParams }, skipLocationChange: true });
return;
}
// const view: string = this.activatedRoute.snapshot.data.view;
// if (['filter', '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();

View File

@@ -79,7 +79,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
})
);
fetching$: Observable<boolean> = this.store.fetching$;
fetching$: Observable<boolean> = this.store.fetchingList$;
searchboxHint$ = this.store.searchboxHint$;
@@ -126,7 +126,7 @@ export class PickUpShelfInListComponent implements OnInit, AfterViewInit {
.subscribe(([_, queryParams]) => {
if (!this.store.list.length || !isEqual(queryParams, this.cleanupQueryParams(this.store.filter.getQueryParams()))) {
this.store.setQueryParams(queryParams);
this.store.fetchList();
this.store.fetchList({ emitFetchListResponse: false });
}
const scrollPos = this._getScrollPositionFromCache();

View File

@@ -13,7 +13,6 @@
<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.fetching$;
fetching$: Observable<boolean> = this.store.fetchingList$;
searchboxHint$ = this.store.searchboxHint$;

View File

@@ -96,6 +96,10 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
const order = await this.detailsStore.order$.pipe(take(1)).toPromise();
if (!order) {
return;
}
if (order?.orderNumber) {
return order?.orderNumber;
}
@@ -110,6 +114,12 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
const orderItemSubsetId = this.detailsStore.orderItemSubsetId;
const compartmentInfo = this.detailsStore.compartmentInfo;
// Ticket #4692 - Wenn keine Order vorhanden ist, dann soll Breadcrumb nicht erstellt werden
// Dies kann z.B. passieren bei Datenbankproblemen wenn das Fetchen der Order sehr lange braucht
if (!order) {
return;
}
return this._pickupShelfInNavigationService.detailRoute({
item: {
orderId: order?.id,

View File

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

View File

@@ -16,7 +16,6 @@
<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.fetching$;
fetching$: Observable<boolean> = this.store.fetchingList$;
searchboxHint$ = this.store.searchboxHint$;

View File

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

View File

@@ -1,6 +1,7 @@
<button
type="button"
class="px-2 py-3 bg-[#C6CBD0] rounded flex flex-row items-center open:bg-[#596470] open:text-white z-dropdown"
[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"
[cdkMenuTriggerFor]="navMenu"
#menuTrigger="cdkMenuTriggerFor"
[class.open]="menuTrigger.isOpen()"

View File

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

View File

@@ -7,11 +7,12 @@ 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 {
customerId?: number;
customer?: CustomerInfoDTO;
showCustomerDetails: boolean;
}
@@ -25,26 +26,26 @@ export interface PickUpShelfDetailsHeaderNavMenuComponentState {
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
})
export class PickUpShelfDetailsHeaderNavMenuComponent extends ComponentStore<PickUpShelfDetailsHeaderNavMenuComponentState> {
@Input() set customerId(value: NumberInput) {
this.patchState({ customerId: coerceNumberProperty(value) });
@Input() set customer(value: CustomerInfoDTO) {
this.patchState({ customer: value });
}
@Input() set showCustomerDetails(value: BooleanInput) {
this.patchState({ showCustomerDetails: coerceBooleanProperty(value) });
}
readonly customerId$ = this.select((state) => state.customerId);
readonly customer$ = this.select((state) => state.customer);
readonly showCustomerDetails$ = this.select((state) => state.showCustomerDetails);
ordersRoute$ = this.customerId$.pipe(
map((customerId) => customerId && this._navigation.ordersRoute({ processId: Date.now(), customerId }))
ordersRoute$ = this.customer$.pipe(
map((customer) => !!customer && this._navigation.ordersRoute({ processId: Date.now(), customerId: customer?.id, customer }))
);
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customerId$]).pipe(
customerDetailsRoute$ = combineLatest([this.showCustomerDetails$, this.customer$]).pipe(
map(
([showCustomerDetails, customerId]) =>
showCustomerDetails && customerId && this._navigation.detailsRoute({ processId: Date.now(), customerId })
([showCustomerDetails, customer]) =>
showCustomerDetails && !!customer && this._navigation.detailsRoute({ processId: Date.now(), customerId: customer?.id, customer })
)
);

View File

@@ -20,10 +20,7 @@
<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"
[customerId]="customerId$ | async"
></page-pickup-shelf-details-header-nav-menu>
<page-pickup-shelf-details-header-nav-menu class="mr-2" [customer]="customer$ | 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 }}

View File

@@ -101,9 +101,9 @@ export class PickUpShelfDetailsHeaderComponent {
changeDateDisabled$ = this.changeStatusDisabled$;
customerId$ = this._store.customer$.pipe(map((customer) => customer?.id));
customer$ = this._store.customer$;
features$ = this._store.customer$.pipe(
features$ = this.customer$.pipe(
map((customer) => customer?.features || []),
map((features) => features.filter((f) => f.enabled && !!f.description)),
shareReplay()

View File

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

View File

@@ -33,7 +33,7 @@ export class PickupShelfFilterComponent {
return this._activatedRoute?.parent?.children?.find((childRoute) => childRoute?.outlet === 'primary')?.snapshot?.routeConfig?.path;
}
fetching$: Observable<boolean> = this.store.fetching$;
fetching$: Observable<boolean> = this.store.fetchingList$;
searchboxHint$ = this.store.searchboxHint$;

View File

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

View File

@@ -491,13 +491,15 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
};
fetchCustomerSuccess = (res: ListResponseArgsOfCustomerInfoDTO) => {
const customers = res.result?.filter((res) => res.customerNumber === this.order.buyer.buyerNumber);
// check if response contains exactly one customer
if (res.result.length > 1) {
if (customers.length > 1) {
this._modal.error('Fehler beim Laden des Kunden', new Error('Es wurde mehr als ein Kunde gefunden.'));
return;
}
const customer = res.result[0];
const customer = customers[0];
if (!customer) {
this.patchState({ fetchingCustomer: false });

View File

@@ -1,7 +1,19 @@
import { ComponentStore, tapResponse, OnStoreInit } from '@ngrx/component-store';
import { PickupShelfState } from './pickup-shelf.state';
import { PickupShelfIOService } from '@domain/pickup-shelf';
import { delayWhen, filter, map, skipWhile, switchMap, take, takeUntil, tap, throttleTime, withLatestFrom } from 'rxjs/operators';
import {
debounceTime,
delayWhen,
filter,
map,
skipWhile,
switchMap,
take,
takeUntil,
tap,
throttleTime,
withLatestFrom,
} from 'rxjs/operators';
import {
DBHOrderItemListItemDTO,
ListResponseArgsOfDBHOrderItemListItemDTO,
@@ -13,7 +25,7 @@ import { ToasterService } from '@shared/shell';
import { UiModalService } from '@ui/modal';
import * as Selectors from './pickup-shelf.selectors';
import { Subject, combineLatest } from 'rxjs';
import { Observable, Subject, combineLatest } from 'rxjs';
import { CacheService } from '@core/cache';
import { Filter } from '@shared/components/filter';
@@ -71,22 +83,21 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
readonly listHits$ = this.select(Selectors.selectListHits);
readonly fetching$ = this.select(Selectors.selectFetchingList);
get listHits() {
return this.get(Selectors.selectListHits);
}
searchboxHint$ = combineLatest([this.fetching$, this.fetchListResponse$]).pipe(
map(([fetching, response]) => (response?.response?.hits === 0 && !fetching ? 'Keine Suchergebnisse' : ''))
);
readonly fetchingList$ = this.select(Selectors.selectFetchingList);
get fetchingList() {
return this.get(Selectors.selectFetchingList);
}
searchboxHint$ = combineLatest([this.fetchingList$, this.fetchListResponse$]).pipe(
debounceTime(100),
map(([fetching, response]) => (response?.response?.hits === 0 && !fetching ? 'Keine Suchergebnisse' : ''))
);
readonly selectedListItems$ = this.select(Selectors.selectSelectedListItems);
get selectedListItems() {
@@ -100,6 +111,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
this._destronyRef.onDestroy(() => {
this.cancelListRequests();
this._fetchListResponse.complete();
this.patchState({ fetchingList: false });
});
}
@@ -124,7 +136,6 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
cancelListRequests() {
this._cancelListRequests.next();
this.patchState({ fetchingList: false });
}
addSelectedListItem = this.updater((state, listItem: DBHOrderItemListItemDTO) => ({
@@ -193,19 +204,21 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
this.patchState({ fetchingQuerySettings: false });
};
delayWhenFilterIsNotReady = delayWhen(() =>
delayWhenFilterIsNotReady = delayWhen((value: { emitFetchListResponse: boolean } | void) =>
this.filter$.pipe(
filter((filter) => !!filter),
take(1)
)
);
fetchList = this.effect((trigger$) =>
fetchList = this.effect((trigger$: Observable<{ emitFetchListResponse: boolean } | void>) =>
trigger$.pipe(
this.delayWhenFilterIsNotReady,
withLatestFrom(this.filter$, this.processId$),
map(([_, filter, processId]) => this.beforeFetchList(filter, processId)),
switchMap(({ filter, processId, list }) =>
map(([{ emitFetchListResponse } = { emitFetchListResponse: true }, filter, processId]) =>
this.beforeFetchList(emitFetchListResponse, filter, processId)
),
switchMap(({ emitFetchListResponse, filter, processId, list }) =>
this._pickupShelfIOService
.search({
...filter.getQueryToken(),
@@ -214,7 +227,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
.pipe(
takeUntil(this._cancelListRequests),
tapResponse(
(res) => this.fetchListDone({ processId, queryParams: filter.getQueryParams() })(res),
(res) => this.fetchListDone({ processId, queryParams: filter.getQueryParams(), emitFetchListResponse })(res),
(err) => this.fetchListError(err)
)
)
@@ -222,7 +235,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
)
);
private beforeFetchList = (filter: Filter, processId: number) => {
private beforeFetchList = (emitFetchListResponse: boolean, filter: Filter, processId: number) => {
this.cancelListRequests();
const queryToken = filter.getQueryParams();
const cachedListResponse = this._cacheService.get<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken });
@@ -236,14 +249,23 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
this.patchState({ fetchingList: true, list: [], listHits: 0 });
}
return { filter, processId, list };
return { emitFetchListResponse, filter, processId, list };
};
private fetchListDone = ({ processId, queryParams }: { processId: number; queryParams: Record<string, string> }) => (
response: ListResponseArgsOfDBHOrderItemListItemDTO
) => {
private fetchListDone = ({
processId,
queryParams,
emitFetchListResponse,
}: {
processId: number;
queryParams: Record<string, string>;
emitFetchListResponse: boolean;
}) => (response: ListResponseArgsOfDBHOrderItemListItemDTO) => {
this.patchState({ fetchingList: false, list: response.result, listHits: response.hits });
this._fetchListResponse.next({ processId, response, queryParams });
if (emitFetchListResponse) {
this._fetchListResponse.next({ processId, response, queryParams });
}
this._cacheService.set<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken: queryParams }, response, { persist: true });
};

View File

@@ -12,12 +12,6 @@
>
{{ item.title }}
</h3>
<span class="product-group block text-right whitespace-nowrap text-active-branch"
>{{ item.productGroup }}:{{ item.productGroup | productGroup }}</span
>
<div class="department block text-right text-inactive-branch overflow-hidden overflow-ellipsis whitespace-nowrap">
{{ item.department }}
</div>
</div>
<div *ngIf="showImpediment" class="grid grid-flow-col items-start text-p2 font-bold text-dark-goldenrod">
<span
@@ -79,6 +73,15 @@
</div>
</div>
<div class="grid grid-flow-row-dense text-active-branch text-right">
<div>
{{ item?.dto?.department }}
<span *ngIf="item?.dto?.department && item?.dto?.shelfLabel">/</span>
{{ item?.dto?.shelfLabel }}
</div>
<div>{{ item.productGroup }}:{{ item.productGroup | productGroup }}</div>
</div>
<ng-container *ngIf="!!returnDto; else removeItem">
<div class="grid grid-flow-col justify-self-end gap-2 items-center">
<button

View File

@@ -1,7 +1,7 @@
:host {
@apply grid grid-flow-row gap-2 p-4 bg-white rounded w-full;
height: 368px;
max-height: 368px;
height: 23rem;
max-height: 23rem;
}
.item-header-details {
@@ -51,5 +51,7 @@
}
.product-data-grid {
grid-template-columns: 90px 190px auto;
max-width: 100%;
overflow: hidden;
grid-template-columns: 5.625rem 11.875rem 1fr;
}

View File

@@ -17,7 +17,7 @@
<cdk-virtual-scroll-viewport
#scrollContainer
class="remission-list"
[itemSize]="368"
[itemSize]="368 * shellService.scale"
minBufferPx="1200"
maxBufferPx="1200"
(scrolledIndexChange)="scrolledIndexChange($event)"
@@ -97,10 +97,10 @@
</div>
</div>
</div>
<div class="h-px-100"></div>
<div class="h-24"></div>
</cdk-virtual-scroll-viewport>
<div class="actions flex items-center justify-center bottom-40 sticky gap-7">
<div class="actions flex items-center justify-center bottom-12 sticky gap-7">
<ng-container>
<a
*ngIf="showStartRemissionAction$ | async"

View File

@@ -1,6 +1,6 @@
:host {
@apply flex flex-col w-full box-content relative;
max-height: calc(100vh - 284px);
max-height: calc(100vh - 10.25rem);
height: 100vh;
}
@@ -10,8 +10,6 @@
.remission-list-result {
@apply list-none bg-white rounded mb-2;
height: 368px;
max-height: 368px;
}
.filter {

View File

@@ -1,5 +1,15 @@
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild, ViewChildren, QueryList, TrackByFunction } from '@angular/core';
import {
Component,
ChangeDetectionStrategy,
OnInit,
OnDestroy,
ViewChild,
ViewChildren,
QueryList,
TrackByFunction,
inject,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
@@ -11,6 +21,7 @@ import { debounceTime, first, map, shareReplay, takeUntil, withLatestFrom } from
import { RemissionListItemComponent } from './remission-list-item';
import { RemissionListComponentStore } from './remission-list.component-store';
import { RemissionComponentStore } from './remission.component-store';
import { ShellService } from '@shared/shell';
@Component({
selector: 'page-remission-list',
@@ -20,6 +31,8 @@ import { RemissionComponentStore } from './remission.component-store';
providers: [RemissionComponentStore],
})
export class RemissionListComponent implements OnInit, OnDestroy {
shellService = inject(ShellService);
@ViewChildren(RemissionListItemComponent) listItems: QueryList<RemissionListItemComponent>;
@ViewChild('scrollContainer', { static: true })
scrollContainer: CdkVirtualScrollViewport;

View File

@@ -13,7 +13,7 @@ h1 {
img {
@apply rounded-customerCard;
width: 190px;
height: 310px;
max-height: 310px;
}
h2 {

View File

@@ -44,7 +44,7 @@
<ng-container *ngIf="expanded[i]">
<ui-form-control label="Abholfachnummer" [clearable]="true" variant="inline">
<input uiInput formControlName="compartmentCode" />
<input pattern="^\d{3,4}_\d{4}_\d{1,5}$" uiInput formControlName="compartmentCode" />
</ui-form-control>
<shared-goods-in-out-order-details-tags

View File

@@ -22,8 +22,9 @@
@apply text-center mr-6;
width: 78px;
img {
@apply rounded w-auto h-auto;
@apply rounded w-auto;
width: 60px;
max-height: 5.625rem;
box-shadow: 0 0 18px 0 #b8b3b7;
}
}

View File

@@ -10,7 +10,7 @@
<div class="w-28 page-package-list-item__estimated-delivery-date">
{{ package?.estimatedDeliveryDate | date }}
</div>
<div class="w-32 page-package-list-item__items-count">{{ package?.items ?? '-' }}<span class="font-normal">Exemplare</span></div>
<div class="w-32 page-package-list-item__items-count">{{ package?.items ?? '-' }} <span class="font-normal">Exemplare</span></div>
<div class="text-right grow">
<div
class="rounded inline-block px-4 text-white page-package-list-item__arrival-status whitespace-nowrap"

View File

@@ -83,11 +83,6 @@
</div>
<div class="shared-purchase-options-list-item__price text-right ml-4 flex flex-col items-end">
<div class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center">
<ui-svg-icon class="mr-3" [uiOverlayTrigger]="tooltip" icon="mat-info" *ngIf="priceMaintained$ | async"></ui-svg-icon>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [xOffset]="5" [closeable]="true">
Günstigerer Preis aus Hugendubel Katalog wird übernommen
</ui-tooltip>
<div class="relative flex flex-row justify-end items-start">
<ui-select
*ngIf="canEditVat$ | async"

View File

@@ -88,8 +88,6 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
price$ = this.item$.pipe(switchMap((item) => this._store.getPrice$(item.id)));
priceMaintained$ = this.price$.pipe(map((price) => price?.fromCatalogue));
priceValue$ = this.price$.pipe(map((price) => price?.value?.value));
// Ticket #4074 analog zu Ticket #2244

View File

@@ -15,6 +15,7 @@ import {
import { isGiftCard, Item, PurchaseOption, PurchaseOptionsStore } from './store';
import { delay, map, shareReplay, skip, switchMap, takeUntil, tap } from 'rxjs/operators';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
import { provideComponentStore } from '@ngrx/component-store';
@Component({
selector: 'shared-purchase-options-modal',
@@ -22,7 +23,7 @@ import { KeyValueDTOOfStringAndString } from '@swagger/cat';
styleUrls: ['purchase-options-modal.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
providers: [PurchaseOptionsStore],
providers: [provideComponentStore(PurchaseOptionsStore)],
imports: [
CommonModule,
PurchaseOptionsListHeaderComponent,

View File

@@ -256,29 +256,64 @@ export function getAvailabilityPriceForPurchaseOption(
let availability = availabilities.find((availability) => availability.purchaseOption === purchaseOption);
if (purchaseOption === 'delivery') {
/**
* Wenn Abholung oder Versand, dann den günstigeren Preis nehmen wenn priceMaintained false ist
* Genauere Infos https://dev.azure.com/hugendubel/ISA/_wiki/wikis/ISA.wiki/186/Anzeige-Preise
* 18.01.24 #4611 Preis // Preisunterschiede im Warenkorb
*/
if (purchaseOption === 'delivery' || purchaseOption === 'pickup') {
let deliveryAvailability = availabilities.find((availability) => availability.purchaseOption === 'delivery');
const digAvailability = availabilities.find((availability) => availability.purchaseOption === 'dig-delivery');
const b2bAvailability = availabilities.find((availability) => availability.purchaseOption === 'b2b-delivery');
if (isDigCustomer(state)) {
availability = digAvailability;
} else if (isB2bCustomer(state)) {
availability = b2bAvailability;
if (purchaseOption === 'delivery') {
if (isDigCustomer(state)) {
deliveryAvailability = digAvailability ?? deliveryAvailability;
} else if (isB2bCustomer(state)) {
deliveryAvailability = b2bAvailability ?? deliveryAvailability;
}
}
availability = availability || digAvailability || b2bAvailability;
deliveryAvailability = deliveryAvailability ?? digAvailability ?? b2bAvailability;
if (purchaseOption === 'delivery') {
availability = deliveryAvailability ?? availability;
}
// Wennn Artkkel nicht Preisgebinden ist, dann den günstigeren Preis nehmen wenn priceMaintained false ist
if (!availability?.data?.priceMaintained) {
// if price is not maintained, use the cheaper price
if (isItemDTO(item, type)) {
const catalogAvailability = item?.catalogAvailability?.price;
if (
catalogAvailability?.value?.value &&
availability?.data?.price?.value?.value &&
catalogAvailability?.value?.value < availability?.data?.price?.value?.value
) {
return { ...catalogAvailability, fromCatalogue: true };
const pickupAvailability = availabilities.find((availability) => availability.purchaseOption === 'pickup');
const catalogAvailability = availabilities.find((availability) => availability.purchaseOption === 'catalog');
const availabilityHasInvalidPrice = !(availability?.data?.price?.value?.value > 0);
const pickupAvailabilityHasInvalidPrice = !(pickupAvailability?.data?.price?.value?.value > 0);
const catalogAvailabilityHasInvalidPrice = !(catalogAvailability?.data?.price?.value?.value > 0);
// Early exit wenn Rücklagepreis und Versandpreis nicht vorhanden sind, dann auf den Katalogpreis zurückgreifen
if (availabilityHasInvalidPrice && pickupAvailabilityHasInvalidPrice) {
return catalogAvailabilityHasInvalidPrice ? DEFAULT_PRICE_DTO : catalogAvailability?.data?.price;
}
// Wenn beide Preise vorhanden sind, dann den günstigeren Preis nehmen
if (!availabilityHasInvalidPrice && !pickupAvailabilityHasInvalidPrice) {
// Wenn beide Preise gleich sind dann nehme den Preis für die ausgeählte Option
if (deliveryAvailability?.data?.price?.value?.value === pickupAvailability?.data?.price?.value?.value) {
return availability?.data?.price;
}
return deliveryAvailability?.data?.price?.value?.value > pickupAvailability?.data?.price?.value?.value
? pickupAvailability?.data?.price
: deliveryAvailability?.data?.price;
}
// Wenn nur der Versandpreis vorhanden ist, dann diesen nehmen
if (!availabilityHasInvalidPrice && pickupAvailabilityHasInvalidPrice) {
return deliveryAvailability?.data?.price;
}
// Wenn nur der Rücklagepreis vorhanden ist, dann diesen nehmen
if (availabilityHasInvalidPrice && !pickupAvailabilityHasInvalidPrice) {
return pickupAvailability?.data?.price;
}
}
}
@@ -295,10 +330,7 @@ export function getAvailabilityPriceForPurchaseOption(
};
}
export function getPriceForPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
export function getPriceForPurchaseOption(itemId: number, purchaseOption: PurchaseOption): (state: PurchaseOptionsState) => PriceDTO {
return (state) => {
if (getCanEditPrice(itemId)(state)) {
const price = getPrices(state)[itemId];

View File

@@ -30,6 +30,8 @@ import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE, DEFAULT_VAT_VALUE } from '../co
import { AddToShoppingCartDTO, EntityDTOContainerOfDestinationDTO, UpdateShoppingCartItemDTO } from '@swagger/checkout';
import { uniqueId } from 'lodash';
import { VATDTO } from '@swagger/oms';
import { DomainCatalogService } from '@domain/catalog';
import { ItemDTO } from '@swagger/cat';
@Injectable()
export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
@@ -133,7 +135,7 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
return this._service.getVats$();
}
constructor(private _service: PurchaseOptionsService) {
constructor(private _service: PurchaseOptionsService, private _catalogService: DomainCatalogService) {
super({
defaultBranch: undefined,
inStoreBranch: undefined,
@@ -191,6 +193,8 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
const promises: Promise<any>[] = [];
this._loadCatalogueAvailability();
items.forEach((item) => {
const itemData = mapToItemData(item, this.type);
if (isDownload(item)) {
@@ -237,6 +241,32 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
await Promise.all(promises);
}
private async _loadCatalogueAvailability() {
const items = this.items;
if (this.type === 'update') {
const items = await this._catalogService.searchByEans({ eans: this.items.map((item) => item.product.ean) }).toPromise();
if (items.result.length > 0) this._addCatalogueAvailabilities(items.result);
} else {
this._addCatalogueAvailabilities(items as ItemDTO[]);
}
}
private _addCatalogueAvailabilities(items: ItemDTO[]) {
this.updater((state) => {
const availabilities = [...state.availabilities];
items.forEach((item) => {
const availability = item.catalogAvailability;
availabilities.push({ purchaseOption: 'catalog', itemId: item.id, data: { price: availability.price } });
});
return {
...state,
availabilities,
};
});
}
private async _loadPickupAvailability(itemData: ItemData) {
const branch = this.pickupBranch;
if (!branch) return Promise.resolve();

View File

@@ -4,7 +4,7 @@ import { AvailabilityDTO, BranchDTO, ItemPayload, ShoppingCartItemDTO } from '@s
export type ActionType = 'add' | 'update';
export type PurchaseOption = 'delivery' | 'dig-delivery' | 'b2b-delivery' | 'pickup' | 'in-store' | 'download';
export type PurchaseOption = 'delivery' | 'dig-delivery' | 'b2b-delivery' | 'pickup' | 'in-store' | 'download' | 'catalog';
export type OrderType = 'Rücklage' | 'Abholung' | 'Versand' | 'Download';

View File

@@ -1,16 +0,0 @@
import { Pipe, PipeTransform } from '@angular/core';
import { Gender } from '@swagger/crm';
@Pipe({
name: 'gender',
})
export class GenderPipe implements PipeTransform {
readonly genderMap = {
2: 'Herr',
4: 'Frau',
};
transform(value: Gender): string {
return this.genderMap[value] || '';
}
}

View File

@@ -15,9 +15,9 @@ export class CustomerNamePipe implements PipeTransform {
const isB2b = customer.features?.some((f) => f.key === 'b2b') && customer.organisation?.name;
if (!isB2b) {
return `${customer.lastName} ${customer.firstName}`;
return `${customer?.lastName ?? ''} ${customer?.firstName ?? ''}`;
} else {
return `${customer.organisation.name}`;
return `${customer.organisation.name ?? ''}`;
}
}
}

View File

@@ -2,7 +2,6 @@ import { NgModule } from '@angular/core';
import { OrderStatusPipe } from './order-status.pipe';
import { AddressPipe } from './address.pipe';
import { CountryPipe } from './country.pipe';
import { GenderPipe } from './gender.pipe';
import { OrderTypePipe } from './order-type.pipe';
import { ProcessingStatusPipe } from './processing-status.pipe';
import { PaymentTypePipe } from './payment-type.pipe';
@@ -13,7 +12,6 @@ import { CustomerNamePipe } from './name.pipe';
@NgModule({
exports: [
CountryPipe,
GenderPipe,
AddressPipe,
OrderTypePipe,
ProcessingStatusPipe,
@@ -23,7 +21,7 @@ import { CustomerNamePipe } from './name.pipe';
OrderTargetPipe,
CustomerNamePipe,
],
declarations: [CountryPipe, GenderPipe, OrderTypePipe, ProcessingStatusPipe, OrderStatusPipe, ClientChannelPipe, OrderTargetPipe],
declarations: [CountryPipe, OrderTypePipe, ProcessingStatusPipe, OrderStatusPipe, ClientChannelPipe, OrderTargetPipe],
imports: [CustomerNamePipe, AddressPipe, PaymentTypePipe],
})
export class CustomerPipesModule {}

View File

@@ -1,6 +1,5 @@
// start:ng42.barrel
export * from './lib/country.pipe';
export * from './lib/gender.pipe';
export * from './lib/address.pipe';
export * from './lib/order-type.pipe';
export * from './lib/client-channel.pipe';

View File

@@ -1,5 +0,0 @@
export interface RouteData {
processId?: number;
itemId?: number;
ean?: string;
}

View File

@@ -0,0 +1,6 @@
import { GenderValue } from '../types';
export interface Gender {
value: GenderValue;
label: string;
}

View File

@@ -1,3 +1,3 @@
// start:ng42.barrel
export * from './route-data';
export * from './gender';
// end:ng42.barrel

View File

@@ -0,0 +1,24 @@
import { SpectatorService, createServiceFactory } from '@ngneat/spectator';
import { GenderSettingsService } from './gender-settings.service';
import { Config } from '@core/config';
describe('GenderSettingsService', () => {
let spectator: SpectatorService<GenderSettingsService>;
const createComponent = createServiceFactory({
service: GenderSettingsService,
mocks: [Config],
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
describe('getGenderByValue()', () => {
it('should return gender based on input value', () => {});
});
describe('_transformGendersArray()', () => {
it('should return genders array based on available genders from config', () => {});
});
});

View File

@@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import { Config } from '@core/config';
import { Gender } from './defs';
import { GenderValue } from './types';
@Injectable({ providedIn: 'root' })
export class GenderSettingsService {
private get _genderConfig(): Record<string, string> {
return this._config.get('gender');
}
get genders(): Gender[] {
return this._transformGendersArray();
}
constructor(private _config: Config) {}
getGenderByValue(value: GenderValue): string {
return this._genderConfig[value];
}
private _transformGendersArray(): Gender[] {
const genders: Gender[] = [];
for (const key of Object.keys(this._genderConfig)) {
const value = Number(key) as GenderValue;
genders.push({ value, label: this._genderConfig[key] });
}
// #4603 Genders sorted reverse { "4": "Frau", "2": "Mann", "1": "Enby", "0": "Keine Angabe" }
return genders.sort((a, b) => (a.value > b.value ? -1 : 1));
}
}

View File

@@ -0,0 +1,3 @@
// start:ng42.barrel
export * from './gender-settings.service';
// end:ng42.barrel

View File

@@ -0,0 +1,8 @@
import { Gender as CrmGender } from '@swagger/crm';
import { Gender as OmsGender } from '@swagger/oms';
import { Gender as WwsGender } from '@swagger/wws';
import { Gender as CheckoutGender } from '@swagger/checkout';
import { Gender as RemiGender } from '@swagger/remi';
import { Gender as PrintGender } from '@swagger/print';
export type GenderValue = CrmGender | OmsGender | WwsGender | CheckoutGender | RemiGender | PrintGender;

View File

@@ -0,0 +1,3 @@
// start:ng42.barrel
export * from './gender.type';
// end:ng42.barrel

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