Merge branch 'release/1.4' into develop

This commit is contained in:
Lorenz Hilpert
2021-07-20 18:00:18 +02:00
14 changed files with 131 additions and 88 deletions

View File

@@ -53,7 +53,9 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
readonly recommendations$ = this.item$.pipe(
filter((item) => !!item),
switchMap((item) => this.domainCatalogService.getRecommendations({ digId: item.ids['dig'] }).pipe(map((res) => res.result)))
switchMap((item) =>
item.ids?.dig ? this.domainCatalogService.getRecommendations({ digId: item.ids['dig'] }).pipe(map((res) => res.result)) : of([])
)
);
//#region Lesepunkte

View File

@@ -8,18 +8,24 @@
Artikel
</span>
<ui-slider [scrollDistance]="210">
<a
class="article"
*ngFor="let recommendation of store.recommendations$ | async"
[routerLink]="['/product', 'details', 'ean', recommendation.product.ean]"
(click)="close.emit()"
>
<img [src]="recommendation.images[0]?.url" alt="product-image" />
<ng-container *ngIf="store.recommendations$ | async; let recommendations">
<span *ngIf="recommendations.length === 0" class="empty-message">
Keine Empfehlungen verfügbar
</span>
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
</a>
</ui-slider>
<ui-slider *ngIf="recommendations.length > 0" [scrollDistance]="210">
<a
class="article"
*ngFor="let recommendation of store.recommendations$ | async"
[routerLink]="['/product', 'details', 'ean', recommendation.product.ean]"
(click)="close.emit()"
>
<img [src]="recommendation.images[0]?.url" alt="product-image" />
<span class="format">{{ recommendation.product?.formatDetail }}</span>
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
</a>
</ui-slider>
</ng-container>
</div>
</ng-container>

View File

