Merged PR 1226: #3061 Remission List Performance

#3061 Remission List Performance
This commit is contained in:
Nino Righi
2022-05-09 15:31:47 +00:00
committed by Andreas Schickinger
parent 08fefb1c4b
commit ec2bd0bd5d
5 changed files with 87 additions and 39 deletions

View File

@@ -86,23 +86,29 @@
<ng-container *ngIf="!!returnDto; else removeItem">
<div class="grid grid-flow-col justify-self-end gap-2 items-center">
<button *ngIf="enableChangeRemissionQuantity" class="text-brand text-base font-bold px-6 py-3" (click)="addProductToShippingDocument()">
Remi-Menge / Platzierung ändern
<button
[disabled]="loading$ | async"
*ngIf="enableChangeRemissionQuantity"
class="text-brand text-base font-bold px-6 py-3"
(click)="addProductToShippingDocument()"
>
<ui-spinner [show]="loading$ | async">Remi-Menge / Platzierung ändern</ui-spinner>
</button>
<button
*ngIf="enableToRemit"
[disabled]="loading$ | async"
class="bg-white border-brand border-solid border-2 text-brand font-bold text-base px-6 py-3 rounded-full h-12"
(click)="remit()"
>
Remittieren
<ui-spinner [show]="loading$ | async">Remittieren</ui-spinner>
</button>
</div>
</ng-container>
<ng-template #removeItem>
<div *ngIf="item?.dto?.source === 'manually-added'" class="grid grid-flow-col justify-self-end gap-2">
<button class="text-brand text-base font-bold px-6 py-3" (click)="removeReturnItem()">
Entfernen
<button [disabled]="loading$ | async" class="text-brand text-base font-bold px-6 py-3" (click)="removeReturnItem()">
<ui-spinner [show]="loading$ | async">Entfernen</ui-spinner>
</button>
</div>
</ng-template>

View File

