Compare commits

...

72 Commits

Author SHA1 Message Date
Michael Auer
c162c00c8d Merge branch 'release/1.5' 2021-12-07 11:08:37 +01:00
Lorenz Hilpert
5997e67322 Merged PR 988: #2364 - dashboard book tile - overflow set to y - hidden and line-break auto
#2364 - dashboard book tile - overflow set to y - hidden and line-break auto
2021-11-26 13:42:20 +00:00
Lorenz Hilpert
cfee8e571e Merge branch 'develop' into release/1.5 2021-11-26 14:05:04 +01:00
Nino Righi
b4a7d3c879 Merged PR 987: #2456 Checkout Review Fix Setting Initial Notification Channel correctly
#2456 Checkout Review Fix Setting Initial Notification Channel correctly
2021-11-26 12:11:11 +00:00
Lorenz Hilpert
9c1565dfcc Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2021-11-26 13:10:41 +01:00
Lorenz Hilpert
a8b8573734 #2452 Abholung auf Lieferschein - Schritte zusammenfassen 2021-11-26 13:10:34 +01:00
Andreas Schickinger
c09e559430 Merged PR 985: #2449 WA WE Benachrichtigungen Take auf 4 gesetzt
#2449 WA WE Benachrichtigungen Take auf 4 gesetzt

Related work items: #2449
2021-11-25 16:53:10 +00:00
Lorenz Hilpert
cbc1b85d92 #2454 scanner fix test 2021-11-25 17:22:49 +01:00
Lorenz Hilpert
d6f3ae7179 #2454 Remi Scan Button 2021-11-25 17:14:47 +01:00
Andreas Schickinger
12c87aaa25 Merged PR 986: #2450 Artikelsuche Filter Button ipad Klasse entfernt
#2450 Artikelsuche Filter Button ipad Klasse entfernt

Related work items: #2450
2021-11-25 16:07:05 +00:00
Lorenz Hilpert
67a0230b7b #2450 piad6 - ui fehler - remission scan button 2021-11-25 15:20:55 +01:00
Lorenz Hilpert
539a6d420e #2450 piad6 ui fehler - Filter Groeße 2021-11-25 15:09:33 +01:00
Nino Righi
f1baa7c0d2 Merged PR 984: #129 Checkout Bugfixes - Näheres steht im Ticket in den Kommentaren
#129 Checkout Bugfixes - Näheres steht im Ticket in den Kommentaren
2021-11-24 15:41:23 +00:00
Andreas Schickinger
36ac3776b7 Merged PR 983: #2452 WA WE COLLECT_ON_DELIVERYNOTE Action
#2452 WA WE COLLECT_ON_DELIVERYNOTE Action

Related work items: #2452
2021-11-24 15:26:16 +00:00
Lorenz Hilpert
a6b50631dc #2382 iPad 6 - Wareneingangsliste verschiebbar 2021-11-24 15:49:25 +01:00
Lorenz Hilpert
71fdedb2d9 Merge branch 'release/1.5' into develop 2021-11-24 15:38:26 +01:00
Lorenz Hilpert
f4be47a0d7 Merge branch 'develop' into release/1.5 2021-11-24 15:38:09 +01:00
Lorenz Hilpert
295400892a #2440 ipad 6 - Vorgangszeichen 2021-11-24 15:37:02 +01:00
Lorenz Hilpert
5c3d28033f #2450 piad6 ui fehler 2021-11-24 15:36:25 +01:00
Andreas Schickinger
229cb55b46 Merged PR 982: #2419, #2426, #2413 IconBadgeComponent und Anzeige des Prämienkatalog Badge
#2419, #2426, #2413 IconBadgeComponent und Anzeige des Prämienkatalog Badge

Related work items: #2413, #2419, #2426
2021-11-23 16:32:04 +00:00
Lorenz Hilpert
99fea8c2df Merge branch 'release/1.5' into develop 2021-11-22 17:18:02 +01:00
Andreas Schickinger
1d7fd4fab1 Merged PR 981: #2444 HFI Kundensuche B2B Typ bei Menü Routing entfernt
#2444 HFI Kundensuche B2B Typ bei Menü Routing entfernt

Related work items: #2444
2021-11-22 15:31:38 +00:00
Lorenz Hilpert
76e1b7d320 Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2021-11-22 16:22:54 +01:00
Lorenz Hilpert
0d4ca1b6a6 #2426 Icon Gift and Baookmark bg 2021-11-22 16:22:45 +01:00
Andreas Schickinger
9050f33fcc Merged PR 979: #2413 PDP IsPrebooked Badge wieder entfernt
#2413 PDP IsPrebooked Badge wieder entfernt

Related work items: #2413
2021-11-22 14:31:32 +00:00
Lorenz Hilpert
8a1b151740 #2434 ipad 6 - breadcrumb funktioniert nicht 2021-11-22 15:15:58 +01:00
Andreas Schickinger
f088098159 Merged PR 978: #2413 PDP IsPrebooked Badge anzeigen
#2413 PDP IsPrebooked Badge anzeigen

Related work items: #2413
2021-11-19 08:31:13 +00:00
Andreas Schickinger
d9462a2d17 Merged PR 977: #2418 Aktuelleste Benachrichtigung wird angezeigt und Anzeige mehrerer Benach...
#2418 Aktuelleste Benachrichtigung wird angezeigt und Anzeige mehrerer Benachrichtigungen vorbereitet

Related work items: #2418
2021-11-18 15:28:49 +00:00
Nino Righi
eec72499e7 Merged PR 976: #2408 Customer Orders added History inside Order Details on every item
#2408 Customer Orders added History inside Order Details on every item
2021-11-18 12:20:44 +00:00
Andreas Schickinger
93016af766 Merged PR 975: #2413 Bookmark Tooltips angepasst, Page-Tooltip durch UI-Tooltip ersetzt
#2413 Bookmark Tooltips angepasst, Page-Tooltip durch UI-Tooltip ersetzt

Related work items: #2413
2021-11-18 11:18:48 +00:00
Nino Righi
5d699dff8f Merged PR 973: #2412 Neue Ola Availability Service Update
#2412 Neue Ola Availability Service Update
2021-11-18 10:25:03 +00:00
Nino Righi
12e4f48460 Merged PR 974: #2140 WA WE Display EstimatedShippingDate on Details Page
#2410 WA WE Display EstimatedShippingDate on Details Page
2021-11-17 17:09:40 +00:00
Andreas Schickinger
68ad3cd407 Merged PR 972: #2372 AHF Wareneingangsliste Bestelldatum anzeigen
#2372 AHF Wareneingangsliste Bestelldatum anzeigen

Related work items: #2372
2021-11-17 12:30:04 +00:00
Nino Righi
c19c4d471d Merged PR 971: #2379 TK Pdf Viewer Broad Content Fix
#2379 TK Pdf Viewer Broad Content Fix
2021-11-16 14:56:57 +00:00
Andreas Schickinger
9cf5f23b5a Merged PR 970: #2397 Update-Notiz Label zu Kommentar geändert
#2397 Update-Notiz Label zu Kommentar geändert

Related work items: #2397
2021-11-16 14:31:54 +00:00
Andreas Schickinger
b71d383c74 Merged PR 968: #129 WK Versand günstigsten Preis verwenden
#129 WK Versand günstigsten Preis verwenden

Related work items: #129
2021-11-16 13:37:36 +00:00
Nino Righi
5086c3c82d Merged PR 969: #2379 TK Pdf Viewer Scrolling Fix
#2379 TK Pdf Viewer Scrolling Fix
2021-11-16 13:37:07 +00:00
Andreas Schickinger
d98c30506e Merged PR 967: #2374 Reorder Button disabled, wenn keine Availability ausgewählt ist
#2374 Reorder Button disabled, wenn keine Availability ausgewählt ist

Related work items: #2374
2021-11-15 16:56:52 +00:00
Andreas Schickinger
6a4e5a121f Merged PR 966: #2378 Abholfrist Dropdown durch Datepicker ersetzt
#2378 Abholfrist Dropdown durch Datepicker ersetzt

Related work items: #2378
2021-11-15 16:55:56 +00:00
Nino Righi
15a8d2151e Merged PR 965: #129 Checkout PurchasingOptionsModal Price Fix
#129 Checkout PurchasingOptionsModal Price Fix
2021-11-15 12:41:05 +00:00
Nino Righi
025672bd91 Merged PR 964: #2379 TK Pdf Scrolling Fix
#2379 TK Pdf Scrolling Fix
2021-11-15 10:06:41 +00:00
Nino Righi
a925050912 Merged PR 961: #2375 PDP Added Timeout for Shipping Availability Requests
#2375 PDP Added Timeout for Shipping Availability Requests
2021-11-15 08:48:36 +00:00
Nino Righi
a4a4fc344a Merged PR 962: #2374 Reorder Modal Fix Disable Order Button if Store And TakeAway Availabili...
#2374 Reorder Modal Fix Disable Order Button if Store And TakeAway Availabilites don't exist
2021-11-15 08:25:55 +00:00
Nino Righi
bc1fc27a5d Merged PR 963: #2379 TK PDF Scrolling Ipad Fix
#2379 TK PDF Scrolling Ipad Fix
2021-11-15 08:15:37 +00:00
Nino Righi
acfd88ec55 Merged PR 960: #129 Checkout Options Modal Shipping Take Cheapest Price
#129 Checkout Options Modal Shipping Take Cheapest Price
2021-11-11 17:35:23 +00:00
Nino Righi
3745583e1c Merged PR 958: #2371 Customer Search Filter Fix
#2371 Customer Search Filter Fix
2021-11-11 15:50:48 +00:00
Andreas Schickinger
60f1a89984 Merged PR 959: #2336 WK DIG Versand Grouping fix
#2336 WK DIG Versand Grouping fix

Related work items: #2336
2021-11-11 15:48:12 +00:00
Andreas Schickinger
cbfa2fc98c Merged PR 957: #2370 Zubuchen Button disabled, wenn Zusatz abweicht
#2370 Zubuchen Button disabled, wenn Zusatz abweicht

Related work items: #2370
2021-11-11 11:52:55 +00:00
Nino Righi
7c37a0bb05 Merged PR 956: #2329 WE WA Date Range Filter Fix if no date gets selected
#2329 WE WA Date Range Filter Fix if no date gets selected
2021-11-11 10:58:04 +00:00
Michael Auer
699745526e Merge branch 'master' into develop 2021-11-11 11:03:12 +01:00
Michael Auer
ef9573a74d Merge branch 'release/1.5' 2021-11-11 10:58:04 +01:00
Michael Auer
30bdc0ee54 Merge branch 'master' into develop 2021-11-11 09:19:00 +01:00
Nino Righi
8c7217ad56 Merged PR 955: #2329 WE WA Date Range Filter Stop Value Update
#2329 WE WA Date Range Filter Stop Value Update
2021-11-10 15:43:06 +00:00
Nino Righi
4f15bc257d Merged PR 954: #2324 Added Backdrop to UiSelect Dropdowns
#2324 Added Backdrop to UiSelect Dropdowns
2021-11-10 12:22:00 +00:00
Andreas Schickinger
4c25c5769a Merged PR 950: #1965 Kundendaten erfassen Validierungslogik angepasst
#1965 Kundendaten erfassen Validierungslogik angepasst
- Speichern Button ist immer aktiv
- Validierung wird bei Klick auf Speichern ausgeführt
- Cursor springt automatisch ins erste Feld mit Validierungsfehler

Related work items: #1965
2021-11-10 12:21:05 +00:00
Nino Righi
30a9225227 Merged PR 948: #2336 WK Change Wording of DIG-Versand to Versand
#2336 WK Change Wording of DIG-Versand to Versand
2021-11-10 09:22:00 +00:00
Nino Righi
4a0d79b138 Merged PR 953: #2364 Fix Dashboard Artikelanzeige
#2364 Fix Dashboard Artikelanzeige
2021-11-10 08:14:35 +00:00
Nino Righi
8856c984dc Merged PR 952: Merge release/1.5 into develop
Merge release/1.5 into develop

Related work items: #882, #1985, #2244, #2331
2021-11-09 16:05:43 +00:00
Nino Righi
0198e635b3 Merged PR 951: Merge Develop into release/1.5
Merge Develop into release/1.5

Related work items: #882, #1985, #2244
2021-11-09 16:03:43 +00:00
Nino Righi
f477411667 Merged PR 949: #1967 WK Notification Channel Dont Tick SMS Checkbox if Email and Mobile Numb...
#1967 WK Notification Channel Dont Tick SMS Checkbox if Email and Mobile Number exist
2021-11-09 15:32:26 +00:00
Andreas Schickinger
963aaa974e Merged PR 947: #1985 Kundentyp-Checkbox nicht mehr abwählbar
#1985 Kundentyp-Checkbox nicht mehr abwählbar

Related work items: #1985
2021-11-09 15:00:32 +00:00
Andreas Schickinger
49bb23805e Merged PR 946: #2244 Preiseingabe bei Archivartikeln nur noch anzeigen, wenn keine der avail...
#2244 Preiseingabe bei Archivartikeln nur noch anzeigen, wenn keine der availabilities einen Preis besitzt

Related work items: #2244
2021-11-08 16:41:17 +00:00
Andreas Schickinger
a1dea02498 Merged PR 944: #2244 Archivartikel Preis übernehmen, wenn vorhanden
#2244 Archivartikel Preis übernehmen, wenn vorhanden

