Merged PR 499: #1243 Warenausgabe Status in Trefferliste setzen

#1243 Warenausgabe Status in Trefferliste setzen
This commit is contained in:
Andreas Schickinger
2021-02-22 13:11:07 +00:00
committed by Lorenz Hilpert
11 changed files with 338 additions and 269 deletions

View File

@@ -1,19 +1,9 @@
<div class="tags" *ngIf="item.features">
<lib-icon
*ngIf="item.features?.prebooked && item.processingStatus !== 256"
name="tag_icon_preorder"
height="48px"
></lib-icon>
<lib-icon *ngIf="item.features?.prebooked && item.processingStatus !== 256" name="tag_icon_preorder" height="48px"></lib-icon>
</div>
<div class="feature-spacing" *ngIf="item.features?.prebooked"></div>
<div
class="isa-text-right isa-mb-9 isa-font-size-18"
*ngIf="item.compartmentCode && displayCompartmentCode"
>
<strong
>{{ item.compartmentCode
}}{{ item.compartmentInfo && '_' + item.compartmentInfo }}
</strong>
<div class="isa-text-right isa-mb-9 isa-font-size-18" *ngIf="item.compartmentCode && displayCompartmentCode">
<strong>{{ item.compartmentCode }}{{ item.compartmentInfo && '_' + item.compartmentInfo }} </strong>
</div>
<div class="isa-paid-marker isa-mt-9 isa-mb-9" *ngIf="item.features?.paid">
<lib-icon height="24px" width="24px" name="Check_green_circle"></lib-icon>
@@ -24,16 +14,9 @@
<img class="thumbnail" src="{{ cdnProdutctPictures }}/{{ item.product.ean }}.jpg?showDummy=true" alt="item.product.name" />
</div>
<div class="title">
<strong class="product-name">
{{ [item.product.contributors, item.product.name] | title }}</strong
>
<strong class="product-name"> {{ [item.product.contributors, item.product.name] | title }}</strong>
<strong class="processing-status">
<lib-icon
class="isa-mr-9"
*ngIf="item.processingStatus | processingStatus: 'icon'; let icon"
[name]="icon"
[ngClass]="icon"
>
<lib-icon class="isa-mr-9" *ngIf="item.processingStatus | processingStatus: 'icon'; let icon" [name]="icon" [ngClass]="icon">
</lib-icon>
{{ item.processingStatus | processingStatus }}
</strong>
@@ -43,57 +26,29 @@
</div>
<div class="details">
<div class="item-type">
<lib-icon
class="isa-mr-9"
name="Icon_{{ item.product.format }}"
></lib-icon>
<lib-icon class="isa-mr-9" name="Icon_{{ item.product.format }}"></lib-icon>
<strong class="item-type-text">{{ item.product.formatDetail }}</strong>
</div>
<ng-container [ngSwitch]="item.processingStatus">
<div class="order-date spec" *ngSwitchCase="256">
<span>Geändert</span>
<strong
>{{
item.processingStatusDate | date: 'dd.MM.yy | HH:mm'
}}
Uhr</strong
>
<strong>{{ item.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</strong>
</div>
<div class="order-date spec" *ngSwitchCase="512">
<span>Geändert</span>
<strong
>{{
item.processingStatusDate | date: 'dd.MM.yy | HH:mm'
}}
Uhr</strong
>
<strong>{{ item.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</strong>
</div>
<div class="order-date spec" *ngSwitchCase="1024">
<span>Geändert</span>
<strong
>{{
item.processingStatusDate | date: 'dd.MM.yy | HH:mm'
}}
Uhr</strong
>
<strong>{{ item.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</strong>
</div>
<div class="order-date spec" *ngSwitchCase="2048">
<span>Geändert</span>
<strong
>{{
item.processingStatusDate | date: 'dd.MM.yy | HH:mm'
}}
Uhr</strong
>
<strong>{{ item.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</strong>
</div>
<div class="order-date spec" *ngSwitchCase="4096">
<span>Geändert</span>
<strong
>{{
item.processingStatusDate | date: 'dd.MM.yy | HH:mm'
}}
Uhr</strong
>
<strong>{{ item.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</strong>
</div>
<div class="order-date spec" *ngSwitchDefault>
<span>Bestelldatum</span>
@@ -114,5 +69,10 @@
<span>Zielfiliale</span>
<strong>{{ item.targetBranch }}</strong>
</div>
<div class="selection" *ngIf="item.processingStatus === 128" [class.hide]="checkboxDisabled$ | async">
<input type="checkbox" [ngModel]="selected" />
<span class="checkmark" (click)="selectionChange()"></span>
</div>
</div>
</div>

View File

@@ -48,15 +48,74 @@
}
}
}
.details {
display: grid;
grid-template-columns: 1fr 340px;
grid-template-columns: 1fr 340px 80px;
grid-template-rows: auto auto auto;
grid-template-areas: 'item-type order-date' 'item-number quantity' 'price target-branch';
grid-area: details;
}
.selection {
grid-row: 1 / span 3;
display: flex;
align-items: center;
text-align: right;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
input {
opacity: 0;
cursor: pointer;
}
.checkmark {
position: absolute;
right: 15px;
height: 30px;
width: 30px;
background-color: #eee;
-webkit-box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.2);
-moz-box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.2);
box-shadow: inset 0px 0px 2px 0px rgba(0, 0, 0, 0.2);
border-radius: 50%;
}
&:hover input ~ .checkmark {
background-color: #ccc;
}
input:checked ~ .checkmark {
background-color: #1f466d;
}
.checkmark:after {
content: '';
position: absolute;
display: none;
}
input:checked ~ .checkmark:after {
display: block;
}
.checkmark:after {
left: 10px;
top: 5px;
width: 7px;
height: 13px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
}
.item-type {
grid-area: item-type;
position: relative;
@@ -115,3 +174,7 @@
.feature-spacing {
height: 40px;
}
.hide {
visibility: collapse;
}

View File

@@ -1,6 +1,8 @@
import { Component, OnInit, ChangeDetectionStrategy, Input, Inject } from '@angular/core';
import { Component, OnInit, ChangeDetectionStrategy, Input, Inject, Output, EventEmitter, OnDestroy } from '@angular/core';
import { OrderItemListItemDTO } from '@swagger/oms';
import { CDN_PRODUCT_PICTURES } from 'apps/sales/src/app/tokens';
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
@Component({
selector: 'app-search-result-group-item',
@@ -8,14 +10,51 @@ import { CDN_PRODUCT_PICTURES } from 'apps/sales/src/app/tokens';
styleUrls: ['search-result-group-item.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SearchResultGroupItemComponent implements OnInit {
export class SearchResultGroupItemComponent implements OnInit, OnDestroy {
@Input()
item: OrderItemListItemDTO;
@Input()
displayCompartmentCode = true;
@Input()
disableSelection: EventEmitter<OrderItemListItemDTO>;
@Input()
setSelection: EventEmitter<OrderItemListItemDTO>;
@Output()
itemSelected = new EventEmitter<{ item: OrderItemListItemDTO; checked: boolean }>();
setSelectionSubscribtion: Subscription;
selected: boolean;
checkboxDisabled$: Observable<boolean>;
constructor(@Inject(CDN_PRODUCT_PICTURES) public cdnProdutctPictures: string) {}
ngOnInit() {}
ngOnInit() {
this.setSelectionSubscribtion = this.setSelection.subscribe((item) => {
this.selected = this.item.orderItemSubsetId === item.orderItemSubsetId;
});
this.checkboxDisabled$ = this.disableSelection.pipe(
map((item) => {
if (!item) return false;
return this.item.orderId != item.orderId || this.item.processingStatus != item.processingStatus;
})
);
}
ngOnDestroy(): void {
if (this.setSelectionSubscribtion) {
this.setSelectionSubscribtion.unsubscribe();
}
}
selectionChange() {
this.selected = !this.selected;
this.itemSelected.emit({ item: this.item, checked: this.selected });
}
}

View File

@@ -1,40 +1,18 @@
<div class="isa-card">
<h3 class="heading">
{{ group.items[0].firstName }} {{ group.items[0].lastName }}
</h3>
<ng-container
*ngFor="
let byOrderNumber of group.items | groupBy: byOrderNumberFn;
let lastOrder = last
"
>
<ng-container
*ngFor="
let byProcessingStatus of byOrderNumber.items
| groupBy: byProcessingStatusFn;
let lastStatus = last
"
>
<ng-container
*ngFor="
let groupedBy of byProcessingStatus.items
| groupBy: byCompartmentCodeFn;
let lastCompartment = last
"
>
<h3 class="heading">{{ group.items[0].firstName }} {{ group.items[0].lastName }}</h3>
<ng-container *ngFor="let byOrderNumber of group.items | groupBy: byOrderNumberFn; let lastOrder = last">
<ng-container *ngFor="let byProcessingStatus of byOrderNumber.items | groupBy: byProcessingStatusFn; let lastStatus = last">
<ng-container *ngFor="let groupedBy of byProcessingStatus.items | groupBy: byCompartmentCodeFn; let lastCompartment = last">
<div class="isa-mb-12 isa-mt-12">
<app-search-result-group-item
*ngFor="
let item of groupedBy.items;
let firstItem = first;
let lastItem = last
"
*ngFor="let item of groupedBy.items; let firstItem = first; let lastItem = last"
[item]="item"
[class.group-item-bottom-space]="!lastItem"
(click)="selectOrderItemListItem.emit(item)"
[displayCompartmentCode]="
firstItem | showCompartmentCode: item:group.items
"
(click)="onItemClick({ e: $event, item: item })"
(itemSelected)="onItemSelected($event)"
[disableSelection]="disableSelection"
[setSelection]="setSelection"
[displayCompartmentCode]="firstItem | showCompartmentCode: item:group.items"
>
</app-search-result-group-item>
</div>

View File

@@ -1,11 +1,4 @@
import {
Component,
OnInit,
ChangeDetectionStrategy,
Input,
EventEmitter,
Output,
} from '@angular/core';
import { Component, OnInit, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core';
import { OrderItemListItemDTO } from '@swagger/oms';
import { Group } from 'apps/sales/src/app/utils';
import { ShelfNavigationService } from '../../shared/services';
@@ -23,6 +16,18 @@ export class SearchResultGroupComponent implements OnInit {
@Output()
selectOrderItemListItem = new EventEmitter<OrderItemListItemDTO>();
@Output()
orderListItemChecked = new EventEmitter<{ item: OrderItemListItemDTO; checked: boolean }>();
@Input()
disableSelection: EventEmitter<OrderItemListItemDTO>;
@Input()
setSelection: EventEmitter<OrderItemListItemDTO>;
@Output()
refreshCheckedState = new EventEmitter<void>();
byOrderNumberFn = (item: OrderItemListItemDTO) => item.orderNumber;
byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;
@@ -31,9 +36,21 @@ export class SearchResultGroupComponent implements OnInit {
constructor(private navigationFacade: ShelfNavigationService) {}
ngOnInit() {}
ngOnInit() {
this.refreshCheckedState.emit();
}
navigateToDetails(orderItem: OrderItemListItemDTO) {
this.navigationFacade.navigateToDetails(orderItem);
}
onItemClick({ e, item }) {
if (e.target.className !== 'checkmark') {
this.selectOrderItemListItem.emit(item);
}
}
onItemSelected({ item, checked }) {
this.orderListItemChecked.emit({ item, checked });
}
}

View File

@@ -6,9 +6,10 @@ import { SearchResultGroupItemComponent } from './search-result-group-item.compo
import { IconModule } from '@libs/ui';
import { PipesModule } from 'apps/sales/src/app/pipes/pipes.module';
import { ShelfPipesModule } from '../../pipes';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [CommonModule, IconModule, PipesModule, ShelfPipesModule],
imports: [CommonModule, FormsModule, IconModule, PipesModule, ShelfPipesModule],
exports: [SearchResultGroupComponent],
declarations: [SearchResultGroupComponent, SearchResultGroupItemComponent],
providers: [],

View File

@@ -1,23 +1,6 @@
import {
ChangeDetectionStrategy,
Component,
QueryList,
ViewChild,
ViewChildren,
} from '@angular/core';
import { ChangeDetectionStrategy, Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
map,
switchMap,
first,
filter,
flatMap,
distinctUntilChanged,
shareReplay,
withLatestFrom,
take,
tap,
} from 'rxjs/operators';
import { map, switchMap, first, filter, flatMap, distinctUntilChanged, shareReplay, withLatestFrom, take, tap } from 'rxjs/operators';
import {
OrderItemProcessingStatusValue,
OrderItemListItemDTO,
@@ -67,14 +50,10 @@ export class ShelfOrderDetailsComponent {
get processingStatusChangeData() {
return {
...{
...(this.compartmentInfoFromTag
? { compartmentInfo: this.compartmentInfoFromTag }
: {}),
...(this.compartmentInfoFromTag ? { compartmentInfo: this.compartmentInfoFromTag } : {}),
},
...{
...(this.quantityForPartialPickup
? { quantityForPartialPickup: this.quantityForPartialPickup }
: {}),
...(this.quantityForPartialPickup ? { quantityForPartialPickup: this.quantityForPartialPickup } : {}),
},
};
}
@@ -89,9 +68,7 @@ export class ShelfOrderDetailsComponent {
};
}
processingStatus$ = this.activatedRoute.params.pipe(
map((params) => params['processingStatus'])
);
processingStatus$ = this.activatedRoute.params.pipe(map((params) => params['processingStatus']));
partialPickup = false;
activeButton: HTMLButtonElement;
@@ -118,13 +95,9 @@ export class ShelfOrderDetailsComponent {
orderItems$ = this.getOrderItems$();
orderDetailsCard$ = this.orderDetailsService.orderDetailsCardFromOrderItems$(
this.orderItems$
);
orderDetailsCard$ = this.orderDetailsService.orderDetailsCardFromOrderItems$(this.orderItems$);
showPickUpAndPrintOption$ = this.orderDetailsService.showPickUpAndPrintOption$(
this.orderDetailsCard$
);
showPickUpAndPrintOption$ = this.orderDetailsService.showPickUpAndPrintOption$(this.orderDetailsCard$);
constructor(
private activatedRoute: ActivatedRoute,
@@ -165,36 +138,22 @@ export class ShelfOrderDetailsComponent {
const items = await this.orderItems$.pipe(first()).toPromise();
let results: ResponseArgsOfValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO[];
results = await this.orderDetailsService.setProcessingStatus(
items,
status,
this.processingStatusChangeData
);
results = await this.orderDetailsService.setProcessingStatus(items, status, this.processingStatusChangeData);
if (data.shouldPrint) {
await this.abholfachEtikettService.print(
this.abholfachEtikettService.orderSubsetIdsForWhichToPrintAbholfachEtikett(
items
),
this.abholfachEtikettService.orderSubsetIdsForWhichToPrintAbholfachEtikett(items),
this.printData
);
}
if (data.createShippingNote) {
const receipts = await this.shippingNoteService.create(
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(
items
),
this.printData
);
const existingReceipts: ReceiptDTO[] = items.reduce(
(acc, curr) => [...(acc || []), ...curr.receipts],
[]
);
await this.shippingNoteService.print(
[...existingReceipts, ...(receipts || [])],
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(items),
this.printData
);
const existingReceipts: ReceiptDTO[] = items.reduce((acc, curr) => [...(acc || []), ...curr.receipts], []);
await this.shippingNoteService.print([...existingReceipts, ...(receipts || [])], this.printData);
}
this.statusChangeInProgress$.next(false);
@@ -231,9 +190,7 @@ export class ShelfOrderDetailsComponent {
async navigateToDetails() {
const data = await race(
this.orderNumber$.pipe(map((orderNumber) => ({ orderNumber }))),
this.compartmentCode$.pipe(
map((compartmentCode) => ({ compartmentCode }))
)
this.compartmentCode$.pipe(map((compartmentCode) => ({ compartmentCode })))
)
.pipe(
withLatestFrom(this.processingStatus$),
@@ -265,9 +222,7 @@ export class ShelfOrderDetailsComponent {
})[]
) {
const totalQuantity = items.reduce((sum, item) => sum + item.quantity, 0);
const totalSelected = Array.from(
this.quantityForPartialPickup.values()
).reduce((sum, q) => sum + q, 0);
const totalSelected = Array.from(this.quantityForPartialPickup.values()).reduce((sum, q) => sum + q, 0);
const shouldNavigate = totalQuantity === totalSelected;
return shouldNavigate;
@@ -290,8 +245,7 @@ export class ShelfOrderDetailsComponent {
results: ResponseArgsOfValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO[]
) {
const firstItem = Array.isArray(items) && items[0];
const firstResult =
Array.isArray(results) && results[0] && results[0].result;
const firstResult = Array.isArray(results) && results[0] && results[0].result;
if (!firstItem || !firstResult) {
return;
@@ -311,10 +265,7 @@ export class ShelfOrderDetailsComponent {
);
}
async handleActionClick(
action: KeyValueDTOOfStringAndString,
target: HTMLButtonElement
) {
async handleActionClick(action: KeyValueDTOOfStringAndString, target: HTMLButtonElement) {
this.activeButton = target;
this.setState(action);
}
@@ -323,9 +274,7 @@ export class ShelfOrderDetailsComponent {
let navigate = true;
let items = await this.orderItems$.pipe(first()).toPromise();
let results: ResponseArgsOfValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO[];
let currentReceipts: ReceiptDTO[] = this.orderDetailsService.getReceipts(
items
);
let currentReceipts: ReceiptDTO[] = this.orderDetailsService.getReceipts(items);
let remainingItemsAfterPartialRemit: (OrderItemListItemDTO & {
receipts?: ReceiptDTO[];
})[] = [];
@@ -349,35 +298,24 @@ export class ShelfOrderDetailsComponent {
return false;
});
items = items.filter(({ orderItemId }) =>
this.selectedForPartialPickup.has(orderItemId)
);
items = items.filter(({ orderItemId }) => this.selectedForPartialPickup.has(orderItemId));
}
const actionsToTake = this.actionHandlerService.getActions(action);
for (const a of actionsToTake) {
try {
const {
results: newResults,
receipts: newReceipts,
} = await this.executeAction(a, items, currentReceipts);
const { results: newResults, receipts: newReceipts } = await this.executeAction(a, items, currentReceipts);
if (newResults) {
results = newResults;
}
if (Array.isArray(newReceipts)) {
currentReceipts = Array.from(
new Set([...(currentReceipts || []), ...(newReceipts || [])])
);
currentReceipts = Array.from(new Set([...(currentReceipts || []), ...(newReceipts || [])]));
}
} catch (err) {
console.error(
`Fehler beim Aktualisieren des Postens ${
err.message ? '(Meldung ' + err.message + ')' : ''
})`
);
console.error(`Fehler beim Aktualisieren des Postens ${err.message ? '(Meldung ' + err.message + ')' : ''})`);
return false;
}
}
@@ -419,9 +357,7 @@ export class ShelfOrderDetailsComponent {
let receipts: ReceiptDTO[];
if (this.actionHandlerService.shouldDetermineSupplier(command)) {
const updatedItems = await this.orderDetailsService.setDetermineSupplier(
items
);
const updatedItems = await this.orderDetailsService.setDetermineSupplier(items);
this.navigateAfterSupplierDetermined(updatedItems);
}
@@ -431,9 +367,7 @@ export class ShelfOrderDetailsComponent {
if (this.actionHandlerService.shouldCreateShippingNote(command)) {
receipts = await this.shippingNoteService.create(
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(
items
),
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(items),
this.printData
);
}
@@ -444,9 +378,7 @@ export class ShelfOrderDetailsComponent {
if (this.actionHandlerService.shouldPrintAbholfachetikett(command)) {
await this.abholfachEtikettService.print(
this.abholfachEtikettService.orderSubsetIdsForWhichToPrintAbholfachEtikett(
items
),
this.abholfachEtikettService.orderSubsetIdsForWhichToPrintAbholfachEtikett(items),
this.printData
);
}
@@ -460,38 +392,22 @@ export class ShelfOrderDetailsComponent {
receipts?: ReceiptDTO[];
})[]
) {
const targetStatus = this.actionHandlerService.getNewProcessingStatus(
command
);
const results = await this.orderDetailsService.setProcessingStatus(
items,
targetStatus,
this.processingStatusChangeData
);
const targetStatus = this.actionHandlerService.getNewProcessingStatus(command);
const results = await this.orderDetailsService.setProcessingStatus(items, targetStatus, this.processingStatusChangeData);
return results.filter((result) => !!result);
}
private getOrderItems$() {
return race(
this.orderNumber$.pipe(
switchMap((orderNumber) =>
this.detailsFacade.getOrderItemsByOrderNumber$(orderNumber)
)
),
this.compartmentCode$.pipe(
switchMap((compartmentCode) =>
this.detailsFacade.getOrderItemsByCompartmentCode$(compartmentCode)
)
)
this.orderNumber$.pipe(switchMap((orderNumber) => this.detailsFacade.getOrderItemsByOrderNumber$(orderNumber))),
this.compartmentCode$.pipe(switchMap((compartmentCode) => this.detailsFacade.getOrderItemsByCompartmentCode$(compartmentCode)))
).pipe(
flatMap((items) =>
this.processingStatus$.pipe(
map((processingStatus) => {
if (!!processingStatus) {
return items.filter(
(item) => item.processingStatus === +processingStatus
);
return items.filter((item) => item.processingStatus === +processingStatus);
}
return items;
})
@@ -506,14 +422,8 @@ export class ShelfOrderDetailsComponent {
this.quantityForPartialPickup.delete(orderItem.orderItemId);
}
private setItemForPartialPickUp(
orderItem: OrderItemListItemDTO,
quantity?: number
) {
private setItemForPartialPickUp(orderItem: OrderItemListItemDTO, quantity?: number) {
this.selectedForPartialPickup.set(orderItem.orderItemId, orderItem);
this.quantityForPartialPickup.set(
orderItem.orderItemId,
isNullOrUndefined(quantity) ? orderItem.quantity : quantity
);
this.quantityForPartialPickup.set(orderItem.orderItemId, isNullOrUndefined(quantity) ? orderItem.quantity : quantity);
}
}

View File

@@ -4,6 +4,10 @@
*ngFor="let group of grouped$ | async; let i = index"
[group]="group"
(selectOrderItemListItem)="navigateToDetails($event)"
(orderListItemChecked)="itemChecked($event)"
(refreshCheckedState)="refreshCheckedState($event)"
[disableSelection]="disableSelection"
[setSelection]="setSelection"
>
</app-search-result-group>
<app-loading
@@ -14,3 +18,26 @@
text="Inhalte werden geladen"
></app-loading>
</div>
<div class="actions" *ngIf="checkedItems.length > 0">
<button
*ngIf="pickUpAndPrintActionVisible"
class="isa-btn isa-btn-primary isa-btn-pill isa-btn-xl"
(click)="pickUpAndPrintClick()"
[disabled]="statusChangeInProgress$ | async"
>
<span>abgeholt und Lieferschein drucken</span>
<div class="spinner isa-btn-loader" *ngIf="statusChangeInProgress$ | async"></div>
</button>
<button
*ngIf="pickUpActionVisible"
class="isa-btn isa-btn-primary isa-btn-pill isa-btn-xl"
(click)="pickUpClick()"
[disabled]="statusChangeInProgress$ | async"
>
<span>abgeholt</span>
<div class="spinner isa-btn-loader" *ngIf="statusChangeInProgress$ | async"></div>
</button>
</div>
<app-printer-selection></app-printer-selection>

View File

@@ -1,3 +1,5 @@
@import 'variables';
cdk-virtual-scroll-viewport {
height: 100%;
width: 100%;
@@ -10,3 +12,16 @@ cdk-virtual-scroll-viewport {
.item {
height: 500px;
}
.actions {
width: 100%;
display: flex;
position: absolute;
justify-content: center;
bottom: 15px;
button {
margin-left: 2px;
margin-right: 2px;
}
}

View File

@@ -1,19 +1,6 @@
import {
Subject,
fromEvent,
combineLatest,
Observable,
BehaviorSubject,
} from 'rxjs';
import { Subject, fromEvent, combineLatest, Observable, BehaviorSubject } from 'rxjs';
import {
Component,
OnDestroy,
OnInit,
ViewChild,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
import { Component, OnDestroy, OnInit, ViewChild, ElementRef, ChangeDetectionStrategy, EventEmitter } from '@angular/core';
import { SearchStateFacade } from 'apps/sales/src/app/store/customer';
import { first, takeUntil, map, withLatestFrom } from 'rxjs/operators';
import { Group, groupBy } from 'apps/sales/src/app/utils';
@@ -22,6 +9,8 @@ import { ShelfNavigationService } from '../../shared/services';
import { Select } from '@ngxs/store';
import { ProcessSelectors } from 'apps/sales/src/app/core/store/selectors/process.selectors';
import { ORDER_DETAILS_PREFIX } from './order-details-prefix';
import { ShelfOrderDetailsService, ShelfShippingNoteService } from '../../services';
import { PrinterSelectionComponent } from 'apps/sales/src/app/components/printer-selection/printer-selection.component';
@Component({
selector: 'app-shelf-search-results',
@@ -33,9 +22,13 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
@ViewChild('scroll', { static: true })
scrollContainer: ElementRef;
@Select(ProcessSelectors.getCurrentProcessId) currentProcessId$: Observable<
number
>;
@Select(ProcessSelectors.getCurrentProcessId) currentProcessId$: Observable<number>;
@ViewChild(PrinterSelectionComponent, {
read: PrinterSelectionComponent,
static: true,
})
printerSelectionComponent: PrinterSelectionComponent;
destroy$ = new Subject();
isFetching$ = new BehaviorSubject<boolean>(false);
@@ -43,12 +36,42 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
grouped$: Observable<Group<string, OrderItemListItemDTO>[]>;
fetching$: Observable<boolean>;
statusChangeInProgress$ = new BehaviorSubject<boolean>(false);
scrollStorageKey = ORDER_DETAILS_PREFIX;
disableSelection = new EventEmitter<OrderItemListItemDTO>();
setSelection = new EventEmitter<OrderItemListItemDTO>();
checkedItems = new Array<OrderItemListItemDTO>();
get pickUpActionVisible(): boolean {
if (this.checkedItems.length <= 0) return false;
return this.checkedItems.every((item) => item.processingStatus === 128 && (!item.features || item.features['paid'] !== 'Bezahlt'));
}
get pickUpAndPrintActionVisible(): boolean {
if (this.checkedItems.length <= 0) return false;
return this.checkedItems.every((item) => item.processingStatus === 128 && item.features && item.features['paid'] === 'Bezahlt');
}
get printData(): {
printerComponent: PrinterSelectionComponent;
statusObs$?: BehaviorSubject<boolean>;
} {
return {
printerComponent: this.printerSelectionComponent,
statusObs$: this.statusChangeInProgress$,
};
}
constructor(
private searchStateFacade: SearchStateFacade,
private shelfNavigationService: ShelfNavigationService
private shelfNavigationService: ShelfNavigationService,
private orderDetailsService: ShelfOrderDetailsService,
private shippingNoteService: ShelfShippingNoteService
) {}
ngOnInit() {
@@ -57,6 +80,15 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
this.initFetch();
}
refreshCheckedState() {
setTimeout(() => {
this.checkedItems.forEach((i) => {
this.setSelection.emit(i);
this.disableSelection.emit(i);
});
}, 1);
}
initFetch() {
if (this.isFromSearchPage()) {
this.fetch();
@@ -66,11 +98,7 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
}
initFacade() {
this.grouped$ = this.searchStateFacade.result$.pipe(
map((results) =>
groupBy(results, (item) => (item ? item.buyerNumber : ''))
)
);
this.grouped$ = this.searchStateFacade.result$.pipe(map((results) => groupBy(results, (item) => (item ? item.buyerNumber : ''))));
this.fetching$ = this.searchStateFacade.fetching$;
}
@@ -87,30 +115,17 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
reachedBottom() {
const scrollContainer: HTMLElement = this.scrollContainer.nativeElement;
return (
scrollContainer.scrollHeight -
(scrollContainer.scrollTop + scrollContainer.clientHeight) -
100 <=
0
);
return scrollContainer.scrollHeight - (scrollContainer.scrollTop + scrollContainer.clientHeight) - 100 <= 0;
}
async fetch(force = false, isInitialFetch = false) {
const [hits, result, fetching] = await combineLatest([
this.searchStateFacade.hits$,
this.searchStateFacade.result$,
this.fetching$,
])
const [hits, result, fetching] = await combineLatest([this.searchStateFacade.hits$, this.searchStateFacade.result$, this.fetching$])
.pipe(first())
.toPromise();
if (isInitialFetch) {
await this.searchStateFacade.fetchResult({ isNewSearch: true });
} else if (
(force && this.resultListNotFullyLoaded(hits, result.length || 0)) ||
!hits ||
(!result.length && !fetching)
) {
} else if ((force && this.resultListNotFullyLoaded(hits, result.length || 0)) || !hits || (!result.length && !fetching)) {
await this.searchStateFacade.fetchResult({});
}
}
@@ -119,14 +134,57 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
this.shelfNavigationService.navigateToDetails(orderItemListItem);
}
itemChecked({ item, checked }) {
if (checked) {
this.checkedItems.push(item);
} else {
let index = this.checkedItems.indexOf(item);
if (index > -1) {
this.checkedItems.splice(index, 1);
}
}
if (this.checkedItems.length === 0) {
this.disableSelection.emit(undefined);
} else {
this.disableSelection.emit(item);
}
}
async pickUpClick() {
if (this.checkedItems.length <= 0) return;
this.statusChangeInProgress$.next(true);
await this.orderDetailsService.setProcessingStatus(this.checkedItems, 256, {});
await this.searchStateFacade.reloadResults();
this.statusChangeInProgress$.next(false);
this.checkedItems.splice(0, this.checkedItems.length);
}
async pickUpAndPrintClick() {
if (this.checkedItems.length <= 0) return;
this.statusChangeInProgress$.next(true);
await this.orderDetailsService.setProcessingStatus(this.checkedItems, 256, {});
let receipts = await this.shippingNoteService.create(
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(this.checkedItems),
this.printData
);
if (await this.shippingNoteService.print(receipts, this.printData)) {
await this.searchStateFacade.reloadResults();
}
this.statusChangeInProgress$.next(false);
this.checkedItems.splice(0, this.checkedItems.length);
}
ngOnDestroy() {
this.destroy$.next();
}
private resultListNotFullyLoaded(
hits: number,
numberOfFetchedResults: number
): boolean {
private resultListNotFullyLoaded(hits: number, numberOfFetchedResults: number): boolean {
return numberOfFetchedResults < hits;
}

View File

@@ -4,9 +4,10 @@ import { CommonModule } from '@angular/common';
import { LoadingModule } from '@libs/ui';
import { SearchResultGroupModule } from '../../components/search-result-group';
import { SharedModule } from 'apps/sales/src/app/shared/shared.module';
import { PrinterSelectionModule } from 'apps/sales/src/app/components/printer-selection/printer-selection.module';
@NgModule({
imports: [CommonModule, LoadingModule, SearchResultGroupModule, SharedModule],
imports: [CommonModule, LoadingModule, SearchResultGroupModule, SharedModule, PrinterSelectionModule],
exports: [ShelfSearchResultsComponent],
declarations: [ShelfSearchResultsComponent],
providers: [],