mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
#3790 Kundenbestellungen
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { AuthService } from './auth.service';
|
||||
import { OAuthModule } from 'angular-oauth2-oidc';
|
||||
@NgModule({})
|
||||
import { IfRoleDirective } from './if-role.directive';
|
||||
@NgModule({
|
||||
declarations: [IfRoleDirective],
|
||||
exports: [IfRoleDirective],
|
||||
})
|
||||
export class AuthModule {
|
||||
static forRoot(): ModuleWithProviders<AuthModule> {
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { coerceArray, coerceStringArray } from '@angular/cdk/coercion';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Config } from '@core/config';
|
||||
import { isNullOrUndefined } from '@utils/common';
|
||||
@@ -85,4 +86,16 @@ export class AuthService {
|
||||
async logout() {
|
||||
await this._oAuthService.revokeTokenAndLogout();
|
||||
}
|
||||
|
||||
hasRole(role: string | string[]) {
|
||||
const roles = coerceArray(role);
|
||||
|
||||
const userRoles = this.getClaimByKey('role');
|
||||
|
||||
if (isNullOrUndefined(userRoles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return roles.every((r) => userRoles.includes(r));
|
||||
}
|
||||
}
|
||||
|
||||
65
apps/core/auth/src/lib/if-role.directive.spec.ts
Normal file
65
apps/core/auth/src/lib/if-role.directive.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator';
|
||||
import { IfRoleDirective } from './if-role.directive';
|
||||
import { AuthService } from './auth.service';
|
||||
import { TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
|
||||
describe('IfRoleDirective', () => {
|
||||
let spectator: SpectatorDirective<IfRoleDirective>;
|
||||
const createDirective = createDirectiveFactory({
|
||||
directive: IfRoleDirective,
|
||||
mocks: [AuthService],
|
||||
});
|
||||
|
||||
it('should create an instance', () => {
|
||||
spectator = createDirective(`<div *ifRole="'admin'"></div>`);
|
||||
expect(spectator.directive).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render template when user has the role', () => {
|
||||
spectator = createDirective(`<div *ifRole="'admin'"></div>`);
|
||||
const authService = spectator.inject(AuthService);
|
||||
const viewContainerRef = spectator.inject(ViewContainerRef);
|
||||
const templateRef = spectator.inject(TemplateRef);
|
||||
authService.hasRole.and.returnValue(true);
|
||||
spectator.directive.ngOnChanges();
|
||||
expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(templateRef, spectator.directive.getContext());
|
||||
});
|
||||
|
||||
it('should render else template when user does not have the role', () => {
|
||||
authService.hasRole.and.returnValue(false);
|
||||
const elseTemplateRef = {} as TemplateRef<any>;
|
||||
spectator = createDirective(`<ng-template #elseTemplateRef></ng-template><div *ifRole="'admin'; else elseTemplateRef"></div>`, {
|
||||
hostProps: {
|
||||
elseTemplateRef,
|
||||
},
|
||||
});
|
||||
spectator.directive.ngOnChanges();
|
||||
expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(elseTemplateRef, spectator.directive.getContext());
|
||||
});
|
||||
|
||||
it('should render else template when user does not have the role using ifNotRole input', () => {
|
||||
authService.hasRole.and.returnValue(false);
|
||||
const elseTemplateRef = {} as TemplateRef<any>;
|
||||
spectator = createDirective(`<ng-template #elseTemplateRef></ng-template><div *ifNotRole="'admin'; else elseTemplateRef"></div>`, {
|
||||
hostProps: {
|
||||
elseTemplateRef,
|
||||
},
|
||||
});
|
||||
spectator.directive.ngOnChanges();
|
||||
expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(elseTemplateRef, spectator.directive.getContext());
|
||||
});
|
||||
|
||||
it('should clear view when user does not have the role and elseTemplateRef is not defined', () => {
|
||||
authService.hasRole.and.returnValue(false);
|
||||
spectator = createDirective(`<div *ifRole="'admin'"></div>`);
|
||||
spectator.directive.ngOnChanges();
|
||||
expect(viewContainerRef.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set $implicit to ifRole or ifNotRole input', () => {
|
||||
spectator = createDirective(`<div *ifRole="'admin'"></div>`);
|
||||
expect(spectator.directive.getContext().$implicit).toEqual('admin');
|
||||
spectator.setInput('ifNotRole', 'user');
|
||||
expect(spectator.directive.getContext().$implicit).toEqual('user');
|
||||
});
|
||||
});
|
||||
59
apps/core/auth/src/lib/if-role.directive.ts
Normal file
59
apps/core/auth/src/lib/if-role.directive.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[ifRole],[ifRoleElse],[ifNotRole],[ifNotRoleElse]',
|
||||
})
|
||||
export class IfRoleDirective implements OnChanges {
|
||||
@Input()
|
||||
ifRole: string | string[];
|
||||
|
||||
@Input()
|
||||
ifRoleElse: TemplateRef<any>;
|
||||
|
||||
@Input()
|
||||
ifNotRole: string | string[];
|
||||
|
||||
@Input()
|
||||
ifNotRoleElse: TemplateRef<any>;
|
||||
|
||||
get renderTemplateRef() {
|
||||
if (this.ifRole) {
|
||||
return this._authService.hasRole(this.ifRole);
|
||||
}
|
||||
if (this.ifNotRole) {
|
||||
return !this._authService.hasRole(this.ifNotRole);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get elseTemplateRef() {
|
||||
return this.ifRoleElse || this.ifNotRoleElse;
|
||||
}
|
||||
|
||||
constructor(private _templateRef: TemplateRef<any>, private _viewContainer: ViewContainerRef, private _authService: AuthService) {}
|
||||
|
||||
ngOnChanges() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.renderTemplateRef) {
|
||||
this._viewContainer.createEmbeddedView(this._templateRef, this.getContext());
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.elseTemplateRef) {
|
||||
this._viewContainer.createEmbeddedView(this.elseTemplateRef, this.getContext());
|
||||
return;
|
||||
}
|
||||
|
||||
this._viewContainer.clear();
|
||||
}
|
||||
|
||||
getContext(): { $implicit: string | string[] } {
|
||||
return {
|
||||
$implicit: this.ifRole || this.ifNotRole,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
AutocompleteTokenDTO,
|
||||
BranchService,
|
||||
ChangeStockStatusCodeValues,
|
||||
HistoryDTO,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
OrderItemSubsetDTO,
|
||||
OrderListItemDTO,
|
||||
OrderService,
|
||||
QueryTokenDTO,
|
||||
ReceiptService,
|
||||
ResponseArgsOfIEnumerableOfHistoryDTO,
|
||||
StatusValues,
|
||||
@@ -246,4 +248,25 @@ export class DomainOmsService {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
complete(payload: AutocompleteTokenDTO) {
|
||||
return this.orderService.OrderQueryOrderItemAutocomplete(payload);
|
||||
}
|
||||
|
||||
search(payload: QueryTokenDTO) {
|
||||
return this.orderService.OrderQueryOrderItem(payload);
|
||||
}
|
||||
|
||||
getOrderItemsByOrderNumber(orderNumber: string) {
|
||||
return this.orderService.OrderQueryOrderItem({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
input: {
|
||||
qs: orderNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
settings() {
|
||||
return this.orderService.OrderOrderItemQuerySettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,17 @@ const routes: Routes = [
|
||||
canActivate: [CanActivateProductWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'order',
|
||||
loadChildren: () => import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateGoodsOutGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/order',
|
||||
loadChildren: () => import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateGoodsOutWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'customer',
|
||||
loadChildren: () => import('@page/customer').then((m) => m.PageCustomerModule),
|
||||
|
||||
@@ -47,10 +47,14 @@
|
||||
<ui-icon icon="customer" size="24px"></ui-icon>
|
||||
Kundensuche
|
||||
</a>
|
||||
<a [routerLink]="[customerBasePath$ | async, 'goods', 'out']" routerLinkActive="active">
|
||||
<a *ifRole="'Store'" [routerLink]="[customerBasePath$ | async, 'goods', 'out']" routerLinkActive="active">
|
||||
<ui-icon icon="box_out" size="24px"></ui-icon>
|
||||
Warenausgabe
|
||||
</a>
|
||||
<a *ifRole="'CallCenter'" [routerLink]="[customerBasePath$ | async, 'order']" routerLinkActive="active">
|
||||
<ui-svg-icon icon="package-variant-closed" [size]="28"></ui-svg-icon>
|
||||
Kundenbestellungen
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="section === 'branch'">
|
||||
<a [routerLink]="['/filiale/assortment']" routerLinkActive="active">
|
||||
|
||||
@@ -9,9 +9,10 @@ import { ShellFooterModule } from '@shell/footer';
|
||||
import { ShellComponent } from './shell.component';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { AuthModule } from '@core/auth';
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule, CommonModule, ShellHeaderModule, ShellProcessModule, ShellFooterModule, UiIconModule, OverlayModule],
|
||||
imports: [RouterModule, CommonModule, ShellHeaderModule, ShellProcessModule, ShellFooterModule, UiIconModule, OverlayModule, AuthModule],
|
||||
exports: [ShellComponent],
|
||||
declarations: [ShellComponent],
|
||||
providers: [],
|
||||
|
||||
7
apps/page/customer-order/ng-package.json
Normal file
7
apps/page/customer-order/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/page/goods-out",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<shared-goods-in-out-order-details (actionHandled)="actionHandled($event)" [itemsSelectable]="true">
|
||||
<shared-goods-in-out-order-details-header [order]="order$ | async" (editClick)="navigateToEditPage($event)">
|
||||
</shared-goods-in-out-order-details-header>
|
||||
<shared-goods-in-out-order-details-item
|
||||
*ngFor="let item of items$ | async"
|
||||
[orderItem]="item"
|
||||
[order]="order$ | async"
|
||||
></shared-goods-in-out-order-details-item>
|
||||
<shared-goods-in-out-order-details-tags></shared-goods-in-out-order-details-tags>
|
||||
</shared-goods-in-out-order-details>
|
||||
@@ -0,0 +1,4 @@
|
||||
:host {
|
||||
@apply block overflow-y-scroll box-border;
|
||||
max-height: calc(100vh - 280px);
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface CustomerOrderDetailsComponentState {
|
||||
fetching: boolean;
|
||||
orderNumber?: string;
|
||||
buyerNumber?: string;
|
||||
processingStatus?: OrderItemProcessingStatusValue;
|
||||
compartmentCode?: string;
|
||||
items?: OrderItemListItemDTO[];
|
||||
orderId?: number;
|
||||
archive?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-details',
|
||||
templateUrl: 'customer-order-details.component.html',
|
||||
styleUrls: ['customer-order-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderDetailsComponent extends ComponentStore<CustomerOrderDetailsComponentState> implements OnInit, OnDestroy {
|
||||
orderNumber$ = this.select((s) => decodeURIComponent(s.orderNumber ?? '') || undefined);
|
||||
|
||||
buyerNumber$ = this.select((s) => s.buyerNumber);
|
||||
|
||||
compartmentCode$ = this.select((s) => decodeURIComponent(s.compartmentCode ?? '') || undefined);
|
||||
|
||||
processingStatus$ = this.select((s) => s.processingStatus);
|
||||
|
||||
archive$ = this.select((s) => s.archive);
|
||||
get archive() {
|
||||
return this.get((s) => s.archive);
|
||||
}
|
||||
|
||||
items$ = this.select((s) => s.items ?? []);
|
||||
|
||||
orderId$ = this.select((s) => s.orderId);
|
||||
|
||||
get items() {
|
||||
return this.get((s) => s.items);
|
||||
}
|
||||
|
||||
order$ = this.orderId$.pipe(
|
||||
filter((orderId) => !!orderId),
|
||||
switchMap((orderId) => this._omsService.getOrder(orderId)),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
get processId() {
|
||||
return +this._activatedRoute.snapshot.parent.data.processId;
|
||||
}
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((params) => +params.processId));
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _domainGoodsInService: DomainOmsService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router,
|
||||
private _uiModal: UiModalService
|
||||
) {
|
||||
super({
|
||||
fetching: false,
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$)).subscribe((params) => {
|
||||
const buyerNumber: string = decodeURIComponent(params.buyerNumber ?? '');
|
||||
const archive = params?.archive || false;
|
||||
this.patchState({ buyerNumber, archive });
|
||||
});
|
||||
|
||||
this._activatedRoute.params.pipe(takeUntil(this._onDestroy$)).subscribe(async (params) => {
|
||||
const orderNumber: string = params?.orderNumber;
|
||||
const compartmentCode = params?.compartmentCode;
|
||||
const processingStatus = +params?.processingStatus as OrderItemProcessingStatusValue;
|
||||
|
||||
this.patchState({ orderNumber, compartmentCode, processingStatus });
|
||||
await this.removeDetailsCrumbs();
|
||||
this.loadItems();
|
||||
});
|
||||
|
||||
this.removeBreadcrumbs();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.unsubscribe();
|
||||
}
|
||||
|
||||
async updateBreadcrumb(item: OrderItemListItemDTO) {
|
||||
if (item) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this.processId,
|
||||
name: item?.compartmentCode || item?.orderNumber,
|
||||
path: this.getDetailsPath(item),
|
||||
params: {
|
||||
buyerNumber: item.buyerNumber,
|
||||
archive: this.archive,
|
||||
},
|
||||
section: 'customer',
|
||||
tags: ['customer-order', 'details', item?.compartmentCode || item?.orderNumber],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
const editCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.processId, ['customer-order', 'edit'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async removeDetailsCrumbs() {
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.processId, ['customer-order', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: member-ordering
|
||||
loadItems = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => this.patchState({ fetching: false })),
|
||||
debounceTime(500),
|
||||
withLatestFrom(this.orderNumber$, this.compartmentCode$, this.archive$),
|
||||
switchMap(([_, orderNumber, compartmentCode, archive]) => {
|
||||
let request$: Observable<ListResponseArgsOfOrderItemListItemDTO>;
|
||||
// if (compartmentCode) {
|
||||
// request$ = this._domainGoodsInService.getWarenausgabeItemByCompartment(compartmentCode, archive);
|
||||
// } else {
|
||||
request$ = this._domainGoodsInService.getOrderItemsByOrderNumber(orderNumber);
|
||||
// }
|
||||
|
||||
return combineLatest([request$, this.processingStatus$, this.buyerNumber$]).pipe(
|
||||
tapResponse(
|
||||
([res, processingStatus, buyerNumber]) => {
|
||||
const items = res.result.filter((item) => {
|
||||
return item.processingStatus === processingStatus && (!!buyerNumber ? item.buyerNumber === buyerNumber : true);
|
||||
});
|
||||
|
||||
this.openModalIfItemsHaveDifferentCustomers(items);
|
||||
|
||||
this.patchState({
|
||||
items,
|
||||
orderId: items[0].orderId,
|
||||
});
|
||||
this.updateBreadcrumb(items[0]);
|
||||
},
|
||||
(err) => {},
|
||||
() => {
|
||||
this.patchState({ fetching: false });
|
||||
}
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
openModalIfItemsHaveDifferentCustomers(items: OrderItemListItemDTO[]) {
|
||||
const buyerNumbers = new Set(items.map((item) => item.buyerNumber));
|
||||
if (buyerNumbers.size > 1) {
|
||||
this._uiModal.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Achtung',
|
||||
data: {
|
||||
message: 'Anzeige ist fehlerhaft.\nEs wurden mehrere Positionen gefunden, die mehreren Kunden unterschiedlichen sind.',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
navigateToEditPage(orderItem: OrderItemListItemDTO) {
|
||||
this._router.navigate([this.getEditPath(orderItem)], {
|
||||
queryParams: { buyerNumber: orderItem.buyerNumber, archive: this.archive },
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetailsPage(item: OrderItemListItemDTO) {
|
||||
this._router.navigate([this.getDetailsPath(item)], {
|
||||
queryParams: { buyerNumber: item.buyerNumber, archive: this.archive },
|
||||
});
|
||||
}
|
||||
|
||||
navigateToLandingPage() {
|
||||
this._router.navigate([`/kunde/${this.processId}/order`]);
|
||||
}
|
||||
|
||||
async actionHandled(handler: { orderItemsContext: OrderItemsContext; command: string; navigation: 'details' | 'main' | 'reservation' }) {
|
||||
if (handler.navigation === 'main') {
|
||||
this.navigateToLandingPage();
|
||||
} else {
|
||||
this.navigateToDetailsPage(handler.orderItemsContext.items.find((_) => true));
|
||||
await this.removeDetailsCrumbs();
|
||||
this.loadItems();
|
||||
}
|
||||
}
|
||||
|
||||
getDetailsPath(item: OrderItemListItemDTO) {
|
||||
return item?.compartmentCode
|
||||
? `/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}`
|
||||
: `/kunde/${this.processId}/order/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`;
|
||||
}
|
||||
|
||||
getEditPath(item: OrderItemListItemDTO) {
|
||||
return item?.compartmentCode
|
||||
? `/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}/edit`
|
||||
: `/kunde/${this.processId}/order/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}/edit`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedGoodsInOutOrderDetailsModule } from '@shared/components/goods-in-out';
|
||||
|
||||
import { CustomerOrderDetailsComponent } from './customer-order-details.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedGoodsInOutOrderDetailsModule],
|
||||
exports: [CustomerOrderDetailsComponent],
|
||||
declarations: [CustomerOrderDetailsComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class CustomerOrderDetailsModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './customer-order-details.component';
|
||||
export * from './customer-order-details.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<div *ngIf="items$ | async; let items">
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
div {
|
||||
@apply overflow-y-scroll;
|
||||
height: calc(100vh - 280px);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainGoodsService, DomainOmsService } from '@domain/oms';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-edit',
|
||||
templateUrl: 'customer-order-edit.component.html',
|
||||
styleUrls: ['customer-order-edit.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderEditComponent implements OnInit {
|
||||
get processId() {
|
||||
return +this._activatedRoute.snapshot.parent.data.processId;
|
||||
}
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((params) => +params.processId));
|
||||
|
||||
orderNumber$: Observable<string> = this._activatedRoute.params.pipe(
|
||||
map((params) => decodeURIComponent(params.orderNumber ?? '') || undefined)
|
||||
);
|
||||
|
||||
processingStatus$ = this._activatedRoute.params.pipe(map((params) => params?.processingStatus || undefined));
|
||||
|
||||
archive$ = this._activatedRoute.queryParams.pipe(map((params) => params?.archive || false));
|
||||
|
||||
compartmentCode$: Observable<string> = this._activatedRoute.params.pipe(
|
||||
map((params) => decodeURIComponent(params?.compartmentCode ?? '') || undefined)
|
||||
);
|
||||
|
||||
buyerNumber$ = this._activatedRoute.queryParams.pipe(map((params) => params.buyerNumber));
|
||||
|
||||
items$ = combineLatest([this.orderNumber$, this.compartmentCode$, this.archive$]).pipe(
|
||||
switchMap(([orderNumber, compartmentCode, archive]) =>
|
||||
// compartmentCode
|
||||
// ? this._domainGoodsInService.getWarenausgabeItemByCompartment(compartmentCode, archive)
|
||||
// : this._domainGoodsInService.getWarenausgabeItemByOrderNumber(orderNumber, archive)
|
||||
this._domainGoodsInService.getOrderItemsByOrderNumber(orderNumber)
|
||||
),
|
||||
withLatestFrom(this.processingStatus$, this.buyerNumber$),
|
||||
map(([response, processingStatus, buyerNumber]) => {
|
||||
return response.result.filter(
|
||||
(item) => item.processingStatus === +processingStatus && (!!buyerNumber ? item.buyerNumber === buyerNumber : true)
|
||||
);
|
||||
}),
|
||||
tap((items) => this.openModalIfItemsHaveDifferentCustomers(items)),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _domainGoodsInService: DomainOmsService,
|
||||
private _router: Router,
|
||||
private _uiModal: UiModalService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.updateBreadcrumb();
|
||||
}
|
||||
|
||||
async updateBreadcrumb() {
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode;
|
||||
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
|
||||
const buyerNumber = this._activatedRoute.snapshot.queryParams.buyerNumber;
|
||||
const archive = this._activatedRoute.snapshot.queryParams.archive;
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this.processId,
|
||||
name: 'Bearbeiten',
|
||||
path: compartmentCode
|
||||
? `/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}/edit`
|
||||
: `/kunde/${this.processId}/order/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}/edit`,
|
||||
section: 'customer',
|
||||
params: { buyerNumber, archive },
|
||||
tags: ['customer-order', 'edit', compartmentCode || orderNumber],
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode;
|
||||
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
|
||||
const buyerNumber = this._activatedRoute.snapshot.queryParams.buyerNumber;
|
||||
const archive = this._activatedRoute.snapshot.queryParams.archive;
|
||||
compartmentCode
|
||||
? this._router.navigate(
|
||||
[`/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`],
|
||||
{ queryParams: { buyerNumber, archive } }
|
||||
)
|
||||
: this._router.navigate([`/kunde/${this.processId}/order/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`], {
|
||||
queryParams: { buyerNumber, archive },
|
||||
});
|
||||
}
|
||||
|
||||
openModalIfItemsHaveDifferentCustomers(items: OrderItemListItemDTO[]) {
|
||||
const buyerNumbers = new Set(items.map((item) => item.buyerNumber));
|
||||
if (buyerNumbers.size > 1) {
|
||||
this._uiModal.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Achtung',
|
||||
data: {
|
||||
message: 'Anzeige ist fehlerhaft.\nEs wurden mehrere Positionen gefunden, die unterschiedlichen Kunden zugeordnet sind.',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedGoodsInOutOrderEditModule } from '@shared/components/goods-in-out';
|
||||
import { CustomerOrderEditComponent } from './customer-order-edit.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedGoodsInOutOrderEditModule],
|
||||
exports: [CustomerOrderEditComponent],
|
||||
declarations: [CustomerOrderEditComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class CustomerOrderEditModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './customer-order-edit.component';
|
||||
export * from './customer-order-edit.module';
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CustomerOrderDetailsComponent, CustomerOrderDetailsModule } from './customer-order-details';
|
||||
import { CustomerOrderEditComponent } from './customer-order-edit';
|
||||
import { CustomerOrderSearchComponent, CustomerOrderSearchModule } from './customer-order-search';
|
||||
import { CustomerOrderSearchMainComponent, CustomerOrderSearchMainModule } from './customer-order-search/search-main';
|
||||
import { CustomerOrderSearchResultsComponent, CustomerOrderSearchResultsModule } from './customer-order-search/search-results';
|
||||
import { CustomerOrderComponent } from './customer-order.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CustomerOrderComponent,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: CustomerOrderSearchComponent,
|
||||
children: [
|
||||
{ path: '', component: CustomerOrderSearchMainComponent },
|
||||
{ path: 'results', component: CustomerOrderSearchResultsComponent },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'details/compartment/:compartmentCode/:processingStatus',
|
||||
component: CustomerOrderDetailsComponent,
|
||||
},
|
||||
{
|
||||
path: 'details/order/:orderNumber/:processingStatus',
|
||||
component: CustomerOrderDetailsComponent,
|
||||
},
|
||||
{
|
||||
path: 'details/compartment/:compartmentCode/:processingStatus/edit',
|
||||
component: CustomerOrderEditComponent,
|
||||
},
|
||||
{
|
||||
path: 'details/order/:orderNumber/:processingStatus/edit',
|
||||
component: CustomerOrderEditComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CustomerOrderSearchModule,
|
||||
CustomerOrderSearchMainModule,
|
||||
CustomerOrderSearchResultsModule,
|
||||
CustomerOrderDetailsModule,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CustomerOrderRoutingModule {}
|
||||
@@ -0,0 +1,29 @@
|
||||
<div class="goods-out-search-filter-content">
|
||||
<button class="btn-close" type="button" (click)="close.emit()">
|
||||
<ui-icon icon="close" size="20px"></ui-icon>
|
||||
</button>
|
||||
|
||||
<div class="goods-out-search-filter-content-main">
|
||||
<h1 class="title">Filter</h1>
|
||||
<ui-filter
|
||||
[filter]="filter"
|
||||
[loading]="loading$ | async"
|
||||
(search)="applyFilter()"
|
||||
[hint]="message"
|
||||
resizeInputOptionsToElement="page-goods-out-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>
|
||||
@@ -0,0 +1,45 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.goods-out-search-filter-content {
|
||||
@apply relative mx-auto;
|
||||
}
|
||||
|
||||
.btn-close {
|
||||
@apply absolute text-inactive-customer top-3 p-4 right-4 outline-none border-none bg-transparent;
|
||||
}
|
||||
|
||||
.goods-out-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;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ChangeDetectorRef,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Input,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiFilter, UiFilterComponent } from '@ui/filter';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { CustomerOrderSearchStore } from '../customer-order-search.store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-search-filter',
|
||||
templateUrl: 'customer-order-search-filter.component.html',
|
||||
styleUrls: ['customer-order-search-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
close = new EventEmitter();
|
||||
|
||||
filter: UiFilter;
|
||||
|
||||
loading$: Observable<boolean>;
|
||||
|
||||
message: string;
|
||||
|
||||
@Input()
|
||||
processId: number;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
@ViewChild(UiFilterComponent, { static: false })
|
||||
uiFilter: UiFilterComponent;
|
||||
|
||||
constructor(
|
||||
private _goodsOutSearchStore: CustomerOrderSearchStore,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this._initSettings();
|
||||
this._initLoading$();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
private _initSettings() {
|
||||
this.filter = UiFilter.create(this._goodsOutSearchStore.filter);
|
||||
}
|
||||
|
||||
private _initLoading$() {
|
||||
this.loading$ = this._goodsOutSearchStore.fetching$;
|
||||
}
|
||||
|
||||
async resetFilter() {
|
||||
const queryParams = { main_qs: this.filter?.getQueryParams()?.main_qs || '' };
|
||||
this._goodsOutSearchStore.resetFilter(queryParams);
|
||||
this._initSettings();
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
async applyFilter() {
|
||||
this.uiFilter?.cancelAutocomplete();
|
||||
this._goodsOutSearchStore.setFilter(this.filter);
|
||||
this.message = undefined;
|
||||
|
||||
await this.updateQueryParams();
|
||||
|
||||
this._goodsOutSearchStore.searchResult$.pipe(takeUntil(this._onDestroy$), take(1)).subscribe((result) => {
|
||||
if (result.results.error) {
|
||||
} else {
|
||||
if (result.results.hits > 0) {
|
||||
if (result.results.hits === 1) {
|
||||
const orderItem = result.results.result[0];
|
||||
this._router.navigate([this.getDetailsPath(orderItem)]);
|
||||
} else {
|
||||
this._router.navigate(['/kunde', this.processId, 'order', 'results'], {
|
||||
queryParams: this._goodsOutSearchStore.filter.getQueryParams(),
|
||||
});
|
||||
}
|
||||
|
||||
this.close.emit();
|
||||
} else {
|
||||
this.message = 'keine Suchergebnisse';
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
});
|
||||
|
||||
this._goodsOutSearchStore.search({ clear: true });
|
||||
}
|
||||
|
||||
async updateBreadcrumb() {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this.processId,
|
||||
name: 'Warenausgabe',
|
||||
path: `/kunde/${this.processId}/order`,
|
||||
tags: ['customer-order', 'main', 'filter'],
|
||||
section: 'customer',
|
||||
params: this._goodsOutSearchStore.filter?.getQueryParams(),
|
||||
});
|
||||
}
|
||||
|
||||
async updateQueryParams() {
|
||||
await this._router.navigate([], { queryParams: this._goodsOutSearchStore.filter?.getQueryParams() });
|
||||
this.updateBreadcrumb();
|
||||
}
|
||||
|
||||
getDetailsPath(item: OrderItemListItemDTO) {
|
||||
return item?.compartmentCode
|
||||
? `/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}`
|
||||
: `/kunde/${this.processId}/order/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './customer-order-search-filter.component';
|
||||
@@ -0,0 +1,15 @@
|
||||
<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-customer-order-search-filter
|
||||
[processId]="processId$ | async"
|
||||
(close)="shellFilterOverlay.close()"
|
||||
*ngIf="shellFilterOverlay.isOpen"
|
||||
>
|
||||
</page-customer-order-search-filter>
|
||||
</shell-filter-overlay>
|
||||
@@ -0,0 +1,16 @@
|
||||
:host {
|
||||
@apply flex flex-col w-full box-content relative;
|
||||
}
|
||||
|
||||
.filter {
|
||||
@apply absolute font-sans flex items-center font-bold bg-wild-blue-yonder border-0 text-regular -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-dark-cerulean text-white ml-px-5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
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 { UiFilter, UiFilterAutocompleteProvider } from '@ui/filter';
|
||||
import { isEqual } from 'lodash';
|
||||
import { combineLatest, Subject } from 'rxjs';
|
||||
import { map, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { CustomerOrderSearchStore } from './customer-order-search.store';
|
||||
import { CustomerOrderSearchMainAutocompleteProvider } from './providers/customer-order-search-main-autocomplete.provider';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-search',
|
||||
templateUrl: 'customer-order-search.component.html',
|
||||
styleUrls: ['customer-order-search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
CustomerOrderSearchStore,
|
||||
{
|
||||
provide: UiFilterAutocompleteProvider,
|
||||
useClass: CustomerOrderSearchMainAutocompleteProvider,
|
||||
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 CustomerOrderSearchComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
hasFilter$ = combineLatest([this._goodsOutSearchStore.filter$, this._goodsOutSearchStore.defaultSettings$]).pipe(
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), UiFilter.create(defaultFilter).getQueryParams()))
|
||||
);
|
||||
|
||||
processId$ = this._activatedRoute.data.pipe(map((data) => +data.processId));
|
||||
|
||||
constructor(
|
||||
private _goodsOutSearchStore: CustomerOrderSearchStore,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _activatedRoute: ActivatedRoute
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.processId$.pipe(takeUntil(this._onDestroy$), withLatestFrom(this._activatedRoute.queryParams)).subscribe(([processId]) => {
|
||||
// if (params && Object.keys(params).length === 0) {
|
||||
// console.log('params is empty');
|
||||
// this._goodsOutSearchStore.setQueryParams(params);
|
||||
// this._goodsOutSearchStore.loadSettings();
|
||||
// } else {
|
||||
// // this._goodsOutSearchStore.resetFilter(params);
|
||||
// }
|
||||
|
||||
this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Kundenbestellung',
|
||||
path: `/kunde/${processId}/order`,
|
||||
tags: ['customer-order', 'main', 'filter'],
|
||||
section: 'customer',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CustomerOrderSearchComponent } from './customer-order-search.component';
|
||||
import { CustomerOrderSearchFilterComponent } from './customer-order-search-filter';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { ShellFilterOverlayModule } from '@shell/filter-overlay';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiIconModule, RouterModule, UiFilterNextModule, ShellFilterOverlayModule, UiSpinnerModule],
|
||||
exports: [CustomerOrderSearchComponent],
|
||||
declarations: [CustomerOrderSearchComponent, CustomerOrderSearchFilterComponent],
|
||||
})
|
||||
export class CustomerOrderSearchModule {}
|
||||
@@ -0,0 +1,228 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { DomainGoodsService, DomainOmsService } 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 { Observable, Subject } from 'rxjs';
|
||||
import { switchMap, filter, tap, first, map } from 'rxjs/operators';
|
||||
|
||||
export interface CustomerOrderSearchState {
|
||||
defaultSettings?: QuerySettingsDTO;
|
||||
queryParams?: Record<string, string>;
|
||||
filter?: UiFilter;
|
||||
message?: string;
|
||||
fetching: boolean;
|
||||
silentFetching: boolean;
|
||||
hits: number;
|
||||
results: OrderItemListItemDTO[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearchState> {
|
||||
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 silentFetching() {
|
||||
return this.get((s) => s.silentFetching);
|
||||
}
|
||||
|
||||
get queryParams() {
|
||||
return this.get((s) => s.queryParams);
|
||||
}
|
||||
|
||||
readonly queryParams$ = this.select((s) => s.queryParams);
|
||||
|
||||
private _searchResultSubject = new Subject<{ results: ListResponseArgsOfOrderItemListItemDTO; cached: boolean }>();
|
||||
|
||||
readonly searchResult$ = this._searchResultSubject.asObservable();
|
||||
|
||||
private _searchResultFromCacheSubject = new Subject<{ hits: number; results: OrderItemListItemDTO[] }>();
|
||||
|
||||
readonly searchResultFromCache$ = this._searchResultFromCacheSubject.asObservable();
|
||||
|
||||
private _searchResultClearedSubject = new Subject<void>();
|
||||
|
||||
readonly searchResultCleared = this._searchResultClearedSubject.asObservable();
|
||||
|
||||
constructor(private _domainGoodsInService: DomainOmsService, private _cache: CacheService) {
|
||||
super({
|
||||
fetching: false,
|
||||
silentFetching: false,
|
||||
hits: 0,
|
||||
results: [],
|
||||
});
|
||||
this.loadDefaultSettings();
|
||||
}
|
||||
|
||||
async loadDefaultSettings() {
|
||||
const defaultSettings = await this._domainGoodsInService
|
||||
.settings()
|
||||
.pipe(map((res) => res?.result))
|
||||
.toPromise();
|
||||
|
||||
const filter = UiFilter.create(defaultSettings);
|
||||
if (this.queryParams) {
|
||||
filter.fromQueryParams(this.queryParams);
|
||||
}
|
||||
this.setFilter(filter);
|
||||
this.patchState({ defaultSettings });
|
||||
}
|
||||
|
||||
setFilter(filter: UiFilter | QuerySettingsDTO) {
|
||||
if (filter instanceof UiFilter) {
|
||||
this.patchState({ filter });
|
||||
} else {
|
||||
this.patchState({ filter: UiFilter.create(filter) });
|
||||
}
|
||||
}
|
||||
|
||||
resetFilter(queryParams?: Record<string, string>) {
|
||||
const filter = UiFilter.create(this.defaultSettings);
|
||||
if (!!queryParams) {
|
||||
filter?.fromQueryParams(queryParams);
|
||||
}
|
||||
|
||||
this.patchState({ queryParams });
|
||||
this.setFilter(filter);
|
||||
}
|
||||
|
||||
setQueryParams(queryParams: Record<string, string>) {
|
||||
this.patchState({ queryParams });
|
||||
if (this.filter instanceof UiFilter) {
|
||||
this.filter?.fromQueryParams(queryParams);
|
||||
this.setFilter(this.filter);
|
||||
}
|
||||
}
|
||||
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
|
||||
options$.pipe(
|
||||
switchMap((options) => {
|
||||
return this.results$.pipe(
|
||||
map((results) => [options, results]),
|
||||
first()
|
||||
);
|
||||
}),
|
||||
switchMap(([options, results]) => {
|
||||
return this.filter$.pipe(
|
||||
filter((f) => f instanceof UiFilter),
|
||||
map((filter) => [options, results, filter]),
|
||||
first()
|
||||
);
|
||||
}),
|
||||
tap(([options, _, filter]: [{ clear?: boolean; siletReload?: boolean }, OrderItemListItemDTO[], UiFilter]) => {
|
||||
if (!options?.siletReload) {
|
||||
this.patchState({ fetching: true });
|
||||
} else {
|
||||
this.patchState({ silentFetching: true });
|
||||
}
|
||||
|
||||
if (options?.clear) {
|
||||
this._searchResultClearedSubject.next();
|
||||
this._cache.delete(filter?.getQueryToken());
|
||||
}
|
||||
}),
|
||||
switchMap(([options, results, filter]) => {
|
||||
const queryToken = filter?.getQueryToken() ?? {};
|
||||
|
||||
let cachedResultCount: number;
|
||||
|
||||
const cached = options?.siletReload && this._cache.get(filter?.getQueryToken());
|
||||
if (cached) {
|
||||
const cachedResults = this._cache.get(queryToken);
|
||||
if (cachedResults?.results?.length > 0) {
|
||||
this.patchState(cachedResults);
|
||||
cachedResultCount = cachedResults.results.length;
|
||||
|
||||
this._searchResultFromCacheSubject.next({ hits: cachedResults.hits, results: cachedResults.results });
|
||||
}
|
||||
}
|
||||
|
||||
if (options.clear) {
|
||||
queryToken.skip = 0;
|
||||
queryToken.take = 50;
|
||||
} else if (options.siletReload) {
|
||||
queryToken.skip = 0;
|
||||
queryToken.take = cachedResultCount || results.length || 50;
|
||||
} else {
|
||||
queryToken.skip = results.length;
|
||||
queryToken.take = 50;
|
||||
}
|
||||
|
||||
return this._domainGoodsInService.search(queryToken).pipe(
|
||||
tapResponse(
|
||||
(res) => {
|
||||
let _results: OrderItemListItemDTO[] = [];
|
||||
if (options.siletReload) {
|
||||
_results = res.result;
|
||||
} else if (options.clear) {
|
||||
_results = res.result;
|
||||
} else {
|
||||
_results = [...results, ...(res.result ?? [])];
|
||||
}
|
||||
|
||||
this.patchState({
|
||||
hits: res.hits,
|
||||
results: _results,
|
||||
fetching: false,
|
||||
silentFetching: false,
|
||||
});
|
||||
|
||||
this._cache.set(filter?.getQueryToken(), {
|
||||
hits: res.hits,
|
||||
results: _results,
|
||||
fetching: false,
|
||||
});
|
||||
|
||||
this._searchResultSubject.next({ results: res, cached });
|
||||
},
|
||||
(err: Error) => {
|
||||
if (err instanceof HttpErrorResponse && isResponseArgs(err.error)) {
|
||||
this._searchResultSubject.next({ results: err.error, cached });
|
||||
} else {
|
||||
this._searchResultSubject.next({
|
||||
results: {
|
||||
error: true,
|
||||
message: err.message,
|
||||
},
|
||||
cached,
|
||||
});
|
||||
}
|
||||
this.patchState({ fetching: false, silentFetching: false });
|
||||
console.error('GoodsInSearchStore.search()', err);
|
||||
}
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './customer-order-search-filter';
|
||||
export * from './providers';
|
||||
export * from './search-main';
|
||||
export * from './search-results';
|
||||
export * from './customer-order-search.component';
|
||||
export * from './customer-order-search.module';
|
||||
export * from './customer-order-search.store';
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { UiFilterAutocomplete, UiFilterAutocompleteProvider, UiInput } from '@ui/filter';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class CustomerOrderSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
|
||||
for = 'goods-out';
|
||||
|
||||
constructor(private omsService: DomainOmsService) {
|
||||
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.omsService
|
||||
.complete({
|
||||
filter,
|
||||
input: input.value,
|
||||
take: 5,
|
||||
type,
|
||||
})
|
||||
.pipe(
|
||||
catchError(() => of({ result: [] })),
|
||||
map((res) => res.result)
|
||||
);
|
||||
} else {
|
||||
return of([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './customer-order-search-main-autocomplete.provider';
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="search-main">
|
||||
<h1 class="search-main-title">Kundenbestellung</h1>
|
||||
<p class="search-main-paragraph">
|
||||
Suchen Sie den Kundenbestellungen via <br />
|
||||
Kundenname, ISBN/EAN, Bestellnummer, <br />
|
||||
Vorgang ID, Abholscheinnummer, <br />
|
||||
Abholfachnummer, Kundenkartennummer oder scannen Sie <br />
|
||||
die Kundenkarte.
|
||||
</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"
|
||||
(queryChange)="queryChangeDebounce()"
|
||||
>
|
||||
</ui-filter-input-group-main>
|
||||
</ng-container>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.goods-out-list-navigation {
|
||||
@apply text-center text-2xl text-active-branch block bg-white rounded-t-card font-bold no-underline py-5;
|
||||
}
|
||||
|
||||
.search-main {
|
||||
@apply bg-white text-center rounded-t-card py-5 shadow-card;
|
||||
height: calc(100vh - 380px);
|
||||
|
||||
.search-main-title {
|
||||
@apply text-2xl font-bold;
|
||||
}
|
||||
|
||||
.search-main-paragraph {
|
||||
@apply text-2xl mb-12 mt-6;
|
||||
}
|
||||
|
||||
ui-filter-input-group-main {
|
||||
@apply block mx-auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiFilterInputGroupMainComponent } from '@ui/filter';
|
||||
import { debounce, isEqual } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
|
||||
import { debounceTime, first, map, withLatestFrom } from 'rxjs/operators';
|
||||
import { CustomerOrderSearchStore } from '../customer-order-search.store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-search-main',
|
||||
templateUrl: 'customer-order-search-main.component.html',
|
||||
styleUrls: ['customer-order-search-main.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderSearchMainComponent implements OnInit, OnDestroy {
|
||||
filter$ = this._goodsOutSearchStore.filter$;
|
||||
|
||||
loading$ = this._goodsOutSearchStore.fetching$;
|
||||
|
||||
queryChanged$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
message: string;
|
||||
|
||||
lastProcessId: number | undefined;
|
||||
|
||||
private _subscriptions = new Subscription();
|
||||
|
||||
get processId() {
|
||||
return +this._activatedRoute.snapshot.data.processId;
|
||||
}
|
||||
|
||||
processId$ = this._activatedRoute.data.pipe(map((data) => +data.processId));
|
||||
|
||||
@ViewChild(UiFilterInputGroupMainComponent, { static: false })
|
||||
filterInputGroup: UiFilterInputGroupMainComponent;
|
||||
|
||||
constructor(
|
||||
private _goodsOutSearchStore: CustomerOrderSearchStore,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
// Clear scroll position
|
||||
localStorage.removeItem(`SCROLL_POSITION_${this.processId}`);
|
||||
|
||||
this._subscriptions.add(
|
||||
this._goodsOutSearchStore.filter$.subscribe((f) => {
|
||||
this._cdr.markForCheck();
|
||||
})
|
||||
);
|
||||
|
||||
this._subscriptions.add(
|
||||
combineLatest([this.processId$, this._activatedRoute.queryParams])
|
||||
.pipe(debounceTime(50), withLatestFrom(this.queryChanged$))
|
||||
.subscribe(([[processId, queryParams], queryChanged]) => {
|
||||
if (!isEqual(queryParams, this._goodsOutSearchStore.filter?.getQueryParams()) && !queryChanged) {
|
||||
this._goodsOutSearchStore.resetFilter(queryParams);
|
||||
}
|
||||
this.queryChanged$.next(false);
|
||||
this.removeBreadcrumbs(processId);
|
||||
this.updateBreadcrumb(processId, queryParams);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
async removeBreadcrumbs(processId: number) {
|
||||
const resultCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'results'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'edit']).pipe(first()).toPromise();
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
resultCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async search() {
|
||||
this.filterInputGroup?.cancelAutocomplete();
|
||||
await this.updateQueryParams(this.processId);
|
||||
this.message = undefined;
|
||||
|
||||
this._goodsOutSearchStore.searchResult$.pipe(first()).subscribe((result) => {
|
||||
if (result.results.error) {
|
||||
} else {
|
||||
if (result.results.hits > 0) {
|
||||
if (result.results.hits === 1) {
|
||||
const orderItem = result.results.result[0];
|
||||
this._router.navigate([this.getDetailsPath(orderItem, this.processId)]);
|
||||
} else {
|
||||
this._router.navigate(['/kunde', this.processId, 'order', 'results'], {
|
||||
queryParams: this._goodsOutSearchStore.filter.getQueryParams(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.message = 'keine Suchergebnisse';
|
||||
}
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
});
|
||||
|
||||
this._goodsOutSearchStore.search({ clear: true });
|
||||
}
|
||||
|
||||
async updateBreadcrumb(processId: number, params: Record<string, string>) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Kundenbestellung',
|
||||
path: `/kunde/${processId}/order`,
|
||||
tags: ['customer-order', 'main', 'filter'],
|
||||
section: 'customer',
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
async updateQueryParams(processId: number) {
|
||||
const queryParams = { ...this._goodsOutSearchStore.filter?.getQueryParams() };
|
||||
queryParams.main_qs = queryParams.main_qs ?? '';
|
||||
await this._router.navigate([], { queryParams });
|
||||
this.updateBreadcrumb(processId, queryParams);
|
||||
}
|
||||
|
||||
getDetailsPath(item: OrderItemListItemDTO, processId: number) {
|
||||
return item?.compartmentCode
|
||||
? `/kunde/${processId}/order/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}`
|
||||
: `/kunde/${processId}/order/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`;
|
||||
}
|
||||
|
||||
queryChangeDebounce = debounce(async () => {
|
||||
this.queryChanged$.next(true);
|
||||
await this.updateQueryParams(this.processId);
|
||||
}, 500);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CustomerOrderSearchMainComponent } from './customer-order-search-main.component';
|
||||
import { UiFilterNextModule } from '@ui/filter';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiFilterNextModule, RouterModule],
|
||||
exports: [CustomerOrderSearchMainComponent],
|
||||
declarations: [CustomerOrderSearchMainComponent],
|
||||
})
|
||||
export class CustomerOrderSearchMainModule {}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './customer-order-search-main.component';
|
||||
export * from './customer-order-search-main.module';
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
|
||||
|
||||
@Pipe({
|
||||
name: 'goodsOutItemSelectable',
|
||||
})
|
||||
export class CustomerOrderItemSelectablePipe implements PipeTransform {
|
||||
transform(
|
||||
item: OrderItemListItemDTO,
|
||||
selectionRules: (action: KeyValueDTOOfStringAndString) => boolean,
|
||||
selectedItems: OrderItemListItemDTO[]
|
||||
): boolean {
|
||||
if (selectedItems.length > 0) {
|
||||
const firstSelectedItem = selectedItems[0];
|
||||
const selectedLabel = firstSelectedItem.actions.find((action) => selectionRules(action)).label;
|
||||
return item?.actions?.some((action) => selectionRules(action) && action.label === selectedLabel);
|
||||
} else {
|
||||
return item?.actions?.some((action) => selectionRules(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
|
||||
@Pipe({
|
||||
name: 'goodsOutItemSelected',
|
||||
})
|
||||
export class CustomerOrderItemSelectedPipe implements PipeTransform {
|
||||
transform(item: OrderItemListItemDTO, selectedOrderItemSubsetIds: number[]): any {
|
||||
return selectedOrderItemSubsetIds?.includes(item?.orderItemSubsetId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<div class="hits">{{ hits$ | async }} Titel</div>
|
||||
<ui-scroll-container
|
||||
*ngIf="!(listEmpty$ | async); else emptyMessage"
|
||||
[loading]="loading$ | async"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[itemLength]="itemLength$ | async"
|
||||
[initialScroll]="scrollTo"
|
||||
>
|
||||
<ng-container *ngIf="processId$ | async; let processId">
|
||||
<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; trackBy: trackByFn"
|
||||
[item]="item"
|
||||
[showCompartmentCode]="firstItem"
|
||||
(click)="navigateToDetails(processId, item)"
|
||||
[selectable]="item | goodsOutItemSelectable: selectionRules:selectedItems"
|
||||
[selected]="item | goodsOutItemSelected: selectedOrderItemSubsetIds"
|
||||
(selectedChange)="setSelectedItem(item, $event)"
|
||||
></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>
|
||||
</ng-container>
|
||||
</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>
|
||||
|
||||
<div class="actions" *ngIf="actions$ | async; let actions">
|
||||
<button
|
||||
[disabled]="(loadingFetchedActionButton$ | async) || (loading$ | async)"
|
||||
class="cta-action"
|
||||
*ngFor="let action of actions"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(loadingFetchedActionButton$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
:host {
|
||||
@apply block relative;
|
||||
}
|
||||
|
||||
.hits {
|
||||
@apply text-active-customer text-right mb-3 font-semibold text-base;
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
@apply bg-white text-center font-semibold text-inactive-customer py-10 rounded-card;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
shared-goods-in-out-order-group-item {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@apply fixed bottom-28 inline-grid grid-flow-col gap-7;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.cta-action {
|
||||
@apply border-2 border-solid border-brand rounded-full py-3 px-6 font-bold text-lg outline-none self-end whitespace-nowrap;
|
||||
|
||||
&:disabled {
|
||||
@apply bg-inactive-branch border-inactive-branch text-white;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-action-primary {
|
||||
@apply bg-brand text-white;
|
||||
}
|
||||
|
||||
.cta-action-secondary {
|
||||
@apply bg-white text-brand;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .desktop page-goods-out-search-results ui-scroll-container {
|
||||
.scrollbar-gap::-webkit-scrollbar-track {
|
||||
margin-bottom: 7.25rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild, TrackByFunction } from '@angular/core';
|
||||
import { debounceTime, first, map, shareReplay, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { CustomerOrderSearchStore } from '../customer-order-search.store';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subject, Subscription } from 'rxjs';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { CommandService } from '@core/command';
|
||||
import { OrderItemsContext } from '@domain/oms';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiScrollContainerComponent } from '@ui/scroll-container';
|
||||
import { UiFilter } from '@ui/filter';
|
||||
|
||||
export interface CustomerOrderSearchResultsState {
|
||||
selectedOrderItemSubsetIds: number[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order-search-results',
|
||||
templateUrl: 'customer-order-search-results.component.html',
|
||||
styleUrls: ['customer-order-search-results.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderSearchResultsComponent extends ComponentStore<CustomerOrderSearchResultsState> implements OnInit, OnDestroy {
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
items$: Observable<OrderItemListItemDTO[]> = this._goodsOutSearchStore.results$;
|
||||
|
||||
itemLength$ = this.items$.pipe(map((items) => items?.length));
|
||||
|
||||
hits$ = this._goodsOutSearchStore.hits$;
|
||||
|
||||
loading$ = this._goodsOutSearchStore.fetching$.pipe(shareReplay());
|
||||
loadingFetchedActionButton$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
selectedOrderItemSubsetIds$ = this.select((s) => s.selectedOrderItemSubsetIds);
|
||||
|
||||
get selectedOrderItemSubsetIds() {
|
||||
return this.get((s) => s.selectedOrderItemSubsetIds);
|
||||
}
|
||||
|
||||
actions$ = combineLatest([this.items$, this.selectedOrderItemSubsetIds$]).pipe(
|
||||
map(([items, selectedOrderItemSubsetIds]) =>
|
||||
items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
|
||||
),
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
|
||||
);
|
||||
|
||||
get selectedItems() {
|
||||
const items = this._goodsOutSearchStore.results;
|
||||
const selectedOrderItemSubsetIds = this.selectedOrderItemSubsetIds;
|
||||
|
||||
return items.filter((item) => selectedOrderItemSubsetIds.some((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((data) => +data.processId));
|
||||
|
||||
previousProcessId: undefined | number;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
trackByFn: TrackByFunction<OrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
|
||||
|
||||
private _searchResultSubscription: Subscription;
|
||||
|
||||
scrollTo: number;
|
||||
|
||||
constructor(
|
||||
private _goodsOutSearchStore: CustomerOrderSearchStore,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _commandService: CommandService,
|
||||
private _modal: UiModalService
|
||||
) {
|
||||
super({
|
||||
selectedOrderItemSubsetIds: [],
|
||||
});
|
||||
}
|
||||
|
||||
saveScrollPosition(processId: number, scrollPosition: number) {
|
||||
localStorage.setItem(`SCROLL_POSITION_${processId}`, JSON.stringify(scrollPosition));
|
||||
}
|
||||
|
||||
getScrollPosition(processId: number): number | undefined {
|
||||
try {
|
||||
const scroll_position = localStorage.getItem(`SCROLL_POSITION_${processId}`);
|
||||
return scroll_position ? JSON.parse(scroll_position) : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
removeScrollPosition(processId: number) {
|
||||
localStorage.removeItem(`SCROLL_POSITION_${processId}`);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.processId$
|
||||
.pipe(takeUntil(this._onDestroy$), debounceTime(10), withLatestFrom(this._activatedRoute.queryParams))
|
||||
.subscribe(async ([processId, params]) => {
|
||||
if (this.previousProcessId && this.previousProcessId !== processId) {
|
||||
this.saveScrollPosition(this.previousProcessId, this.scrollContainer?.scrollPos);
|
||||
}
|
||||
|
||||
if (!(this._goodsOutSearchStore.filter instanceof UiFilter)) {
|
||||
await this._goodsOutSearchStore.loadDefaultSettings();
|
||||
}
|
||||
|
||||
this._goodsOutSearchStore.resetFilter(params);
|
||||
this.updateBreadcrumb(processId, params);
|
||||
|
||||
if (this.previousProcessId !== processId) {
|
||||
this.initInitialSearch(processId);
|
||||
this.createBreadcrumb(processId, params);
|
||||
this.removeBreadcrumbs(processId);
|
||||
|
||||
this.previousProcessId = processId;
|
||||
|
||||
if (!this._goodsOutSearchStore.results?.length) {
|
||||
this._goodsOutSearchStore.search({ siletReload: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._goodsOutSearchStore.searchResultCleared.pipe(takeUntil(this._onDestroy$)).subscribe((_) => this.clearSelectedItems());
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._onDestroy$.next();
|
||||
this._onDestroy$.complete();
|
||||
|
||||
if (this._searchResultSubscription) {
|
||||
this._searchResultSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this.updateBreadcrumb(this.previousProcessId, this._goodsOutSearchStore.filter?.getQueryParams());
|
||||
this.saveScrollPosition(this.previousProcessId, this.scrollContainer?.scrollPos);
|
||||
}
|
||||
|
||||
async removeBreadcrumbs(processId: number) {
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'edit']).pipe(first()).toPromise();
|
||||
|
||||
editCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
|
||||
detailsCrumbs.forEach((crumb) => {
|
||||
this._breadcrumb.removeBreadcrumb(crumb.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
async createBreadcrumb(processId: number, params: Record<string, string>) {
|
||||
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: this.getBreadcrumbName(params),
|
||||
path: `/kunde/${processId}/order/results`,
|
||||
section: 'customer',
|
||||
params,
|
||||
tags: ['customer-order', 'results', 'filter'],
|
||||
});
|
||||
}
|
||||
|
||||
async updateBreadcrumb(processId: number, queryParams: Record<string, string>) {
|
||||
if (queryParams) {
|
||||
const crumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(processId, ['customer-order', 'results', 'filter'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
const name = queryParams.main_qs ? queryParams.main_qs : 'Alle Artikel';
|
||||
const params = { ...queryParams };
|
||||
|
||||
for (const crumb of crumbs) {
|
||||
this._breadcrumb.patchBreadcrumb(crumb.id, {
|
||||
name,
|
||||
params,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBreadcrumbName(params: Record<string, string>) {
|
||||
const input = params?.main_qs;
|
||||
|
||||
return input?.replace('ORD:', '') ?? 'Alle';
|
||||
}
|
||||
|
||||
initInitialSearch(processId: number) {
|
||||
if (this._searchResultSubscription) {
|
||||
this._searchResultSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this._searchResultSubscription = new Subscription();
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
this._goodsOutSearchStore.searchResult$.subscribe(async (result) => {
|
||||
const queryParams = this._goodsOutSearchStore.filter?.getQueryParams();
|
||||
if (result.results.hits === 0) {
|
||||
await this._router.navigate([`/kunde/${processId}/order`], {
|
||||
queryParams,
|
||||
});
|
||||
} else {
|
||||
await this.createBreadcrumb(processId, queryParams);
|
||||
if (result.results.hits === 1) {
|
||||
await this.navigateToDetails(processId, result.results.result[0]);
|
||||
} else if (!result.cached) {
|
||||
const scrollPos = this.getScrollPosition(processId) || 0;
|
||||
this.scrollTo = scrollPos;
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
this.removeScrollPosition(processId);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
this._goodsOutSearchStore.searchResultFromCache$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
if (result?.hits > 0) {
|
||||
const scrollPos = this.getScrollPosition(processId) || 0;
|
||||
this.scrollTo = scrollPos;
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
this.removeScrollPosition(processId);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async loadMore() {
|
||||
if (
|
||||
this._goodsOutSearchStore.hits > this._goodsOutSearchStore.results.length &&
|
||||
!this._goodsOutSearchStore.fetching &&
|
||||
!this._goodsOutSearchStore.silentFetching
|
||||
) {
|
||||
this.saveScrollPosition(this.previousProcessId, this.scrollContainer?.scrollPos);
|
||||
this._goodsOutSearchStore.search({});
|
||||
}
|
||||
}
|
||||
|
||||
navigateToDetails(processId: number, orderItem: OrderItemListItemDTO) {
|
||||
const orderNumber = orderItem.orderNumber;
|
||||
const processingStatus = orderItem.processingStatus;
|
||||
const compartmentCode = orderItem.compartmentCode;
|
||||
const archive = !!this._goodsOutSearchStore.filter?.getQueryParams()?.main_archive || false;
|
||||
|
||||
if (compartmentCode) {
|
||||
this._router.navigate([`/kunde/${processId}/order/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`], {
|
||||
queryParams: {
|
||||
buyerNumber: orderItem.buyerNumber,
|
||||
archive,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this._router.navigate([`/kunde/${processId}/order/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`], {
|
||||
queryParams: {
|
||||
archive,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedItem(item: OrderItemListItemDTO, selected: boolean) {
|
||||
const included = this.selectedOrderItemSubsetIds.includes(item.orderItemSubsetId);
|
||||
|
||||
if (!included && selected) {
|
||||
this.patchState({
|
||||
selectedOrderItemSubsetIds: [...this.selectedOrderItemSubsetIds, item.orderItemSubsetId],
|
||||
});
|
||||
} else if (included && !selected) {
|
||||
this.patchState({
|
||||
selectedOrderItemSubsetIds: this.selectedOrderItemSubsetIds.filter((id) => id !== item?.orderItemSubsetId),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clearSelectedItems() {
|
||||
this.patchState({ selectedOrderItemSubsetIds: [] });
|
||||
}
|
||||
|
||||
selectionRules(action: KeyValueDTOOfStringAndString) {
|
||||
return (
|
||||
(action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false) ||
|
||||
action.command.includes('COLLECT_ON_DELIVERYNOTE')
|
||||
);
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
this.loadingFetchedActionButton$.next(true);
|
||||
const commandData: OrderItemsContext = {
|
||||
items: this.selectedItems,
|
||||
};
|
||||
try {
|
||||
this.saveScrollPosition(this.previousProcessId, this.scrollContainer?.scrollPos);
|
||||
await this._commandService.handleCommand(action.command, commandData);
|
||||
this._goodsOutSearchStore.search({ siletReload: true, clear: true });
|
||||
this.clearSelectedItems();
|
||||
this.loadingFetchedActionButton$.next(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this._modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
data: error,
|
||||
});
|
||||
this.loadingFetchedActionButton$.next(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CustomerOrderSearchResultsComponent } from './customer-order-search-results.component';
|
||||
import { GoodsInOutOrderGroupModule } from '@shared/components/goods-in-out';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { CustomerOrderItemSelectablePipe } from './customer-order-item-selectable.pipe';
|
||||
import { CustomerOrderItemSelectedPipe } from './customer-order-item-selectede.pipe';
|
||||
import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
|
||||
import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, GoodsInOutOrderGroupModule, UiCommonModule, UiSpinnerModule, UiScrollContainerModule],
|
||||
exports: [CustomerOrderSearchResultsComponent],
|
||||
declarations: [CustomerOrderSearchResultsComponent, CustomerOrderItemSelectablePipe, CustomerOrderItemSelectedPipe],
|
||||
})
|
||||
export class CustomerOrderSearchResultsModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './customer-order-item-selectable.pipe';
|
||||
export * from './customer-order-item-selectede.pipe';
|
||||
export * from './customer-order-search-results.component';
|
||||
export * from './customer-order-search-results.module';
|
||||
@@ -0,0 +1,3 @@
|
||||
<shell-breadcrumb [key]="processId$ | async" [includesTags]="['customer-order']"></shell-breadcrumb>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
shell-breadcrumb {
|
||||
@apply sticky z-sticky top-0 py-4;
|
||||
}
|
||||
15
apps/page/customer-order/src/lib/customer-order.component.ts
Normal file
15
apps/page/customer-order/src/lib/customer-order.component.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-order',
|
||||
templateUrl: 'customer-order.component.html',
|
||||
styleUrls: ['customer-order.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderComponent {
|
||||
processId$ = this._activatedRoute.data.pipe(map((data) => String(data.processId)));
|
||||
|
||||
constructor(private _activatedRoute: ActivatedRoute) {}
|
||||
}
|
||||
94
apps/page/customer-order/src/lib/customer-order.module.ts
Normal file
94
apps/page/customer-order/src/lib/customer-order.module.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CustomerOrderComponent } from './customer-order.component';
|
||||
|
||||
import {
|
||||
AcceptedActionHandler,
|
||||
ArrivedActionHandler,
|
||||
AssembledActionHandler,
|
||||
AvailableForDownloadActionHandler,
|
||||
BackToStockActionHandler,
|
||||
CanceledByBuyerActionHandler,
|
||||
CanceledByRetailerActionHandler,
|
||||
CanceledBySupplierActionHandler,
|
||||
CreateShippingNoteActionHandler,
|
||||
DeliveredActionHandler,
|
||||
DetermineSupplierActionHandler,
|
||||
DispatchedActionHandler,
|
||||
DownloadedActionHandler,
|
||||
FetchedActionHandler,
|
||||
InProcessActionHandler,
|
||||
NotAvailableActionHandler,
|
||||
NotFetchedActionHandler,
|
||||
OrderAtSupplierActionHandler,
|
||||
OrderingActionHandler,
|
||||
OverdueActionHandler,
|
||||
PackedActionHandler,
|
||||
ParkedActionHandler,
|
||||
PlacedActionHandler,
|
||||
PreparationForShippingActionHandler,
|
||||
PrintCompartmentLabelActionHandler,
|
||||
PrintShippingNoteActionHandler,
|
||||
ReOrderActionHandler,
|
||||
RedirectedInternaqllyActionHandler,
|
||||
RequestedActionHandler,
|
||||
ReserverdActionHandler,
|
||||
ReturnedByBuyerActionHandler,
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
ReOrderedActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
PrintPriceDiffQrCodeLabelActionHandler,
|
||||
} from '@domain/oms';
|
||||
import { CoreCommandModule } from '@core/command';
|
||||
import { ShellBreadcrumbModule } from '@shell/breadcrumb';
|
||||
import { CustomerOrderRoutingModule } from './customer-order-routing.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CustomerOrderComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CustomerOrderRoutingModule,
|
||||
ShellBreadcrumbModule,
|
||||
CoreCommandModule.forChild([
|
||||
AcceptedActionHandler,
|
||||
ArrivedActionHandler,
|
||||
AssembledActionHandler,
|
||||
AvailableForDownloadActionHandler,
|
||||
BackToStockActionHandler,
|
||||
CanceledByBuyerActionHandler,
|
||||
CanceledByRetailerActionHandler,
|
||||
CanceledBySupplierActionHandler,
|
||||
CreateShippingNoteActionHandler,
|
||||
DeliveredActionHandler,
|
||||
DetermineSupplierActionHandler,
|
||||
DispatchedActionHandler,
|
||||
DownloadedActionHandler,
|
||||
FetchedActionHandler,
|
||||
InProcessActionHandler,
|
||||
NotAvailableActionHandler,
|
||||
NotFetchedActionHandler,
|
||||
OrderAtSupplierActionHandler,
|
||||
OrderingActionHandler,
|
||||
OverdueActionHandler,
|
||||
PackedActionHandler,
|
||||
ParkedActionHandler,
|
||||
PlacedActionHandler,
|
||||
PreparationForShippingActionHandler,
|
||||
PrintCompartmentLabelActionHandler,
|
||||
PrintShippingNoteActionHandler,
|
||||
ReOrderActionHandler,
|
||||
ReOrderedActionHandler,
|
||||
RedirectedInternaqllyActionHandler,
|
||||
RequestedActionHandler,
|
||||
ReserverdActionHandler,
|
||||
ReturnedByBuyerActionHandler,
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
PrintPriceDiffQrCodeLabelActionHandler,
|
||||
]),
|
||||
],
|
||||
exports: [CustomerOrderComponent],
|
||||
})
|
||||
export class CustomerOrderModule {}
|
||||
9
apps/page/customer-order/src/lib/index.ts
Normal file
9
apps/page/customer-order/src/lib/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// start:ng42.barrel
|
||||
export * from './customer-order-routing.module';
|
||||
export * from './customer-order.component';
|
||||
export * from './customer-order.module';
|
||||
export * from './customer-order-search';
|
||||
export * from './customer-order-search/customer-order-search-filter';
|
||||
export * from './customer-order-search/search-main';
|
||||
export * from './customer-order-search/search-results';
|
||||
// end:ng42.barrel
|
||||
5
apps/page/customer-order/src/public-api.ts
Normal file
5
apps/page/customer-order/src/public-api.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Public API Surface of goods-out
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -156,7 +156,6 @@ export { CompanyDTO } from './models/company-dto';
|
||||
export { EntityDTOBaseOfCompanyDTOAndICompany } from './models/entity-dtobase-of-company-dtoand-icompany';
|
||||
export { EntityDTOContainerOfCategoryDTO } from './models/entity-dtocontainer-of-category-dto';
|
||||
export { CategoryDTO } from './models/category-dto';
|
||||
export { EntityDTOBaseOfCategoryDTOAndICategory } from './models/entity-dtobase-of-category-dtoand-icategory';
|
||||
export { EntityDTOContainerOfFileDTO } from './models/entity-dtocontainer-of-file-dto';
|
||||
export { FileDTO } from './models/file-dto';
|
||||
export { EntityDTOBaseOfFileDTOAndIFile } from './models/entity-dtobase-of-file-dtoand-ifile';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* tslint:disable */
|
||||
import { EntityDTOBaseOfCategoryDTOAndICategory } from './entity-dtobase-of-category-dtoand-icategory';
|
||||
import { EntityDTOBase } from './entity-dtobase';
|
||||
import { EntityDTOContainerOfCategoryDTO } from './entity-dtocontainer-of-category-dto';
|
||||
import { EntityDTOContainerOfTenantDTO } from './entity-dtocontainer-of-tenant-dto';
|
||||
export interface CategoryDTO extends EntityDTOBaseOfCategoryDTOAndICategory{
|
||||
export interface CategoryDTO extends EntityDTOBase{
|
||||
key?: string;
|
||||
name?: string;
|
||||
parent?: EntityDTOContainerOfCategoryDTO;
|
||||
|
||||
@@ -7,5 +7,6 @@ export interface EntityDTO extends TouchedBase{
|
||||
id?: number;
|
||||
pId?: string;
|
||||
status?: EntityStatus;
|
||||
uId?: string;
|
||||
version?: number;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/* tslint:disable */
|
||||
import { EntityDTOBase } from './entity-dtobase';
|
||||
export interface EntityDTOBaseOfCategoryDTOAndICategory extends EntityDTOBase{
|
||||
}
|
||||
@@ -8,4 +8,5 @@ export interface EntityDTOReferenceContainer extends TouchedBase{
|
||||
id?: number;
|
||||
pId?: string;
|
||||
selected?: boolean;
|
||||
uId?: string;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ import { ResponseArgsOfOrderItemSubsetDTO } from '../models/response-args-of-ord
|
||||
import { ResponseArgsOfIEnumerableOfOrderDTO } from '../models/response-args-of-ienumerable-of-order-dto';
|
||||
import { ListResponseArgsOfOrderListItemDTO } from '../models/list-response-args-of-order-list-item-dto';
|
||||
import { QueryTokenDTO } from '../models/query-token-dto';
|
||||
import { ResponseArgsOfQuerySettingsDTO } from '../models/response-args-of-query-settings-dto';
|
||||
import { ResponseArgsOfIEnumerableOfAutocompleteDTO } from '../models/response-args-of-ienumerable-of-autocomplete-dto';
|
||||
import { AutocompleteTokenDTO } from '../models/autocomplete-token-dto';
|
||||
import { ListResponseArgsOfOrderItemListItemDTO } from '../models/list-response-args-of-order-item-list-item-dto';
|
||||
import { ResponseArgsOfIEnumerableOfOrderItemDTO } from '../models/response-args-of-ienumerable-of-order-item-dto';
|
||||
import { OrderItemDTO } from '../models/order-item-dto';
|
||||
@@ -45,6 +48,8 @@ class OrderService extends __BaseService {
|
||||
static readonly OrderGetOrdersByCompartmentPath = '/order/compartment';
|
||||
static readonly OrderGetOrdersByBuyerNumberPath = '/buyer/order';
|
||||
static readonly OrderQueryOrdersPath = '/order/s';
|
||||
static readonly OrderOrderItemQuerySettingsPath = '/order/item/s/settings';
|
||||
static readonly OrderQueryOrderItemAutocompletePath = '/order/item/s/complete';
|
||||
static readonly OrderQueryOrderItemPath = '/order/item/s';
|
||||
static readonly OrderGetOrderItemPath = '/order/orderitem/{orderItemId}';
|
||||
static readonly OrderGetOrderItemsPath = '/order/orderitem';
|
||||
@@ -462,6 +467,75 @@ class OrderService extends __BaseService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warenausgabe-Filter
|
||||
*/
|
||||
OrderOrderItemQuerySettingsResponse(): __Observable<__StrictHttpResponse<ResponseArgsOfQuerySettingsDTO>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
let req = new HttpRequest<any>(
|
||||
'GET',
|
||||
this.rootUrl + `/order/item/s/settings`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ResponseArgsOfQuerySettingsDTO>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Warenausgabe-Filter
|
||||
*/
|
||||
OrderOrderItemQuerySettings(): __Observable<ResponseArgsOfQuerySettingsDTO> {
|
||||
return this.OrderOrderItemQuerySettingsResponse().pipe(
|
||||
__map(_r => _r.body as ResponseArgsOfQuerySettingsDTO)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autocomplete für Bestellposten
|
||||
* @param payload Suchkriterien
|
||||
*/
|
||||
OrderQueryOrderItemAutocompleteResponse(payload?: AutocompleteTokenDTO): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfAutocompleteDTO>> {
|
||||
let __params = this.newParams();
|
||||
let __headers = new HttpHeaders();
|
||||
let __body: any = null;
|
||||
__body = payload;
|
||||
let req = new HttpRequest<any>(
|
||||
'POST',
|
||||
this.rootUrl + `/order/item/s/complete`,
|
||||
__body,
|
||||
{
|
||||
headers: __headers,
|
||||
params: __params,
|
||||
responseType: 'json'
|
||||
});
|
||||
|
||||
return this.http.request<any>(req).pipe(
|
||||
__filter(_r => _r instanceof HttpResponse),
|
||||
__map((_r) => {
|
||||
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfAutocompleteDTO>;
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Autocomplete für Bestellposten
|
||||
* @param payload Suchkriterien
|
||||
*/
|
||||
OrderQueryOrderItemAutocomplete(payload?: AutocompleteTokenDTO): __Observable<ResponseArgsOfIEnumerableOfAutocompleteDTO> {
|
||||
return this.OrderQueryOrderItemAutocompleteResponse(payload).pipe(
|
||||
__map(_r => _r.body as ResponseArgsOfIEnumerableOfAutocompleteDTO)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param queryToken undefined
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user