@@ -1,9 +1,16 @@
import { Component, ChangeDetectionStrategy, Input, OnDestroy, EventEmitter, Output } from '@angular/core';
import { Component, ChangeDetectionStrategy, Input, OnDestroy } from '@angular/core';
import { DomainRemissionService, RemissionListItem } from '@domain/remission';
import { RemissionPlacementType } from '@isa/remission';
import { ReturnDTO } from '@swagger/remi';
import {
ReturnDTO,
ValueTupleOfReceiptItemDTOAndReturnItemDTO,
ValueTupleOfReceiptItemDTOAndReturnSuggestionDTO,
ReturnItemDTO,
ReturnSuggestionDTO,
} from '@swagger/remi';
import { UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal';
import { Subject } from 'rxjs';
import { mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from 'apps/domain/remission/src/lib/mappings';
import { BehaviorSubject, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { AddProductToShippingDocumentModalComponent } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component';
import { RemissionListComponentStore } from '../remission-list.component-store';
@@ -39,6 +46,8 @@ export class RemissionListItemComponent implements OnDestroy {
return this.enableToRemit ? this.item.inStock > 0 && this.item.remissionQuantity > 0 : this.item.remissionQuantity > 0;
}
loading$ = new BehaviorSubject<boolean>(false);
constructor(
private _modal: UiModalService,
private _remissionService: DomainRemissionService,
@@ -99,18 +108,19 @@ export class RemissionListItemComponent implements OnDestroy {
}
async removeReturnItem() {
this.loading$.next(true);
try {
this._store.clearItems();
this._store.setFetching(true);
await this._remissionService.removeReturnItemFromList({ itemId: this.item?.dto?.id }).toPromise();
this._store.removeItem(this.item);
} catch (err) {
this._modal.open({
content: UiErrorModalComponent,
title: 'Fehler beim entfernen eines Artikels von der Remissionsliste',
data: err?.status === 404 ? { message: 'Artikel wurde bereits entfernt.' } : err,
});
this.reload();
}
this.reload();
this.loading$.next(false);
}
async addReturnItemOrSuggestion({
@@ -124,13 +134,13 @@ export class RemissionListItemComponent implements OnDestroy {
impedimentComment?: string;
remainingQuantity?: number;
}) {
this.loading$.next(true);
try {
this._store.clearItems();
this._store.setFetching(true);
let response: ValueTupleOfReceiptItemDTOAndReturnItemDTO | ValueTupleOfReceiptItemDTOAndReturnSuggestionDTO;
// Pflichtremission
if (this.item.dtoType === 'return') {
await this._remissionService
response = await this._remissionService
.addReturnItem({
returnId: this.returnDto.id,
receiptId: this.firstReceipt.id,
@@ -143,7 +153,7 @@ export class RemissionListItemComponent implements OnDestroy {
}
// Abteilungsremission
else if (this.item.dtoType === 'suggestion') {
await this._remissionService
response = await this._remissionService
.addReturnSuggestion({
returnId: this.returnDto.id,
receiptId: this.firstReceipt.id,
@@ -155,23 +165,29 @@ export class RemissionListItemComponent implements OnDestroy {
remainingQuantity,
})
.toPromise();
} else {
throw new Error('Item hat keinen passenden DTO Typ');
}
this._store.removeItem(this.item);
if (response && !!response?.item2) {
this.addItem(response.item2);
}
this._store.updateCache();
} catch (err) {
this._modal.open({
content: UiErrorModalComponent,
title: 'Fehler beim remittieren',
data: err?.status === 409 ? { message: 'Artikel wurde bereits remittiert.' } : err,
});
this.reload();
}
this.reload();
this.loading$.next(false);
}
async returnImpediment() {
this.loading$.next(true);
try {
this._store.clearItems();
this._store.setFetching(true);
// Pflichtremission
if (this.item.dtoType === 'return') {
await this._remissionService.returnImpediment(this.item.dto?.id).toPromise();
@@ -181,17 +197,32 @@ export class RemissionListItemComponent implements OnDestroy {
await this._remissionService.returnSuggestion(this.item.dto?.id).toPromise();
}
this.reload();
this._store.removeItem(this.item);
this._store.updateCache();
} catch (err) {
this._modal.open({
content: UiErrorModalComponent,
title: 'Fehler bei Artikel nicht gefunden',
data: err,
});
this.reload();
}
this.loading$.next(false);
}
reload() {
this._store.search({ newSearch: true });
}
addItem(responseItem: ReturnItemDTO | ReturnSuggestionDTO) {
let item: RemissionListItem;
if (this.item.dtoType === 'return') {
item = mapFromReturnItemDTO(responseItem);
} else if (this.item.dtoType === 'suggestion') {
item = mapFromReturnSuggestionDTO(responseItem);
} else {
throw new Error('Fehler beim Mapping der DTO');
}
this._store.addItems([item]);
}
}

View File

@@ -7,11 +7,13 @@ import { RemissionPipeModule } from '../../pipes';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
import { AddProductToShippingDocumentModalModule } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module';
import { UiSpinnerModule } from '@ui/spinner';
@NgModule({
imports: [
CommonModule,
UiCommonModule,
UiSpinnerModule,
ProductImageModule,
RemissionPipeModule,
UiTooltipModule,

View File

@@ -7,7 +7,7 @@ import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { SupplierDTO } from '@swagger/remi';
import { UiFilter } from '@ui/filter';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { debounceTime, filter, switchMap, takeLast, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
import { debounceTime, filter, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
export interface RemissionState {
suppliers: SupplierDTO[];
@@ -159,7 +159,6 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
.subscribe(([change, filter]) => {
const data = this.getCachedData();
if (change || data.items?.length === 0 || filter) {
this.clearItems();
this.search({ newSearch: true });
}
});
@@ -168,7 +167,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
// tslint:disable: member-ordering
loadSuppliers = this.effect(($) =>
$.pipe(
tap((_) => this.setFetching(true)),
tap((_) => (this.getCachedData()?.hits === 0 ? this.setFetching(true) : null)),
switchMap((_) =>
this._domainRemissionService.getSuppliers().pipe(
tapResponse(
@@ -182,7 +181,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
loadSources = this.effect(($) =>
$.pipe(
tap((_) => this.setFetching(true)),
tap((_) => (this.getCachedData()?.hits === 0 ? this.setFetching(true) : null)),
switchMap((_) =>
this._domainRemissionService.getSources().pipe(
tapResponse(
@@ -196,7 +195,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
loadRequiredCapacities = this.effect(($) =>
$.pipe(
tap((_) => this.setFetching(true)),
tap((_) => (this.getCachedData()?.hits === 0 ? this.setFetching(true) : null)),
withLatestFrom(this.selectedSupplier$, this.filter$),
switchMap(([_, supplier, filter]) => {
let departments = [];
@@ -216,7 +215,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
loadFilter = this.effect(($) =>
$.pipe(
tap((_) => this.setFetching(true)),
tap((_) => (this.getCachedData()?.hits === 0 ? this.setFetching(true) : null)),
withLatestFrom(this.selectedSupplier$, this.selectedSource$, this._activatedRoute.queryParams),
filter(([, selectedSupplier, selectedSource]) => !!selectedSupplier?.id && !!selectedSource),
switchMap(([, selectedSupplier, selectedSource, queryParams]) =>
@@ -247,7 +246,10 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
search = this.effect((options$: Observable<{ newSearch?: boolean }>) =>
options$.pipe(
tap((_) => this.setFetching(true)),
tap((options) => {
this.setFetching(true);
options?.newSearch ? this.setSearchResult({ result: [], hits: 0 }) : null;
}),
withLatestFrom(this.filter$, this.selectedSource$, this.selectedSupplier$, this.searchOptions$, this.items$),
filter(([, filter, source, supplier]) => !!supplier?.id && !!source && !!filter),
switchMap(([options, filter, source, supplier, searchOptions, items]) =>
@@ -256,7 +258,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
queryToken: {
...filter?.getQueryToken(),
skip: options?.newSearch ? 0 : searchOptions?.skip ?? items?.length,
take: searchOptions?.take ?? 20,
take: searchOptions?.take ?? 40,
},
source,
supplierId: supplier.id,
@@ -268,6 +270,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
? { result: [...res.result], hits: res.hits ?? 0 }
: { result: [...(items ?? []), ...(res.result ?? [])], hits: res.hits ?? 0 };
this.setSearchResult(results);
this.setFetching(false);
this._searchCompleted.next(this.get());
},
(err) => {}
@@ -286,7 +289,6 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
...state,
items: result,
hits,
fetching: false,
}));
setSuppliers = this.updater<SupplierDTO[]>((state, suppliers) => ({
@@ -333,9 +335,16 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
hits,
}));
addItems = this.updater<RemissionListItem[]>((state, items) => ({
...state,
items: [...state.items, ...items],
hits: state.hits + items.length,
}));
removeItem = this.updater<RemissionListItem>((state, item) => ({
...state,
items: state.items.filter((i) => i.dto?.id !== item.dto?.id),
hits: state.hits - 1,
}));
setRequiredCapacities = this.updater<any>((state, requiredCapacities) => ({
@@ -366,9 +375,9 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
this._cache.delete({ processId: String(this.processId) });
}
clearItems() {
this.setFetching(true);
this.setSearchResult({ result: [], hits: 0 });
updateCache() {
this.clearCache();
this.cacheCurrentData();
}
async applyFilter(filter: UiFilter) {

View File

@@ -70,6 +70,11 @@ export class RemissionListComponent implements OnInit, OnDestroy {
return this._remissionStore.return$;
}
listEmpty$ = combineLatest([this.fetching$, this.hits$]).pipe(
map(([loading, hits]) => !loading && hits === 0),
shareReplay()
);
get queryParams$() {
return this._activatedRoute.queryParams.pipe(
withLatestFrom(this._remissionListStore.filter$, this.selectedSupplierId$, this.selectedSource$),
@@ -87,7 +92,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
return this._remissionStore.returnCount$;
}
showStartRemissionAction$ = combineLatest([this.hits$, this.return$]).pipe(map(([hits, r]) => hits > 0 && !r));
showStartRemissionAction$ = combineLatest([this.listEmpty$, this.return$]).pipe(map(([empty, r]) => !empty && !r));
filteredSuppliers$ = combineLatest([this._remissionStore.uncompleted$, this.suppliers$, this.selectedSupplier$]).pipe(
map(([uncompleted, suppliers, selectedSupplier]) => {
@@ -98,11 +103,6 @@ export class RemissionListComponent implements OnInit, OnDestroy {
})
);
listEmpty$ = combineLatest([this.fetching$, this.hits$]).pipe(
map(([loading, hits]) => !loading && hits === 0),
shareReplay()
);
trackByItemId = (item: RemissionListItem) => item?.dto?.id;
showScrollArrow$ = new BehaviorSubject<boolean>(false);