Related work items: #2244
2021-11-08 13:24:59 +00:00
Nino Righi
e7eef7f169 Merged PR 943: #2342 #2358 Autocomplete Adjust DebounceTime Values based on current Section
#2342 #2358 Autocomplete Adjust DebounceTime Values based on current Section
2021-11-08 12:56:27 +00:00
Nino Righi
072045ed59 Merged PR 942: #2343 WE, WA Fix ProcessingStatusName Mapping
#2343 WE, WA Fix ProcessingStatusName Mapping
2021-11-08 11:21:28 +00:00
Nino Righi
9078c57909 Merged PR 940: #2342 Autocomplete Requests Delay Hotfix
#2342 Autocomplete Requests Delay Hotfix
2021-11-04 06:09:47 +00:00
Nino Righi
abb15a9e28 Merged PR 941: #2331 Hotfix WA WE Edit Navigation after Status Change due to changeStockStatusCode request
#2331 Hotfix WA WE Edit Navigation after Status Change due to changeStockStatusCode request
2021-11-03 06:38:25 +00:00
Andreas Schickinger
2766751c79 Merged PR 937: #882 Filialkürzel im Header anzeigen
#882 Filialkürzel im Header anzeigen

Related work items: #882
2021-10-28 15:37:56 +00:00
Andreas Schickinger
d9df337ff0 Merged PR 939: #2331 WE/WA Bearbeiten: changeStockStatusCode Aufruf vor patch
#2331 WE/WA Bearbeiten: changeStockStatusCode Aufruf vor patch

Related work items: #2331
2021-10-28 14:03:42 +00:00
Lorenz Hilpert
5d2cfaf269 Revert "#2333 Erstellung einer Remission abbrechen"
This reverts commit e475ab6047.
2021-10-27 15:18:02 +02:00
Nino Righi
4a9b5ace01 Merged PR 936: #2334 WA Fix Routing to Details Page if Search Result Hits is 1
#2334 WA Fix Routing to Details Page if Search Result Hits is 1
2021-10-27 13:09:06 +00:00
Lorenz Hilpert
e475ab6047 #2333 Erstellung einer Remission abbrechen 2021-10-27 14:56:31 +02:00
129 changed files with 1930 additions and 1103 deletions

View File

