mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
#4365 RD // Abholfach - Alte Bestellpostensuche Seite ist erreichbar
This commit is contained in:
@@ -2,19 +2,12 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { GoodsInCleanupListComponent } from './goods-in-cleanup-list/goods-in-cleanup-list.component';
|
||||
import { GoodsInCleanupListModule } from './goods-in-cleanup-list/goods-in-cleanup-list.module';
|
||||
import { GoodsInDetailsComponent } from './goods-in-details';
|
||||
import { GoodsInEditModule } from './goods-in-edit';
|
||||
import { GoodsInEditComponent } from './goods-in-edit/goods-in-edit.component';
|
||||
import { GoodsInListComponent } from './goods-in-list/goods-in-list.component';
|
||||
import { GoodsInListModule } from './goods-in-list/goods-in-list.module';
|
||||
import { GoodsInRemissionPreviewComponent } from './goods-in-remission-preview/goods-in-remission-preview.component';
|
||||
import { GoodsInRemissionPreviewModule } from './goods-in-remission-preview/goods-in-remission-preview.module';
|
||||
import { GoodsInReservationComponent } from './goods-in-reservation/goods-in-reservation.component';
|
||||
import { GoodsInReservationModule } from './goods-in-reservation/goods-in-reservation.module';
|
||||
import { GoodsInSearchComponent, GoodsInSearchModule } from './goods-in-search';
|
||||
import { GoodsInSearchMainComponent, GoodsInSearchMainModule } from './goods-in-search/search-main';
|
||||
import { GoodsInSearchResultsModule } from './goods-in-search/search-results';
|
||||
import { GoodsInSearchResultsComponent } from './goods-in-search/search-results/goods-in-search-results.component';
|
||||
import { GoodsInComponent } from './goods-in.component';
|
||||
|
||||
const routes: Routes = [
|
||||
@@ -22,22 +15,6 @@ const routes: Routes = [
|
||||
path: '',
|
||||
component: GoodsInComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: GoodsInSearchComponent,
|
||||
children: [
|
||||
{ path: '', component: GoodsInSearchMainComponent },
|
||||
{ path: 'results', component: GoodsInSearchResultsComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'details/customer/:customerNumber/order/:orderNumber/item/:orderItemId/:processingStatus',
|
||||
component: GoodsInDetailsComponent,
|
||||
},
|
||||
{
|
||||
path: 'details/customer/:customerNumber/order/:orderNumber/item/:orderItemId/:processingStatus/edit',
|
||||
component: GoodsInEditComponent,
|
||||
},
|
||||
{ path: 'list', component: GoodsInListComponent },
|
||||
{ path: 'reservation', component: GoodsInReservationComponent },
|
||||
{ path: 'cleanup', component: GoodsInCleanupListComponent },
|
||||
@@ -48,10 +25,6 @@ const routes: Routes = [
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
GoodsInSearchModule,
|
||||
GoodsInSearchResultsModule,
|
||||
GoodsInSearchMainModule,
|
||||
GoodsInEditModule,
|
||||
GoodsInListModule,
|
||||
GoodsInCleanupListModule,
|
||||
GoodsInReservationModule,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CommandService } from '@core/command';
|
||||
@@ -11,6 +11,7 @@ import { UiScrollContainerComponent } from '@ui/scroll-container';
|
||||
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
|
||||
import { first, map, shareReplay, takeUntil } from 'rxjs/operators';
|
||||
import { GoodsInCleanupListStore } from './goods-in-cleanup-list.store';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-cleanup-list',
|
||||
@@ -20,6 +21,7 @@ import { GoodsInCleanupListStore } from './goods-in-cleanup-list.store';
|
||||
providers: [GoodsInCleanupListStore],
|
||||
})
|
||||
export class GoodsInCleanupListComponent implements OnInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
@ViewChildren(GoodsInOutOrderGroupItemComponent) listItems: QueryList<GoodsInOutOrderGroupItemComponent>;
|
||||
|
||||
@@ -186,16 +188,9 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const customerNumber = orderItem.buyerNumber;
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const orderItemId = orderItem.orderItemSubsetId;
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
<shared-goods-in-out-order-details (actionHandled)="actionHandled($event)">
|
||||
<shared-goods-in-out-order-details-header
|
||||
[selectedOrderItemId]="selectedOrderItemId$ | async"
|
||||
[order]="order$ | async"
|
||||
(editClick)="navigateToEditPage($event)"
|
||||
></shared-goods-in-out-order-details-header>
|
||||
<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]="coverItems$ | async"
|
||||
[selectedOrderItem]="selectedItem$ | async"
|
||||
(coverClick)="coverClick($event)"
|
||||
[fetching]="fetchingCoverItems$ | async"
|
||||
></shared-goods-in-out-order-details-covers>
|
||||
</shared-goods-in-out-order-details>
|
||||
<div class="spacer"></div>
|
||||
@@ -1,8 +0,0 @@
|
||||
:host {
|
||||
@apply block overflow-y-scroll;
|
||||
max-height: calc(100vh - 135px - 80px);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
@apply h-16;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// import { ActivatedRoute, Router } from '@angular/router';
|
||||
// import { RouterTestingModule } from '@angular/router/testing';
|
||||
// import { ProductImageService } from '@cdn/product-image';
|
||||
// import { ApplicationService } from '@core/application';
|
||||
// import { BreadcrumbService } from '@core/breadcrumb';
|
||||
// import { CoreCommandModule, CommandService } from '@core/command';
|
||||
// import { CrmCustomerService } from '@domain/crm';
|
||||
// import { DomainGoodsService, DomainOmsService, DomainReceiptService } from '@domain/oms';
|
||||
// import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
|
||||
// import { DateAdapter } from '@ui/common';
|
||||
// import { UiModalService } from '@ui/modal';
|
||||
// import { SharedGoodsInOutOrderDetailsModule } from 'apps/shared/goods-in-out/src/lib/goods-in-out-order-details';
|
||||
// import { of } from 'rxjs';
|
||||
// import { GoodsInDetailsComponent } from '.';
|
||||
|
||||
// describe('GoodsInDetailsCompoonent', () => {
|
||||
// let spectator: Spectator<GoodsInDetailsComponent>;
|
||||
|
||||
// let breadcrumbServiceMock = {
|
||||
// addOrUpdateBreadcrumbIfNotExists: jasmine.createSpy(),
|
||||
// getBreadcrumbsByKeyAndTags$: jasmine.createSpy(),
|
||||
// };
|
||||
// let dateAdapterMock = {
|
||||
// today: () => {},
|
||||
// addCalendarDays: () => {},
|
||||
// };
|
||||
|
||||
// let routerMock: jasmine.SpyObj<Router>;
|
||||
// let activatedRouteMock = {
|
||||
// orderId: 1,
|
||||
// orderItemId: 2,
|
||||
// processingStatus: 16,
|
||||
// compartmentCode: 3,
|
||||
// };
|
||||
|
||||
// const createComponent = createComponentFactory({
|
||||
// component: GoodsInDetailsComponent,
|
||||
// imports: [RouterTestingModule, SharedGoodsInOutOrderDetailsModule, CoreCommandModule],
|
||||
// providers: [
|
||||
// mockProvider(DomainGoodsService),
|
||||
// mockProvider(DomainOmsService),
|
||||
// mockProvider(ApplicationService),
|
||||
// mockProvider(CommandService),
|
||||
// mockProvider(CrmCustomerService),
|
||||
// mockProvider(DomainReceiptService),
|
||||
// mockProvider(UiModalService),
|
||||
// mockProvider(ProductImageService),
|
||||
// ],
|
||||
// });
|
||||
|
||||
// beforeEach(async () => {
|
||||
// spectator = createComponent({
|
||||
// providers: [
|
||||
// { provide: BreadcrumbService, useValue: breadcrumbServiceMock },
|
||||
// {
|
||||
// provide: ActivatedRoute,
|
||||
// useValue: {
|
||||
// params: of(activatedRouteMock),
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// provide: DateAdapter,
|
||||
// useValue: dateAdapterMock,
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// routerMock = spectator.inject(Router);
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(spectator.component).toBeTruthy();
|
||||
// });
|
||||
|
||||
// // describe('shared-goods-in-out-order-details', () => {
|
||||
// // it('should render', () => {
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('shared-goods-in-out-order-details-header', () => {
|
||||
// // it('should render', () => {
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details-header')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('shared-goods-in-out-order-details-item', () => {
|
||||
// // it('should render', () => {
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details-item')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('shared-goods-in-out-order-details-tags', () => {
|
||||
// // it('should not render when no selectedItem is set', () => {
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details-tags')).not.toBeVisible();
|
||||
// // });
|
||||
|
||||
// // it('should render when selectedItem$ with command arrived is set', () => {
|
||||
// // spectator.component.selectedItem$ = of({ orderItemId: 1, actions: [{ command: 'ARRIVED' }] });
|
||||
// // spectator.detectComponentChanges();
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details-tags')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('shared-goods-in-out-order-details-covers', () => {
|
||||
// // it('should render', () => {
|
||||
// // expect(spectator.query('shared-goods-in-out-order-details-covers')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('updateBreadcrumb', () => {
|
||||
// // it('should call addOrUpdateBreadcrumbIfNotExists', () => {
|
||||
// // const orderNumber = '1';
|
||||
// // const orderItemId = 2;
|
||||
// // const processingStatus = 16;
|
||||
|
||||
// // spectator.component.updateBreadcrumb({ orderNumber, orderItemId, processingStatus });
|
||||
// // expect(breadcrumbServiceMock.addOrUpdateBreadcrumbIfNotExists).toHaveBeenCalledWith({
|
||||
// // key: 'goods-in',
|
||||
// // name: orderNumber,
|
||||
// // path: `/goods/in/details/order/${orderNumber}/item/${orderItemId}/${processingStatus}`,
|
||||
// // section: 'branch',
|
||||
// // tags: ['goods-in', 'details', orderNumber],
|
||||
// // });
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('navigate', () => {
|
||||
// // it('should navigate to /goods/in when parameter is "search"', () => {
|
||||
// // spyOn(routerMock, 'navigate');
|
||||
// // spectator.component.navigate('search');
|
||||
// // expect(routerMock.navigate).toHaveBeenCalledWith(['/goods/in']);
|
||||
// // });
|
||||
|
||||
// // it('should call navigateToEdit when parameter is "edit"', () => {
|
||||
// // spyOn(spectator.component, 'navigateToEdit');
|
||||
// // spectator.component.navigate('edit');
|
||||
// // expect(spectator.component.navigateToEdit).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('navigateToEdit', () => {
|
||||
// // it('should navigate to /goods/in/details/compartment when compartmentCode is set', async () => {
|
||||
// // activatedRouteMock.compartmentCode = 3;
|
||||
// // spyOn(routerMock, 'navigate');
|
||||
// // await spectator.component.navigateToEdit();
|
||||
// // expect(routerMock.navigate).toHaveBeenCalledWith([
|
||||
// // `/goods/in/details/compartment/${activatedRouteMock.compartmentCode}/item/${activatedRouteMock.orderItemId}/${activatedRouteMock.processingStatus}/edit`,
|
||||
// // ]);
|
||||
// // });
|
||||
|
||||
// // it('should navigate to /goods/in/details/order when compartmentCode is not set', async () => {
|
||||
// // activatedRouteMock.compartmentCode = undefined;
|
||||
// // spyOn(routerMock, 'navigate');
|
||||
// // await spectator.component.navigateToEdit();
|
||||
// // expect(routerMock.navigate).toHaveBeenCalledWith([
|
||||
// // `/goods/in/details/order/${activatedRouteMock.orderId}/item/${activatedRouteMock.orderItemId}/${activatedRouteMock.processingStatus}/edit`,
|
||||
// // ]);
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,259 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
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, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface GoodsInDetailsComponentState {
|
||||
fetching: boolean;
|
||||
fetchingCoverItems: boolean;
|
||||
customerNumber?: string;
|
||||
orderNumber?: string;
|
||||
processingStatus?: OrderItemProcessingStatusValue;
|
||||
selectedOrderItemId?: number;
|
||||
items?: OrderItemListItemDTO[];
|
||||
coverItems?: OrderItemListItemDTO[];
|
||||
orderId?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-details',
|
||||
templateUrl: 'goods-in-details.component.html',
|
||||
styleUrls: ['goods-in-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsComponentState> implements OnInit, OnDestroy {
|
||||
customerNumber$ = this.select((s) => decodeURIComponent(s.customerNumber ?? ''));
|
||||
orderNumber$ = this.select((s) => decodeURIComponent(s.orderNumber ?? ''));
|
||||
|
||||
selectedOrderItemId$ = this.select((s) => +s.selectedOrderItemId);
|
||||
|
||||
items$ = this.select((s) => s.items ?? []);
|
||||
coverItems$ = this.select((s) => s.coverItems ?? []);
|
||||
|
||||
orderId$ = this.select((s) => s.orderId);
|
||||
|
||||
get items() {
|
||||
return this.get((s) => s.items);
|
||||
}
|
||||
|
||||
processingStatus$ = this.select((s) => s.processingStatus);
|
||||
|
||||
itemsWithProcessingStatus$ = combineLatest([this.items$, this.processingStatus$]).pipe(
|
||||
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.orderItemSubsetId === orderItemId) || items[0])
|
||||
);
|
||||
|
||||
fetchingCoverItems$ = this.select((s) => s.fetchingCoverItems);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _domainGoodsInService: DomainGoodsService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router,
|
||||
private readonly _config: Config
|
||||
) {
|
||||
super({
|
||||
fetching: false,
|
||||
fetchingCoverItems: false,
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._activatedRoute.params.pipe(takeUntil(this._onDestroy$)).subscribe(async (params) => {
|
||||
const customerNumber: string = params?.customerNumber;
|
||||
const orderNumber: string = params?.orderNumber;
|
||||
const selectedOrderItemId: number = +params?.orderItemId || undefined;
|
||||
const processingStatus = +params?.processingStatus as OrderItemProcessingStatusValue;
|
||||
|
||||
this.patchState({ customerNumber, orderNumber, selectedOrderItemId, processingStatus });
|
||||
await this.removeDetailsCrumbs();
|
||||
this.loadItems();
|
||||
});
|
||||
|
||||
this.removeBreadcrumbs();
|
||||
this.loadCoverItems();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.unsubscribe();
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
const editCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async removeDetailsCrumbs() {
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async updateBreadcrumb(item: OrderItemListItemDTO) {
|
||||
if (item) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: item?.orderNumber,
|
||||
path: `/filiale/goods/in/details/customer/${encodeURIComponent(item.buyerNumber)}/order/${encodeURIComponent(
|
||||
item?.orderNumber
|
||||
)}/item/${item?.orderItemSubsetId}/${item?.processingStatus}`,
|
||||
section: 'branch',
|
||||
tags: ['goods-in', 'details', item?.orderNumber],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: member-ordering
|
||||
loadItems = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => this.patchState({ fetching: true })),
|
||||
debounceTime(500),
|
||||
withLatestFrom(this.orderNumber$, this.selectedOrderItemId$, this.processingStatus$),
|
||||
switchMap(([_, orderNumber, selectedOrderItemId, processingStatus]) =>
|
||||
this._domainGoodsInService.getWareneingangItemByOrderNumber(orderNumber).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
this.patchState({
|
||||
items: res.result.map((item) => ({
|
||||
...item,
|
||||
// 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.orderItemSubsetId === selectedOrderItemId && item.processingStatus === processingStatus)
|
||||
);
|
||||
},
|
||||
() => {},
|
||||
() => {
|
||||
this.patchState({ fetching: false });
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
loadCoverItems = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => this.patchState({ fetchingCoverItems: true })),
|
||||
debounceTime(200),
|
||||
withLatestFrom(this.customerNumber$),
|
||||
switchMap(([_, customerNumber]) =>
|
||||
this._domainGoodsInService.getWareneingangItemByCustomerNumber(customerNumber).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
this.patchState({
|
||||
coverItems: res?.result?.map((item) => ({
|
||||
...item,
|
||||
})),
|
||||
fetchingCoverItems: false,
|
||||
});
|
||||
},
|
||||
() => {},
|
||||
() => {
|
||||
this.patchState({ fetchingCoverItems: false });
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
navigateToLandingPage() {
|
||||
this._router.navigate(['/filiale/goods/in/']);
|
||||
}
|
||||
|
||||
navigateToReservationPage() {
|
||||
this._router.navigate(['/filiale/goods/in/reservation']);
|
||||
}
|
||||
|
||||
navigateToEditPage(orderItem: OrderItemListItemDTO) {
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(orderItem.buyerNumber)}/order/${encodeURIComponent(
|
||||
orderItem?.orderNumber
|
||||
)}/item/${orderItem?.orderItemSubsetId}/${orderItem?.processingStatus}/edit`,
|
||||
]);
|
||||
}
|
||||
|
||||
navigateToDetailsPage(orderItem: OrderItemListItemDTO) {
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(orderItem?.buyerNumber)}/order/${encodeURIComponent(
|
||||
orderItem?.orderNumber
|
||||
)}/item/${orderItem?.orderItemSubsetId}/${orderItem?.processingStatus}`,
|
||||
]);
|
||||
}
|
||||
|
||||
coverClick(orderItems: OrderItemListItemDTO[]) {
|
||||
if (orderItems.length === 1) {
|
||||
this.navigateToDetailsPage(orderItems[0]);
|
||||
} else {
|
||||
if (orderItems.every((item) => item.processingStatus === 128)) {
|
||||
// Search by processingStatus and compartmentCode
|
||||
const queryParams = {
|
||||
main_qs: encodeURIComponent(orderItems.find((_) => true).compartmentCode),
|
||||
filter_orderitemprocessingstatus: orderItems.find((_) => true).processingStatus,
|
||||
};
|
||||
this._router.navigate(['/filiale/goods/in', 'results'], { queryParams });
|
||||
} else {
|
||||
// Search by processingStatus and orderNumber
|
||||
const queryParams = {
|
||||
main_qs: encodeURIComponent(orderItems.find((_) => true).orderNumber),
|
||||
filter_orderitemprocessingstatus: orderItems
|
||||
.map((item) => item.processingStatus)
|
||||
.filter((v, i, a) => a.indexOf(v) === i)
|
||||
.join(';'),
|
||||
};
|
||||
this._router.navigate(['/filiale/goods/in', 'results'], { queryParams });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async actionHandled(handler: { orderItemsContext: OrderItemsContext; command: string; navigation: 'details' | 'main' | 'reservation' }) {
|
||||
switch (handler.navigation) {
|
||||
case 'details':
|
||||
this.navigateToDetailsPage(handler.orderItemsContext.items.find((_) => true));
|
||||
await this.removeDetailsCrumbs();
|
||||
this.loadItems();
|
||||
break;
|
||||
case 'main':
|
||||
this.navigateToLandingPage();
|
||||
break;
|
||||
case 'reservation':
|
||||
this.navigateToReservationPage();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedGoodsInOutOrderDetailsModule } from '@shared/components/goods-in-out';
|
||||
|
||||
import { GoodsInDetailsComponent } from './goods-in-details.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedGoodsInOutOrderDetailsModule],
|
||||
exports: [GoodsInDetailsComponent],
|
||||
declarations: [GoodsInDetailsComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class GoodsInDetailsModule {}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-details.component';
|
||||
export * from './goods-in-details.module';
|
||||
// end:ng42.barrel
|
||||
@@ -1,3 +0,0 @@
|
||||
<div>
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items$ | async"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
@@ -1,4 +0,0 @@
|
||||
div {
|
||||
@apply overflow-y-scroll;
|
||||
height: calc(100vh - 280px);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { DomainGoodsService } from '@domain/oms';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-edit',
|
||||
templateUrl: 'goods-in-edit.component.html',
|
||||
styleUrls: ['goods-in-edit.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInEditComponent implements OnInit {
|
||||
orderNumber$: Observable<string> = this._activatedRoute.params.pipe(
|
||||
map((params) => decodeURIComponent(params.orderNumber ?? '') || undefined)
|
||||
);
|
||||
|
||||
items$ = this.orderNumber$
|
||||
.pipe(switchMap((orderNumber) => this._domainGoodsInService.getWareneingangItemByOrderNumber(orderNumber)))
|
||||
.pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _domainGoodsInService: DomainGoodsService,
|
||||
private _router: Router,
|
||||
private readonly _config: Config
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.updateBreadcrumb();
|
||||
}
|
||||
|
||||
async updateBreadcrumb() {
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const orderItemId = this._activatedRoute.snapshot.params.orderItemId;
|
||||
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Bearbeiten',
|
||||
path: `/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}/edit`,
|
||||
section: 'branch',
|
||||
tags: ['goods-in', 'edit', orderNumber],
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
|
||||
const customerNumber = this._activatedRoute.snapshot.params.customerNumber;
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const orderItemId = this._activatedRoute.snapshot.params.orderItemId;
|
||||
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedGoodsInOutOrderEditModule } from '@shared/components/goods-in-out';
|
||||
import { GoodsInEditComponent } from './goods-in-edit.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedGoodsInOutOrderEditModule],
|
||||
exports: [GoodsInEditComponent],
|
||||
declarations: [GoodsInEditComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class GoodsInEditModule {}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-edit.component';
|
||||
export * from './goods-in-edit.module';
|
||||
// end:ng42.barrel
|
||||
@@ -1,4 +1,14 @@
|
||||
import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
QueryList,
|
||||
ViewChild,
|
||||
ViewChildren,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
@@ -11,6 +21,7 @@ import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, first, map, shareReplay, takeUntil, tap } from 'rxjs/operators';
|
||||
import { GoodsInListItemComponent } from './goods-in-list-item/goods-in-list-item.component';
|
||||
import { GoodsInListStore } from './goods-in-list.store';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-list',
|
||||
@@ -20,6 +31,7 @@ import { GoodsInListStore } from './goods-in-list.store';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
@ViewChildren(GoodsInListItemComponent) listItems: QueryList<GoodsInListItemComponent>;
|
||||
|
||||
@ViewChild('scrollContainer', { static: false })
|
||||
@@ -135,16 +147,9 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const customerNumber = orderItem.buyerNumber;
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const orderItemId = orderItem.orderItemSubsetId;
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
|
||||
@@ -9,6 +9,7 @@ import { first, map, shareReplay, takeUntil } from 'rxjs/operators';
|
||||
import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store';
|
||||
import { Config } from '@core/config';
|
||||
import { ToasterService } from '@shared/shell';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-remission-preview',
|
||||
@@ -18,6 +19,7 @@ import { ToasterService } from '@shared/shell';
|
||||
providers: [GoodsInRemissionPreviewStore],
|
||||
})
|
||||
export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
private _scrollPosition: number;
|
||||
@@ -146,16 +148,9 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const customerNumber = orderItem.buyerNumber;
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const orderItemId = orderItem.orderItemSubsetId;
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CommandService } from '@core/command';
|
||||
@@ -11,6 +11,7 @@ import { UiScrollContainerComponent } from '@ui/scroll-container';
|
||||
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
|
||||
import { map, shareReplay, takeUntil, first } from 'rxjs/operators';
|
||||
import { GoodsInReservationStore } from './goods-in-reservation.store';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-reservation',
|
||||
@@ -20,6 +21,8 @@ import { GoodsInReservationStore } from './goods-in-reservation.store';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInReservationComponent implements OnInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
@ViewChildren(GoodsInOutOrderGroupItemComponent) listItems: QueryList<GoodsInOutOrderGroupItemComponent>;
|
||||
|
||||
@@ -147,16 +150,9 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const customerNumber = orderItem.buyerNumber;
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const orderItemId = orderItem.orderItemSubsetId;
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem });
|
||||
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
this._router.navigate(nav.path, { queryParams: nav.queryParams });
|
||||
}
|
||||
|
||||
initInitialSearch() {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<div class="goods-in-search-filter-content">
|
||||
<button class="btn-close" type="button" (click)="close.emit()">
|
||||
<ui-icon icon="close" size="20px"></ui-icon>
|
||||
</button>
|
||||
|
||||
<div class="goods-in-search-filter-content-main">
|
||||
<h1 class="title">Filter</h1>
|
||||
<ui-filter
|
||||
[filter]="filter"
|
||||
[loading]="loading$ | async"
|
||||
(search)="applyFilter()"
|
||||
[hint]="message"
|
||||
resizeInputOptionsToElement="page-goods-in-search-filter .cta-wrapper"
|
||||
[scanner]="true"
|
||||
></ui-filter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cta-wrapper">
|
||||
<button class="cta-reset-filter" (click)="resetFilter()" [disabled]="loading$ | async">
|
||||
Filter zurücksetzen
|
||||
</button>
|
||||
|
||||
<button class="cta-apply-filter" (click)="applyFilter()" [disabled]="loading$ | async">
|
||||
<ui-spinner [show]="loading$ | async">
|
||||
Filter anwenden
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
@@ -1,45 +0,0 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.goods-in-search-filter-content {
|
||||
@apply relative mx-auto;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
@apply absolute text-cool-grey top-3 right-4 outline-none border-none bg-transparent;
|
||||
}
|
||||
|
||||
.goods-in-search-filter-content-main {
|
||||
@apply px-4;
|
||||
h1.title {
|
||||
@apply text-center text-3xl font-bold py-4;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep ui-filter-input-options .input-options {
|
||||
max-height: calc(100vh - 555px);
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
@apply fixed bottom-8 whitespace-nowrap;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.cta-reset-filter,
|
||||
.cta-apply-filter {
|
||||
@apply text-lg font-bold px-6 py-3 rounded-full border-solid border-2 border-brand outline-none mx-2;
|
||||
|
||||
&:disabled {
|
||||
@apply bg-inactive-branch cursor-not-allowed border-none text-white;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-reset-filter {
|
||||
@apply bg-white text-brand;
|
||||
}
|
||||
|
||||
.cta-apply-filter {
|
||||
@apply text-white bg-brand;
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
|
||||
// import { IUiFilter, UiFilter, UiFilterComponent } from '@ui/filter';
|
||||
// import { UiIconModule } from '@ui/icon';
|
||||
// import { cold, hot } from 'jasmine-marbles';
|
||||
// import { MockComponent } from 'ng-mocks';
|
||||
// import { BehaviorSubject } from 'rxjs';
|
||||
// // import { GoodsInSearchService } from '../goods-in-search.service';
|
||||
// import { GoodsInSearchFilterComponent } from './goods-in-search-filter.component';
|
||||
|
||||
// describe('GoodsInSearchFilterComponent', () => {
|
||||
// let spectator: Spectator<GoodsInSearchFilterComponent>;
|
||||
// const createComponent = createComponentFactory({
|
||||
// component: GoodsInSearchFilterComponent,
|
||||
// imports: [UiIconModule],
|
||||
// declarations: [MockComponent(UiFilterComponent)],
|
||||
// // providers: [mockProvider(GoodsInSearchService)],
|
||||
// });
|
||||
|
||||
// // let goodsInServiceMock: jasmine.SpyObj<GoodsInSearchService>;
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createComponent();
|
||||
// // goodsInServiceMock = spectator.inject(GoodsInSearchService);
|
||||
// });
|
||||
|
||||
// // it('should create the component', () => {
|
||||
// // expect(spectator.component).toBeTruthy();
|
||||
// // });
|
||||
|
||||
// // describe('.btn-close', () => {
|
||||
// // it('should emit close on click', () => {
|
||||
// // let emitted = false;
|
||||
// // spectator.output('close').subscribe(() => (emitted = true));
|
||||
// // spectator.click('.btn-close');
|
||||
// // expect(emitted).toBe(true);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('settings', () => {
|
||||
// // it('should be an instance of UiFilter', () => {
|
||||
// // expect(spectator.component.filter instanceof UiFilter).toBe(true);
|
||||
// // });
|
||||
|
||||
// // it('should UiFilter.create with GoodsInSearchService.settings to create a new instance of UiFilter', () => {
|
||||
// // const settings = UiFilter.create({ input: [], filter: [] });
|
||||
|
||||
// // spyOn(UiFilter, 'create').and.callThrough();
|
||||
// // goodsInServiceMock.settings = settings;
|
||||
|
||||
// // spectator.component['_initSettings']();
|
||||
|
||||
// // expect(UiFilter.create).toHaveBeenCalledWith(settings);
|
||||
// // expect(settings).not.toBe(spectator.component.filter);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('loading$', () => {
|
||||
// // it('should set loading$ with GoodsInSearchService.loading$', () => {
|
||||
// // goodsInServiceMock.loading$ = new BehaviorSubject(true);
|
||||
|
||||
// // spyOn(goodsInServiceMock.loading$, 'asObservable').and.returnValue(hot('a--b--a--', { a: true, b: false }));
|
||||
|
||||
// // spectator.component['_initLoading$']();
|
||||
|
||||
// // expect(goodsInServiceMock.loading$.asObservable).toHaveBeenCalled();
|
||||
|
||||
// // expect(spectator.component.loading$).toBeObservable(cold('a--b--a--', { a: true, b: false }));
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('resetFilter()', () => {
|
||||
// // it('should call GoodsInSearchService.loadSettings()', async () => {
|
||||
// // goodsInServiceMock.loadSettings.and.returnValue(Promise.resolve());
|
||||
// // await spectator.component.resetFilter();
|
||||
|
||||
// // expect(goodsInServiceMock.loadSettings).toHaveBeenCalled();
|
||||
// // });
|
||||
|
||||
// // it('should UiFilter.create with GoodsInSearchService.settings to create a new instance of UiFilter', async () => {
|
||||
// // const settings = UiFilter.create({ input: [], filter: [] });
|
||||
|
||||
// // spyOn(UiFilter, 'create').and.callThrough();
|
||||
// // goodsInServiceMock.loadSettings.and.returnValue(Promise.resolve());
|
||||
// // goodsInServiceMock.settings = settings;
|
||||
|
||||
// // await spectator.component.resetFilter();
|
||||
|
||||
// // expect(UiFilter.create).toHaveBeenCalledWith(settings);
|
||||
// // expect(settings).not.toBe(spectator.component.filter);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('applyFilter', () => {
|
||||
// // it('should call GoodsInSearchService.setSettings with settings', () => {
|
||||
// // spectator.component.filter = UiFilter.create({});
|
||||
|
||||
// // spectator.component.applyFilter();
|
||||
// // expect(goodsInServiceMock.setSettings).toHaveBeenCalledWith(spectator.component.filter);
|
||||
// // });
|
||||
|
||||
// // it('should call GoodsInSearchService.search', () => {
|
||||
// // spectator.component.applyFilter();
|
||||
// // expect(goodsInServiceMock.search).toHaveBeenCalledWith();
|
||||
// // });
|
||||
|
||||
// // it('should call close.emit', () => {
|
||||
// // spyOn(spectator.component.close, 'emit');
|
||||
// // spectator.component.applyFilter();
|
||||
// // expect(spectator.component.close.emit).toHaveBeenCalledWith();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('Element .cta-reset-filter', () => {
|
||||
// // it('should call resetFilter() on click', () => {
|
||||
// // spyOn(spectator.component, 'resetFilter');
|
||||
// // spectator.click('.cta-reset-filter');
|
||||
// // expect(spectator.component.resetFilter).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('Element .cta-apply-filter', () => {
|
||||
// // it('should call resetFilter() on click', () => {
|
||||
// // spyOn(spectator.component, 'applyFilter');
|
||||
// // spectator.click('.cta-apply-filter');
|
||||
// // expect(spectator.component.applyFilter).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('Element ui-filter', () => {
|
||||
// // it('should call applyFilter() when on search', () => {
|
||||
// // spyOn(spectator.component, 'applyFilter');
|
||||
// // spectator.triggerEventHandler('ui-filter', 'search', undefined);
|
||||
// // expect(spectator.component.applyFilter).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,116 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, Output, EventEmitter, ChangeDetectorRef, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { UiFilter, UiFilterComponent } from '@ui/filter';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { first, take, takeUntil } from 'rxjs/operators';
|
||||
import { GoodsInSearchStore } from '../goods-in-search.store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-search-filter',
|
||||
templateUrl: 'goods-in-search-filter.component.html',
|
||||
styleUrls: ['goods-in-search-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInSearchFilterComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
close = new EventEmitter();
|
||||
|
||||
filter: UiFilter;
|
||||
|
||||
loading$: Observable<boolean>;
|
||||
|
||||
message: string;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
@ViewChild(UiFilterComponent, { static: false })
|
||||
uiFilter: UiFilterComponent;
|
||||
|
||||
constructor(
|
||||
private _goodsInSearchStore: GoodsInSearchStore,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _router: Router,
|
||||
private _config: Config
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this._initSettings();
|
||||
this._initLoading$();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
private _initSettings() {
|
||||
this.filter = UiFilter.create(this._goodsInSearchStore.filter);
|
||||
}
|
||||
|
||||
private _initLoading$() {
|
||||
this.loading$ = this._goodsInSearchStore.fetching$;
|
||||
}
|
||||
|
||||
async resetFilter() {
|
||||
const queryParams = { main_qs: this.filter?.getQueryParams()?.main_qs || '' };
|
||||
this._goodsInSearchStore.resetFilter(queryParams);
|
||||
this._initSettings();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
async applyFilter() {
|
||||
this.uiFilter?.cancelAutocomplete();
|
||||
this._goodsInSearchStore.clearResults();
|
||||
this._goodsInSearchStore.setFilter(this.filter);
|
||||
this.message = undefined;
|
||||
|
||||
await this.updateQueryParams();
|
||||
|
||||
this._goodsInSearchStore.searchResult$.pipe(takeUntil(this._onDestroy$), take(1)).subscribe((result) => {
|
||||
if (result.error) {
|
||||
} else {
|
||||
if (result.hits > 0) {
|
||||
if (result.hits === 1) {
|
||||
const orderItem = result.result[0];
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(orderItem.buyerNumber)}/order/${encodeURIComponent(
|
||||
orderItem.orderNumber
|
||||
)}/item/${orderItem.orderItemSubsetId}/${orderItem.processingStatus}`,
|
||||
]);
|
||||
} else {
|
||||
this._router.navigate(['/filiale', 'goods', 'in', 'results'], {
|
||||
queryParams: this._goodsInSearchStore.filter.getQueryParams(),
|
||||
});
|
||||
}
|
||||
|
||||
this.close.emit();
|
||||
} else {
|
||||
this.message = 'keine Suchergebnisse';
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
});
|
||||
|
||||
this._goodsInSearchStore.search();
|
||||
}
|
||||
|
||||
async updateBreadcrumb() {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Abholfach',
|
||||
path: '/filiale/goods/in',
|
||||
tags: ['goods-in', 'main', 'filter'],
|
||||
section: 'branch',
|
||||
params: this._goodsInSearchStore.filter?.getQueryParams(),
|
||||
});
|
||||
}
|
||||
|
||||
async updateQueryParams() {
|
||||
await this._router.navigate([], { queryParams: this._goodsInSearchStore.filter?.getQueryParams() });
|
||||
this.updateBreadcrumb();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-search-filter.component';
|
||||
// end:ng42.barrel
|
||||
@@ -1,10 +0,0 @@
|
||||
<button class="filter" [class.active]="hasFilter$ | async" (click)="shellFilterOverlay.open()">
|
||||
<ui-icon size="20px" icon="filter_alit"></ui-icon>
|
||||
<span class="label">Filter</span>
|
||||
</button>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<shell-filter-overlay #shellFilterOverlay>
|
||||
<page-goods-in-search-filter *ngIf="shellFilterOverlay.isOpen" (close)="shellFilterOverlay.close()"> </page-goods-in-search-filter>
|
||||
</shell-filter-overlay>
|
||||
@@ -1,16 +0,0 @@
|
||||
:host {
|
||||
@apply flex flex-col w-full box-content relative;
|
||||
}
|
||||
|
||||
.filter {
|
||||
@apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-p2 -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky;
|
||||
min-width: 106px;
|
||||
|
||||
.label {
|
||||
@apply ml-px-5;
|
||||
}
|
||||
|
||||
&.active {
|
||||
@apply bg-active-branch text-white ml-px-5;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// import { By } from '@angular/platform-browser';
|
||||
// import { RouterTestingModule } from '@angular/router/testing';
|
||||
// import { BreadcrumbService } from '@core/breadcrumb';
|
||||
// import { DomainGoodsService } from '@domain/oms';
|
||||
// import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
|
||||
// import { UiIconComponent } from '@ui/icon';
|
||||
// import { MockComponent } from 'ng-mocks';
|
||||
// import { GoodsInSearchFilterComponent } from './goods-in-search-filter';
|
||||
// import { GoodsInSearchComponent } from './goods-in-search.component';
|
||||
// import { GoodsInSearchStore } from './goods-in-search.store';
|
||||
|
||||
// describe('GoodsInSearchComponent', () => {
|
||||
// let spectator: Spectator<GoodsInSearchComponent>;
|
||||
// let goodsInSearchStoreMock: jasmine.SpyObj<GoodsInSearchStore>;
|
||||
|
||||
// const createGoodsInComponent = createComponentFactory({
|
||||
// component: GoodsInSearchComponent,
|
||||
// declarations: [MockComponent(GoodsInSearchFilterComponent), MockComponent(UiIconComponent)],
|
||||
// imports: [RouterTestingModule],
|
||||
// componentProviders: [
|
||||
// mockProvider(DomainGoodsService),
|
||||
// mockProvider(GoodsInSearchStore, {
|
||||
// loadSettings: async () => {},
|
||||
// }),
|
||||
// mockProvider(BreadcrumbService),
|
||||
// ],
|
||||
// });
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createGoodsInComponent();
|
||||
// goodsInSearchStoreMock = spectator.inject(GoodsInSearchStore, true);
|
||||
// });
|
||||
|
||||
// it('should create', () => {
|
||||
// expect(spectator.component).toBeTruthy();
|
||||
// });
|
||||
|
||||
// describe('ngOnInit()', () => {
|
||||
// it('should call GoodsInSearchService.loadSettings()', () => {
|
||||
// spyOn(goodsInSearchStoreMock, 'loadSettings');
|
||||
// spectator.component.ngOnInit();
|
||||
// expect(goodsInSearchStoreMock.loadSettings).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('toggleFilterOverlay()', () => {
|
||||
// it('should set showFilterOverlay to true', () => {
|
||||
// spectator.component.showFilterOverlay = false;
|
||||
// spectator.component.toggleFilterOverlay();
|
||||
// expect(spectator.component.showFilterOverlay).toBe(true);
|
||||
// });
|
||||
|
||||
// it('should set showFilterOverlay to false', () => {
|
||||
// spectator.component.showFilterOverlay = true;
|
||||
// spectator.component.toggleFilterOverlay();
|
||||
// expect(spectator.component.showFilterOverlay).toBe(false);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('button.filter', () => {
|
||||
// it('should call toggleFilterOverlay() on click', () => {
|
||||
// spyOn(spectator.component, 'toggleFilterOverlay');
|
||||
// spectator.click('button.filter');
|
||||
|
||||
// expect(spectator.component.toggleFilterOverlay).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('page-goods-in-search-filter', () => {
|
||||
// it('should not render the component if showFilterOverlay is false', () => {
|
||||
// spectator.component.showFilterOverlay = false;
|
||||
// spectator.detectComponentChanges();
|
||||
// expect(spectator.query('page-goods-in-search-filter')).toBeNull();
|
||||
// });
|
||||
|
||||
// it('should render the component if showFilterOverlay is true', () => {
|
||||
// spectator.component.showFilterOverlay = true;
|
||||
// spectator.detectComponentChanges();
|
||||
// expect(spectator.query('page-goods-in-search-filter')).not.toBeNull();
|
||||
// });
|
||||
|
||||
// it('should call toggleFilterOverlay() on (close)', () => {
|
||||
// spyOn(spectator.component, 'toggleFilterOverlay');
|
||||
|
||||
// spectator.component.showFilterOverlay = true;
|
||||
// spectator.detectComponentChanges();
|
||||
// spectator.query('page-goods-in-search-filter');
|
||||
// const filter: GoodsInSearchFilterComponent = spectator.debugElement.query(By.directive(GoodsInSearchFilterComponent))
|
||||
// .componentInstance;
|
||||
// filter.close.emit();
|
||||
|
||||
// expect(spectator.component.toggleFilterOverlay).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
@@ -1,58 +0,0 @@
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { UiFilter, UiFilterAutocompleteProvider } from '@ui/filter';
|
||||
import { isEqual } from 'lodash';
|
||||
import { combineLatest, Subject } from 'rxjs';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
|
||||
import { GoodsInSearchStore } from './goods-in-search.store';
|
||||
import { GoodsInSearchMainAutocompleteProvider } from './providers/goods-in-search-main-autocomplete.provider';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-search',
|
||||
templateUrl: 'goods-in-search.component.html',
|
||||
styleUrls: ['goods-in-search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
GoodsInSearchStore,
|
||||
{
|
||||
provide: UiFilterAutocompleteProvider,
|
||||
useClass: GoodsInSearchMainAutocompleteProvider,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger('slideInOut', [
|
||||
transition(':enter', [style({ transform: 'translateX(100%)' }), animate('250ms', style({ transform: 'translateX(0%)' }))]),
|
||||
transition(':leave', [style({ transform: '*' }), animate('250ms', style({ transform: 'translateX(100%)' }))]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class GoodsInSearchComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
hasFilter$ = combineLatest([this._goodsInSearchStore.filter$, this._goodsInSearchStore.defaultSettings$]).pipe(
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), UiFilter.create(defaultFilter).getQueryParams()))
|
||||
);
|
||||
|
||||
constructor(private _goodsInSearchStore: GoodsInSearchStore, private _activatedRoute: ActivatedRoute) {}
|
||||
|
||||
ngOnInit() {
|
||||
this._goodsInSearchStore.loadSettings();
|
||||
this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$)).subscribe((params) => {
|
||||
// Reset Filter when query params are empty
|
||||
if (params && Object.keys(params).length === 0) {
|
||||
this._goodsInSearchStore.setQueryParams(params);
|
||||
this._goodsInSearchStore.loadSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { GoodsInSearchComponent } from './goods-in-search.component';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { GoodsInSearchFilterComponent } from './goods-in-search-filter';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { SharedFilterOverlayModule } from '@shared/components/filter-overlay';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RouterModule, UiIconModule, UiFilterNextModule, SharedFilterOverlayModule, UiSpinnerModule],
|
||||
exports: [GoodsInSearchComponent],
|
||||
declarations: [GoodsInSearchComponent, GoodsInSearchFilterComponent],
|
||||
})
|
||||
export class GoodsInSearchModule {}
|
||||
@@ -1,78 +0,0 @@
|
||||
// import { DomainGoodsService } from '@domain/oms';
|
||||
// import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator';
|
||||
// import { QuerySettingsDTO } from '@swagger/oms';
|
||||
// import { UiFilter } from '@ui/filter';
|
||||
// import { isObservable } from 'rxjs';
|
||||
// import { GoodsInSearchStore } from './goods-in-search.store';
|
||||
|
||||
// describe('GoodsInSearchStore', () => {
|
||||
// let spectator: SpectatorService<GoodsInSearchStore>;
|
||||
// const createService = createServiceFactory({
|
||||
// service: GoodsInSearchStore,
|
||||
// providers: [mockProvider(DomainGoodsService, {})],
|
||||
// });
|
||||
|
||||
// let goodsInSearchMock: jasmine.SpyObj<DomainGoodsService>;
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createService();
|
||||
// goodsInSearchMock = spectator.inject(DomainGoodsService);
|
||||
// });
|
||||
|
||||
// it('should create the service', () => {
|
||||
// expect(spectator.service).toBeTruthy();
|
||||
// });
|
||||
|
||||
// // describe('get defaultSettings()', () => {
|
||||
// // it('should return the defaultSettings from the component store', () => {
|
||||
// // const settings: QuerySettingsDTO = {
|
||||
// // filter: [],
|
||||
// // input: [],
|
||||
// // };
|
||||
|
||||
// // spectator.service.patchState({ defaultSettings: settings });
|
||||
|
||||
// // expect(spectator.service.defaultSettings).toBe(settings);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// describe('get filter()', () => {
|
||||
// it('shoudl return the filter from the component store', () => {
|
||||
// const filter = UiFilter.create({});
|
||||
// spectator.service.patchState({ filter });
|
||||
// expect(spectator.service.filter).toBe(filter);
|
||||
// });
|
||||
// });
|
||||
|
||||
// // describe('get results()', () => {
|
||||
// // it('shoudl return the filter from the component store', () => {
|
||||
// // const results = [];
|
||||
// // spectator.service.patchState({ results });
|
||||
// // expect(spectator.service.results).toBe(results);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// describe('searchResult$', () => {
|
||||
// it('should be an observable', () => {
|
||||
// expect(isObservable(spectator.service.searchResult$)).toBe(true);
|
||||
// });
|
||||
|
||||
// it('should return a value when _searchResultSubject emits a value', () => {
|
||||
// const next = { error: false };
|
||||
// let result;
|
||||
// spectator.service.searchResult$.subscribe((res) => (result = res));
|
||||
|
||||
// spectator.service['_searchResultSubject'].next(next);
|
||||
|
||||
// expect(result).toBe(next);
|
||||
// });
|
||||
// });
|
||||
|
||||
// // describe('effect loadSettings', () => {
|
||||
// // it('should call DomainGoodsInService.querySettings()', () => {
|
||||
// // spectator.service.loadSettings();
|
||||
|
||||
// // expect(goodsInSearchMock.querySettings).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,189 +0,0 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainGoodsService } from '@domain/oms';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, QuerySettingsDTO } from '@swagger/oms';
|
||||
import { UiFilter } from '@ui/filter';
|
||||
import { isResponseArgs } from '@utils/object';
|
||||
import { Subject } from 'rxjs';
|
||||
import { filter, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface GoodsInSearchState {
|
||||
defaultSettings?: QuerySettingsDTO;
|
||||
queryParams?: Record<string, string>;
|
||||
filter?: UiFilter;
|
||||
message?: string;
|
||||
fetching: boolean;
|
||||
hits: number;
|
||||
results: OrderItemListItemDTO[];
|
||||
searchOptions?: { take?: number; skip?: number };
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class GoodsInSearchStore extends ComponentStore<GoodsInSearchState> {
|
||||
get defaultSettings() {
|
||||
return this.get((s) => s.defaultSettings);
|
||||
}
|
||||
|
||||
readonly defaultSettings$ = this.select((s) => s.defaultSettings);
|
||||
|
||||
get filter() {
|
||||
return this.get((s) => s.filter);
|
||||
}
|
||||
|
||||
readonly filter$ = this.select((s) => s.filter);
|
||||
|
||||
get results() {
|
||||
return this.get((s) => s.results);
|
||||
}
|
||||
|
||||
readonly results$ = this.select((s) => s.results);
|
||||
|
||||
get hits() {
|
||||
return this.get((s) => s.hits);
|
||||
}
|
||||
|
||||
readonly hits$ = this.select((s) => s.hits);
|
||||
|
||||
get fetching() {
|
||||
return this.get((s) => s.fetching);
|
||||
}
|
||||
|
||||
readonly fetching$ = this.select((s) => s.fetching);
|
||||
|
||||
get queryParams() {
|
||||
return this.get((s) => s.queryParams);
|
||||
}
|
||||
|
||||
readonly queryParams$ = this.select((s) => s.queryParams);
|
||||
|
||||
get searchOptions() {
|
||||
return this.get((s) => s.searchOptions);
|
||||
}
|
||||
set searchOptions(searchOptions: { take?: number; skip?: number }) {
|
||||
this.patchState({ searchOptions });
|
||||
}
|
||||
|
||||
private _searchResultSubject = new Subject<ListResponseArgsOfOrderItemListItemDTO>();
|
||||
|
||||
readonly searchResult$ = this._searchResultSubject.asObservable();
|
||||
|
||||
constructor(private _domainGoodsInService: DomainGoodsService) {
|
||||
super({
|
||||
fetching: false,
|
||||
hits: 0,
|
||||
results: [],
|
||||
});
|
||||
}
|
||||
|
||||
loadSettings = this.effect(($) =>
|
||||
$.pipe(
|
||||
switchMap(() =>
|
||||
this._domainGoodsInService.goodsInQuerySettings().pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
this.setDefaultSettings(res.result);
|
||||
const filter = UiFilter.create(res.result);
|
||||
if (this.queryParams) {
|
||||
filter.fromQueryParams(this.queryParams);
|
||||
}
|
||||
this.setFilter(filter);
|
||||
},
|
||||
(err) => {
|
||||
console.error('GoodsInSearchStore.loadSettings()', err);
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
setDefaultSettings(defaultSettings: QuerySettingsDTO) {
|
||||
this.patchState({ defaultSettings });
|
||||
}
|
||||
|
||||
setFilter(filter: UiFilter | QuerySettingsDTO) {
|
||||
if (filter instanceof UiFilter) {
|
||||
this.patchState({ filter });
|
||||
} else {
|
||||
this.patchState({ filter: UiFilter.create(filter) });
|
||||
}
|
||||
}
|
||||
|
||||
resetFilter(defaultQueryParams?: Record<string, string>) {
|
||||
const filter = UiFilter.create(this.defaultSettings);
|
||||
|
||||
if (!!defaultQueryParams) {
|
||||
filter?.fromQueryParams(defaultQueryParams);
|
||||
}
|
||||
|
||||
this.setFilter(filter);
|
||||
}
|
||||
|
||||
clearResults() {
|
||||
this.patchState({
|
||||
fetching: false,
|
||||
hits: 0,
|
||||
message: undefined,
|
||||
results: [],
|
||||
});
|
||||
}
|
||||
|
||||
setQueryParams(queryParams: Record<string, string>) {
|
||||
this.patchState({ queryParams });
|
||||
if (this.filter instanceof UiFilter) {
|
||||
this.filter?.fromQueryParams(queryParams);
|
||||
this.setFilter(this.filter);
|
||||
}
|
||||
}
|
||||
|
||||
searchRequest(options?: { take?: number; skip?: number }) {
|
||||
return this.filter$.pipe(
|
||||
filter((f) => f instanceof UiFilter),
|
||||
take(1),
|
||||
mergeMap((filter) =>
|
||||
this._domainGoodsInService.searchWareneingang({
|
||||
...filter.getQueryToken(),
|
||||
skip: options?.skip ?? this.results.length,
|
||||
take: options?.take ?? 50,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
search = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap((_) => this.patchState({ fetching: true })),
|
||||
withLatestFrom(this.results$),
|
||||
switchMap(([_, _results]) =>
|
||||
this.searchRequest(this.searchOptions).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
const results = [...(_results ?? []), ...(res.result ?? [])];
|
||||
|
||||
this.patchState({
|
||||
hits: res.hits,
|
||||
results,
|
||||
fetching: false,
|
||||
});
|
||||
|
||||
this._searchResultSubject.next(res);
|
||||
},
|
||||
(err: Error) => {
|
||||
if (err instanceof HttpErrorResponse && isResponseArgs(err.error)) {
|
||||
this._searchResultSubject.next(err.error);
|
||||
} else {
|
||||
this._searchResultSubject.next({
|
||||
error: true,
|
||||
message: err.message,
|
||||
});
|
||||
}
|
||||
this.patchState({ fetching: false });
|
||||
console.error('GoodsInSearchStore.search()', err);
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-search.component';
|
||||
export * from './goods-in-search.module';
|
||||
// end:ng42.barrel
|
||||
@@ -1,51 +0,0 @@
|
||||
// import { DomainGoodsService } from '@domain/oms';
|
||||
// import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
|
||||
// import { UiInput, UiInputType } from '@ui/filter';
|
||||
// import { cold, hot } from 'jasmine-marbles';
|
||||
// import { of } from 'rxjs';
|
||||
// import { GoodsInSearchMainAutocompleteProvider } from './goods-in-search-main-autocomplete.provider';
|
||||
|
||||
// describe('GoodsInSearchMainAutocompleteProvider', () => {
|
||||
// let spectator: SpectatorService<GoodsInSearchMainAutocompleteProvider>;
|
||||
// let domainGoodsInServiceMock: jasmine.SpyObj<DomainGoodsService>;
|
||||
// const createProvider = createServiceFactory({
|
||||
// service: GoodsInSearchMainAutocompleteProvider,
|
||||
// mocks: [DomainGoodsService],
|
||||
// });
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createProvider();
|
||||
// domainGoodsInServiceMock = spectator.inject(DomainGoodsService);
|
||||
// });
|
||||
|
||||
// it('should create the provider', () => {
|
||||
// expect(spectator.service).toBeDefined();
|
||||
// });
|
||||
|
||||
// it('should have the value "main" for the property for', () => {
|
||||
// expect(spectator.service.for).toBe('main');
|
||||
// });
|
||||
|
||||
// // describe('complete(input: UiInput)', () => {
|
||||
// // it('should call DomainGoodsInService.complete with the UiInput.value as input', () => {
|
||||
// // domainGoodsInServiceMock.complete.and.returnValue(of({ result: [] }));
|
||||
// // spectator.service.complete(UiInput.create({ value: 'my value', type: UiInputType.Text }));
|
||||
// // expect(domainGoodsInServiceMock.complete).toHaveBeenCalledWith({ input: 'my value', take: 5 });
|
||||
// // });
|
||||
|
||||
// // it('should map the response to the response.result', () => {
|
||||
// // const result = [{ display: 'display 1' }, { display: 'display 1' }];
|
||||
// // domainGoodsInServiceMock.complete.and.returnValue(hot('-(a|)', { a: { result } }));
|
||||
// // expect(spectator.service.complete(UiInput.create({ value: 'my value', type: UiInputType.Text }))).toBeObservable(
|
||||
// // cold('-(a|)', { a: result })
|
||||
// // );
|
||||
// // });
|
||||
|
||||
// // it('should catch the error', () => {
|
||||
// // domainGoodsInServiceMock.complete.and.returnValue(hot('-#', {}, new Error()));
|
||||
// // expect(spectator.service.complete(UiInput.create({ value: 'my value', type: UiInputType.Text }))).toBeObservable(
|
||||
// // cold('-(a|)', { a: [] })
|
||||
// // );
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainGoodsService } from '@domain/oms';
|
||||
import { UiFilterAutocomplete, UiFilterAutocompleteProvider, UiInput } from '@ui/filter';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class GoodsInSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
|
||||
for = 'goods-in';
|
||||
|
||||
constructor(private domainGoodsInService: DomainGoodsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
complete(input: UiInput): Observable<UiFilterAutocomplete[]> {
|
||||
const token = input?.parent?.parent?.getQueryToken();
|
||||
const filter = token?.filter;
|
||||
const type = Object.keys(token?.input).join(';');
|
||||
|
||||
if (input.value.length >= 3) {
|
||||
return this.domainGoodsInService
|
||||
.wareneingangComplete({
|
||||
filter,
|
||||
input: input.value,
|
||||
take: 5,
|
||||
type,
|
||||
})
|
||||
.pipe(
|
||||
catchError(() => of({ result: [] })),
|
||||
map((res) => res.result)
|
||||
);
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<a class="goods-in-list-navigation" [routerLink]="['/filiale/goods/in', 'list']">
|
||||
Wareneingangsliste
|
||||
</a>
|
||||
<a class="goods-in-list-remission-preview" [routerLink]="['/filiale/goods/in', 'preview']">
|
||||
Abholfachremissionsvorschau
|
||||
</a>
|
||||
<a class="goods-in-list-cleanup" [routerLink]="['/filiale/goods/in', 'cleanup']">
|
||||
Abholfachbereinigung
|
||||
</a>
|
||||
<a class="goods-in-list-reservation" [routerLink]="['/filiale/goods/in', 'reservation']">
|
||||
Reservierungen
|
||||
</a>
|
||||
<div class="search-main">
|
||||
<h1 class="search-main-title">Bestellpostensuche</h1>
|
||||
<p class="search-main-paragraph">
|
||||
Scannen Sie den Artikel,<br />
|
||||
um diesen einzubuchen oder<br />
|
||||
suchen Sie nach Kundennamen.
|
||||
</p>
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
<ui-filter-input-group-main
|
||||
*ngIf="filter?.input | group: 'main'; let inputGroup"
|
||||
[inputGroup]="inputGroup"
|
||||
[loading]="loading$ | async"
|
||||
(search)="search()"
|
||||
[hint]="message"
|
||||
[scanner]="true"
|
||||
></ui-filter-input-group-main>
|
||||
</ng-container>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.goods-in-list-navigation,
|
||||
.goods-in-list-reservation,
|
||||
.goods-in-list-cleanup,
|
||||
.goods-in-list-remission-preview {
|
||||
@apply text-center text-xl text-inactive-branch block bg-white rounded-t font-bold no-underline py-4;
|
||||
}
|
||||
|
||||
.goods-in-list-reservation,
|
||||
.goods-in-list-cleanup,
|
||||
.goods-in-list-remission-preview {
|
||||
@apply shadow-card;
|
||||
}
|
||||
|
||||
.search-main {
|
||||
@apply bg-white text-center rounded-t py-5 shadow-card;
|
||||
height: calc(100vh - 510px);
|
||||
|
||||
.search-main-title {
|
||||
@apply text-h3 font-bold;
|
||||
}
|
||||
|
||||
.search-main-paragraph {
|
||||
@apply text-h3 mb-12 mt-6;
|
||||
}
|
||||
|
||||
ui-filter-input-group-main {
|
||||
@apply block mx-auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// import { fakeAsync } from '@angular/core/testing';
|
||||
// import { RouterTestingModule } from '@angular/router/testing';
|
||||
// import { BreadcrumbService } from '@core/breadcrumb';
|
||||
// import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
|
||||
// import { UiFilter, UiInputGroupSelectorPipe } from '@ui/filter';
|
||||
// import { hot } from 'jasmine-marbles';
|
||||
// import { MockPipe } from 'ng-mocks';
|
||||
// import { EMPTY } from 'rxjs';
|
||||
// import { GoodsInSearchStore } from '../goods-in-search.store';
|
||||
// // import { GoodsInSearchService } from '../goods-in-search.service';
|
||||
// import { GoodsInSearchMainComponent } from './goods-in-search-main.component';
|
||||
|
||||
// describe('GoodsInSearchMainComponent', () => {
|
||||
// let spectator: Spectator<GoodsInSearchMainComponent>;
|
||||
// const createComponent = createComponentFactory({
|
||||
// component: GoodsInSearchMainComponent,
|
||||
// imports: [RouterTestingModule],
|
||||
// providers: [
|
||||
// mockProvider(BreadcrumbService),
|
||||
// mockProvider(GoodsInSearchStore, {
|
||||
// loadSettings: async () => {},
|
||||
// }),
|
||||
// ],
|
||||
// declarations: [MockPipe(UiInputGroupSelectorPipe)],
|
||||
// });
|
||||
|
||||
// // let goodsInSearchServiceMock: jasmine.SpyObj<GoodsInSearchService>;
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createComponent();
|
||||
// // goodsInSearchServiceMock = spectator.inject(GoodsInSearchService);
|
||||
// });
|
||||
|
||||
// // it('should create the component', () => {
|
||||
// // expect(spectator.component).toBeTruthy();
|
||||
// // });
|
||||
|
||||
// // describe('get settings()', () => {
|
||||
// // it('should retun the settings from GoodsInSearchService.settings', () => {
|
||||
// // const settings = UiFilter.create({});
|
||||
// // goodsInSearchServiceMock.settings = settings;
|
||||
// // expect(spectator.component.settings).toBe(settings);
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('ngOnInit()', () => {
|
||||
// // it('should subscribe to settingsChange and add it to the subscriptions', () => {
|
||||
// // spyOn(spectator.component['_subscriptions'], 'add');
|
||||
|
||||
// // spectator.component.ngOnInit();
|
||||
|
||||
// // expect(spectator.component['_subscriptions'].add).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('ngOnDestroy()', () => {
|
||||
// // it('should unsubscribe the subscriptions', () => {
|
||||
// // spyOn(spectator.component['_subscriptions'], 'unsubscribe');
|
||||
|
||||
// // spectator.component.ngOnDestroy();
|
||||
|
||||
// // expect(spectator.component['_subscriptions'].unsubscribe).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('search()', () => {
|
||||
// // it('should call GoodsInSearchService.search() to start a search', () => {
|
||||
// // spectator.component.search();
|
||||
// // expect(goodsInSearchServiceMock.search).toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,166 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { UiFilterInputGroupMainComponent } from '@ui/filter';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { first, debounceTime } from 'rxjs/operators';
|
||||
import { GoodsInSearchStore } from '../goods-in-search.store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-search-main',
|
||||
templateUrl: 'goods-in-search-main.component.html',
|
||||
styleUrls: ['goods-in-search-main.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInSearchMainComponent implements OnInit, OnDestroy {
|
||||
filter$ = this._goodsInSearchStore.filter$;
|
||||
|
||||
loading$ = this._goodsInSearchStore.fetching$;
|
||||
|
||||
message: string;
|
||||
|
||||
private _subscriptions = new Subscription();
|
||||
|
||||
@ViewChild(UiFilterInputGroupMainComponent, { static: false })
|
||||
uiFilterGroupMain: UiFilterInputGroupMainComponent;
|
||||
|
||||
constructor(
|
||||
private _goodsInSearchStore: GoodsInSearchStore,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private readonly _config: Config
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this._subscriptions.add(
|
||||
this._goodsInSearchStore.filter$.subscribe(() => {
|
||||
this._cdr.markForCheck();
|
||||
})
|
||||
);
|
||||
|
||||
this._subscriptions.add(
|
||||
this._activatedRoute.queryParams.pipe(debounceTime(50)).subscribe((queryParams) => {
|
||||
this._goodsInSearchStore.setQueryParams(queryParams);
|
||||
|
||||
this.removeBreadcrumbs();
|
||||
this.updateBreadcrumb(queryParams);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
const resultCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'results'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const editCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const listCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const reservationCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'reservation'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const cleanupCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'cleanup'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const previewCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'preview'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
resultCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
listCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
reservationCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
cleanupCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
previewCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async search() {
|
||||
this.uiFilterGroupMain?.cancelAutocomplete();
|
||||
this._goodsInSearchStore.clearResults();
|
||||
await this.updateQueryParams();
|
||||
this.message = undefined;
|
||||
|
||||
this._goodsInSearchStore.searchResult$.pipe(first()).subscribe((result) => {
|
||||
if (result.error) {
|
||||
} else {
|
||||
if (result.hits > 0) {
|
||||
if (result.hits === 1) {
|
||||
const orderItem = result.result[0];
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(orderItem.buyerNumber)}/order/${encodeURIComponent(
|
||||
orderItem.orderNumber
|
||||
)}/item/${orderItem.orderItemSubsetId}/${orderItem.processingStatus}`,
|
||||
]);
|
||||
} else {
|
||||
this._router.navigate(['/filiale', 'goods', 'in', 'results'], {
|
||||
queryParams: this._goodsInSearchStore.filter.getQueryParams(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.message = 'keine Suchergebnisse';
|
||||
}
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
});
|
||||
|
||||
this._goodsInSearchStore.search();
|
||||
}
|
||||
|
||||
async updateBreadcrumb(queryParams: Record<string, string>) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Abholfach',
|
||||
path: '/filiale/goods/in',
|
||||
tags: ['goods-in', 'main', 'filter'],
|
||||
section: 'branch',
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
async updateQueryParams() {
|
||||
const queryParams = this._goodsInSearchStore.filter?.getQueryParams();
|
||||
await this._router.navigate([], { queryParams });
|
||||
this.updateBreadcrumb(queryParams);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { GoodsInSearchMainComponent } from './goods-in-search-main.component';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiFilterNextModule, RouterModule],
|
||||
exports: [GoodsInSearchMainComponent],
|
||||
declarations: [GoodsInSearchMainComponent],
|
||||
})
|
||||
export class GoodsInSearchMainModule {}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-search-main.component';
|
||||
export * from './goods-in-search-main.module';
|
||||
// end:ng42.barrel
|
||||
@@ -1,48 +0,0 @@
|
||||
<div class="goods-in-search-results-headline">
|
||||
<h1 class="goods-in-search-results-title">
|
||||
Bestellpostentrefferliste
|
||||
</h1>
|
||||
<p class="goods-in-search-results-paragraph">
|
||||
Setzen Sie die Bestellposten<br />
|
||||
auf eingetroffen
|
||||
</p>
|
||||
</div>
|
||||
<div class="hits">{{ hits$ | async }} Titel</div>
|
||||
<ui-scroll-container
|
||||
*ngIf="!(listEmpty$ | async); else emptyMessage"
|
||||
[loading]="loading$ | async"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[itemLength]="(items$ | async).length"
|
||||
[containerHeight]="24.5"
|
||||
>
|
||||
<shared-goods-in-out-order-group *ngFor="let bueryNumberGroup of items$ | async | groupBy: byBuyerNumberFn">
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; let lastOrderNumber = last">
|
||||
<ng-container
|
||||
*ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; let lastProcessingStatus = last"
|
||||
>
|
||||
<ng-container
|
||||
*ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; let lastCompartmentCode = last"
|
||||
>
|
||||
<shared-goods-in-out-order-group-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first"
|
||||
[item]="item"
|
||||
[showCompartmentCode]="firstItem"
|
||||
(click)="navigateToDetails(item)"
|
||||
>
|
||||
</shared-goods-in-out-order-group-item>
|
||||
<div class="divider" *ngIf="!lastCompartmentCode"></div>
|
||||
</ng-container>
|
||||
<div class="divider" *ngIf="!lastProcessingStatus"></div>
|
||||
</ng-container>
|
||||
<div class="divider" *ngIf="!lastOrderNumber"></div>
|
||||
</ng-container>
|
||||
</shared-goods-in-out-order-group>
|
||||
</ui-scroll-container>
|
||||
|
||||
<ng-template #emptyMessage>
|
||||
<div class="empty-message">
|
||||
Es sind im Moment keine Bestellposten vorhanden,<br />
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -1,42 +0,0 @@
|
||||
:host {
|
||||
@apply block relative;
|
||||
}
|
||||
|
||||
.goods-in-search-results-headline {
|
||||
@apply text-center;
|
||||
margin-bottom: -16px;
|
||||
|
||||
.goods-in-search-results-title {
|
||||
@apply text-h3 font-bold m-0;
|
||||
}
|
||||
|
||||
.goods-in-search-results-paragraph {
|
||||
@apply text-h3 m-0 mt-1;
|
||||
}
|
||||
}
|
||||
|
||||
.hits {
|
||||
@apply text-active-branch text-right mb-3 font-semibold text-p2;
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
@apply bg-white text-center font-semibold text-inactive-branch py-10 rounded;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
shared-goods-in-out-order-group-item {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
::ng-deep page-goods-in-search-results ui-scroll-container .cta-scroll {
|
||||
bottom: 35px !important;
|
||||
}
|
||||
|
||||
::ng-deep .desktop page-goods-in-search-results ui-scroll-container {
|
||||
.scrollbar-gap::-webkit-scrollbar-track {
|
||||
margin-bottom: 7.25rem;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator';
|
||||
// import { GoodsInSearchResultsComponent } from './goods-in-search-results.component';
|
||||
// import { BehaviorSubject, of } from 'rxjs';
|
||||
// import { UiCommonModule } from '@ui/common';
|
||||
// // import { GoodsInSearchService } from '../goods-in-search.service';
|
||||
// import { RouterTestingModule } from '@angular/router/testing';
|
||||
// import { GoodsInSearchStore } from '../goods-in-search.store';
|
||||
// import { BreadcrumbService } from '@core/breadcrumb';
|
||||
|
||||
// describe('GoodsInSearchResultsComponent', () => {
|
||||
// let spectator: Spectator<GoodsInSearchResultsComponent>;
|
||||
// let goodsInSearchStore: GoodsInSearchStore;
|
||||
// const createComponent = createComponentFactory({
|
||||
// component: GoodsInSearchResultsComponent,
|
||||
// imports: [UiCommonModule, RouterTestingModule],
|
||||
// providers: [
|
||||
// mockProvider(BreadcrumbService, {
|
||||
// getBreadcrumbsByKeyAndTags$: async () => {},
|
||||
// }),
|
||||
// mockProvider(GoodsInSearchStore, {
|
||||
// results$: new BehaviorSubject({ error: false }),
|
||||
// fetching$: new BehaviorSubject<boolean>(false),
|
||||
// search: async () => {},
|
||||
// loadSettings: async () => {},
|
||||
// }),
|
||||
// ],
|
||||
// });
|
||||
|
||||
// beforeEach(() => {
|
||||
// spectator = createComponent();
|
||||
// goodsInSearchStore = spectator.inject(GoodsInSearchStore);
|
||||
// });
|
||||
|
||||
// // it('should create', () => {
|
||||
// // expect(spectator.component).toBeTruthy();
|
||||
// // });
|
||||
|
||||
// // describe('loading$', () => {
|
||||
// // it('should render the UiLoaderComponent if loading$ is true', () => {
|
||||
// // spectator.component.loading$.next(true);
|
||||
// // spectator.detectComponentChanges();
|
||||
// // expect(spectator.query('.loading-text')).toBeVisible();
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // describe('checkIfReload()', () => {
|
||||
// // it('should call GoodsInFilterService.search() if HTML Element contains the class last and itemLength < hits', async () => {
|
||||
// // const ele = document.createElement('div');
|
||||
// // ele.classList.add('last');
|
||||
// // spectator.component.hits$ = of(6);
|
||||
// // spectator.component.items$ = of(new Array(5));
|
||||
// // spectator.detectComponentChanges();
|
||||
// // await spectator.component.checkIfReload(ele);
|
||||
// // expect(goodsInSearchService.search).toHaveBeenCalled();
|
||||
// // });
|
||||
|
||||
// // it('should not call GoodsInFilterService.search() if itemLength >= hits', async () => {
|
||||
// // const ele = document.createElement('div');
|
||||
// // ele.classList.add('last');
|
||||
// // spectator.component.hits$ = of(5);
|
||||
// // spectator.component.items$ = of(new Array(6));
|
||||
// // spectator.detectComponentChanges();
|
||||
// // await spectator.component.checkIfReload(ele);
|
||||
// // expect(goodsInSearchService.search).not.toHaveBeenCalled();
|
||||
// // });
|
||||
|
||||
// // it('should not call GoodsInFilterService.search() if HTML Element does not contain the class last', async () => {
|
||||
// // const ele = document.createElement('div');
|
||||
// // spectator.component.hits$ = of(6);
|
||||
// // spectator.component.items$ = of(new Array(5));
|
||||
// // spectator.detectComponentChanges();
|
||||
// // await spectator.component.checkIfReload(ele);
|
||||
// // expect(goodsInSearchService.search).not.toHaveBeenCalled();
|
||||
// // });
|
||||
// // });
|
||||
// });
|
||||
@@ -1,170 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { first, map, shareReplay, takeUntil } from 'rxjs/operators';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { GoodsInSearchStore } from '../goods-in-search.store';
|
||||
import { combineLatest, Subject } from 'rxjs';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { UiScrollContainerComponent } from '@ui/scroll-container';
|
||||
import { Config } from '@core/config';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-search-results',
|
||||
templateUrl: 'goods-in-search-results.component.html',
|
||||
styleUrls: ['goods-in-search-results.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInSearchResultsComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
items$ = this._goodsInSearchStore.results$.pipe(shareReplay());
|
||||
|
||||
hits$ = this._goodsInSearchStore.hits$;
|
||||
|
||||
loading$ = this._goodsInSearchStore.fetching$.pipe(shareReplay());
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
byBuyerNumberFn = (item: OrderItemListItemDTO) => item.buyerNumber;
|
||||
|
||||
byOrderNumberFn = (item: OrderItemListItemDTO) => item.orderNumber;
|
||||
|
||||
byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;
|
||||
|
||||
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
|
||||
!!item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
constructor(
|
||||
private _goodsInSearchStore: GoodsInSearchStore,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private readonly _config: Config
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this._goodsInSearchStore.setQueryParams(this._activatedRoute.snapshot.queryParams);
|
||||
this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$)).subscribe((queryParams) => {
|
||||
this.updateBreadcrumb(queryParams);
|
||||
});
|
||||
|
||||
this.initInitialSearch();
|
||||
this.createBreadcrumb();
|
||||
this.removeBreadcrumbs();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
|
||||
this.updateBreadcrumb(this._goodsInSearchStore.filter?.getQueryParams());
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const editCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async createBreadcrumb() {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: this.getBreadcrumbName(),
|
||||
path: '/filiale/goods/in/results',
|
||||
section: 'branch',
|
||||
params: this._goodsInSearchStore.filter?.getQueryParams(),
|
||||
tags: ['goods-in', 'results', 'filter'],
|
||||
});
|
||||
}
|
||||
|
||||
async updateBreadcrumb(queryParams: Record<string, string> = this._goodsInSearchStore.filter?.getQueryParams()) {
|
||||
const scroll_position = this.scrollContainer?.scrollPos;
|
||||
const take = this._goodsInSearchStore.results?.length;
|
||||
|
||||
if (queryParams) {
|
||||
const crumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'results', 'filter'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
|
||||
const params = { ...queryParams, scroll_position, take };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this._breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
name,
|
||||
params,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBreadcrumbName() {
|
||||
const input = this._goodsInSearchStore.filter?.getQueryParams()?.main_qs;
|
||||
|
||||
return input?.replace('ORD:', '') ?? 'Alle';
|
||||
}
|
||||
|
||||
initInitialSearch() {
|
||||
if (this._goodsInSearchStore.hits === 0) {
|
||||
this._goodsInSearchStore.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
if (result.hits === 0) {
|
||||
await this._router.navigate(['/filiale/goods/in'], { queryParams: this._goodsInSearchStore.filter.getQueryParams() });
|
||||
} else {
|
||||
await this.createBreadcrumb();
|
||||
if (result.hits === 1) {
|
||||
await this.navigateToDetails(result.result[0]);
|
||||
} else {
|
||||
if (!!this._goodsInSearchStore.searchOptions?.take) {
|
||||
this._goodsInSearchStore.searchOptions = undefined;
|
||||
this.scrollContainer.scrollTo(Number(scroll_position ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this._goodsInSearchStore.search();
|
||||
}
|
||||
|
||||
const { scroll_position, take } = this._goodsInSearchStore.queryParams;
|
||||
if (!!take && !!scroll_position) {
|
||||
this._goodsInSearchStore.searchOptions = { take: Number(take) };
|
||||
}
|
||||
}
|
||||
|
||||
async loadMore() {
|
||||
if (this._goodsInSearchStore.hits > this._goodsInSearchStore.results.length && !this._goodsInSearchStore.fetching) {
|
||||
this._goodsInSearchStore.search();
|
||||
}
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const customerNumber = orderItem.buyerNumber;
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const orderItemId = orderItem.orderItemSubsetId;
|
||||
|
||||
this._router.navigate([
|
||||
`/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
|
||||
orderNumber
|
||||
)}/item/${orderItemId}/${processingStatus}`,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { GoodsInSearchResultsComponent } from './goods-in-search-results.component';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { GoodsInOutOrderGroupModule } from '@shared/components/goods-in-out';
|
||||
import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, GoodsInOutOrderGroupModule, UiCommonModule, UiScrollContainerModule],
|
||||
exports: [GoodsInSearchResultsComponent],
|
||||
declarations: [GoodsInSearchResultsComponent],
|
||||
})
|
||||
export class GoodsInSearchResultsModule {}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './goods-in-search-results.component';
|
||||
export * from './goods-in-search-results.module';
|
||||
// end:ng42.barrel
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, inject } from '@angular/core';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { PickupShelfInNavigationService } from '@shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in',
|
||||
@@ -9,15 +10,21 @@ import { Config } from '@core/config';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GoodsInComponent implements OnInit {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
|
||||
goodsInKey = this._config.get('process.ids.goodsIn');
|
||||
pickupShelfKey = this._config.get('process.ids.pickupShelf');
|
||||
|
||||
constructor(private readonly _config: Config, private _breadcrumb: BreadcrumbService) {}
|
||||
|
||||
ngOnInit() {
|
||||
const nav = this._pickupShelfInNavigationService.defaultRoute();
|
||||
|
||||
this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this._config.get('process.ids.goodsIn'),
|
||||
name: 'Abholfach',
|
||||
path: '/filiale/goods/in',
|
||||
path: nav.path,
|
||||
params: nav.queryParams,
|
||||
tags: ['goods-in', 'main', 'filter'],
|
||||
section: 'branch',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user