mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
498 lines
15 KiB
TypeScript
498 lines
15 KiB
TypeScript
import {
|
|
ChangeDetectionStrategy,
|
|
ChangeDetectorRef,
|
|
Component,
|
|
Injector,
|
|
OnDestroy,
|
|
OnInit,
|
|
inject,
|
|
} from '@angular/core';
|
|
import { DomainCheckoutService } from '@domain/checkout';
|
|
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
|
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
|
import { first, map, shareReplay, switchMap } from 'rxjs/operators';
|
|
import { CrmCustomerService } from '@domain/crm';
|
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
import { DomainOmsService } from '@domain/oms';
|
|
import { DomainCatalogService } from '@domain/catalog';
|
|
import {
|
|
DisplayOrderDTO,
|
|
DisplayOrderItemDTO,
|
|
DisplayOrderItemSubsetDTO,
|
|
} from '@generated/swagger/oms-api';
|
|
import { BreadcrumbService } from '@core/breadcrumb';
|
|
import { ApplicationService } from '@core/application';
|
|
import { DomainPrinterService } from '@domain/printer';
|
|
import { BehaviorSubject, combineLatest, NEVER, Subject } from 'rxjs';
|
|
import { DateAdapter } from '@ui/common';
|
|
import {
|
|
CheckoutNavigationService,
|
|
PickUpShelfOutNavigationService,
|
|
ProductCatalogNavigationService,
|
|
} from '@shared/services/navigation';
|
|
import { EnvironmentService } from '@core/environment';
|
|
import { SendOrderConfirmationModalService } from '@modal/send-order-confirmation';
|
|
import { ToasterService } from '@shared/shell';
|
|
|
|
@Component({
|
|
selector: 'page-checkout-summary',
|
|
templateUrl: 'checkout-summary.component.html',
|
|
styleUrls: ['checkout-summary.component.scss'],
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
standalone: false,
|
|
})
|
|
export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
|
private _injector = inject(Injector);
|
|
|
|
get sendOrderConfirmationModalService() {
|
|
return this._injector.get(SendOrderConfirmationModalService);
|
|
}
|
|
|
|
private _toaster = inject(ToasterService);
|
|
|
|
private _onDestroy$ = new Subject<void>();
|
|
processId = Date.now();
|
|
selectedDate = this.dateAdapter.today();
|
|
minDateDatepicker = this.dateAdapter.addCalendarDays(
|
|
this.dateAdapter.today(),
|
|
-1,
|
|
);
|
|
|
|
updatingPreferredPickUpDate$ = new BehaviorSubject<Record<string, string>>(
|
|
{},
|
|
);
|
|
|
|
displayOrders$ = combineLatest([
|
|
this.domainCheckoutService.getOrders(),
|
|
this._route.params,
|
|
]).pipe(
|
|
map(([orders, params]) => {
|
|
let filteredOrders: DisplayOrderDTO[] = [];
|
|
if (params?.orderIds) {
|
|
const orderIds: string[] = params.orderIds.split(',');
|
|
filteredOrders = orders.filter((order) =>
|
|
orderIds.find((id) => Number(id) === order.id),
|
|
);
|
|
} else {
|
|
return filteredOrders;
|
|
}
|
|
|
|
// Ticket #4228 Für die korrekte Gruppierung der Items bei gleichem Bestellziel (Aufsplitten von Abholung und Rücklage)
|
|
const ordersWithMultipleFeatures = filteredOrders.filter((order) =>
|
|
order.items.find(
|
|
(item) => item.features.orderType !== order.features.orderType,
|
|
),
|
|
);
|
|
|
|
if (ordersWithMultipleFeatures) {
|
|
for (let orderWithMultipleFeatures of ordersWithMultipleFeatures) {
|
|
if (orderWithMultipleFeatures?.items?.length > 1) {
|
|
const itemsWithOrderFeature =
|
|
orderWithMultipleFeatures.items.filter(
|
|
(item) =>
|
|
item.features.orderType ===
|
|
orderWithMultipleFeatures.features.orderType,
|
|
);
|
|
const itemsWithDifferentOrderFeature =
|
|
orderWithMultipleFeatures.items.filter(
|
|
(item) =>
|
|
item.features.orderType !==
|
|
orderWithMultipleFeatures.features.orderType,
|
|
);
|
|
|
|
filteredOrders = [
|
|
...filteredOrders.filter(
|
|
(order) => order.id !== orderWithMultipleFeatures.id,
|
|
),
|
|
];
|
|
|
|
if (itemsWithOrderFeature?.length > 0) {
|
|
filteredOrders = [
|
|
...filteredOrders,
|
|
{ ...orderWithMultipleFeatures, items: itemsWithOrderFeature },
|
|
];
|
|
}
|
|
|
|
if (itemsWithDifferentOrderFeature?.length > 0) {
|
|
filteredOrders = [
|
|
...filteredOrders,
|
|
{
|
|
...orderWithMultipleFeatures,
|
|
items: itemsWithDifferentOrderFeature,
|
|
},
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return filteredOrders?.map((order) => {
|
|
return {
|
|
...order,
|
|
items: [...order.items]?.sort((a, b) =>
|
|
a.product?.name.localeCompare(b.product?.name),
|
|
),
|
|
};
|
|
});
|
|
}),
|
|
shareReplay(),
|
|
);
|
|
|
|
hasAbholung$ = this.displayOrders$.pipe(
|
|
map(
|
|
(displayOrders) =>
|
|
displayOrders.filter(
|
|
(order) => order.features?.orderType === 'Abholung',
|
|
)?.length > 0,
|
|
),
|
|
);
|
|
|
|
totalItemCount$ = this.displayOrders$.pipe(
|
|
map((displayOrders) =>
|
|
displayOrders.reduce(
|
|
(total, displayOrder) =>
|
|
total +
|
|
displayOrder?.items?.reduce(
|
|
(subTotal, order) => subTotal + order?.quantity,
|
|
0,
|
|
),
|
|
0,
|
|
),
|
|
),
|
|
);
|
|
|
|
totalReadingPoints$ = this.displayOrders$.pipe(
|
|
switchMap((displayOrders) => {
|
|
const items = displayOrders
|
|
.reduce<DisplayOrderItemDTO[]>(
|
|
(items, order) => [...items, ...order.items],
|
|
[],
|
|
)
|
|
.map((i) => {
|
|
if (i?.product?.catalogProductNumber) {
|
|
return {
|
|
id: Number(i.product?.catalogProductNumber),
|
|
quantity: i.quantity,
|
|
price: i.price?.value?.value,
|
|
};
|
|
}
|
|
})
|
|
.filter((item) => item !== undefined);
|
|
if (items.length !== 0) {
|
|
return this.domainCatalogService
|
|
.getPromotionPoints({ items })
|
|
.pipe(
|
|
map((response) =>
|
|
Object.values(response.result).reduce(
|
|
(sum, points) => sum + points,
|
|
0,
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
return NEVER;
|
|
}
|
|
}),
|
|
);
|
|
|
|
totalPrice$ = this.displayOrders$.pipe(
|
|
map((displayOrders) =>
|
|
displayOrders.reduce(
|
|
(total, displayOrder) =>
|
|
total +
|
|
displayOrder?.items?.reduce(
|
|
(subTotal, order) =>
|
|
subTotal + order?.price?.value?.value * order.quantity,
|
|
0,
|
|
),
|
|
0,
|
|
),
|
|
),
|
|
);
|
|
|
|
isPrinting$ = new BehaviorSubject(false);
|
|
|
|
totalPriceCurrency$ = this.displayOrders$.pipe(
|
|
map((displayOrders) => displayOrders[0]?.items[0]?.price?.value?.currency),
|
|
);
|
|
|
|
containsDeliveryOrder$ = this.displayOrders$.pipe(
|
|
map(
|
|
(displayOrders) =>
|
|
displayOrders.filter(
|
|
(o) =>
|
|
['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(
|
|
o.features?.orderType,
|
|
) > -1,
|
|
)?.length > 0,
|
|
),
|
|
);
|
|
|
|
customer$ = this.displayOrders$.pipe(
|
|
switchMap((o) =>
|
|
this.customerService.getCustomers(o[0].buyerNumber, { take: 5 }),
|
|
),
|
|
map((customers) => customers.result[0]),
|
|
shareReplay(),
|
|
);
|
|
|
|
isB2BCustomer$ = this.customer$.pipe(
|
|
map((customer) => customer?.features?.find((f) => f.key === 'b2b') != null),
|
|
);
|
|
|
|
takeNowOrders$ = this.displayOrders$.pipe(
|
|
map((displayOrders) =>
|
|
displayOrders.filter(
|
|
(o) =>
|
|
o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null,
|
|
),
|
|
),
|
|
);
|
|
|
|
get isDesktop$() {
|
|
return this._environmentService.matchDesktopLarge$;
|
|
}
|
|
|
|
get isTablet() {
|
|
return this._environmentService.matchTablet();
|
|
}
|
|
|
|
expanded: boolean[] = [];
|
|
|
|
constructor(
|
|
private domainCheckoutService: DomainCheckoutService,
|
|
private customerService: CrmCustomerService,
|
|
private domainCatalogService: DomainCatalogService,
|
|
private router: Router,
|
|
private _route: ActivatedRoute,
|
|
private omsService: DomainOmsService,
|
|
private uiModal: UiModalService,
|
|
private breadcrumb: BreadcrumbService,
|
|
public applicationService: ApplicationService,
|
|
private domainPrinterService: DomainPrinterService,
|
|
private dateAdapter: DateAdapter,
|
|
private _navigation: CheckoutNavigationService,
|
|
private _productNavigationService: ProductCatalogNavigationService,
|
|
private _shelfOutNavigationService: PickUpShelfOutNavigationService,
|
|
private _environmentService: EnvironmentService,
|
|
private _cdr: ChangeDetectorRef,
|
|
) {
|
|
this.breadcrumb
|
|
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, [
|
|
'checkout',
|
|
])
|
|
.pipe(first())
|
|
.subscribe(async (crumbs) => {
|
|
for await (const crumb of crumbs) {
|
|
this.breadcrumb.removeBreadcrumb(crumb.id);
|
|
}
|
|
this.breadcrumb.addBreadcrumbIfNotExists({
|
|
key: this.applicationService.activatedProcessId,
|
|
name: 'Bestellbestätigung',
|
|
path: this._navigation.getCheckoutSummaryPath({
|
|
processId: this.applicationService.activatedProcessId,
|
|
orderIds: this._route.snapshot.params.orderIds,
|
|
}).path,
|
|
tags: ['checkout', 'cart'],
|
|
section: 'customer',
|
|
});
|
|
});
|
|
}
|
|
|
|
async ngOnInit() {
|
|
const displayOrders = await this.displayOrders$.pipe(first()).toPromise();
|
|
if (displayOrders?.length === 1) {
|
|
this.expanded = [true];
|
|
} else {
|
|
this.expanded = [];
|
|
}
|
|
this._cdr.markForCheck();
|
|
}
|
|
|
|
async ngOnDestroy() {
|
|
const checkoutProcess = await this.applicationService
|
|
.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
|
|
.pipe(first())
|
|
.toPromise();
|
|
|
|
// Wenn es keine Bestellabschluss Prozesse mehr gibt, werden alle Orders aus dem Store removed um die Performance zu verbessern
|
|
if (!checkoutProcess) {
|
|
this.domainCheckoutService.removeAllOrders();
|
|
}
|
|
|
|
this._onDestroy$.next();
|
|
this._onDestroy$.complete();
|
|
}
|
|
|
|
getProductSearchDetailsPath(ean: string) {
|
|
return this._productNavigationService.getArticleDetailsPathByEan({
|
|
processId: this.processId,
|
|
ean,
|
|
}).path;
|
|
}
|
|
|
|
getProductSearchDetailsQueryParams(item: DisplayOrderItemDTO) {
|
|
return {
|
|
main_qs: item?.product?.ean,
|
|
filter_format:
|
|
item?.features?.orderType === 'Download' ? 'eb;dl' : undefined,
|
|
};
|
|
}
|
|
|
|
async updatePreferredPickUpDate(item: DisplayOrderItemDTO, date: Date) {
|
|
const data: Record<string, string> = {};
|
|
|
|
try {
|
|
const items = item ? [item] : await this.getAllOrderItems();
|
|
const subsetItems = items
|
|
.filter((item) =>
|
|
['Rücklage', 'Abholung'].includes(item.features.orderType),
|
|
)
|
|
// .flatMap((item) => item.subsetItems);
|
|
.reduce<DisplayOrderItemSubsetDTO[]>(
|
|
(acc, item) => [...acc, ...item.subsetItems],
|
|
[],
|
|
);
|
|
subsetItems.forEach((item) => (data[`${item.id}`] = date?.toISOString()));
|
|
|
|
try {
|
|
this.updatingPreferredPickUpDate$.next(data);
|
|
await this.omsService.setPreferredPickUpDate({ data }).toPromise();
|
|
} catch (error) {
|
|
this.uiModal.open({
|
|
content: UiErrorModalComponent,
|
|
title: 'Fehler beim setzen des Wunschdatums',
|
|
data: error,
|
|
});
|
|
} finally {
|
|
this.updatingPreferredPickUpDate$.next({});
|
|
}
|
|
|
|
items.forEach((item) => {
|
|
this.updateDisplayOrderItem({
|
|
...item,
|
|
subsetItems: subsetItems.map((subsetItem) => {
|
|
return {
|
|
...subsetItem,
|
|
preferredPickUpDate: date?.toISOString(),
|
|
};
|
|
}),
|
|
});
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
async getAllOrderItems() {
|
|
const orders = await this.displayOrders$.pipe(first()).toPromise();
|
|
return orders.reduce<DisplayOrderItemDTO[]>(
|
|
(agg, order) => [...agg, ...order.items],
|
|
[],
|
|
);
|
|
}
|
|
|
|
async updateDisplayOrderItem(item: DisplayOrderItemDTO) {
|
|
this.domainCheckoutService.updateOrderItem(item);
|
|
}
|
|
|
|
async navigateToShelfOut() {
|
|
let takeNowOrders = await this.takeNowOrders$.pipe(first()).toPromise();
|
|
if (takeNowOrders.length != 1) return;
|
|
|
|
try {
|
|
await this.router.navigate(
|
|
this._shelfOutNavigationService.listRoute({ processId: Date.now() })
|
|
.path,
|
|
{
|
|
queryParams: {
|
|
main_qs: takeNowOrders[0].orderNumber,
|
|
filter_supplier_id: '16',
|
|
},
|
|
},
|
|
);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
|
|
async sendOrderConfirmation() {
|
|
const orders = await this.displayOrders$.pipe(first()).toPromise();
|
|
|
|
await this.sendOrderConfirmationModalService.open(orders);
|
|
}
|
|
|
|
async printOrderConfirmation() {
|
|
this.isPrinting$.next(true);
|
|
const orders = await this.displayOrders$.pipe(first()).toPromise();
|
|
const selectedPrinter = await this.domainPrinterService
|
|
.getAvailableLabelPrinters()
|
|
.pipe(
|
|
first(),
|
|
map((printers) => {
|
|
if (Array.isArray(printers))
|
|
return printers.find((printer) => printer.selected === true);
|
|
}),
|
|
)
|
|
.toPromise();
|
|
|
|
console.log(selectedPrinter);
|
|
if (!selectedPrinter || this.isTablet) {
|
|
await this.uiModal
|
|
.open({
|
|
content: PrintModalComponent,
|
|
data: {
|
|
printerType: 'Label',
|
|
printImmediately: !this.isTablet,
|
|
print: async (printer) => {
|
|
try {
|
|
const result = await this.domainPrinterService
|
|
.printOrder({ orderIds: orders.map((o) => o.id), printer })
|
|
.toPromise();
|
|
this._toaster.open({
|
|
type: 'success',
|
|
message: 'Bestellbestätigung wurde gedruckt',
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
this._toaster.open({
|
|
type: 'danger',
|
|
message: 'Fehler beim Drucken der Bestellbestätigung',
|
|
});
|
|
} finally {
|
|
this.isPrinting$.next(false);
|
|
}
|
|
},
|
|
} as PrintModalData,
|
|
config: {
|
|
panelClass: [],
|
|
showScrollbarY: false,
|
|
},
|
|
})
|
|
.afterClosed$.toPromise();
|
|
this.isPrinting$.next(false);
|
|
} else {
|
|
try {
|
|
const result = await this.domainPrinterService
|
|
.printOrder({
|
|
orderIds: orders.map((o) => o.id),
|
|
printer: selectedPrinter.key,
|
|
})
|
|
.toPromise();
|
|
this._toaster.open({
|
|
type: 'success',
|
|
message: 'Bestellbestätigung wurde gedruckt',
|
|
});
|
|
return result;
|
|
} catch (error) {
|
|
this._toaster.open({
|
|
type: 'danger',
|
|
message: 'Fehler beim Drucken der Bestellbestätigung',
|
|
});
|
|
} finally {
|
|
this.isPrinting$.next(false);
|
|
}
|
|
}
|
|
}
|
|
}
|