@@ -3,7 +3,7 @@ import { ItemDTO } from '@swagger/cat';
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, StoreCheckoutService, SupplierDTO } from '@swagger/checkout';
import { Observable } from 'rxjs';
import { AvailabilityService as SwaggerAvailabilityService } from '@swagger/availability';
import { map, shareReplay, switchMap, withLatestFrom, mergeMap } from 'rxjs/operators';
import { map, shareReplay, switchMap, withLatestFrom, mergeMap, timeout } from 'rxjs/operators';
import { isArray, memorize } from '@utils/common';
import { OrderService } from '@swagger/oms';
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
@@ -184,7 +184,9 @@ export class DomainAvailabilityService {
estimatedShippingDate: preferred?.at,
price: preferred?.price,
inStock: totalAvailable,
supplierProductNumber: preferred.supplierProductNumber,
supplierProductNumber: preferred?.supplierProductNumber,
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}
@@ -205,6 +207,7 @@ export class DomainAvailabilityService {
},
])
.pipe(
timeout(5000),
map((r) => {
const availabilities = r.result;
const preferred = availabilities.find((f) => f.preferred === 1);
@@ -216,7 +219,9 @@ export class DomainAvailabilityService {
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.at,
price: preferred?.price,
supplierProductNumber: preferred.supplierProductNumber,
supplierProductNumber: preferred?.supplierProductNumber,
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
@@ -236,6 +241,7 @@ export class DomainAvailabilityService {
},
])
.pipe(
timeout(5000),
map((r) => {
const availabilities = r.result;
const preferred = availabilities.find((f) => f.preferred === 1);
@@ -249,7 +255,9 @@ export class DomainAvailabilityService {
estimatedShippingDate: preferred?.at,
price: preferred?.price,
logistician: { id: preferred?.logisticianId },
supplierProductNumber: preferred.supplierProductNumber,
supplierProductNumber: preferred?.supplierProductNumber,
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
@@ -266,6 +274,7 @@ export class DomainAvailabilityService {
const currentBranch$ = this.getCurrentBranch();
return currentBranch$.pipe(
timeout(5000),
mergeMap((branch) =>
this.getPickUpAvailability({ item, quantity, branch }).pipe(
mergeMap((availability) => logistician$.pipe(map((logistician) => ({ ...availability, logistician: { id: logistician.id } })))),
@@ -299,8 +308,10 @@ export class DomainAvailabilityService {
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.at,
price: preferred?.price,
supplierProductNumber: preferred.supplierProductNumber,
supplierProductNumber: preferred?.supplierProductNumber,
logistician: { id: preferred?.logisticianId },
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),

View File

@@ -395,11 +395,20 @@ export class DomainCheckoutService {
return availability$.pipe(
mergeMap((availability) => {
let updatedAvailability = availability;
// Bei Preisupdate der Versandoptionen soll immer der Preis genommen werden, der im Warenkorb steht
if (orderType === 'DIG-Versand' || orderType === 'B2B-Versand') {
const itemPrice = itemToUpdate.data.availability.price;
updatedAvailability = {
...availability,
price: itemPrice,
};
}
return this.updateItemInShoppingCart({
processId,
shoppingCartItemId: itemToUpdate.id,
update: {
availability,
availability: updatedAvailability,
},
});
})

View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderService } from '@swagger/oms';
import { OrderItemsContext } from './order-items.context';
@Injectable()
export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItemsContext> {
constructor(private orderService: OrderService) {
super('COLLECT_ON_DELIVERYNOTE');
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
const orderItemSubsetIds = data.items.map((item) => item.orderItemSubsetId);
const response = await this.orderService
.OrderCollectOnDeliveryNote({
data: orderItemSubsetIds,
})
.toPromise();
// Für korrekte Navigation nach Aufruf, da ProcessingStatus Serverseitig auf abgeholt gesetzt wird
data.items?.forEach((i) => (i.processingStatus = 256));
return {
...data,
receipts: response.result,
};
}
}

View File

@@ -34,4 +34,5 @@ export * from './reserved.action-handler';
export * from './returned-by-buyer.action-handler';
export * from './shipping-note.action-handler';
export * from './supplier-temporarily-out-of-stock.action-handler copy';
export * from './collect-on-deliverynote.action-handler';
// end:ng42.barrel

View File

@@ -202,20 +202,25 @@ export class DomainOmsService {
orderId,
orderItemId,
orderItemSubsetId,
take,
skip,
}: {
orderId: number;
orderItemId: number;
orderItemSubsetId: number;
}): Observable<Record<string, Date>> {
take?: number;
skip?: number;
}): Observable<Record<string, Date[]>> {
return this.orderService
.OrderGetOrderItemSubsetTasks({ orderId, orderItemId, orderItemSubsetId, completed: new Date(0).toISOString() })
.OrderGetOrderItemSubsetTasks({ orderId, orderItemId, orderItemSubsetId, completed: new Date(0).toISOString(), take, skip })
.pipe(
map((res) =>
res.result.reduce((data, result) => {
data[result.name] = new Date(result.completed);
return data;
}, {} as Record<string, Date>)
res.result
.sort((a, b) => new Date(b.completed).getTime() - new Date(a.completed).getTime())
.reduce((data, result) => {
(data[result.name] = data[result.name] || []).push(new Date(result.completed));
return data;
}, {} as Record<string, Date[]>)
)
);
}

View File

@@ -2,13 +2,20 @@
<div class="customer">
<div class="row">
<span class="label">Kundenname</span>
<strong *ngIf="ref.data.item.organisation">{{ ref.data.item.organisation }}, </strong>
<strong>{{ ref.data.item.lastName }}, {{ ref.data.item.firstName }}</strong>
<ng-container *ngIf="ref.data.mode === 'customer' || ref.data.mode === 'goods'">
<strong *ngIf="ref.data.item.organisation">{{ ref.data.item.organisation }}, </strong>
<strong>{{ ref.data.item.lastName }}, {{ ref.data.item.firstName }}</strong>
</ng-container>
<ng-container *ngIf="ref.data.mode === 'order'">
<strong *ngIf="ref.data.item.order.buyer.organisation">{{ ref.data.item.order.buyer.organisation }}, </strong>
<strong>{{ ref.data.item.order.buyer.lastName }}, {{ ref.data.item.order.buyer.firstName }}</strong>
</ng-container>
</div>
<div class="row">
<span class="label">Kundennummer</span>
<strong *ngIf="ref.data.mode === 'customer'">{{ ref.data.item.customerNumber }}</strong>
<strong *ngIf="ref.data.mode === 'goods'">{{ ref.data.item.buyerNumber }}</strong>
<strong *ngIf="ref.data.mode === 'order'">{{ ref.data.item.order.buyer.buyerNumber }}</strong>
</div>
</div>
<hr />
@@ -22,7 +29,7 @@
<div *ngIf="history.changedBy">| {{ history.changedBy }}</div>
</div>
<div class="row details title">
<ng-container *ngIf="ref?.data?.mode === 'goods'; else customer">
<ng-container *ngIf="ref?.data?.mode === 'goods' || ref?.data?.mode === 'order'; else customer">
Status der Bestellung wurde geändert
</ng-container>
<ng-template #customer>

View File

@@ -3,7 +3,7 @@ import { CrmCustomerService } from '@domain/crm';
import { DomainOmsService } from '@domain/oms';
import { CustomerDTO } from '@swagger/crm';
import { HistoryDTO as CrmHistoryDTO } from '@swagger/crm';
import { HistoryDTO as OmsHistoryDTO } from '@swagger/oms';
import { HistoryDTO as OmsHistoryDTO, OrderDTO, OrderItemDTO } from '@swagger/oms';
import { OrderItemListItemDTO } from '@swagger/oms';
import { UiModalRef } from '@ui/modal';
import { BehaviorSubject, Observable, of } from 'rxjs';
@@ -23,7 +23,10 @@ export class HistoryComponent implements OnInit {
history$: Observable<CrmHistoryDTO | OmsHistoryDTO>;
constructor(
public ref: UiModalRef<any, { mode: 'goods' | 'customer'; item: CustomerDTO | OrderItemListItemDTO }>,
public ref: UiModalRef<
any,
{ mode: 'goods' | 'customer' | 'order'; item: CustomerDTO | OrderItemListItemDTO | { order: OrderDTO; orderItemSubsetId: number } }
>,
private customerService: CrmCustomerService,
private omsService: DomainOmsService
) {}
@@ -62,6 +65,24 @@ export class HistoryComponent implements OnInit {
})
);
break;
case 'order':
this.history$ = this.omsService
.getHistory((this.ref.data.item as { order: OrderDTO; orderItemSubsetId: number }).orderItemSubsetId)
.pipe(
map((history) => {
this.spinner$.next(false);
if ((history as any).length === 0) {
this.historyEmpty$.next(true);
}
return history;
}),
catchError(() => {
this.historyError$.next(true);
this.spinner$.next(false);
return of();
})
);
break;
}
}
}

View File

@@ -77,7 +77,7 @@
Nicht lieferbar
</ui-spinner>
</button>
<button class="cta-reorder cta-action-primary" [disabled]="ctaDisabled$ | async" (click)="reorder()">
<button class="cta-reorder cta-action-primary" [disabled]="ctaReorderDisabled$ | async" (click)="reorder()">
<ui-spinner [show]="ctaDisabled$ | async">
Bestellen
</ui-spinner>

View File

@@ -4,7 +4,7 @@ import { ComponentStore } from '@ngrx/component-store';
import { AvailabilityDTO2, OrderItemListItemDTO } from '@swagger/oms';
import { UiModalRef } from '@ui/modal';
import { combineLatest } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators';
import { ReorderResult } from './reorder.result';
interface GoodsInListReorderModalState {
@@ -37,6 +37,8 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
}
}
readonly checkedAvailability$ = this.select((s) => s.checkedAvailability);
get ctaDisabled() {
return this.get((s) => s.ctaDisabled);
}
@@ -47,8 +49,20 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
}
readonly ctaDisabled$ = this.select((s) => s.ctaDisabled);
readonly storeAvailabilityError$ = this.select((s) => s.storeAvailabilityError);
readonly takeAwayAvailabilityError$ = this.select((s) => s.takeAwayAvailabilityError);
readonly storeAvailabilityError$ = this.select((s) => s.storeAvailabilityError).pipe(shareReplay());
readonly takeAwayAvailabilityError$ = this.select((s) => s.takeAwayAvailabilityError).pipe(shareReplay());
readonly ctaReorderDisabled$ = combineLatest([
this.ctaDisabled$,
this.storeAvailabilityError$,
this.takeAwayAvailabilityError$,
this.checkedAvailability$,
]).pipe(
map(
([ctaDisabled, storeAvailabilityError, takeAwayAvailabilityError, checkedAvailability]) =>
ctaDisabled || !checkedAvailability || (storeAvailabilityError && takeAwayAvailabilityError)
)
);
readonly currentBranch$ = this.domainAvailabilityService.getCurrentBranch();

View File

@@ -1,14 +1,25 @@
<div #detailsContainer class="product-card">
<ng-container *ngIf="store.item$ | async; let item">
<div class="product-details">
<div class="bookmark" *ngIf="item?.features && (item?.features)[0]?.key === 'PFO'">
<button class="bookmark-badge" (click)="showTooltip = !showTooltip">
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
</button>
<page-tooltip *ngIf="showTooltip" (close)="showTooltip = false"
>Dieser Artikel ist nur über die Abo <br />
Abteilung bestellbar.</page-tooltip
>
<div class="bookmark">
<div *ngIf="showSubscriptionBadge$ | async">
<button [uiOverlayTrigger]="subscribtionTooltip" class="bookmark-badge">
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
</button>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true"
>Artikel ist ein Fortsetzungsartikel,<br />
Artikel muss über eine Aboabteilung<br />
bestellt werden.
</ui-tooltip>
</div>
<div *ngIf="showPromotionBadge$ | async" class="promotion-badge">
<button [uiOverlayTrigger]="promotionTooltip" class="bookmark-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">
Dieser Artikel befindet sich im Prämienkatalog.
</ui-tooltip>
</button>
</div>
</div>
<div class="product-image">
@@ -25,7 +36,7 @@
</div>
<div class="product-info">
<div class="row" [class.bookmark-badge-gap]="item?.features && (item?.features)[0]?.key === 'PFO'">
<div class="row" [class.bookmark-badge-gap]="isBadgeVisible$ | async">
<div>
<a
*ngFor="let contributor of contributors$ | async; let last = last"

View File

@@ -11,7 +11,7 @@
@apply flex flex-row p-5;
.bookmark {
@apply absolute;
@apply flex absolute;
right: 37px;
top: 57px;
}
@@ -20,6 +20,10 @@
@apply p-0 m-0 outline-none border-none bg-transparent relative;
}
.promotion-badge {
margin-top: -1px;
}
.bookmark-badge-gap {
@apply mt-px-35;
}
@@ -258,9 +262,3 @@
.autor {
@apply text-active-customer !important font-bold no-underline;
}
page-tooltip {
@apply relative;
bottom: 35px;
left: 15px;
}

View File

@@ -57,8 +57,15 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
this.store.isDeliveryB2BAvailabilityAvailable$,
]).pipe(map(([digDelivery, b2bDelivery]) => b2bDelivery && !digDelivery));
showSubscriptionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'PFO')));
showPromotionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'Promotion')));
isBadgeVisible$ = combineLatest([this.showSubscriptionBadge$, this.showPromotionBadge$]).pipe(
map(([showSubscriptionBadge, showPromotionBadge]) => showSubscriptionBadge || showPromotionBadge)
);
contributors$ = this.store.item$.pipe(map((item) => item?.product?.contributors?.split(';').map((m) => m.trim())));
showTooltip = false;
constructor(
private applicationService: ApplicationService,

View File

@@ -7,11 +7,22 @@ import { RouterModule } from '@angular/router';
import { UiStarsModule } from '@ui/stars';
import { UiSliderModule } from '@ui/slider';
import { ArticleRecommendationsComponent } from './recommendations/article-recommendations.component';
import { TooltipModule } from '../shared/tooltip/tooltip.module';
import { PipesModule } from '../shared/pipes/pipes.module';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
@NgModule({
imports: [CommonModule, ProductImageModule, UiIconModule, RouterModule, UiStarsModule, UiSliderModule, TooltipModule, PipesModule],
imports: [
CommonModule,
ProductImageModule,
UiIconModule,
RouterModule,
UiStarsModule,
UiSliderModule,
UiCommonModule,
UiTooltipModule,
PipesModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],
})

View File

@@ -1,4 +1,4 @@
<button class="filter" [class.positionIpad]="isIpad" [class.active]="hasFilter$ | async" (click)="toggleFilterOverlay()">
<button class="filter" [class.active]="hasFilter$ | async" (click)="toggleFilterOverlay()">
<ui-icon size="20px" icon="filter_alit"></ui-icon>
<span class="label">Filter</span>
</button>

View File

@@ -1,6 +1,11 @@
:host {
@apply flex flex-col w-full box-content absolute;
}
.filter {
@apply absolute font-sans flex items-center font-bold bg-inactive-customer border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0 top-px-10;
@apply absolute font-sans flex items-center font-bold bg-inactive-customer border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0;
min-width: 106px;
top: -52px;
.label {
@apply ml-px-5;
@@ -11,10 +16,6 @@
}
}
.positionIpad {
@apply top-0 !important;
}
page-article-search-filter {
@apply fixed right-0 bottom-0 left-0 z-fixed;
top: 136px;

View File

@@ -5,7 +5,6 @@ import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
import { UiFilterAutocompleteProvider, UiFilterScanProvider } from '@ui/filter';
import { UiFilter } from 'apps/ui/filter/src/lib/next';
import { NativeContainerService } from 'native-container';
import { Observable, Subscription } from 'rxjs';
import { filter, first, map } from 'rxjs/operators';
import { ArticleSearchService } from './article-search.store';
@@ -43,8 +42,6 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
subscriptions = new Subscription();
isIpad = this.nativeContainer.isUiWebview()?.isNative;
hasFilter$: Observable<boolean> = this.articleSearch.filter$.pipe(
filter((filter: UiFilter) => !!filter?.getQueryToken()?.filter),
map((filter: UiFilter) => {
@@ -56,8 +53,7 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
private breadcrumb: BreadcrumbService,
private application: ApplicationService,
private router: Router,
private articleSearch: ArticleSearchService,
private nativeContainer: NativeContainerService
private articleSearch: ArticleSearchService
) {}
ngOnInit() {

View File

@@ -49,14 +49,14 @@
</ui-searchbox-autocomplete>
</ui-searchbox>
<ng-container *ngIf="isFilter$ | async">
<button class="info-tooltip-button" (click)="showTooltip = !showTooltip" type="button">
<button class="info-tooltip-button" [uiOverlayTrigger]="infoTooltip" type="button">
i
</button>
<page-tooltip *ngIf="showTooltip" (close)="showTooltip = false"
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #infoTooltip [closeable]="true"
>Um Begriffe auszuschließen, geben Sie diese <br />
mit einem ! oder ~ davor in das Suchfeld ein. <br />
Um exakt einen Begriff zu suchen, wählen <br />
Sie &bdquo;Exakt&ldquo; aus.</page-tooltip
Sie &bdquo;Exakt&ldquo; aus.</ui-tooltip
>
</ng-container>
</div>

View File

@@ -5,12 +5,12 @@ import { UiCommonModule } from '@ui/common';
import { UiIconModule } from '@ui/icon';
import { UiInputModule } from '@ui/input';
import { UiSearchboxModule } from '@ui/searchbox';
import { TooltipModule } from '../../../shared/tooltip/tooltip.module';
import { UiTooltipModule } from '@ui/tooltip';
import { ArticleSearchboxComponent } from './article-searchbox.component';
@NgModule({
imports: [CommonModule, FormsModule, UiIconModule, UiSearchboxModule, UiInputModule, UiCommonModule, TooltipModule],
imports: [CommonModule, FormsModule, UiIconModule, UiSearchboxModule, UiInputModule, UiCommonModule, UiTooltipModule],
exports: [ArticleSearchboxComponent],
declarations: [ArticleSearchboxComponent],
providers: [],

View File

@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
@Injectable()
export class ArticleSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
for = 'main';
for = 'catalog';
constructor(private domainCatalogSearch: DomainCatalogService) {
super();

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { UiFilterScanProvider } from '@ui/filter';
import { NativeContainerService } from 'native-container';
import { NEVER, Observable, of } from 'rxjs';
import { catchError, delay, filter, map, mergeMap, tap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { catchError, filter, map } from 'rxjs/operators';
@Injectable()
export class ArticleSearchMainScanProviderService extends UiFilterScanProvider {

View File

@@ -6,10 +6,15 @@
<div class="catalog-search-filter-content-main">
<h1 class="title">Filter</h1>
<ui-filter [filter]="filter" [loading]="fetching$ | async" (search)="applyFilter(filter)" [hint]="searchboxHint$ | async"></ui-filter>
<ui-filter
[filter]="filter"
[loading]="fetching$ | async"
(search)="applyFilter(filter)"
[hint]="searchboxHint$ | async"
resizeInputOptionsToElement="page-article-search-filter .cta-wrapper"
></ui-filter>
</div>
</div>
<div class="cta-wrapper">
<button class="cta-reset-filter" (click)="resetFilter(filter)">
Filter zurücksetzen

View File

@@ -17,10 +17,6 @@
}
}
::ng-deep ui-filter-input-options .input-options {
max-height: calc(100vh - 555px);
}
.cta-wrapper {
@apply fixed bottom-8 whitespace-nowrap;
left: 50%;

View File

@@ -1,12 +0,0 @@
<div (click)="close.emit(true)" class="backdrop"></div>
<div class="info-tooltip-box">
<button class="close-btn" (click)="close.emit(true)">
<ui-icon icon="close" size="15px"></ui-icon>
</button>
<div class="info-tooltip">
<p>
<ng-content></ng-content>
</p>
</div>
<div class="info-tooltip-triangle"></div>
</div>

View File

@@ -1,37 +0,0 @@
:host {
@apply flex self-center;
}
.backdrop {
@apply fixed w-screen h-screen z-tooltip left-0 top-0;
}
.info-tooltip-box {
@apply absolute z-modal;
.close-btn {
@apply bg-transparent border-none text-white relative cursor-pointer z-modal;
bottom: 115px;
left: 50px;
}
.info-tooltip {
@apply relative bg-dark-cerulean rounded-card p-4;
width: 375px;
left: -325px;
top: -146px;
p {
@apply m-0 text-white font-bold text-base;
}
}
.info-tooltip-triangle {
@apply relative bg-dark-cerulean;
transform: rotate(45deg);
width: 20px;
height: 20px;
top: -159px;
left: 6px;
}
}

View File

@@ -1,15 +0,0 @@
import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core';
@Component({
selector: 'page-tooltip',
templateUrl: 'tooltip.component.html',
styleUrls: ['tooltip.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TooltipComponent implements OnInit {
@Output() close = new EventEmitter<boolean>();
isOpen = false;
constructor() {}
ngOnInit() {}
}

View File

@@ -1,13 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { UiIconModule } from '@ui/icon';
import { TooltipComponent } from './tooltip.component';
@NgModule({
imports: [CommonModule, UiIconModule],
exports: [TooltipComponent],
declarations: [TooltipComponent],
providers: [],
})
export class TooltipModule {}

View File

@@ -79,7 +79,11 @@
"
></ui-icon>
<div class="label" [class.dummy]="group.orderType === 'Dummy'">
{{ group.orderType !== 'Dummy' ? group.orderType : 'Manuelle Anlage / Dummy Bestellung' }}
<ng-container [ngSwitch]="group.orderType">
<ng-container *ngSwitchCase="'Dummy'">Manuelle Anlage / Dummy Bestellung</ng-container>
<ng-container *ngSwitchCase="'DIG-Versand'">Versand</ng-container>
<ng-container *ngSwitchDefault> {{ group.orderType }} </ng-container>
</ng-container>
<a *ngIf="group.orderType === 'Dummy'" class="cta-secondary" [routerLink]="['/cart', 'dummy']">Hinzufügen</a>
</div>
</div>
@@ -133,7 +137,7 @@
*ngIf="group.orderType !== 'Rücklage' && group.orderType !== 'Download' && group.orderType !== 'Dummy'"
class="product-delivery"
>
{{ group.orderType }} {{ group.orderType === 'Abholung' ? 'ab' : '' }}
{{ group.orderType === 'DIG-Versand' ? 'Versand' : group.orderType }} {{ group.orderType === 'Abholung' ? 'ab' : '' }}
{{ item?.availability?.estimatedShippingDate | date }}
</div>
<div *ngIf="group.orderType === 'Dummy'" class="product-delivery">

View File

@@ -14,6 +14,7 @@ import { Subject, NEVER } from 'rxjs';
import { DomainCatalogService } from '@domain/catalog';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainPrinterService } from '@domain/printer';
import { ResponseArgsOfItemDTO } from '@swagger/cat';
@Component({
selector: 'page-checkout-review',
@@ -49,13 +50,23 @@ export class CheckoutReviewComponent implements OnInit {
takeUntil(this._orderCompleted),
map((items) =>
items.reduce((grouped, item) => {
// let index = grouped.findIndex((g) => g?.orderType === item?.features?.orderType && g.destination?.id === item?.destination?.id);
let index = grouped.findIndex((g) => g?.orderType === 'Dummy' || g?.orderType === item?.features?.orderType);
let group = index !== -1 ? grouped[index] : undefined;
let index = grouped.findIndex((g) =>
item?.availability?.supplyChannel === 'MANUALLY'
? g?.orderType === 'Dummy'
: item?.features?.orderType === 'DIG-Versand'
? g?.orderType === 'Versand'
: g?.orderType === item?.features?.orderType
);
let group = index !== -1 ? grouped[index] : undefined;
if (!group) {
group = {
orderType: item?.availability?.supplyChannel === 'MANUALLY' ? 'Dummy' : item.features.orderType,
orderType:
item?.availability?.supplyChannel === 'MANUALLY'
? 'Dummy'
: item.features.orderType === 'DIG-Versand'
? 'Versand'
: item.features.orderType,
destination: item.destination?.data,
items: [],
};
@@ -199,16 +210,28 @@ export class CheckoutReviewComponent implements OnInit {
.pipe(map((branches) => branches.find((branch) => (branchId ? branch.id === branchId : branch.branchNumber === branchNo))))
.toPromise();
const takeAwayAvailability = await this.availabilityService
.getTakeAwayAvailability({
item: {
itemId: Number(shoppingCartItem.product.catalogProductNumber),
ean: shoppingCartItem.product.ean,
price: shoppingCartItem.availability.price,
},
quantity,
})
.toPromise();
let catalogItem: ResponseArgsOfItemDTO;
if (Number.isInteger(shoppingCartItem?.product?.catalogProductNumber)) {
catalogItem = await this.domainCatalogService
.getDetailsById({ id: Number(shoppingCartItem.product.catalogProductNumber) })
.toPromise();
} else if (shoppingCartItem?.product?.ean) {
catalogItem = await this.domainCatalogService.getDetailsByEan({ ean: shoppingCartItem.product.ean }).toPromise();
}
let takeAwayAvailability: AvailabilityDTO;
if (!!catalogItem?.result?.product) {
takeAwayAvailability = await this.availabilityService
.getTakeAwayAvailability({
item: {
itemId: catalogItem.result.id,
ean: catalogItem.result.product.ean,
price: catalogItem.result.catalogAvailability?.price,
},
quantity,
})
.toPromise();
}
const pickupAvailability = await this.availabilityService
.getPickUpAvailability({
@@ -345,6 +368,8 @@ export class CheckoutReviewComponent implements OnInit {
async updateItemQuantity(shoppingCartItem: ShoppingCartItemDTO, quantity: number) {
this.showQuantityControlSpinnerItemId = shoppingCartItem.id;
const shoppingCartItemPrice = shoppingCartItem?.availability?.price?.value?.value;
const orderType = shoppingCartItem?.features?.orderType;
let availability: AvailabilityDTO;
@@ -429,14 +454,25 @@ export class CheckoutReviewComponent implements OnInit {
}
}
if (quantity === 0 || availability) {
if (quantity === 0) {
await this.domainCheckoutService
.updateItemInShoppingCart({
processId: this.applicationService.activatedProcessId,
shoppingCartItemId: shoppingCartItem.id,
update: {
quantity,
availability,
availability: null,
},
})
.toPromise();
} else if (availability) {
await this.domainCheckoutService
.updateItemInShoppingCart({
processId: this.applicationService.activatedProcessId,
shoppingCartItemId: shoppingCartItem.id,
update: {
quantity,
availability: this.compareDeliveryAndCatalogPrice(availability, orderType, shoppingCartItemPrice),
},
})
.toPromise();
@@ -446,6 +482,23 @@ export class CheckoutReviewComponent implements OnInit {
this.showQuantityControlSpinnerItemId = undefined;
}
// Bei unbekannten Kunden und DIG Bestellung findet ein Vergleich der Preise statt
compareDeliveryAndCatalogPrice(availability: AvailabilityDTO, orderType: string, shoppingCartItemPrice: number) {
if (['Versand', 'DIG-Versand'].includes(orderType) && shoppingCartItemPrice < availability?.price?.value?.value) {
return {
...availability,
price: {
...availability.price,
value: {
...availability.price.value,
value: shoppingCartItemPrice,
},
},
};
}
return availability;
}
async changeAddress() {
const processId = this.applicationService.activatedProcessId;
const customer = await this.domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();

View File

@@ -2,7 +2,10 @@
<ui-checkbox [ngModel]="ncs.hasEmail && (ncs.notificationChannelEmail$ | async)" (ngModelChange)="check($event, 'email')">
E-Mail
</ui-checkbox>
<ui-checkbox [ngModel]="ncs.hasMobile && (ncs.notificationChannelMobile$ | async)" (ngModelChange)="check($event, 'sms')">
<ui-checkbox
[ngModel]="!(ncs.hasEmail && ncs.hasMobile) && ncs.hasMobile && (ncs.notificationChannelMobile$ | async)"
(ngModelChange)="check($event, 'sms')"
>
SMS
</ui-checkbox>
</div>

View File

@@ -20,6 +20,24 @@ export class NotificationCheckboxComponent implements OnInit {
ngOnInit() {
this.ncs.checkEmailOrMobileExist();
this.setInitialNotificationChannel();
}
setInitialNotificationChannel() {
if (this.ncs.hasEmail && this.ncs.hasMobile) {
this.ncs.smsNotification = undefined;
this.ncs.emailNotification = 1;
} else if (this.ncs.hasEmail && !this.ncs.hasMobile) {
this.ncs.smsNotification = undefined;
this.ncs.emailNotification = 1;
} else if (!this.ncs.hasEmail && this.ncs.hasMobile) {
this.ncs.emailNotification = undefined;
this.ncs.smsNotification = 2;
} else {
this.ncs.emailNotification = undefined;
this.ncs.smsNotification = undefined;
}
this.onChangeNotificationChannel();
}
check(event: true | undefined, option: 'email' | 'sms') {

View File

@@ -7,7 +7,17 @@
<p>
Möchten Sie den Artikel geliefert bekommen?
</p>
<span class="price">{{ availability.price?.value?.value | currency: availability.price?.value?.currency:'code' }}</span>
<div class="price-wrapper">
<span class="price">{{ price$ | async | currency: availability.price?.value?.currency:'code' }}</span>
<ng-container *ngIf="showTooltip$ | async">
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
i
</button>
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
Günstigerer Preis aus Hugendubel Katalog wird übernommen
</ui-tooltip>
</ng-container>
</div>
<div class="grow"></div>
<span class="delivery">Versandkostenfrei</span>
<span class="date"

View File

@@ -0,0 +1,11 @@
.price-wrapper {
@apply mt-2;
}
.info-tooltip-button {
@apply border-font-customer bg-white rounded-full text-base font-bold;
border-style: outset;
width: 31px;
height: 31px;
margin-left: 10px;
}

View File

@@ -1,4 +1,5 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { PurchasingOptionsModalStore } from '../purchasing-options-modal.store';
@@ -13,6 +14,19 @@ export class DeliveryOptionComponent {
readonly availability$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(map((ava) => ava['delivery']));
showTooltip$ = new BehaviorSubject<boolean>(false);
price$ = combineLatest([this.availability$, this.item$]).pipe(
map(([availability, item]) => {
const shippingPrice = availability?.price?.value?.value;
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
if (catalogPrice < shippingPrice) {
this.showTooltip$.next(true);
}
return catalogPrice <= shippingPrice ? catalogPrice : shippingPrice;
})
);
constructor(private purchasingOptionsModalStore: PurchasingOptionsModalStore) {}
select() {

View File

@@ -5,8 +5,17 @@
</div>
<h4>DIG Versand</h4>
<p>Möchten Sie den Artikel geliefert bekommen?</p>
<span class="price">{{ availability.price?.value?.value | currency: availability.price?.value?.currency:'code' }}</span>
<div class="price-wrapper">
<span class="price">{{ price$ | async | currency: availability.price?.value?.currency:'code' }}</span>
<ng-container *ngIf="showTooltip$ | async">
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
i
</button>
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
Günstigerer Preis aus Hugendubel Katalog wird übernommen
</ui-tooltip>
</ng-container>
</div>
<div class="grow"></div>
<span class="delivery">Versandkostenfrei</span>
<span class="date"

View File

@@ -0,0 +1,11 @@
.price-wrapper {
@apply mt-2;
}
.info-tooltip-button {
@apply border-font-customer bg-white rounded-full text-base font-bold;
border-style: outset;
width: 31px;
height: 31px;
margin-left: 10px;
}

View File

@@ -1,4 +1,5 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { PurchasingOptionsModalStore } from '../purchasing-options-modal.store';
@@ -13,6 +14,19 @@ export class DigDeliveryOptionComponent {
readonly availability$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(map((ava) => ava['dig-delivery']));
showTooltip$ = new BehaviorSubject<boolean>(false);
price$ = combineLatest([this.availability$, this.item$]).pipe(
map(([availability, item]) => {
const shippingPrice = availability?.price?.value?.value;
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
if (catalogPrice < shippingPrice) {
this.showTooltip$.next(true);
}
return catalogPrice <= shippingPrice ? catalogPrice : shippingPrice;
})
);
constructor(private purchasingOptionsModalStore: PurchasingOptionsModalStore) {}
select() {

View File

@@ -38,7 +38,7 @@
<div class="grow"></div>
<div class="format">{{ item?.product?.formatDetail }}</div>
<div class="price">
{{ item?.catalogAvailability?.price?.value?.value | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
{{ price$ | async | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
</div>
<div class="date" *ngIf="option$ | async; let option">
<ng-container *ngIf="option === 'pick-up'">Abholung ab</ng-container>

View File

@@ -4,7 +4,7 @@ import { ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { AddToShoppingCartDTO, AvailabilityDTO, VATType } from '@swagger/checkout';
import { UiModalRef } from '@ui/modal';
import { debounceTime, first, map, switchMap } from 'rxjs/operators';
import { debounceTime, first, map, shareReplay, switchMap } from 'rxjs/operators';
import { combineLatest, Observable } from 'rxjs';
import { PurchasingOptionsModalData } from './purchasing-options-modal.data';
import { PurchasingOptions, PurchasingOptionsModalStore } from './purchasing-options-modal.store';
@@ -45,7 +45,9 @@ export class PurchasingOptionsModalComponent {
readonly quantityError$ = this.purchasingOptionsModalStore.selectQuantityError;
readonly showCustomPrice$ = this.item$.pipe(map((item) => !item?.catalogAvailability?.price?.value?.value));
readonly showCustomPrice$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(
map((availabilities) => Object.keys(availabilities)?.every((key) => !availabilities[key]?.price?.value?.value))
);
readonly customPriceInvalid$ = combineLatest([
this.showCustomPrice$,
@@ -113,12 +115,34 @@ export class PurchasingOptionsModalComponent {
map((buyer) => buyer.source)
);
price$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomPrice]).pipe(
map(([item, customPrice]) => item?.catalogAvailability?.price?.value?.value ?? customPrice ?? 0)
price$ = combineLatest([
this.purchasingOptionsModalStore.selectAvailabilities,
this.option$,
this.purchasingOptionsModalStore.selectCustomPrice,
]).pipe(
map(([availabilities, option, customPrice]) => {
if (option && !!availabilities[option]) {
return availabilities[option]?.price?.value?.value ?? customPrice;
} else {
const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.value?.value);
return availabilities[key]?.price?.value?.value ?? customPrice;
}
})
);
vat$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomVat]).pipe(
map(([item, customVat]) => item?.catalogAvailability?.price.vat ?? customVat)
vat$ = combineLatest([
this.purchasingOptionsModalStore.selectAvailabilities,
this.option$,
this.purchasingOptionsModalStore.selectCustomVat,
]).pipe(
map(([availabilities, option, customVat]) => {
if (option && !!availabilities[option]) {
return availabilities[option]?.price?.vat?.vatType ?? customVat;
} else {
const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.vat?.vatType);
return availabilities[key]?.price?.vat?.vatType ?? customVat;
}
})
);
readonly promoPoints$ = combineLatest([this.item$, this.quantity$, this.price$]).pipe(
@@ -224,6 +248,18 @@ export class PurchasingOptionsModalComponent {
vatType: customVat,
},
};
} else {
const price = await this.price$.pipe(first()).toPromise();
const vat = await this.vat$.pipe(first()).toPromise();
newItem.availability.price = {
value: {
value: price,
currency: 'EUR',
},
vat: {
vatType: vat,
},
};
}
switch (option) {

View File

@@ -22,10 +22,13 @@ import { KeyNavigationModule } from '../../shared/key-navigation/key-navigation.
import { RouterModule } from '@angular/router';
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { PurchasingOptionsModalPriceInputModule } from './price-input/purchasing-options-modal-price-input.module';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
@NgModule({
imports: [
CommonModule,
UiCommonModule,
FormsModule,
UiIconModule,
OverlayModule,
@@ -36,6 +39,7 @@ import { PurchasingOptionsModalPriceInputModule } from './price-input/purchasing
KeyNavigationModule,
RouterModule,
PurchasingOptionsModalPriceInputModule,
UiTooltipModule,
],
exports: [PurchasingOptionsModalComponent],
declarations: [

View File

@@ -242,13 +242,29 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
});
readonly setAvailability = this.updater(
(state, { availability, option }: { availability: AvailabilityDTO; option: PurchasingOptions }) => {
(state, { availability, option, item }: { availability: AvailabilityDTO; option: PurchasingOptions; item: ItemDTO }) => {
this.checkCanAdd();
let updatedAvailability = availability;
if ((option && option === 'delivery') || option === 'dig-delivery') {
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
const availabilityPrice = availability?.price?.value?.value;
const updatedPrice = catalogPrice <= availabilityPrice ? catalogPrice : availabilityPrice;
updatedAvailability = {
...availability,
price: {
...availability.price,
value: {
...availability.price.value,
value: updatedPrice,
},
},
};
}
return {
...state,
availabilities: {
...state.availabilities,
[option]: availability,
[option]: updatedAvailability,
},
};
}
@@ -326,6 +342,7 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
});
readonly setAvailabilities = this.updater((state, availabilities: { [key: string]: AvailabilityDTO }) => {
this.checkCanAdd();
return {
...state,
availabilities: {
@@ -440,7 +457,7 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
return availability$.pipe(
tapResponse(
(availability) => {
this.setAvailability({ option, availability });
this.setAvailability({ option, availability, item });
this.patchState({ fetchingAvailability: false });
},
() => {

View File

@@ -7,7 +7,7 @@
</p>
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
<div class="router-outlet-wrapper">
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
<ng-container formGroupName="organisation">
<ui-form-control label="Firmenname" requiredMark="*">
<input uiInput type="text" formControlName="name" tabindex="1" />
@@ -211,7 +211,7 @@
</div>
<div class="center sticky-bottom">
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
<ui-spinner [show]="!control.enabled">
Speichern
</ui-spinner>

View File

@@ -1,5 +1,5 @@
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ViewChild, ElementRef } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
@@ -12,7 +12,7 @@ import { validateEmail } from '../validators/email-validator';
import { DomainCheckoutService } from '@domain/checkout';
import { Subscription } from 'rxjs';
import { genderLastNameValidator } from '../validators/gender-b2b-validator';
import { setInvalidPropertyErrors, UiValidators } from '@ui/validators';
import { UiValidators } from '@ui/validators';
import { organisationB2bDeliveryValidator } from '../validators/organisation-delivery-b2b-validator';
import { HttpErrorResponse } from '@angular/common/http';
@@ -23,8 +23,15 @@ import { HttpErrorResponse } from '@angular/common/http';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerCreateB2BComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
@ViewChild('formControl')
formControlRef: ElementRef;
subscription: Subscription;
get shippingAddressGroup(): AbstractControl {
return this.control?.get('shippingAddress');
}
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
@@ -43,30 +50,14 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
ngOnInit(): void {
this.init();
this.control.get('shippingAddress').disable();
// this.subscription = this.control.get('shippingAddress').valueChanges.subscribe(() => {
// console.log(
// !!this.control.get('shippingAddress').value.lastName &&
// !!this.control.get('shippingAddress').value.firstName &&
// !!this.control.get('shippingAddress').value.gender
// );
// !!this.control.get('shippingAddress').value.lastName &&
// !!this.control.get('shippingAddress').value.firstName &&
// !!this.control.get('shippingAddress').value.gender
// ? this.control.get('shippingAddress').get('organisation').get('name').setValidators(null)
// : this.control.get('shippingAddress').get('organisation').get('name').setValidators(Validators.required);
// // this.control.get('shippingAddress').get('organisation').value.name ? '' : '*'"
// this.control.updateValueAndValidity();
// });
this.shippingAddressGroup.disable();
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
if (isChecked) {
this.control.get('shippingAddress').enable();
this.control.updateValueAndValidity();
this.shippingAddressGroup.enable();
this.setShippingAddressValidators(this.shippingAddressGroup);
} else {
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.clearShippingAddressValidators(this.shippingAddressGroup);
this.control.updateValueAndValidity();
}
});
@@ -90,8 +81,13 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
}),
gender: fb.control(customer?.gender),
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName),
lastName: fb.control(customer?.lastName),
firstName: fb.control(customer?.firstName),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
@@ -100,11 +96,6 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
info: fb.control(customer?.address?.info),
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
}),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
differentShippingAddress: fb.control(false),
shippingAddress: fb.group({
organisation: fb.group({
@@ -117,15 +108,15 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
firstName: fb.control(''),
lastName: fb.control(''),
address: fb.group({
street: fb.control('', [Validators.required]),
streetNumber: fb.control('', [Validators.required]),
zipCode: fb.control('', [Validators.required]),
city: fb.control('', [Validators.required]),
street: fb.control(''),
streetNumber: fb.control(''),
zipCode: fb.control(''),
city: fb.control(''),
info: fb.control(''),
country: fb.control('DEU', [Validators.required]),
country: fb.control('DEU'),
}),
communicationDetails: fb.group({
email: fb.control('', [validateEmail]),
email: fb.control(''),
phone: fb.control(''),
mobile: fb.control(''),
}),
@@ -136,7 +127,16 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
}
async submit(): Promise<void> {
if (this.control.invalid || this.control.disabled) {
if (this.control.disabled) {
return;
}
if (this.control.invalid) {
this.control.disable({ emitEvent: false });
setTimeout(() => {
this.enableControl();
this.focusFirstInvalidFormControl(this.formControlRef);
}, 200);
return;
}

View File

@@ -8,7 +8,7 @@
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
<div class="router-outlet-wrapper">
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
<div class="control-row">
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
<ui-select formControlName="gender" tabindex="1">
@@ -193,7 +193,7 @@
</div>
<div class="center sticky-bottom">
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
<ui-spinner [show]="!control.enabled">
Speichern
</ui-spinner>

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
@@ -11,7 +11,7 @@ import { CustomerCreateComponentBase } from './customer-create.component';
import { validateEmail } from '../validators/email-validator';
import { Subscription } from 'rxjs';
import { DomainCheckoutService } from '@domain/checkout';
import { UiValidators, setInvalidPropertyErrors } from '@ui/validators';
import { UiValidators } from '@ui/validators';
import { isStringEmpty } from '@utils/common';
import { HttpErrorResponse } from '@angular/common/http';
@@ -22,6 +22,9 @@ import { HttpErrorResponse } from '@angular/common/http';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerCreateBranchComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
@ViewChild('formControl')
formControlRef: ElementRef;
shippingAddressSubscription: Subscription;
organisationSubscription: Subscription;
addressSubscription: Subscription;
@@ -30,6 +33,10 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
return this.control?.get('address');
}
get shippingAddressGroup(): AbstractControl {
return this.control?.get('shippingAddress');
}
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
@@ -48,14 +55,14 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
ngOnInit() {
this.init();
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
if (isChecked) {
this.control.get('shippingAddress').enable();
this.control.updateValueAndValidity();
this.shippingAddressGroup.enable();
this.setShippingAddressValidators(this.shippingAddressGroup);
} else {
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.clearShippingAddressValidators(this.shippingAddressGroup);
this.control.updateValueAndValidity();
}
});
@@ -110,9 +117,14 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
}),
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
firstName: fb.control(customer?.firstName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
address: fb.group({
street: fb.control(customer?.address?.street),
streetNumber: fb.control(customer?.address?.streetNumber),
@@ -121,11 +133,6 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
country: fb.control(!this.isAddressEmpty(customer?.address) ? customer?.address?.country : ''),
info: fb.control(customer?.address?.info),
}),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
differentShippingAddress: fb.control(false),
shippingAddress: fb.group({
organisation: fb.group({
@@ -133,24 +140,33 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
department: fb.control(''),
vatId: fb.control(''),
}),
gender: fb.control(undefined, [Validators.required]),
gender: fb.control(undefined),
title: fb.control(''),
firstName: fb.control('', [Validators.required]),
lastName: fb.control('', [Validators.required]),
firstName: fb.control(''),
lastName: fb.control(''),
address: fb.group({
street: fb.control('', [Validators.required]),
streetNumber: fb.control('', [Validators.required]),
zipCode: fb.control('', [Validators.required]),
city: fb.control('', [Validators.required]),
street: fb.control(''),
streetNumber: fb.control(''),
zipCode: fb.control(''),
city: fb.control(''),
info: fb.control(''),
country: fb.control('DEU', [Validators.required]),
country: fb.control('DEU'),
}),
}),
});
}
async submit(): Promise<void> {
if (this.control.invalid || this.control.disabled) {
if (this.control.disabled) {
return;
}
if (this.control.invalid) {
this.control.disable({ emitEvent: false });
setTimeout(() => {
this.enableControl();
this.focusFirstInvalidFormControl(this.formControlRef);
}, 200);
return;
}

View File

@@ -7,7 +7,7 @@
</p>
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
<div class="router-outlet-wrapper">
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
<div class="control-row">
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
<ui-select formControlName="gender" tabindex="1">
@@ -198,7 +198,7 @@
</div>
<div class="center sticky-bottom">
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
<ui-spinner [show]="!control.enabled">
Speichern
</ui-spinner>

View File

@@ -1,5 +1,5 @@
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ElementRef, ViewChild } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { BreadcrumbService } from '@core/breadcrumb';
@@ -13,7 +13,6 @@ import { validateEmail } from '../validators/email-validator';
import { Subscription } from 'rxjs';
import { DomainCheckoutService } from '@domain/checkout';
import { map } from 'rxjs/operators';
import { setInvalidPropertyErrors } from '@ui/validators';
import { HttpErrorResponse } from '@angular/common/http';
@Component({
@@ -23,9 +22,16 @@ import { HttpErrorResponse } from '@angular/common/http';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerCreateGuestComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
@ViewChild('formControl')
formControlRef: ElementRef;
shippingAddressSubscription: Subscription;
organisationSubscription: Subscription;
get shippingAddressGroup(): AbstractControl {
return this.control?.get('shippingAddress');
}
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
@@ -44,14 +50,15 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
ngOnInit(): void {
this.init();
this.countries$ = this.countries$.pipe(map((countries) => countries.filter((country) => country.name === 'Deutschland')));
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
if (isChecked) {
this.control.get('shippingAddress').enable();
this.control.updateValueAndValidity();
this.shippingAddressGroup.enable();
this.setShippingAddressValidators(this.shippingAddressGroup);
} else {
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.clearShippingAddressValidators(this.shippingAddressGroup);
this.control.updateValueAndValidity();
}
});
@@ -85,9 +92,14 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
}),
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
firstName: fb.control(customer?.firstName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [Validators.required, validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
@@ -96,11 +108,6 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
info: fb.control(customer?.address?.info),
}),
communicationDetails: fb.group({
email: fb.control(customer?.communicationDetails?.email, [Validators.required, validateEmail]),
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
differentShippingAddress: fb.control(false),
shippingAddress: fb.group({
organisation: fb.group({
@@ -108,24 +115,33 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
department: fb.control(''),
vatId: fb.control(''),
}),
gender: fb.control(undefined, [Validators.required]),
gender: fb.control(undefined),
title: fb.control(''),
firstName: fb.control('', [Validators.required]),
lastName: fb.control('', [Validators.required]),
firstName: fb.control(''),
lastName: fb.control(''),
address: fb.group({
street: fb.control('', [Validators.required]),
streetNumber: fb.control('', [Validators.required]),
zipCode: fb.control('', [Validators.required]),
city: fb.control('', [Validators.required]),
street: fb.control(''),
streetNumber: fb.control(''),
zipCode: fb.control(''),
city: fb.control(''),
info: fb.control(''),
country: fb.control('DEU', [Validators.required]),
country: fb.control('DEU'),
}),
}),
});
}
async submit(): Promise<void> {
if (this.control.invalid || this.control.disabled) {
if (this.control.disabled) {
return;
}
if (this.control.invalid) {
this.control.disable({ emitEvent: false });
setTimeout(() => {
this.enableControl();
this.focusFirstInvalidFormControl(this.formControlRef);
}, 200);
return;
}

View File

@@ -8,7 +8,7 @@
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
<div class="router-outlet-wrapper">
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
<div class="control-row">
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
<ui-select formControlName="gender" tabindex="1">
@@ -195,7 +195,7 @@
</div>
<div class="center sticky-bottom">
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
<ui-spinner [show]="!control.enabled">
Speichern
</ui-spinner>

View File

@@ -1,4 +1,4 @@
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { ApplicationService } from '@core/application';
@@ -22,6 +22,9 @@ import { HttpErrorResponse } from '@angular/common/http';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
@ViewChild('formControl')
formControlRef: ElementRef;
upgradeCustomerId: number;
upgradeCustomer: CustomerDTO;
shippingAddressSubscription: Subscription;
@@ -37,6 +40,10 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
return null;
};
get shippingAddressGroup(): AbstractControl {
return this.control?.get('shippingAddress');
}
constructor(
public activatedRoute: ActivatedRoute,
public router: Router,
@@ -79,14 +86,14 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
});
}
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
if (isChecked) {
this.control.get('shippingAddress').enable();
this.control.updateValueAndValidity();
this.shippingAddressGroup.enable();
this.setShippingAddressValidators(this.shippingAddressGroup);
} else {
this.control.get('shippingAddress').disable();
this.shippingAddressGroup.disable();
this.clearShippingAddressValidators(this.shippingAddressGroup);
this.control.updateValueAndValidity();
}
});
@@ -120,17 +127,9 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
}),
gender: fb.control(customer?.gender || undefined, [Validators.required, Validators.min(1)]),
title: fb.control(customer?.title),
firstName: fb.control(customer?.firstName, [Validators.required]),
lastName: fb.control(customer?.lastName, [Validators.required]),
firstName: fb.control(customer?.firstName, [Validators.required]),
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
city: fb.control(customer?.address?.city, [Validators.required]),
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
info: fb.control(customer?.address?.info),
}),
communicationDetails: fb.group({
email: fb.control(
customer?.communicationDetails?.email,
@@ -140,6 +139,14 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
}),
address: fb.group({
street: fb.control(customer?.address?.street, [Validators.required]),
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
city: fb.control(customer?.address?.city, [Validators.required]),
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
info: fb.control(customer?.address?.info),
}),
differentShippingAddress: fb.control(false),
shippingAddress: fb.group({
organisation: fb.group({
@@ -147,23 +154,32 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
department: fb.control(''),
vatId: fb.control(''),
}),
gender: fb.control(undefined, [Validators.required]),
gender: fb.control(undefined),
title: fb.control(''),
firstName: fb.control('', [Validators.required]),
lastName: fb.control('', [Validators.required]),
firstName: fb.control(''),
lastName: fb.control(''),
address: fb.group({
street: fb.control('', [Validators.required]),
streetNumber: fb.control('', [Validators.required]),
zipCode: fb.control('', [Validators.required]),
city: fb.control('', [Validators.required]),
street: fb.control(''),
streetNumber: fb.control(''),
zipCode: fb.control(''),
city: fb.control(''),
info: fb.control(''),
country: fb.control('DEU', [Validators.required]),
country: fb.control('DEU'),
}),
}),
});
}
async submit(): Promise<void> {
if (this.control.invalid || this.control.disabled) {
if (this.control.disabled) {
return;
}
if (this.control.invalid) {
this.control.disable({ emitEvent: false });
setTimeout(() => {
this.enableControl();
this.focusFirstInvalidFormControl(this.formControlRef);
}, 200);
return;
}

View File

@@ -1,4 +1,4 @@
import { AbstractControl, AsyncValidatorFn, FormGroup, ValidationErrors } from '@angular/forms';
import { AbstractControl, AsyncValidatorFn, FormArray, FormGroup, ValidationErrors, Validators } from '@angular/forms';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { CrmCustomerService } from '@domain/crm';
import { CountryDTO, CustomerDTO, InputOptionsDTO } from '@swagger/crm';
@@ -9,10 +9,11 @@ import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
import { DomainCheckoutService } from '@domain/checkout';
import { ChangeDetectorRef } from '@angular/core';
import { ChangeDetectorRef, ElementRef } from '@angular/core';
import { isBoolean } from '@utils/common';
import { setInvalidPropertyErrors } from '@ui/validators';
import { camelCase } from 'lodash';
import { validateEmail } from '../validators/email-validator';
export abstract class CustomerCreateComponentBase {
emailExistsValidator: AsyncValidatorFn = async (control): Promise<ValidationErrors | null> => {
@@ -156,6 +157,26 @@ export abstract class CustomerCreateComponentBase {
};
}
setShippingAddressValidators(group: AbstractControl) {
group.get('gender')?.setValidators([Validators.required]);
group.get('firstName')?.setValidators([Validators.required]);
group.get('lastName')?.setValidators([Validators.required]);
group.get('address')?.get('street')?.setValidators([Validators.required]);
group.get('address')?.get('streetNumber')?.setValidators([Validators.required]);
group.get('address')?.get('zipCode')?.setValidators([Validators.required]);
group.get('address')?.get('city')?.setValidators([Validators.required]);
group.get('address')?.get('country')?.setValidators([Validators.required]);
group.get('communicationDetails')?.get('email')?.setValidators([validateEmail]);
}
clearShippingAddressValidators(group: AbstractControl) {
group.get('gender')?.clearValidators();
group.get('firstName')?.clearValidators();
group.get('lastName')?.clearValidators();
group.get('address')?.clearValidators();
group.get('communicationDetails')?.clearValidators();
}
setValidationError(invalidProperties: { [key: string]: string }, formGroup: AbstractControl) {
this.control.enable();
this.control.reset(this.control.value);
@@ -175,6 +196,34 @@ export abstract class CustomerCreateComponentBase {
}
}
focusFirstInvalidFormControl(formControlRef: ElementRef) {
const invalidKeys = this.findInvalidControlsRecursive(this.control);
const invalidControl = formControlRef.nativeElement.querySelector('[formcontrolname="' + invalidKeys?.find((_) => true) + '"]');
invalidControl?.focus();
}
findInvalidControlsRecursive(formToInvestigate: FormGroup | FormArray): string[] {
const invalidControls: string[] = [];
let recursiveFunc = (form: FormGroup | FormArray) => {
Object.keys(form.controls).forEach((field) => {
if (field === 'shippingAddress') {
return;
}
const control = form.get(field);
if (control instanceof FormGroup) {
recursiveFunc(control);
} else if (control instanceof FormArray) {
recursiveFunc(control);
} else if (control.invalid) {
invalidControls.push(field);
}
});
};
recursiveFunc(formToInvestigate);
return invalidControls;
}
abstract createControl(customer?: CustomerDTO): FormGroup;
abstract submit(): Promise<void>;

View File

@@ -33,7 +33,7 @@
*ngFor="let option of value?.values"
[ngModel]="option.selected"
(ngModelChange)="check($event, option)"
[disabled]="!option.enabled"
[disabled]="!option.enabled || option.selected"
>
{{ option.label }}
</ui-checkbox>

View File

@@ -1,3 +1,7 @@
:host {
@apply grid grid-flow-col flex-row text-card-sub justify-center gap-6;
}
ui-checkbox.disabled.checked {
@apply text-black;
}

View File

@@ -105,7 +105,7 @@
</div>
<ui-radio-group
*ngIf="(customer | address) || customer.shippingAddresses.length"
*ngIf="(customer | address) || customer.shippingAddresses?.length"
class="card-customer-address"
[(ngModel)]="selectedAddress"
>

View File

@@ -32,6 +32,7 @@ import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
import { HistoryModule } from '@modal/history';
import { CantSelectGuestModalModule } from '../modals/cant-select-guest/cant-select-guest-modal.module';
import { UiScrollContainerModule } from '@ui/scroll-container';
import { UiTooltipModule } from '@ui/tooltip';
@NgModule({
imports: [
CommonModule,
@@ -45,6 +46,7 @@ import { UiScrollContainerModule } from '@ui/scroll-container';
FormsModule,
UiRadioModule,
UiCommonModule,
UiTooltipModule,
UiScrollContainerModule,
UiSpinnerModule,
HistoryModule,

View File

@@ -1,5 +1,14 @@
<div class="tags">
<img class="tag preorder" src="/assets/images/tag_icon_preorder.svg" alt="Vorbesteller" *ngIf="hasPrebooked" />
<img
*ngIf="hasPrebooked"
[uiOverlayTrigger]="prebookedTooltip"
class="tag preorder"
src="/assets/images/tag_icon_preorder.svg"
alt="Vorbesteller"
/>
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #prebookedTooltip [closeable]="true">
Artikel wird für Sie vorgemerkt.
</ui-tooltip>
</div>
<div class="row">
@@ -115,5 +124,9 @@
</button>
</span>
</ng-container>
<div class="history-wrapper">
<button class="cta-history" (click)="openHistory()">Historie</button>
</div>
</div>
</div>

View File

@@ -60,6 +60,14 @@ img {
width: 60px;
}
.history-wrapper {
@apply text-right;
}
.cta-history {
@apply text-base font-bold text-brand outline-none border-none bg-transparent mr-1;
}
// .row:first-child {
// @apply flex-grow;
// }

View File

@@ -1,6 +1,8 @@
import { Component, Input, OnInit } from '@angular/core';
import { HistoryComponent } from '@modal/history';
import { ImageService } from '@sales/core-services';
import { OrderDTO, OrderItemDTO, OrderItemSubsetDTO } from '@swagger/oms';
import { UiModalService } from '@ui/modal';
import { Observable } from 'rxjs';
@Component({
@@ -39,7 +41,7 @@ export class CustomerOrderItemCardComponent implements OnInit {
}
}
constructor(private imageService: ImageService) {}
constructor(private imageService: ImageService, private _modal: UiModalService) {}
ngOnInit() {
this.imageUrl = this.imageService.getImageUrl(this.orderItem.product.ean, {
@@ -47,4 +49,18 @@ export class CustomerOrderItemCardComponent implements OnInit {
height: 100,
});
}
openHistory() {
this._modal.open({
content: HistoryComponent,
title: 'Historie',
data: {
mode: 'order',
item: {
order: this.order,
orderItemSubsetId: this.subsetItem.id,
},
},
});
}
}

View File

@@ -168,8 +168,8 @@ export abstract class CustomerSearch implements OnInit, OnDestroy {
private initAutocomplete() {
this.autocompleteResult$ = this.inputChange$.pipe(
takeUntil(this.destroy$),
debounceTime(300),
distinctUntilChanged(),
debounceTime(200),
switchMap((queryString) => {
if (queryString.length >= 3) {
return this.customerSearch.complete(queryString, this.getSelecteFiltersAsDictionary()).pipe(

View File

@@ -1,5 +1,6 @@
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
import { Filter } from '@ui/filter';
import { isEqual } from 'lodash';
import { CustomerSearch } from '../customer-search.service';
@Component({
@@ -11,16 +12,28 @@ import { CustomerSearch } from '../customer-search.service';
export class CustomerSearchFilterComponent implements OnInit, OnDestroy {
selectedFilters: Filter[];
initialFilter: Filter[];
initialSelectedFilters: Filter[];
filterValuesChanged = false;
constructor(public search: CustomerSearch, private cdr: ChangeDetectorRef) {}
ngOnInit() {
this.selectedFilters = this.search.queryFilter.filters;
this.initialFilter = this.search.initQueryFilter.initialFilter;
if (!isEqual(this.selectedFilters, this.initialFilter)) {
this.initialSelectedFilters = this.selectedFilters;
}
}
ngOnDestroy() {
this.search.setFilter(this.initialFilter);
if (!!this.initialSelectedFilters && this.filterValuesChanged) {
this.search.setFilter(this.selectedFilters);
} else if (!!this.initialSelectedFilters && !this.filterValuesChanged) {
this.search.setFilter(this.initialSelectedFilters);
} else {
this.search.setFilter(this.initialFilter);
}
}
applyFilters(search: boolean = true) {
@@ -30,6 +43,7 @@ export class CustomerSearchFilterComponent implements OnInit, OnDestroy {
if (search) {
this.search.search({ isNewSearch: true });
}
this.filterValuesChanged = true;
}
updateFilter(filters: Filter[]) {

View File

@@ -3,6 +3,7 @@ import { FormControl } from '@angular/forms';
import { EnvironmentService } from '@core/environment';
import { AutocompleteDTO } from '@swagger/crm';
import { UiSearchboxAutocompleteComponent } from '@ui/searchbox';
import { NativeContainerService } from 'native-container';
import { Observable, Subject } from 'rxjs';
import { delay, takeUntil, tap } from 'rxjs/operators';
import { CustomerSearch } from '../../customer-search.service';
@@ -29,7 +30,12 @@ export class CustomerSearchboxComponent implements OnInit, OnDestroy {
@Output()
searchStarted = new EventEmitter<void>();
constructor(public search: CustomerSearch, private environmentService: EnvironmentService, private cdr: ChangeDetectorRef) {}
constructor(
public search: CustomerSearch,
private environmentService: EnvironmentService,
private cdr: ChangeDetectorRef,
private nativeContainer: NativeContainerService
) {}
startSearch() {
this.search.startSearch();
@@ -74,7 +80,7 @@ export class CustomerSearchboxComponent implements OnInit, OnDestroy {
}
async detectDevice() {
this.isMobile = await this.environmentService.isMobile();
this.isMobile = this.nativeContainer.isUiWebview().isNative;
this.cdr.detectChanges();
}

View File

@@ -1,6 +1,9 @@
<shared-goods-in-out-order-details (actionHandled)="actionHandled($event)">
<shared-goods-in-out-order-details-header (editClick)="navigateToEditPage($event)"></shared-goods-in-out-order-details-header>
<shared-goods-in-out-order-details-item [orderItem]="selectedItem$ | async"></shared-goods-in-out-order-details-item>
<shared-goods-in-out-order-details-item
[orderItem]="selectedItem$ | async"
[order]="order$ | async"
></shared-goods-in-out-order-details-item>
<shared-goods-in-out-order-details-tags></shared-goods-in-out-order-details-tags>
<shared-goods-in-out-order-details-covers
[orderItems]="items$ | async"

View File

@@ -1,11 +1,11 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainGoodsService, OrderItemsContext } from '@domain/oms';
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
import { combineLatest, Subject } from 'rxjs';
import { debounceTime, first, map, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
export interface GoodsInDetailsComponentState {
fetching: boolean;
@@ -13,6 +13,7 @@ export interface GoodsInDetailsComponentState {
processingStatus?: OrderItemProcessingStatusValue;
selectedOrderItemId?: number;
items?: OrderItemListItemDTO[];
orderId?: number;
}
@Component({
@@ -28,6 +29,8 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
items$ = this.select((s) => s.items ?? []);
orderId$ = this.select((s) => s.orderId);
get items() {
return this.get((s) => s.items);
}
@@ -38,6 +41,12 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
map(([items, processingStatus]) => items.filter((item) => item.processingStatus === processingStatus))
);
order$ = this.orderId$.pipe(
filter((orderId) => !!orderId),
switchMap((orderId) => this._omsService.getOrder(orderId)),
shareReplay()
);
selectedItem$ = combineLatest([this.selectedOrderItemId$, this.itemsWithProcessingStatus$]).pipe(
map(([orderItemId, items]) => items.find((item) => item.orderItemId === orderItemId) || items[0])
);
@@ -47,6 +56,7 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
constructor(
private _activatedRoute: ActivatedRoute,
private _domainGoodsInService: DomainGoodsService,
private _omsService: DomainOmsService,
private _breadcrumb: BreadcrumbService,
private _router: Router
) {
@@ -117,6 +127,7 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
// Anzeige von Teilabholung in Wareneingang ausblenden
actions: item.actions.filter((action) => action.command !== 'FETCHED_PARTIAL'),
})),
orderId: res.result[0].orderId,
});
this.updateBreadcrumb(
res.result.find((item) => item.orderItemId === selectedOrderItemId && item.processingStatus === processingStatus)

View File

@@ -1,3 +1,3 @@
<div class="container">
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage()" [items]="items$ | async"></shared-goods-in-out-order-edit>
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items$ | async"></shared-goods-in-out-order-edit>
</div>

View File

@@ -45,10 +45,10 @@ export class GoodsInEditComponent implements OnInit {
});
}
navigateToDetailsPage() {
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
const orderItemId = this._activatedRoute.snapshot.params.orderItemId;
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]);
}
}

View File

@@ -86,6 +86,10 @@
<div class="quantity spec">
<strong>{{ orderItem.quantity }}x</strong>
</div>
<div class="order-date spec">
<span>Bestelldatum</span>
<strong>{{ orderItem.orderDate | date: 'dd.MM.yy' }}</strong>
</div>
<div class="date spec">
<span>vsl. Lieferdatum</span>
<strong>{{ orderItem.estimatedShippingDate | date: 'dd.MM.yy' }}</strong>

View File

@@ -65,8 +65,9 @@
}
.details {
@apply grid grid-cols-2 grid-flow-row gap-1;
grid-template-areas: 'format supplier' 'ean item-id' 'price status' 'quantity date';
@apply grid grid-flow-row gap-1;
grid-template-columns: 40% 60%;
grid-template-areas: 'format supplier' 'ean item-id' 'price status' 'quantity order-date' 'empty date';
grid-area: details;
}
@@ -104,6 +105,10 @@
grid-area: quantity;
}
.order-date {
grid-area: order-date;
}
.date {
grid-area: date;
}
@@ -114,6 +119,10 @@
span:nth-child(1) {
min-width: 150px;
}
strong {
@apply break-all;
}
}
.processing-status {

View File

@@ -5,7 +5,13 @@
<div class="goods-in-search-filter-content-main">
<h1 class="title">Filter</h1>
<ui-filter [filter]="filter" [loading]="loading$ | async" (search)="applyFilter()" [hint]="message"></ui-filter>
<ui-filter
[filter]="filter"
[loading]="loading$ | async"
(search)="applyFilter()"
[hint]="message"
resizeInputOptionsToElement="page-goods-in-search-filter .cta-wrapper"
></ui-filter>
</div>
</div>

View File

@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
@Injectable()
export class GoodsInSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
for = 'main';
for = 'goods-in';
constructor(private domainGoodsInService: DomainGoodsService) {
super();

View File

@@ -38,6 +38,7 @@ import {
ShippingNoteActionHandler,
SupplierTemporarilyOutOfStockActionHandler,
ReOrderedActionHandler,
CollectOnDeliveryNoteActionHandler,
} from '@domain/oms';
@NgModule({
declarations: [GoodsInComponent],
@@ -79,6 +80,7 @@ import {
ReturnedByBuyerActionHandler,
ShippingNoteActionHandler,
SupplierTemporarilyOutOfStockActionHandler,
CollectOnDeliveryNoteActionHandler,
]),
],
exports: [GoodsInComponent],

View File

@@ -3,6 +3,7 @@
<shared-goods-in-out-order-details-item
*ngFor="let item of itemsWithProcessingStatus$ | async"
[orderItem]="item"
[order]="order$ | async"
></shared-goods-in-out-order-details-item>
<shared-goods-in-out-order-details-tags></shared-goods-in-out-order-details-tags>
</shared-goods-in-out-order-details>

View File

@@ -1,12 +1,11 @@
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainGoodsService, OrderItemsContext } from '@domain/oms';
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
import { Command } from 'protractor';
import { combineLatest, Observable, Subject, Subscription } from 'rxjs';
import { debounceTime, first, map, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { combineLatest, Observable, Subject } from 'rxjs';
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
export interface GoodsOutDetailsComponentState {
fetching: boolean;
@@ -14,6 +13,7 @@ export interface GoodsOutDetailsComponentState {
processingStatus?: OrderItemProcessingStatusValue;
compartmentCode?: string;
items?: OrderItemListItemDTO[];
orderId?: number;
}
@Component({
@@ -31,6 +31,8 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
items$ = this.select((s) => s.items ?? []);
orderId$ = this.select((s) => s.orderId);
get items() {
return this.get((s) => s.items);
}
@@ -39,11 +41,18 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
map(([items, processingStatus]) => items.filter((item) => item.processingStatus === processingStatus))
);
order$ = this.orderId$.pipe(
filter((orderId) => !!orderId),
switchMap((orderId) => this._omsService.getOrder(orderId)),
shareReplay()
);
private _onDestroy$ = new Subject();
constructor(
private _activatedRoute: ActivatedRoute,
private _domainGoodsInService: DomainGoodsService,
private _omsService: DomainOmsService,
private _breadcrumb: BreadcrumbService,
private _router: Router
) {
@@ -121,6 +130,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
(res) => {
this.patchState({
items: res.result,
orderId: res.result[0].orderId,
});
this.updateBreadcrumb(res.result.find((item) => item.processingStatus === processingStatus));
},

View File

@@ -1,3 +1,3 @@
<div class="container" *ngIf="items$ | async; let items">
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage()" [items]="items"></shared-goods-in-out-order-edit>
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items"></shared-goods-in-out-order-edit>
</div>

View File

@@ -59,10 +59,10 @@ export class GoodsOutEditComponent implements OnInit {
});
}
navigateToDetailsPage() {
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode;
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
compartmentCode
? this._router.navigate([`/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`])
: this._router.navigate([`/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`]);

View File

@@ -5,7 +5,13 @@
<div class="goods-out-search-filter-content-main">
<h1 class="title">Filter</h1>
<ui-filter [filter]="filter" [loading]="loading$ | async" (search)="applyFilter()" [hint]="message"></ui-filter>
<ui-filter
[filter]="filter"
[loading]="loading$ | async"
(search)="applyFilter()"
[hint]="message"
resizeInputOptionsToElement="page-goods-out-search-filter .cta-wrapper"
></ui-filter>
</div>
</div>

View File

@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
@Injectable()
export class GoodsOutSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
for = 'main';
for = 'goods-out';
constructor(private domainGoodsInService: DomainGoodsService) {
super();

View File

@@ -82,9 +82,9 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy {
if (result.hits === 1) {
const orderItem = result.result[0];
this._router.navigate([this.getDetailsPath(orderItem)]);
} else {
this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() });
}
this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() });
} else {
this.message = 'keine Suchergebnisse';
}

View File

@@ -181,7 +181,10 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
}
selectionRules(action: KeyValueDTOOfStringAndString) {
return action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false;
return (
(action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false) ||
action.command.includes('COLLECT_ON_DELIVERYNOTE')
);
}
async handleAction(action: KeyValueDTOOfStringAndString) {

View File

@@ -37,6 +37,7 @@ import {
ShippingNoteActionHandler,
SupplierTemporarilyOutOfStockActionHandler,
ReOrderedActionHandler,
CollectOnDeliveryNoteActionHandler,
} from '@domain/oms';
import { CoreCommandModule } from '@core/command';
import { ShellBreadcrumbModule } from '@shell/breadcrumb';
@@ -83,6 +84,7 @@ import { GoodsInRoutingModule } from './good-out-routing.module';
ReturnedByBuyerActionHandler,
ShippingNoteActionHandler,
SupplierTemporarilyOutOfStockActionHandler,
CollectOnDeliveryNoteActionHandler,
]),
],
exports: [GoodsOutComponent],

View File

@@ -15,7 +15,7 @@
<hr />
<div class="update-comment">
<div class="update-comment-heading">
Update-Notiz
Kommentar
</div>
<ui-icon icon="refresh" size="19px"></ui-icon>

View File

@@ -5,6 +5,7 @@
[render-text]="true"
[(page)]="page"
(after-load-complete)="callBackFn($event)"
[fit-to-page]="true"
zoom-scale="page-height"
></pdf-viewer>

View File

@@ -1,5 +1,5 @@
:host {
@apply grid grid-cols-1 relative;
@apply grid grid-cols-1 relative overflow-auto;
max-height: calc(100vh - 4rem);
}
@@ -8,16 +8,18 @@ h5 {
}
pdf-viewer {
@apply flex-grow overflow-y-auto max-h-full;
@apply flex-grow mb-4 max-h-full overflow-auto;
::ng-deep .canvasWrapper {
@apply border-2 border-gray-200 border-solid rounded-card;
max-width: 99%;
}
}
button.cta-print {
@apply sticky bottom-6 bg-brand text-xl px-6 py-3 text-white rounded-full border-none outline-none font-bold shadow-cta;
@apply sticky bg-brand text-xl px-6 py-3 text-white rounded-full border-none outline-none font-bold shadow-cta;
transform: translateX(-50%);
left: 50%;
bottom: 5.5rem;
width: 175px;
}

View File

@@ -1,7 +1,7 @@
<lib-offline-overlay>
<!-- lib offline depends on these three elements to be present inside of it -->
<app-header [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-header>
<app-content [ngClass]="{ loading: loading }"></app-content>
<app-menu [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-menu>
<img *ngIf="loading" src="/assets/images/Icon_Loading.svg" class="app-loader" />
<app-header [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-header>
<app-content [ngClass]="{ loading: loading$ | async }"></app-content>
<app-menu [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-menu>
<img *ngIf="loading$ | async" src="/assets/images/Icon_Loading.svg" class="app-loader" />
</lib-offline-overlay>

View File

@@ -1,11 +1,10 @@
import { Component, HostBinding, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Router, NavigationEnd, NavigationStart } from '@angular/router';
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { Store } from '@ngxs/store';
import { Subject } from 'rxjs';
import { BehaviorSubject, Subject } from 'rxjs';
import { take, filter, takeUntil } from 'rxjs/operators';
import { SsoService } from 'sso';
import { AppService } from './core/services/app.service';
import { ModuleSwitcher } from './core/models/app-switcher.enum';
import { AppState } from './core/store/state/app.state';
import { AppConfiguration } from './app-configuration';
import { NativeContainerService } from 'shared/lib/barcode-scanner';
@@ -23,7 +22,7 @@ import { DeviceDetectorService } from 'ngx-device-detector';
})
export class AppComponent implements OnInit, OnDestroy {
title = 'Hugendubel InstoreApp';
loading = true;
loading$ = new BehaviorSubject(true);
includeGoogleAnalytics = this.config.includeGoogleAnalytics;
destroy$ = new Subject();
@@ -45,6 +44,14 @@ export class AppComponent implements OnInit, OnDestroy {
private ngxsActions$: NgxsActions,
private deviceDetectorService: DeviceDetectorService
) {
this.appService.loader$.subscribe(() => {
this.loading$.next(false);
});
this.appService.loader$.pipe(take(1)).subscribe(() => {
this.containerNotificationMessage();
});
// intialisations done only when app loads
this.appService.appLoadInitialisations();
@@ -82,11 +89,6 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.appService.loader$.pipe(take(1)).subscribe(() => {
this.loading = false;
this.containerNotificationMessage();
});
if (this.includeGoogleAnalytics) {
this.initGoogleAnalytics();
}

View File

@@ -11,12 +11,12 @@
</div>
<div class="align-center">
<div class="icons-grid pt-5">
<div class="align-center">
<div class="header-item align-center">
<button class="header-icon dashboard" type="button" [routerLink]="['/dashboard']" routerLinkActive="active">
<ui-icon icon="dashboard" size="26px"></ui-icon>
</button>
</div>
<div class="align-center">
<div class="header-item align-center">
<button
class="header-icon notification"
type="button"
@@ -28,7 +28,10 @@
<ui-icon icon="notification" size="26px"></ui-icon>
</button>
</div>
<div class="align-center">
<div class="header-item align-center">
<span class="current-branch" *ngIf="currentBranch$ | async; let currentBranch" [title]="currentBranch.name">
{{ currentBranch.key | uppercase }}
</span>
<button class="header-icon logout" type="button" (click)="logoff()">
<ui-icon icon="logout" size="26px"></ui-icon>
</button>

View File

@@ -14,6 +14,10 @@ button.header-icon {
@apply text-inactive-customer;
}
}
.current-branch {
@apply text-wild-blue-yonder;
}
}
::ng-deep .branch app-header {
@@ -26,6 +30,10 @@ button.header-icon {
@apply text-active-branch;
}
}
.current-branch {
@apply text-cool-grey;
}
}
button.notification {
@@ -37,6 +45,14 @@ button.notification {
z-index: 10;
}
.header-item {
@apply flex;
.current-branch {
@apply flex items-center font-bold;
}
}
.header-wrapper {
background-color: white;
position: fixed;

View File

@@ -12,6 +12,7 @@ import { ApplicationService } from '@core/application';
import { NotificationsHub } from '@hub/notifications';
import { UiModalService } from '@ui/modal';
import { ModalNotificationsComponent } from 'apps/modal/notifications/src/public-api';
import { DomainAvailabilityService } from '@domain/availability';
@Component({
selector: 'app-header',
@@ -28,6 +29,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length));
currentBranch$ = this._availabilityService.getCurrentBranch();
constructor(
private store: Store,
private router: Router,
@@ -35,7 +38,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
private applicationService: ApplicationService,
private cdr: ChangeDetectorRef,
private _notificationsHub: NotificationsHub,
private _modal: UiModalService
private _modal: UiModalService,
private _availabilityService: DomainAvailabilityService
) {}
customer = 'Kunden';
@@ -64,10 +68,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
this.doubleChoiceSwitch.isFirstSwitchedOn = section === 'customer';
this.cdr.markForCheck();
});
this._notificationsHub.notifications$.subscribe((data) => {
console.log('signalr - notifications', data);
});
}
ngOnDestroy() {

View File

@@ -28,7 +28,7 @@
</div>
<div
class="menu-item-grid align-center active"
(click)="routeToMenu('/customer/search', 'customersearch', { customertype: 'b2b;store;loyalty;webshop' })"
(click)="routeToMenu('/customer/search', 'customersearch', { customertype: 'store;loyalty;webshop' })"
>
<div>
<lib-icon

View File

@@ -1,8 +1,13 @@
@import 'variables';
.grid-container {
display: grid;
grid-column-gap: 1vh;
@apply grid grid-flow-col;
grid-template-columns: 1fr auto;
}
.align-left {
@apply grid grid-flow-col;
grid-template-columns: auto 1fr auto;
}
.grid-container-fix-width-last-col {

View File

@@ -73,7 +73,7 @@ export class AppService implements OnDestroy {
});
this.ssoService.registerAuthentication().then((authenticated) => {
if (authenticated) {
if (!!authenticated) {
this.loadState();
// this.loadVats();
// this.loadSuppliers();
@@ -141,6 +141,8 @@ export class AppService implements OnDestroy {
this.loader$.next();
});
});
} else {
this.loader$.next();
}
}

View File

@@ -20,7 +20,7 @@
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 20px 20px 20px 25px;
padding: 20px 10px 20px 25px;
.title {
font-size: 15px;
@@ -37,12 +37,15 @@
}
.text {
width: 100%;
padding-top: 5px;
font-size: 16px;
color: #000000;
text-align: left;
line-height: 22px;
white-space: pre-wrap;
line-break: auto;
overflow-y: hidden;
}
}

View File

@@ -8,9 +8,9 @@ import { isNullOrUndefined } from 'util';
import { AppService } from '@sales/core-services';
import { RemissionService, ShippingDocument, RemissionProcess } from '@isa/remission';
import { SetRemissionShippingDocument } from 'apps/sales/src/app/core/store/actions/remission.actions';
import { NativeContainerService } from 'shared/public_api';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiSearchboxNextComponent } from '@ui/searchbox';
import { NativeContainerService } from 'native-container';
@Component({
selector: 'app-shipping-document-creation',
@@ -47,9 +47,7 @@ export class ShippingDocumentCreationComponent implements OnInit {
) {}
ngOnInit() {
this.showScanningButton$ = this.enableScan$.pipe(
map((allowScan) => !!allowScan && this.appService.isIPadEnv() && this.nativeService.isUiWebview())
);
this.showScanningButton$ = this.enableScan$.pipe(map((allowScan) => !!allowScan && this.nativeService.isUiWebview().isNative));
this.showSearchbox$ = combineLatest([this.currentRemissionProcess$, this.showScanningButton$]).pipe(
map(([process, showScanButton]) => process?.filter?.target?.name === 'Blank' && !showScanButton)

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Group 3 Copy 2</title>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Tablet_ISA_Filiale_Wareneingang_Detailseite_004_Flow-einbuchen_003" transform="translate(-692.000000, -377.000000)">
<g id="Group-3-Copy-2" transform="translate(692.000000, 377.000000)">
<path d="M12.4449999,-1.77635684e-15 L43.5550001,-1.77635684e-15 C46.0099058,-2.22731569e-15 48,1.99009422 48,4.44499986 L48,4.47368421 L48,4.47368421 L8,4.47368421 L8,4.44499986 C8,1.99009422 9.99009422,-1.32539799e-15 12.4449999,-1.77635684e-15 Z" id="Rectangle" fill="#262B31"></path>
<path d="M4.44499986,0 L42.7806818,0 L42.7806818,0 L42.7806818,43.4807986 C42.7806818,45.9357043 40.7905876,47.9257985 38.335682,47.9257985 C37.6435186,47.9257985 36.9609195,47.7641551 36.342263,47.4537452 L23.3760316,40.9479615 C22.1211621,40.3183329 20.6425928,40.3185691 19.3879245,40.9485986 L6.43968804,47.4505326 C4.24584207,48.5521685 1.57432621,47.6667572 0.472690352,45.4729113 C0.161867564,44.8539251 -8.03354089e-16,44.1708663 0,43.4782231 L0,4.44499986 C-3.00639233e-16,1.99009422 1.99009422,1.33913727e-15 4.44499986,0 Z" id="Rectangle" fill="#596470"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="67px" height="64px" viewBox="0 0 67 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Group 3 Copy 2</title>
<defs>
<filter x="-40.5%" y="-45.3%" width="181.1%" height="190.7%" filterUnits="objectBoundingBox" id="filter-1">
<feGaussianBlur stdDeviation="5" in="SourceGraphic"></feGaussianBlur>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="01_ISA_HIMA_Abholfach_Warenausgang_Trefferliste_01_2_Radio-Buttons-active_02_nochnichtbezahlt_sichtbarerBereich" transform="translate(-680.000000, -622.000000)">
<g id="Group-3-Copy-2" transform="translate(692.000000, 623.000000)">
<path d="M12.4449999,-1.77635684e-15 L43.5550001,-1.77635684e-15 C46.0099058,-2.22731569e-15 48,1.99009422 48,4.44499986 L48,4.47368421 L48,4.47368421 L8,4.47368421 L8,4.44499986 C8,1.99009422 9.99009422,-1.32539799e-15 12.4449999,-1.77635684e-15 Z" id="Rectangle" fill="#172062"></path>
<path d="M7.44499986,14 L40,14 L40,14 L40,42.6321632 C40,45.0870688 38.0099058,47.0771631 35.5550001,47.0771631 C34.8628368,47.0771631 34.1802377,46.9155196 33.5615812,46.6051098 L23.4868424,41.5501273 C22.2319729,40.9204988 20.7534035,40.920735 19.4987352,41.5507644 L9.43968804,46.6018971 C7.24584207,47.703533 4.57432621,46.8181218 3.47269035,44.6242758 C3.16186756,44.0052897 3,43.3222309 3,42.6295876 L3,18.4449999 C3,15.9900942 4.99009422,14 7.44499986,14 Z" id="Rectangle" fill-opacity="0.4" fill="#172062" filter="url(#filter-1)"></path>
<path d="M4.44499986,0 L42.7806818,0 L42.7806818,0 L42.7806818,43.4807986 C42.7806818,45.9357043 40.7905876,47.9257985 38.335682,47.9257985 C37.6435186,47.9257985 36.9609195,47.7641551 36.342263,47.4537452 L23.3760316,40.9479615 C22.1211621,40.3183329 20.6425928,40.3185691 19.3879245,40.9485986 L6.43968804,47.4505326 C4.24584207,48.5521685 1.57432621,47.6667572 0.472690352,45.4729113 C0.161867564,44.8539251 -8.03354089e-16,44.1708663 0,43.4782231 L0,4.44499986 C-3.00639233e-16,1.99009422 1.99009422,1.33913727e-15 4.44499986,0 Z" id="Rectangle" fill="#5A728A"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -1,7 +1,7 @@
export const ProcessingStatusNameMap = new Map<number, { value: string; disabled: boolean }>([
[1, { value: 'neu', disabled: true }],
[2, { value: '', disabled: true }],
[4, { value: '', disabled: true }],
[1, { value: 'neu (angelegt)', disabled: true }],
[2, { value: 'neu (angelegt)', disabled: true }],
[4, { value: 'neu (übernommen)', disabled: true }],
[8, { value: 'geparkt', disabled: true }],
[16, { value: 'bestellt', disabled: false }],
[32, { value: 'Vorbereitung Versand', disabled: true }],

View File

@@ -69,23 +69,23 @@ export class SharedGoodsInOutOrderDetailsCoversComponent extends ComponentStore<
this.subscriptions.add(
this.orderItems$
.pipe(
map(
(orderItems) =>
orderItems
?.filter((a) => a.processingStatus === 128)
?.filter((a) => {
if (a.readyForPickUp) {
const readyForPickUp = new Date(a.readyForPickUp);
return this.dateAdapter.compareDate(readyForPickUp, this.dateAdapter.today()) === 0;
}
return false;
})
?.sort((a, b) => new Date(b.readyForPickUp).getTime() - new Date(a.readyForPickUp).getTime())
?.find((_) => true)?.compartmentCode
map((orderItems) =>
orderItems
?.filter((a) => a.processingStatus === 128)
?.filter((a) => {
if (a.readyForPickUp) {
const readyForPickUp = new Date(a.readyForPickUp);
return this.dateAdapter.compareDate(readyForPickUp, this.dateAdapter.today()) === 0;
}
return false;
})
?.sort((a, b) => new Date(b.readyForPickUp).getTime() - new Date(a.readyForPickUp).getTime())
?.find((_) => true)
)
)
.subscribe((compartmentCode) => {
this.host.latestCompartmentCode = compartmentCode;
.subscribe((orderItem) => {
this.host.latestCompartmentCode = orderItem?.compartmentCode;
this.host.latestCompartmentInfo = orderItem?.compartmentInfo;
})
);
}

View File

@@ -120,21 +120,27 @@
<div class="label">Abholfrist</div>
<div *ngIf="!(changeDateLoader$ | async)" class="value">
<button
[uiOverlayTrigger]="deadlineDropdown"
#deadlineDropdownTrigger="uiOverlayTrigger"
[uiOverlayTrigger]="deadlineDatepicker"
#deadlineDatepickerTrigger="uiOverlayTrigger"
[disabled]="!!orderItem?.features?.paid || (changeDateDisabled$ | async)"
class="cta-pickup-deadline"
>
<strong>
{{ orderItem?.pickUpDeadline | date: 'dd.MM.yy' }}
</strong>
<ui-icon [rotate]="deadlineDropdownTrigger.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
<ui-icon [rotate]="deadlineDatepickerTrigger.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
</button>
<ui-dropdown #deadlineDropdown yPosition="below" xPosition="after" class="deadline-select" [xOffset]="8">
<button uiDropdownItem *ngFor="let dl of pickupDeadlines$ | async | keyvalue" (click)="updatePickupDeadline(dl.value)">
{{ dl.key }}
</button>
</ui-dropdown>
<ui-datepicker
#deadlineDatepicker
yPosition="below"
xPosition="after"
[xOffset]="8"
[min]="minDateDatepicker"
[disabledDaysOfWeek]="[0]"
[selected]="orderItem?.pickUpDeadline"
(save)="updatePickupDeadline($event)"
>
</ui-datepicker>
</div>
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="value loader" [show]="loader"></ui-spinner>
</ng-template>

View File

@@ -56,16 +56,6 @@ export class SharedGoodsInOutOrderDetailsHeaderComponent {
shareReplay()
);
pickupDeadlines$ = this.orderItem$.pipe(
filter((orderItem) => !!orderItem),
map((orderItem) => {
return new Map<string, Date>([
['um 2 Wochen verlängern', this.dateAdapter.addCalendarDays(new Date(orderItem.pickUpDeadline), 14)],
['um 4 Wochen verlängern', this.dateAdapter.addCalendarDays(new Date(orderItem.pickUpDeadline), 28)],
]);
})
);
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
showMultiselect$ = combineLatest([this.host.orderItems$, this.host.fetchPartial$, this.host.itemsSelectable$]).pipe(

View File

@@ -1,19 +1,28 @@
<ng-container *ngIf="orderItem$ | async; let orderItem">
<div class="goods-in-out-order-details-features">
<img *ngIf="orderItem?.features?.prebooked" src="/assets/images/tag_icon_preorder.svg" [alt]="orderItem?.features?.prebooked" />
<ng-container *ngIf="orderItem?.features?.prebooked">
<img [uiOverlayTrigger]="prebookedTooltip" src="/assets/images/tag_icon_preorder.svg" [alt]="orderItem?.features?.prebooked" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #prebookedTooltip [closeable]="true">
Artikel wird für Sie vorgemerkt.
</ui-tooltip>
</ng-container>
<ng-container *ngIf="notificationsSent$ | async; let notificationsSent">
<ng-container *ngIf="notificationsSent?.NOTIFICATION_EMAIL">
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
Per E-Mail benachrichtigt <br />
{{ notificationsSent?.NOTIFICATION_EMAIL | date: 'dd.MM.yyyy | HH:mm' }} Uhr
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_EMAIL">
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
</ng-container>
</ui-tooltip>
</ng-container>
<ng-container *ngIf="notificationsSent?.NOTIFICATION_SMS">
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
Per SMS benachrichtigt <br />
{{ notificationsSent?.NOTIFICATION_SMS | date: 'dd.MM.yyyy | HH:mm' }} Uhr
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_SMS">
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
</ng-container>
</ui-tooltip>
</ng-container>
</ng-container>
@@ -76,6 +85,24 @@
<div class="label">Meldenummer</div>
<div class="value">{{ orderItem.ssc }} - {{ orderItem.sscText }}</div>
</div>
<div class="detail">
<div class="label">
<ng-container
*ngIf="
orderItemFeature(orderItem) === 'Versand' ||
orderItemFeature(orderItem) === 'B2B-Versand' ||
orderItemFeature(orderItem) === 'DIG-Versand'
"
>Lieferung ab</ng-container
>
<ng-container *ngIf="orderItemFeature(orderItem) === 'Abholung' || orderItemFeature(orderItem) === 'Rücklage'">
Abholung ab
</ng-container>
</div>
<div class="value">
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
</div>
</div>
<div class="detail">
<div class="label">Vormerker</div>
<div class="value">{{ orderItem.isPrebooked ? 'Ja' : 'Nein' }}</div>

View File

@@ -3,7 +3,7 @@ import { FormControl } from '@angular/forms';
import { DomainOmsService, DomainReceiptService } from '@domain/oms';
import { HistoryComponent } from '@modal/history';
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@swagger/oms';
import { OrderDTO, OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@swagger/oms';
import { UiModalService } from '@ui/modal';
import { isEqual } from 'lodash';
import { combineLatest, NEVER } from 'rxjs';
@@ -12,6 +12,7 @@ import { SharedGoodsInOutOrderDetailsStore } from '../goods-in-out-order-details
export interface SharedGoodsInOutOrderDetailsItemComponentState {
orderItem?: OrderItemListItemDTO;
order?: OrderDTO;
quantity?: number;
receipts?: ReceiptDTO[];
selected?: boolean;
@@ -47,6 +48,14 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
}
}
@Input()
get order() {
return this.get((s) => s.order);
}
set order(order: OrderDTO) {
this.patchState({ order });
}
@Output()
orderItemChange = new EventEmitter<OrderItemListItemDTO>();
@@ -56,7 +65,7 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
filter((oi) => !!oi),
switchMap((oi) =>
this._omsService
.getCompletedTasks({ orderId: oi.orderId, orderItemId: oi.orderItemId, orderItemSubsetId: oi.orderItemSubsetId })
.getCompletedTasks({ orderId: oi.orderId, orderItemId: oi.orderItemId, orderItemSubsetId: oi.orderItemSubsetId, take: 4, skip: 0 })
.pipe(catchError(() => NEVER))
)
);
@@ -203,4 +212,9 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
this.selectedChange.emit(selected);
this._cdr.markForCheck();
}
orderItemFeature(orderItemListItem: OrderItemListItemDTO) {
const orderItems = this.order?.items;
return orderItems.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features?.orderType;
}
}

View File

@@ -21,7 +21,7 @@
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
</button>
<button
[disabled]="changeActionDisabled$ | async"
[disabled]="(changeActionDisabled$ | async) || (addToPreviousCompartmentActionDisabled$ | async)"
*ngIf="addToPreviousCompartmentAction$ | async; let action"
class="cta-action"
[class.cta-action-primary]="action.selected"

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