@@ -26,6 +26,10 @@ p {
}
}
.empty-message {
@apply flex justify-center text-cta-l mt-12;
}
.article {
@apply flex flex-col mr-7 mt-4 no-underline text-black;

View File

@@ -231,11 +231,11 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
this.router.navigate(['/customer', response.result.id]);
}
} catch (error) {
if (error?.error?.invalidProperties?.Email) {
if (error?.error?.invalidProperties?.Email || error?.error?.invalidProperties?.email) {
this.addInvalidDomain(this.control.value.communicationDetails?.email);
this.enableControl();
} else {
this.setValidationError(error.error?.invalidProperties, this.control);
this.setValidationError(error?.error?.invalidProperties, this.control);
}
}

View File

@@ -55,21 +55,17 @@ export class BookCardComponent implements OnInit {
path: '/dashboard',
};
const path = '/product/details/' + ean;
const queryParams = { type: 'ean' };
const path = '/product/details/ean/' + ean;
const newBread: Breadcrumb = {
name: name.substring(0, 12) + (name.length > 12 ? '...' : ''),
path,
queryParams,
};
this.store.dispatch(new PopBreadcrumbsBeforeCurrent('product'));
this.store.dispatch(new AddBreadcrumb(dashboardBread, 'product'));
this.store.dispatch(new AddBreadcrumb(newBread, 'product'));
this.store.dispatch(new ChangeCurrentRoute(path, false, queryParams));
this.router.navigate([path], {
queryParams,
});
this.store.dispatch(new ChangeCurrentRoute(path, false));
this.router.navigate([path]);
}
createProcess() {

View File

@@ -29,10 +29,8 @@
>
</app-remission-list-card-started>
</ng-container>
<ng-template #loadingComponent>
<app-remission-list-card-loading></app-remission-list-card-loading>
</ng-template>
</ng-container>
<app-remission-list-card-loading #loadingComponent *ngIf="!(isLoading$ | async) && !isFullListLoaded"></app-remission-list-card-loading>
<ng-template #spinner>
<div class="spinner"></div>
</ng-template>

View File

@@ -48,7 +48,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
scrollDistance = 1;
scrollUpDistance = 2;
page = 0;
pageSize = 10;
pageSize = 20;
totalHits = 0;
isItRefetch = false;
isFullLoad = false;
@@ -98,7 +98,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
subscribeToFetchLastProducts() {
this.fetchLastProducts$.pipe(takeUntil(this.destroy$)).subscribe((_) => {
// Increase number of takes to show all residual products (currently: 10)
// Increase number of takes to show all residual products (currently: 20)
const takeParam = this.pageSize;
const skipParam = Math.max(0, this.totalHits - takeParam);

View File

@@ -349,15 +349,15 @@ export class RemissionProductListComponent implements OnInit, OnDestroy, AfterVi
}
subscribeToIntersectionObservers() {
combineLatest(
combineLatest([
this.isNumberOfHitsVisible$,
this.isShippingDocumentVisible$,
this.remissionProductsData$.pipe(
map((data) => data.hits),
distinctUntilChanged()
),
this.isLoading$
)
this.isLoading$,
])
.pipe(
map(([isNumberOfHitsVisible, isShippingDocumentVisible, hits, isLoading]) => {
if (!hits || hits === -1 || !!isLoading) {

View File

@@ -12,10 +12,10 @@
<app-ui-text-input formControlName="quantity" label="Menge" suffix="x"></app-ui-text-input>
<hr class="isa-content-spacer" />
<app-ui-text-input formControlName="price" label="Preis" suffix="EUR"></app-ui-text-input>
<app-ui-text-input formControlName="price" label="Preis *" suffix="EUR"></app-ui-text-input>
<hr class="isa-content-spacer" />
<app-ui-text-input formControlName="ean" label="ISBN/EAN"></app-ui-text-input>
<app-ui-text-input formControlName="ean" label="ISBN/EAN *"></app-ui-text-input>
<hr class="isa-content-spacer" />
<app-ui-text-input formControlName="targetBranch" label="Zielfiliale"></app-ui-text-input>
@@ -24,7 +24,7 @@
<app-ui-text-input formControlName="supplier" label="Lieferant"></app-ui-text-input>
<hr class="isa-content-spacer" />
<app-ui-text-input formControlName="ssc" [suffix]="sscText" label="Meldenummer"></app-ui-text-input>
<app-ui-text-input formControlName="ssc" [suffix]="sscText" label="Meldenummer *"></app-ui-text-input>
<hr class="isa-content-spacer" />
<app-ui-select-input

View File

@@ -1,6 +1,7 @@
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 { isEqual } from 'lodash';
import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -45,12 +46,17 @@ export class SearchResultGroupItemComponent implements OnInit, OnDestroy {
return (
this.item.processingStatus !== item.processingStatus ||
this.item.targetBranchId !== item.targetBranchId ||
this.item.paymentStatus != item.paymentStatus
this.item.paymentStatus != item.paymentStatus ||
!isEqual(this.getQuickActions(this.item), this.getQuickActions(item))
);
})
);
}
getQuickActions(item: OrderItemListItemDTO) {
return item?.actions?.filter((a) => a.enabled == undefined && a.command !== 'FETCHED_PARTIAL') || [];
}
ngOnDestroy(): void {
if (this.setSelectionSubscribtion) {
this.setSelectionSubscribtion.unsubscribe();

View File

@@ -19,23 +19,14 @@
></app-loading>
</div>
<div class="actions" *ngIf="checkedItems.length > 0">
<div class="actions" *ngIf="quickActions.length">
<button
*ngIf="pickUpAndPrintActionVisible"
class="isa-btn isa-btn-primary isa-btn-pill isa-btn-xl"
(click)="pickUpAndPrintClick()"
(click)="handleAction(action)"
[disabled]="statusChangeInProgress$ | async"
*ngFor="let action of quickActions"
>
<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>
<span>{{ action.label }}</span>
<div class="spinner isa-btn-loader" *ngIf="statusChangeInProgress$ | async"></div>
</button>
</div>

View File

@@ -4,13 +4,19 @@ import { Component, OnDestroy, OnInit, ViewChild, ElementRef, ChangeDetectionStr
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';
import { OrderItemListItemDTO } from '@swagger/oms';
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO, ReceiptDTO } from '@swagger/oms';
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 {
ShelfAbholfachEtikettService,
ShelfActionHandlerService,
ShelfOrderDetailsService,
ShelfShippingNoteService,
} from '../../services';
import { PrinterSelectionComponent } from 'apps/sales/src/app/components/printer-selection/printer-selection.component';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
@Component({
selector: 'app-shelf-search-results',
@@ -67,11 +73,18 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
};
}
get quickActions() {
return this.getQuickActions(this.checkedItems?.find((f) => !!f));
}
constructor(
private searchStateFacade: SearchStateFacade,
private shelfNavigationService: ShelfNavigationService,
private orderDetailsService: ShelfOrderDetailsService,
private shippingNoteService: ShelfShippingNoteService
private shippingNoteService: ShelfShippingNoteService,
private actionHandlerService: ShelfActionHandlerService,
private abholfachEtikettService: ShelfAbholfachEtikettService,
private modal: UiModalService
) {}
ngOnInit() {
@@ -80,6 +93,10 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
this.initFetch();
}
getQuickActions(item: OrderItemListItemDTO) {
return item?.actions?.filter((a) => a.enabled == undefined && a.command !== 'FETCHED_PARTIAL') || [];
}
refreshCheckedState() {
setTimeout(() => {
this.checkedItems.forEach((i) => {
@@ -151,35 +168,6 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
}
}
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();
}
@@ -191,4 +179,55 @@ export class ShelfSearchResultsComponent implements OnInit, OnDestroy {
private isFromSearchPage(): boolean {
return !!window && !!window.history && !!window.history.state['fromSearch'];
}
async handleAction(action: KeyValueDTOOfStringAndString) {
this.statusChangeInProgress$.next(true);
try {
const groupedByOrderId = groupBy(this.checkedItems, (i) => i.orderId);
for (const order of groupedByOrderId) {
let receipts: ReceiptDTO[];
for (const command of this.actionHandlerService.getActions(action)) {
if (this.actionHandlerService.shouldDetermineSupplier(command)) {
await this.orderDetailsService.setDetermineSupplier(order.items);
}
if (this.actionHandlerService.getActionIsStatusChange(command)) {
const targetStatus = this.actionHandlerService.getNewProcessingStatus(command);
await this.orderDetailsService.setProcessingStatus(order.items, targetStatus, {});
}
if (this.actionHandlerService.shouldCreateShippingNote(command)) {
receipts = await this.shippingNoteService.create(
this.shippingNoteService.getSubsetIdsForWhichToCreateShippingNotes(order.items),
this.printData
);
}
if (this.actionHandlerService.shouldPrintShippingNote(command) && receipts?.length) {
await this.shippingNoteService.print(receipts, this.printData);
}
if (this.actionHandlerService.shouldPrintAbholfachetikett(command)) {
await this.abholfachEtikettService.print(
this.abholfachEtikettService.orderSubsetIdsForWhichToPrintAbholfachEtikett(order.items),
this.printData
);
}
}
}
await this.searchStateFacade.reloadResults();
} catch (error) {
this.modal.open({
content: UiMessageModalComponent,
data: { message: 'Fehler beim ändern des Status.' },
});
}
this.statusChangeInProgress$.next(false);
this.checkedItems.splice(0, this.checkedItems.length);
}
}

View File

@@ -11,13 +11,13 @@ export class ShelfActionHandlerService {
}
public getActionIsStatusChange(command: string) {
const match = actionCommands[command];
return typeof match === 'number';
const commands = this.getActions({ command });
return commands.map((c) => actionCommands[c]).some((f) => typeof f === 'number');
}
public getNewProcessingStatus(command: string): number {
return actionCommands[command];
const commands = this.getActions({ command });
return commands.map((c) => actionCommands[c]).find((f) => typeof f === 'number');
}
public isPartialPickup(action: KeyValueDTOOfStringAndString): boolean {
@@ -37,15 +37,15 @@ export class ShelfActionHandlerService {
}
public shouldCreateShippingNote(command: string): boolean {
return command === additionalCommands.CREATE_SHIPPINGNOTE;
return command.includes(additionalCommands.CREATE_SHIPPINGNOTE);
}
public shouldDetermineSupplier(command: string): boolean {
return command === additionalCommands.ORDER_AT_SUPPLIER;
return command.includes(additionalCommands.ORDER_AT_SUPPLIER);
}
public shouldPrintShippingNote(command: string): boolean {
return command === additionalCommands.PRINT_SHIPPINGNOTE;
return command.includes(additionalCommands.PRINT_SHIPPINGNOTE);
}
public shouldPrintAbholfachetikett(command: string): boolean {

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { FormBuilder, FormGroup, Validators, FormArray, AbstractControl, ValidationErrors, FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { FormBuilder, FormGroup, Validators, FormArray, AbstractControl, ValidationErrors, FormControl, ValidatorFn } from '@angular/forms';
import { Observable, of } from 'rxjs';
import {
OrderItemListItemDTO,
VATDTO,
@@ -205,6 +205,7 @@ export class ShelfEditFormService {
value: orderItem.product.ean,
disabled: false,
},
[Validators.required],
],
supplier: [{ value: orderItem.supplier, disabled: true }],
title: [{ value: orderItem.product.name, disabled: true }],