Store for Detail Pages

This commit is contained in:
Lorenz Hilpert
2023-09-19 09:52:35 +02:00
parent 18b2230dd7
commit 664053f231
16 changed files with 366 additions and 17 deletions

View File

@@ -1,9 +1,10 @@
import { Injectable, inject } from '@angular/core';
import { AbholfachService, QueryTokenDTO } from '@swagger/oms';
import { AbholfachService, ListResponseArgsOfDBHOrderItemListItemDTO, OrderItemProcessingStatusValue, QueryTokenDTO } from '@swagger/oms';
import { PickupShelfIOService } from './pickup-shelf-io.service';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class PickupShelfService extends PickupShelfIOService {
export class PickupShelfInService extends PickupShelfIOService {
private _abholfachService = inject(AbholfachService);
getQuerySettings() {
@@ -13,4 +14,11 @@ export class PickupShelfService extends PickupShelfIOService {
search(queryToken: QueryTokenDTO) {
return this._abholfachService.AbholfachWareneingang(queryToken);
}
getOrderItemsByOrderNumberOrCompartmentCode(args: {
orderNumber?: string;
compartmentCode?: string;
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO> {
throw new Error('Method not implemented.');
}
}

View File

@@ -1,5 +1,10 @@
import { Injectable } from '@angular/core';
import { ListResponseArgsOfDBHOrderItemListItemDTO, QueryTokenDTO, ResponseArgsOfQuerySettingsDTO } from '@swagger/oms';
import {
ListResponseArgsOfDBHOrderItemListItemDTO,
OrderItemProcessingStatusValue,
QueryTokenDTO,
ResponseArgsOfQuerySettingsDTO,
} from '@swagger/oms';
import { Observable } from 'rxjs';
@Injectable()
@@ -7,4 +12,9 @@ export abstract class PickupShelfIOService {
abstract getQuerySettings(): Observable<ResponseArgsOfQuerySettingsDTO>;
abstract search(queryToken: QueryTokenDTO): Observable<ListResponseArgsOfDBHOrderItemListItemDTO>;
abstract getOrderItemsByOrderNumberOrCompartmentCode(args: {
orderNumber?: string;
compartmentCode?: string;
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO>;
}

View File

@@ -1,6 +1,8 @@
import { Injectable, inject } from '@angular/core';
import { AbholfachService, QueryTokenDTO } from '@swagger/oms';
import { AbholfachService, ListResponseArgsOfDBHOrderItemListItemDTO, OrderItemProcessingStatusValue, QueryTokenDTO } from '@swagger/oms';
import { PickupShelfIOService } from './pickup-shelf-io.service';
import { Observable, throwError } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class PickupShelfOutService extends PickupShelfIOService {
@@ -13,4 +15,28 @@ export class PickupShelfOutService extends PickupShelfIOService {
search(queryToken: QueryTokenDTO) {
return this._abholfachService.AbholfachWarenausgabe(queryToken);
}
getOrderItemsByOrderNumberOrCompartmentCode(args: {
orderNumber?: string;
compartmentCode?: string;
}): Observable<ListResponseArgsOfDBHOrderItemListItemDTO> {
console.log('args', args);
if (!args.orderNumber && !args.compartmentCode) {
return throwError(
'PickupShelfOutService.getOrderItemsByOrderNumberOrCompartmentCode(): Either orderNumber or compartmentCode must be provided.'
);
}
console.log('args', args);
return this._abholfachService.AbholfachWarenausgabe({
input: {
qs: args.compartmentCode ?? args.orderNumber,
},
filter: {
all_branches: String(true),
archive: String(false),
},
});
}
}

View File

@@ -1,4 +1,11 @@
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { OrderService } from '@swagger/oms';
@Injectable({ providedIn: 'root' })
export class PickupShelfInService {}
export class PickupShelfService {
private _orderService = inject(OrderService);
getOrderByOrderId(orderId: number) {
return this._orderService.OrderGetOrder(orderId);
}
}

View File

@@ -1,3 +1,49 @@
import { DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, Router } from '@angular/router';
import { PickupShelfDetailsStore } from './store';
export abstract class PickupShelfDetailsBaseComponent {
constructor() {}
protected destroyRef = inject(DestroyRef);
protected router = inject(Router);
protected activatedRoute = inject(ActivatedRoute);
store = inject(PickupShelfDetailsStore);
constructor() {
this.activatedRoute.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => {
this.store.fetchOrder({ orderId: Number(params.orderId) });
this.store.fetchOrderItems({
orderNumber: params.orderNumber ? decodeURIComponent(params.orderNumber) : undefined,
compartmentCode: params.compartmentCode ? decodeURIComponent(params.compartmentCode) : undefined,
});
if (params.orderItemProcessingStatus) {
this.store.setSelectedOrderItemProcessingStatus(+params.orderItemProcessingStatus as any);
} else {
this.store.setSelectedOrderItemProcessingStatus(undefined);
}
});
}
/**
* Führt die Actions aus
* @param action Actions
*/
execAction(action: any): Promise<void> {
throw new Error('not implemented');
}
/**
* vsl. Lieferdatum
* @param data Datum
*/
updateEstimatedDeliveryDate(data: Date) {}
/**
* Zurücklegen bis
* @param data Datum
*/
updatePickupDeadline(data: Date) {}
}

View File

@@ -3,7 +3,7 @@ import { BreadcrumbModule } from '@shared/components/breadcrumb';
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
import { PickupShelfStore } from '../store';
import { provideComponentStore } from '@ngrx/component-store';
import { PickupShelfIOService, PickupShelfInService } from '@domain/pickup-shelf';
import { PickupShelfIOService, PickupShelfService } from '@domain/pickup-shelf';
import { PickupShelfBaseComponent } from '../pickup-shelf-base.component';
import { NavigationRoute, PickupShelfInNavigationService } from '@shared/services';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
@@ -16,7 +16,7 @@ import { DBHOrderItemListItemDTO } from '@swagger/oms';
host: { class: 'page-pickup-shelf-in' },
standalone: true,
imports: [BreadcrumbModule, SharedSplitscreenComponent],
providers: [provideComponentStore(PickupShelfStore), { provide: PickupShelfIOService, useClass: PickupShelfInService }],
providers: [provideComponentStore(PickupShelfStore), { provide: PickupShelfIOService, useClass: PickupShelfService }],
})
export class PickupShelfInComponent extends PickupShelfBaseComponent {
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);

View File

@@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import { routes } from './routes';
import { provideComponentStore } from '@ngrx/component-store';
import { PickupShelfStore } from '../store/pickup-shelf.store';
import { PickupShelfIOService, PickupShelfInService } from '@domain/pickup-shelf';
import { PickupShelfIOService, PickupShelfService } from '@domain/pickup-shelf';
@NgModule({
imports: [RouterModule.forChild(routes)],

View File

@@ -1 +1,3 @@
<h1>Details</h1>
<pre class="text-slate-700">{{ order$ | async | json }}</pre>
<pre class="text-blue-800">{{ orderItems$ | async | json }}</pre>

View File

@@ -1,5 +1,6 @@
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { PickupShelfDetailsBaseComponent } from '../../pickup-shelf-details-base.component';
import { AsyncPipe, JsonPipe } from '@angular/common';
@Component({
selector: 'page-pickup-shelf-out-details',
@@ -8,9 +9,14 @@ import { PickupShelfDetailsBaseComponent } from '../../pickup-shelf-details-base
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-pickup-shelf-out-details' },
standalone: true,
imports: [],
imports: [AsyncPipe, JsonPipe],
providers: [],
})
export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseComponent {
order$ = this.store.order$;
orderItems$ = this.store.orderItems$;
constructor() {
super();
}

View File

@@ -1,7 +1,7 @@
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
import { BreadcrumbModule } from '@shared/components/breadcrumb';
import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
import { PickupShelfStore } from '../store';
import { PickupShelfStore, PickupShelfDetailsStore } from '../store';
import { provideComponentStore } from '@ngrx/component-store';
import { PickupShelfIOService, PickupShelfOutService } from '@domain/pickup-shelf';
import { PickupShelfBaseComponent } from '../pickup-shelf-base.component';
@@ -18,7 +18,11 @@ import { logAsync } from '@utils/common';
host: { class: 'page-pickup-shelf-out' },
standalone: true,
imports: [BreadcrumbModule, SharedSplitscreenComponent, AsyncPipe],
providers: [provideComponentStore(PickupShelfStore), { provide: PickupShelfIOService, useClass: PickupShelfOutService }],
providers: [
provideComponentStore(PickupShelfStore),
provideComponentStore(PickupShelfDetailsStore),
{ provide: PickupShelfIOService, useClass: PickupShelfOutService },
],
})
export class PickupShelfOutComponent extends PickupShelfBaseComponent {
private _pickupShelfOutNavigationService = inject(PickupShelfOutNavigationService);

View File

@@ -14,8 +14,12 @@ export const routes: Routes = [
{ path: '', component: PickupShelfOutMainComponent, data: { breadcrumb: 'main' } },
{ path: 'list', component: PickupShelfOutListComponent, data: { breadcrumb: 'list' } },
{ path: 'list/filter', component: PickupShelfFilterComponent, data: { breadcrumb: 'filter' } },
{ path: 'order/:orderId/:processingStatus', component: PickupShelfOutDetailsComponent, data: { breadcrumb: 'details' } },
{ path: 'compartment/:compartment', component: PickupShelfOutDetailsComponent, data: { breadcrumb: 'details' } },
{
path: 'order/:orderId/:orderNumber/item/status/:orderItemProcessingStatus',
component: PickupShelfOutDetailsComponent,
data: { breadcrumb: 'details' },
},
{ path: 'order/:orderId/compartment/:compartmentCode', component: PickupShelfOutDetailsComponent, data: { breadcrumb: 'details' } },
// { path: 'main', outlet: 'side' },
// { path: 'list', outlet: 'side' },
],

View File

@@ -1 +1,2 @@
export * from './pickup-shelf-details.store';
export * from './pickup-shelf.store';

View File

@@ -0,0 +1,34 @@
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
export const selectOrder = (s: PickupShelfDetailsState) => s.order;
export const selectFetchingOrder = (s: PickupShelfDetailsState) => s.fetchingOrder;
export const selectOrderItemsRaw = (s: PickupShelfDetailsState) => s.orderItems;
export const selectFetchingOrderItems = (s: PickupShelfDetailsState) => s.fetchingOrderItems;
export const selectSelectedOrderItemProcessingStatus = (s: PickupShelfDetailsState) => s.selectedOrderItemProcessingStatus;
export const selectOrderItemsWithSelectedOrderItemProcessingStatus = (s: PickupShelfDetailsState) =>
s.orderItems?.filter((oi) => oi.processingStatus === s.selectedOrderItemProcessingStatus);
export const selectCustomer = (s: PickupShelfDetailsState) => s.customer;
export const selectFetchingCustomer = (s: PickupShelfDetailsState) => s.fetchingCustomer;
export const selectPickupDeadline = (s: PickupShelfDetailsState) => {
throw new Error('not implemented');
};
export const selectOrderItemProcessingStatus = (s: PickupShelfDetailsState) => {
throw new Error('not implemented');
};
export const selectEstimatedDeliveryDate = (s: PickupShelfDetailsState) => {
throw new Error('not implemented');
};
export const selectCompartmentCode = (s: PickupShelfDetailsState) => {
throw new Error('not implemented');
};

View File

@@ -0,0 +1,15 @@
import { CustomerInfoDTO } from '@swagger/crm';
import { DBHOrderItemListItemDTO, OrderDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
export interface PickupShelfDetailsState {
fetchingOrder?: boolean;
order?: OrderDTO;
fetchingOrderItems?: boolean;
orderItems?: DBHOrderItemListItemDTO[];
selectedOrderItemProcessingStatus?: OrderItemProcessingStatusValue;
fetchingCustomer?: boolean;
customer?: CustomerInfoDTO;
}

View File

@@ -0,0 +1,167 @@
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
import { Observable } from 'rxjs';
import { ListResponseArgsOfDBHOrderItemListItemDTO, OrderItemProcessingStatusValue, ResponseArgsOfOrderDTO } from '@swagger/oms';
import { PickupShelfIOService, PickupShelfService } from '@domain/pickup-shelf';
import { Injectable, inject } from '@angular/core';
import { filter, switchMap, tap } from 'rxjs/operators';
import { UiModalService } from '@ui/modal';
import { CrmCustomerService } from '@domain/crm';
import * as Selectors from './pickup-shelf-details.selectors';
import { ListResponseArgsOfCustomerInfoDTO } from '@swagger/crm';
import { uniq } from 'lodash';
@Injectable()
export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsState> {
private _pickupShelfService = inject(PickupShelfService);
private _pickupShelfIOService = inject(PickupShelfIOService);
private _customerService = inject(CrmCustomerService);
private _modal = inject(UiModalService);
order$ = this.select(Selectors.selectOrder);
get order() {
return this.get(Selectors.selectOrder);
}
fetchingOrder$ = this.select(Selectors.selectFetchingOrder);
get fetchingOrder() {
return this.get(Selectors.selectFetchingOrder);
}
orderItems$ = this.select(Selectors.selectOrderItemsWithSelectedOrderItemProcessingStatus);
get orderItems() {
return this.get(Selectors.selectOrderItemsWithSelectedOrderItemProcessingStatus);
}
fetchingOrderItems$ = this.select(Selectors.selectFetchingOrderItems);
get fetchingOrderItems() {
return this.get(Selectors.selectFetchingOrderItems);
}
selectedOrderItemProcessingStatus$ = this.select(Selectors.selectSelectedOrderItemProcessingStatus);
get selectedOrderItemProcessingStatus() {
return this.get(Selectors.selectSelectedOrderItemProcessingStatus);
}
customer$ = this.select(Selectors.selectCustomer);
get customer() {
return this.get(Selectors.selectCustomer);
}
fetchingCustomer$ = this.select(Selectors.selectFetchingCustomer);
get fetchingCustomer() {
return this.get(Selectors.selectFetchingCustomer);
}
constructor() {
super({});
this.select((s) => s).subscribe(console.log.bind(console));
}
setSelectedOrderItemProcessingStatus(status?: OrderItemProcessingStatusValue) {
this.patchState({ selectedOrderItemProcessingStatus: status });
}
fetchOrder = this.effect((trigger$: Observable<{ orderId: number }>) =>
trigger$.pipe(
filter(({ orderId }) => this.order?.id !== orderId),
tap(this.beforeFetchOrder),
switchMap(({ orderId }) =>
this._pickupShelfService.getOrderByOrderId(orderId).pipe(tapResponse(this.fetchOrderSuccess, this.fetchOrderFailed))
)
)
);
private beforeFetchOrder = () => {
this.patchState({ fetchingOrder: true, order: undefined, customer: undefined });
};
private fetchOrderSuccess = (res: ResponseArgsOfOrderDTO) => {
this.patchState({ fetchingOrder: false, order: res.result });
this.fetchCustomer({ buyerNumber: res.result.buyer.buyerNumber });
};
private fetchOrderFailed = (error: any) => {
this._modal.error('Fehler beim Laden der Bestellung', error);
this.patchState({ fetchingOrder: false });
};
fetchOrderItems = this.effect((trigger$: Observable<{ orderNumber?: string; compartmentCode?: string }>) =>
trigger$.pipe(
tap(this.beforeFetchOrderItems),
switchMap(({ compartmentCode, orderNumber }) =>
this._pickupShelfIOService
.getOrderItemsByOrderNumberOrCompartmentCode({ compartmentCode, orderNumber })
.pipe(tapResponse(this.fetchOrderItemsSuccess, this.fetchOrderItemsFailed))
)
)
);
private beforeFetchOrderItems = () => {
this.patchState({ fetchingOrderItems: true, orderItems: [] });
};
private fetchOrderItemsSuccess = (res: ListResponseArgsOfDBHOrderItemListItemDTO) => {
// check if order items belong to the same customer
const buyerNumbers = uniq(res.result.map((i) => i.buyerNumber));
if (buyerNumbers.length > 1) {
this._modal.error('Fehler beim Laden der Bestellpositionen', new Error('Die Bestellpositionen gehören zu unterschiedlichen Kunden.'));
return;
}
this.patchState({ fetchingOrderItems: false, orderItems: res.result });
};
private fetchOrderItemsFailed = (err: any) => {
this._modal.error('Fehler beim Laden der Bestellpositionen', err);
this.patchState({ fetchingOrderItems: false });
};
fetchCustomer = this.effect((trigger$: Observable<{ buyerNumber: string }>) =>
trigger$.pipe(
filter(({ buyerNumber }) => this.customer?.customerNumber !== buyerNumber),
tap(this.beforeFetchCustomer),
switchMap(({ buyerNumber }) =>
this._customerService.getCustomers(buyerNumber).pipe(tapResponse(this.fetchCustomerSuccess, this.fetchCustomerFailed))
)
)
);
beforeFetchCustomer = () => {
this.patchState({ fetchingCustomer: true, customer: undefined });
};
fetchCustomerSuccess = (res: ListResponseArgsOfCustomerInfoDTO) => {
// check if response contains exactly one customer
if (res.result.length > 1) {
this._modal.error('Fehler beim Laden des Kunden', new Error('Es wurde mehr als ein Kunde gefunden.'));
return;
}
const customer = res.result[0];
// check if the customer is the same as the order's buyer
if (customer.customerNumber !== this.order.buyer.buyerNumber) {
this._modal.error('Fehler beim Laden des Kunden', new Error('Der Kunde ist nicht der Bestellung zugeordnet.'));
return;
}
this.patchState({ fetchingCustomer: false, customer });
};
fetchCustomerFailed = (err: any) => {
this._modal.error('Fehler beim Laden des Kunden', err);
this.patchState({ fetchingCustomer: false });
};
}

View File

@@ -53,9 +53,28 @@ export class PickupShelfOutNavigationService {
let path: any[];
if (item.compartmentCode) {
path = ['/kunde', processId ? processId : undefined, 'pickup-shelf', 'compartment', item.compartmentCode].filter((v) => !!v);
path = [
'/kunde',
processId ? processId : undefined,
'pickup-shelf',
'order',
item.orderId,
encodeURIComponent(item.orderNumber),
'compartment',
encodeURIComponent(item.compartmentCode),
].filter((v) => !!v);
} else {
path = ['/kunde', processId ? processId : undefined, 'pickup-shelf', 'order', item.orderId, item.processingStatus].filter((v) => !!v);
path = [
'/kunde',
processId ? processId : undefined,
'pickup-shelf',
'order',
item.orderId,
encodeURIComponent(item.orderNumber),
'item',
'status',
item.processingStatus,
].filter((v) => !!v);
}
const queryParams = {};