[HIMA-314], [HIMA-313] working on warenausgabe search & search results (API UPDATE)

This commit is contained in:
Eraldo Hasanaj
2019-06-26 15:07:19 +02:00
parent ce9f8df6ee
commit 5f3d9f04ca
20 changed files with 426 additions and 108 deletions

28
.vscode/launch.json vendored
View File

@@ -1,15 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:4200",
"webRoot": "${workspaceFolder}"
}
]
}
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "https://192.168.2.112:4200",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@@ -6,6 +6,38 @@ import { ShelfCustomerOrders } from '../models/shelf-customer-orders.model';
import { Gender } from 'swagger/lib/oms/models/gender';
import { ShlefOrderItem } from '../models/shelf-order-item.model';
export const orderStatusMapper: { [id: number]: string } = {
0: 'Not Set',
1: 'Ordering',
2: 'Placed',
4: 'Accepted',
8: 'Parked',
16: 'In Process',
32: 'Preparation For Shipping',
64: 'Dispatched',
128: 'Arrived',
256: 'Fetched',
512: 'Canceled By Buyer',
1024: 'Canceled By Retailer',
2048: 'Canceled By Supplier',
4096: 'Not Available',
8192: 'ReOrdered',
16384: 'Returned By Buyer',
32768: 'Available For Download',
65536: 'Downloaded',
131072: 'Not Fetched',
262144: 'Back To Stock',
524288: 'Requested',
1048576: 'Redirected Internally',
2097152: 'Overdue',
4194304: 'Delivered',
8388608: 'Determine Supplier',
16777216: 'Supplier Temporarily OutOfStock',
33554432: 'Reserved',
67108864: 'Assembled',
134217728: 'Packed',
};
@Injectable({ providedIn: 'root' })
export class ShelfMapping {
fromOrderItemListItemDTOArrayToShelfCustomerOrders(orders: OrderItemListItemDTO[]): ShelfCustomerOrders[] {
@@ -21,14 +53,10 @@ export class ShelfMapping {
-1;
if (orderFound) {
customerOrders.map(customerOrder => {
if (
customerOrder.firstName === order.firstName &&
customerOrder.lastName === order.lastName &&
customerOrder.gender === order.gender
) {
if (customerOrder.customerId === order.buyerNumber) {
return {
...customerOrder,
orders: this.getCustomerOrders(customerOrder.firstName, customerOrder.lastName, customerOrder.gender, orders),
orders: this.getCustomerOrders(customerOrder.customerId, orders),
};
} else {
return customerOrder;
@@ -36,24 +64,32 @@ export class ShelfMapping {
});
} else {
customerOrders.push(<ShelfCustomerOrders>{
customerId: Math.round(Date.now() + Math.random()),
customerId: order.buyerNumber,
firstName: order.firstName,
lastName: order.lastName,
gender: order.gender,
orders: this.getCustomerOrders(order.firstName, order.lastName, order.gender, orders),
orders: this.getCustomerOrders(order.buyerNumber, orders),
});
}
});
return customerOrders;
}
private getCustomerOrders(firstName: string, lastName: string, gender: Gender, orders: OrderItemListItemDTO[]): ShelfOrder[] {
private getCustomerOrders(customerId: string, orders: OrderItemListItemDTO[]): ShelfOrder[] {
const shelfOrders: ShelfOrder[] = [];
orders.forEach(or => {
if (or.firstName === firstName && or.lastName === lastName && or.gender === gender) {
if (or.buyerNumber === customerId) {
shelfOrders.push(<ShelfOrder>{
orderId: or.orderId,
processingStatus: or.processingStatus,
orderPId: or.orderPId,
shopName: or.shopName,
orderBranchId: or.orderBranchId,
orderNumber: or.orderNumber,
orderType: or.orderType,
orderDate: or.orderDate,
nextProcessingStatus: or.nextProcessingStatus,
orderItems: this.orderItems(or.orderId, orders),
});
}
@@ -70,15 +106,9 @@ export class ShelfMapping {
orderItems.push(<ShlefOrderItem>{
orderItemId: or.orderItemId,
orderItemSubsetId: or.orderItemSubsetId,
orderPId: or.orderPId,
orderItemPId: or.orderItemPId,
orderItemSubsetPId: or.orderItemSubsetPId,
shopName: or.shopName,
orderBranchId: or.orderBranchId,
orderNumber: or.orderNumber,
orderType: or.orderType,
processingStatus: or.processingStatus,
orderDate: or.orderDate,
product: or.product,
quantity: or.quantity,
overallQuantity: or.overallQuantity,
@@ -87,7 +117,6 @@ export class ShelfMapping {
organisation: or.organisation,
title: or.title,
specialComment: or.specialComment,
nextProcessingStatus: or.nextProcessingStatus,
ssc: or.ssc,
sscText: or.sscText,
supplier: or.supplier,

View File

@@ -1,9 +1,10 @@
import { Gender } from 'swagger/lib/oms/models/gender';
import { ShelfOrder } from './shelf-order.model';
export class ShelfCustomerOrders {
customerId?: number;
customerId?: string;
lastName?: string;
firstName?: string;
gender?: Gender;
orders?: ShelfCustomerOrders[];
orders?: ShelfOrder[];
}

View File

@@ -7,15 +7,8 @@ import { KeyValueDTOOfOrderItemProcessingStatusValueAndString } from 'swagger/li
export interface ShlefOrderItem {
orderItemId?: number;
orderItemSubsetId?: number;
orderPId?: string;
orderItemPId?: string;
orderItemSubsetPId?: string;
shopName?: string;
orderBranchId?: number;
orderNumber?: string;
orderType?: OrderType;
processingStatus?: OrderItemProcessingStatusValue;
orderDate?: string;
product?: ProductDTO;
quantity?: number;
overallQuantity?: number;
@@ -24,7 +17,6 @@ export interface ShlefOrderItem {
organisation?: string;
title?: string;
specialComment?: string;
nextProcessingStatus?: Array<KeyValueDTOOfOrderItemProcessingStatusValueAndString>;
ssc?: string;
sscText?: string;
supplier?: string;

View File

@@ -1,6 +1,18 @@
import { ShlefOrderItem } from './shelf-order-item.model';
import { OrderItemProcessingStatusValue } from 'swagger/lib/oms/models/order-item-processing-status-value';
// tslint:disable-next-line: max-line-length
import { KeyValueDTOOfOrderItemProcessingStatusValueAndString } from 'swagger/lib/oms/models/key-value-dtoof-order-item-processing-status-value-and-string';
import { OrderType } from 'swagger/lib/oms/models/order-type';
export interface ShelfOrder {
orderId?: number;
shopName?: string;
orderPId?: string;
orderBranchId?: number;
orderNumber?: string;
orderType?: OrderType;
orderDate?: string;
processingStatus?: OrderItemProcessingStatusValue;
nextProcessingStatus?: Array<KeyValueDTOOfOrderItemProcessingStatusValueAndString>;
orderItems?: ShlefOrderItem[];
}

View File

@@ -3,26 +3,33 @@ import { OrderService } from 'swagger';
import { Suchparameter } from 'swagger/lib/oms/models/suchparameter';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { ShelfCustomerOrders } from '../models/shelf-customer-orders.model';
import { ShelfMapping } from '../mappings/shelf.mapping';
import { OrderItemListItemDTO } from 'swagger/lib/oms/models/order-item-list-item-dto';
@Injectable({
providedIn: 'root',
})
export class CollectingShelfService {
constructor(private omsService: OrderService, private shelfMapping: ShelfMapping) {}
constructor(private omsService: OrderService) {}
searchShelf(input: string, branchNumber: string): Observable<ShelfCustomerOrders[]> {
searchShelf(
input: string,
branchNumber: string,
skip: number = 0,
take: number = 0
): Observable<{ result: OrderItemListItemDTO[]; hits: number }> {
const params = <Suchparameter>{
input: input,
branchNumber: branchNumber,
skip: skip,
take: take,
};
return this.omsService.OrderWarenausgabe(params).pipe(
map(response => {
if (response.error) {
throw new Error(response.message);
}
return this.shelfMapping.fromOrderItemListItemDTOArrayToShelfCustomerOrders(response.result);
return { result: response.result, hits: response.hits };
})
);
}
@@ -31,13 +38,14 @@ export class CollectingShelfService {
const params = <Suchparameter>{
input: input,
branchNumber: branchNumber,
hitsOnly: true,
};
return this.omsService.OrderWarenausgabe(params).pipe(
map(response => {
if (response.error) {
throw new Error(response.message);
}
return response.result ? response.result.length : 0;
return response.hits ? response.hits : 0;
})
);
}

View File

@@ -19,7 +19,7 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
public loading = true;
public results = false;
public firstLoad = true;
public allHits: number = 0;
public allHits = 0;
constructor(
private search: string,

View File

@@ -1,8 +1,8 @@
<div class="card-container">
<div class="card-container" *ngIf="order">
<div class="content-container">
<span class="customer-name">Recipient name</span>
<span class="customer-name">{{ order.firstName }} {{ order.lastName }}</span>
<div class="order-details">
<ng-container *ngFor="let item of customerOrders; let last = last">
<ng-container *ngFor="let item of order.orders; let last = last">
<app-shelf-order-item [order]="item"></app-shelf-order-item>
<hr *ngIf="!last" class="spacer" />
</ng-container>

View File

@@ -1,6 +1,7 @@
import { Component, OnInit, AfterViewInit, ViewChild, OnDestroy, ChangeDetectionStrategy } from '@angular/core';
import { Component, OnInit, AfterViewInit, ViewChild, OnDestroy, ChangeDetectionStrategy, Input } from '@angular/core';
import { Store } from '@ngxs/store';
import { Subject } from 'rxjs';
import { ShelfCustomerOrders } from 'apps/sales/src/app/core/models/shelf-customer-orders.model';
@Component({
selector: 'app-shelf-customer-order',
@@ -9,33 +10,15 @@ import { Subject } from 'rxjs';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShelfCustomerOrderComponent implements OnInit, OnDestroy {
@Input() order: ShelfCustomerOrders;
destroy$ = new Subject();
customerOrders = this.getFakeItems();
constructor(private store: Store) {}
ngOnInit() {}
ngOnInit() {
console.log(this.order);
}
ngOnDestroy() {
this.destroy$.next();
}
getFakeItems() {
return Array(2)
.fill(0)
.map((_x, i) => ({
orderId: 'id_' + i,
status: 'arrived',
items: Array(2)
.fill(0)
.map((_y, j) => ({
title: 'title',
coverUrl: '',
price: 123,
currency: 'EUR',
quantity: j,
payment: new Date(),
})),
}));
}
}

View File

@@ -1,9 +1,9 @@
<div class="order-container">
<div class="order-items">
<div class="item" *ngFor="let item of order.items">
<img [src]="item.coverUrl | bookImageUrl | async" alt="book" class="thumbnail" />
<div class="item" *ngFor="let item of order.orderItems">
<img [src]="item.product.ean | bookImageUrl | async" alt="book" class="thumbnail" />
<div class="overview">
<span class="title">{{ item.title }}</span>
<span class="title">{{ item.product.name }}</span>
<div class="labels">
<span class="label">Preis</span>
<span class="label">Menge</span>
@@ -12,23 +12,22 @@
<div class="data">
<span class="text">{{ item.price | bookPrice }} {{ item.currency }}</span>
<span class="text">{{ item.quantity }}x</span>
<span class="text">{{ item.payment | date: 'dd.MM.yyyy' }}</span>
<span class="text">{{ order.orderDate | date: 'dd.MM.yyyy' }}</span>
</div>
</div>
</div>
</div>
<div class="order-details">
<span class="order-id">{{ order.orderId }}</span>
<span class="order-id">{{ order.orderNumber }}</span>
<div class="order-status">
<div *ngIf="order.status == 'arrived'">
<div *ngIf="order.processingStatus === 128">
<lib-icon name="Check"></lib-icon>
<span>eingetroffen</span>
</div>
<div *ngIf="order.status == 'customer_cancel'">
<div *ngIf="order.processingStatus === 512">
<lib-icon name="Icon_Close_gray"></lib-icon>
<span>storniert Kunde</span>
</div>
<span>{{ order.processingStatus | orderStatus }}</span>
</div>
<app-button [primary]="true">Auswählen</app-button>
<!-- <app-button [primary]="true">Auswählen</app-button> -->
</div>
</div>

View File

@@ -1,6 +1,6 @@
.order-container {
display: flex;
flex-direction: row;
display: grid;
grid-template-columns: auto min-content;
}
.order-items {
margin-top: 30px;

View File

@@ -1,6 +1,7 @@
import { Component, OnInit, AfterViewInit, ViewChild, OnDestroy, ChangeDetectionStrategy, Input } from '@angular/core';
import { Store } from '@ngxs/store';
import { Subject } from 'rxjs';
import { ShelfOrder } from 'apps/sales/src/app/core/models/shelf-order.model';
@Component({
selector: 'app-shelf-order-item',
@@ -10,19 +11,7 @@ import { Subject } from 'rxjs';
})
export class ShelfOrderItemComponent implements OnInit, OnDestroy {
destroy$ = new Subject();
@Input() order: {
orderId: string;
status: string;
items: Array<{
title: string;
coverUrl: string;
price: number;
currency: string;
quantity: number;
payment: Date;
}>;
};
@Input() order: ShelfOrder;
constructor(private store: Store) {}

View File

@@ -0,0 +1,28 @@
<div class="card-container">
<div class="icon-container">
<div class="product-icon"></div>
</div>
<div class="content-container">
<div class="author align-left">
<span>author</span>
</div>
<div class="title-price">
<div class="title align-left">
<div>
<span>title </span>
</div>
</div>
<div class="price align-right">
<span>--</span>
<span class="currency"></span>
</div>
</div>
<div class="type-stock">
<div class="type align-left">
<div class="type-text align-left">
<span>type</span>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,168 @@
@import '../../../../../assets/scss/variables';
.card-container {
display: grid;
grid-template-columns: min-content auto;
grid-gap: 7vh;
background-color: white;
border-radius: 5px;
padding: 15px 20px;
margin-top: 10px;
height: 150px;
}
.recommanded {
background-color: $important-notification;
color: white;
}
.product-icon {
width: 69px;
height: 111px;
background-color: #e9edf9;
color: #e9edf9;
}
.icon-container {
width: 69px;
height: 111px;
text-align: center;
}
.content-container {
display: grid;
grid-template-columns: auto;
opacity: 1;
animation: load 1.5s linear infinite;
}
@keyframes load {
50% {
opacity: 0.3;
}
100% {
opacity: 1;
}
}
.author span {
font-size: 16px;
background-color: #e9edf9;
color: #e9edf9;
width: 400px;
}
.title-price {
display: grid;
grid-template-columns: auto min-content;
// grid-gap: 8vh;
margin-top: 10px;
}
.title span {
font-size: 22px;
font-weight: bold;
background-color: #e9edf9;
color: #e9edf9;
width: 500px;
display: inline-block;
}
.price span {
font-size: 20px;
font-weight: bold;
display: inline-block;
background-color: #e9edf9;
color: #e9edf9;
width: 50px;
}
.title-grid {
display: grid;
grid-template-columns: min-content auto;
grid-gap: 2vh;
}
.rec-icon-container {
padding-top: 4px;
}
.currency {
margin-left: 5px;
display: inline-block;
background-color: #e9edf9;
color: #e9edf9;
width: 20px;
}
.type-stock {
display: grid;
grid-template-columns: auto max-content;
margin-top: 10px;
}
.type {
display: grid;
grid-template-columns: min-content auto;
grid-gap: 2vh;
background-color: #e9edf9;
color: #e9edf9;
width: 350px;
}
.type-text {
font-size: 18px;
font-weight: bold;
height: 26px;
}
.available-stock {
display: grid;
grid-template-columns: auto auto auto auto;
grid-gap: 2vh;
}
.available-icon-container {
padding-top: 2px;
}
.stock-icon {
width: 15px;
}
.available-text span {
font-size: 18px;
font-weight: bold;
}
.stock span {
font-size: 18px;
font-weight: bold;
}
.publisher-order {
display: grid;
grid-template-columns: auto max-content;
margin-top: 10px;
}
.publisher-serial {
display: grid;
grid-template-columns: max-content min-content auto;
grid-gap: 2vh;
}
.publisher-serial span {
font-size: 16px;
}
.order span {
font-size: 16px;
color: #a7b9cb;
}
.type-icon-container {
padding-top: 3px;
}
.publisher {
max-width: 300px;
}

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderLoadingComponent } from './order-loading.component';
describe('OrderLoadingComponent', () => {
let component: OrderLoadingComponent;
let fixture: ComponentFixture<OrderLoadingComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ OrderLoadingComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OrderLoadingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-order-loading',
templateUrl: './order-loading.component.html',
styleUrls: ['./order-loading.component.scss'],
})
export class OrderLoadingComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@@ -1,6 +1,11 @@
<div class="result-container">
<cdk-virtual-scroll-viewport autosize class="viewport" #scroller>
<app-shelf-customer-order *cdkVirtualFor="let order of ds"></app-shelf-customer-order>
<div *cdkVirtualFor="let order of ds">
<app-shelf-customer-order *ngIf="order != null; else loadingComponent" [order]="order"></app-shelf-customer-order>
</div>
<ng-template #loadingComponent>
<app-order-loading></app-order-loading>
</ng-template>
</cdk-virtual-scroll-viewport>
<app-loading

View File

@@ -1,24 +1,46 @@
import { DataSource, CollectionViewer } from '@angular/cdk/collections';
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { Store } from '@ngxs/store';
import { take, takeUntil } from 'rxjs/operators';
import { take, takeUntil, debounceTime } from 'rxjs/operators';
import { SetShelfOrders } from 'apps/sales/src/app/core/store/actions/collecting-shelf.action';
import { ShelfCustomerOrders } from 'apps/sales/src/app/core/models/shelf-customer-orders.model';
import { ShelfSearch } from 'apps/sales/src/app/core/models/shelf-search.modal';
import { CollectingShelfService } from 'apps/sales/src/app/core/services/collecting-shelf.service';
import { OrderItemListItemDTO } from 'swagger/lib/oms/models/order-item-list-item-dto';
export class ShelfSearchDataSource extends DataSource<ShelfCustomerOrders | undefined> {
private pageSize = 5;
private fetchedPages = new Set<number>();
private cachedData = Array.from<ShelfCustomerOrders>({ length: 0 });
private dataStream = new BehaviorSubject<(ShelfCustomerOrders | undefined)[]>(this.cachedData);
destroy$ = new Subject();
public loading = true;
public results = false;
public allHits = 0;
public firstLoad = true;
constructor(private search: ShelfSearch, private store: Store, private collectingShelf: CollectingShelfService) {
super();
}
connect(collectionViewer: CollectionViewer): Observable<(ShelfCustomerOrders | undefined)[]> {
this.fetchAll();
collectionViewer.viewChange.pipe(takeUntil(this.destroy$)).subscribe(range => {
const startPage = this.getPageForIndex(range.start);
const endPage = this.getPageForIndex(range.end - 1);
for (let i = startPage; i <= endPage; i++) {
this.fetchPage(i);
}
});
this.dataStream
.pipe(
takeUntil(this.destroy$),
debounceTime(1000)
)
.subscribe(i => {
this.store.dispatch(new SetShelfOrders([...i]));
});
this.fetchPage(0);
return this.dataStream;
}
@@ -26,19 +48,45 @@ export class ShelfSearchDataSource extends DataSource<ShelfCustomerOrders | unde
this.destroy$.next();
}
private fetchAll() {
public getPageForIndex(index: number): number {
return Math.floor(index / this.pageSize);
}
private fetchPage(page: number): number {
if (page === 0) {
this.results = false;
}
if (this.fetchedPages.has(page)) {
return;
}
this.fetchedPages.add(page);
this.loading = true;
this.collectingShelf
.searchShelf(this.search.input, this.search.branchnumber)
.searchShelf(this.search.input, this.search.branchnumber, page * this.pageSize, this.pageSize)
.pipe(
takeUntil(this.destroy$),
take(1)
)
.subscribe(shelfCustomerOrders => {
this.cachedData = shelfCustomerOrders;
this.dataStream.next(this.cachedData);
this.store.dispatch(new SetShelfOrders(this.cachedData));
.subscribe(({ result, hits }: { result: OrderItemListItemDTO[]; hits: number }) => {
this.loading = false;
this.results = true;
console.log(this.cachedData);
if (page === 0) {
this.cachedData = Array.from<ShelfCustomerOrders>({ length: hits });
this.allHits = hits;
}
this.cachedData.splice(page * this.pageSize, this.pageSize, ...result);
this.dataStream.next(this.cachedData);
if (page === 0) {
// dispatch immediately on first page load
this.firstLoad = false;
this.store.dispatch(new SetShelfOrders(this.cachedData));
}
});
}
}

View File

@@ -23,6 +23,8 @@ import { ShelfSearchResultsComponent } from './pages/shelf-search-results/shelf-
import { ShelfSearchComponent } from './pages/shelf-search/shelf-search.component';
import { ShelfRoutingModule } from './shelf-routing.module';
import { ShelfOrderDetailsComponent } from './pages/shelf-order-details/shelf-order-details.component';
import { OrderStatusPipe } from '../../pipes/order-status.pipe';
import { OrderLoadingComponent } from './components/order-loading/order-loading.component';
@NgModule({
declarations: [
@@ -36,6 +38,8 @@ import { ShelfOrderDetailsComponent } from './pages/shelf-order-details/shelf-or
ShelfOrderDetailsComponent,
ShelfArticleDetailsComponent,
ShelfPartialCollectionModalComponent,
OrderStatusPipe,
OrderLoadingComponent,
],
exports: [],
imports: [

View File

@@ -0,0 +1,15 @@
import { Pipe, PipeTransform } from '@angular/core';
import { orderStatusMapper } from '../core/mappings/shelf.mapping';
@Pipe({
name: 'orderStatus',
})
export class OrderStatusPipe implements PipeTransform {
transform(input: number): string {
if (!input) {
return '';
}
return orderStatusMapper[input];
}
}