mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merge branch 'develop' into prettier-and-vscode-extensions
This commit is contained in:
86
angular.json
86
angular.json
@@ -27,10 +27,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/ui/tsconfig.lib.json",
|
||||
"libs/ui/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["libs/ui/tsconfig.lib.json", "libs/ui/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -89,25 +86,7 @@
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"development": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
}
|
||||
"development": {}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
@@ -148,20 +127,13 @@
|
||||
"includePaths": ["apps/sales/src/scss"]
|
||||
},
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"apps/sales/src/favicon.ico",
|
||||
"apps/sales/src/assets",
|
||||
"apps/sales/src/manifest.webmanifest"
|
||||
]
|
||||
"assets": ["apps/sales/src/favicon.ico", "apps/sales/src/assets", "apps/sales/src/manifest.webmanifest"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/sales/tsconfig.app.json",
|
||||
"apps/sales/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/sales/tsconfig.app.json", "apps/sales/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -220,10 +192,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/sso/tsconfig.lib.json",
|
||||
"libs/sso/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["libs/sso/tsconfig.lib.json", "libs/sso/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -253,10 +222,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/availability/tsconfig.lib.json",
|
||||
"apps/swagger/availability/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/availability/tsconfig.lib.json", "apps/swagger/availability/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -286,10 +252,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/checkout/tsconfig.lib.json",
|
||||
"apps/swagger/checkout/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/checkout/tsconfig.lib.json", "apps/swagger/checkout/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -319,10 +282,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/crm/tsconfig.lib.json",
|
||||
"apps/swagger/crm/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/crm/tsconfig.lib.json", "apps/swagger/crm/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -352,10 +312,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/isa/tsconfig.lib.json",
|
||||
"apps/swagger/isa/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/isa/tsconfig.lib.json", "apps/swagger/isa/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -385,10 +342,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/oms/tsconfig.lib.json",
|
||||
"apps/swagger/oms/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/oms/tsconfig.lib.json", "apps/swagger/oms/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -418,10 +372,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/print/tsconfig.lib.json",
|
||||
"apps/swagger/print/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/print/tsconfig.lib.json", "apps/swagger/print/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -451,10 +402,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/cat/tsconfig.lib.json",
|
||||
"apps/swagger/cat/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/cat/tsconfig.lib.json", "apps/swagger/cat/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -484,10 +432,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/swagger/eis/tsconfig.lib.json",
|
||||
"apps/swagger/eis/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/swagger/eis/tsconfig.lib.json", "apps/swagger/eis/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
@@ -517,10 +462,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/native-container/tsconfig.lib.json",
|
||||
"apps/native-container/tsconfig.spec.json"
|
||||
],
|
||||
"tsConfig": ["apps/native-container/tsconfig.lib.json", "apps/native-container/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,10 @@ import { rootReducer } from './store/root.reducer';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { SearchEffects } from './store/customer';
|
||||
import { HistoryEffects } from '@shelf-store/history';
|
||||
import { DetailsEffects } from '@shelf-store/details';
|
||||
|
||||
// TODO: In Service Speichern
|
||||
export function storeInLocalStorage(
|
||||
reducer: ActionReducer<any>
|
||||
): ActionReducer<any> {
|
||||
export function storeInLocalStorage(reducer: ActionReducer<any>): ActionReducer<any> {
|
||||
const lsKey = 'ISA_NGRX_STATE';
|
||||
|
||||
return function (state, action) {
|
||||
@@ -28,14 +27,12 @@ export function storeInLocalStorage(
|
||||
};
|
||||
}
|
||||
|
||||
export const metaReducers: MetaReducer<RootState>[] = !environment.production
|
||||
? [storeFreeze, storeInLocalStorage]
|
||||
: [storeInLocalStorage];
|
||||
export const metaReducers: MetaReducer<RootState>[] = !environment.production ? [storeFreeze, storeInLocalStorage] : [storeInLocalStorage];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
StoreModule.forRoot(rootReducer, { metaReducers }),
|
||||
EffectsModule.forRoot([SearchEffects, HistoryEffects]),
|
||||
EffectsModule.forRoot([SearchEffects, HistoryEffects, DetailsEffects]),
|
||||
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Store' }),
|
||||
],
|
||||
})
|
||||
|
||||
@@ -55,6 +55,7 @@ import { AppStoreModule } from './app-store.module';
|
||||
import { ModalDialogueModule } from './core/overlay/component';
|
||||
import { AppModalModule } from './app-modal.module';
|
||||
import { OverlaysModule } from './core/overlay/overlays.module';
|
||||
import { DateAdapter, NativeDateAdapter } from '@isa-ui/core/date';
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
|
||||
@@ -158,6 +159,10 @@ export function remissionModuleOptionsFactory(config: AppConfiguration): Remissi
|
||||
provide: SsoInterface,
|
||||
useClass: SsoConfigurationService,
|
||||
},
|
||||
{
|
||||
provide: DateAdapter,
|
||||
useClass: NativeDateAdapter,
|
||||
},
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
||||
@@ -6,13 +6,16 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
AfterViewInit
|
||||
AfterViewInit,
|
||||
} from '@angular/core';
|
||||
import { Subject, Observable } from 'rxjs';
|
||||
import { Breadcrumb } from '../../core/models/breadcrumb.model';
|
||||
import { Process } from '../../core/models/process.model';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { ChangeCurrentRoute, ResetProcessProductFilters } from '../../core/store/actions/process.actions';
|
||||
import {
|
||||
ChangeCurrentRoute,
|
||||
ResetProcessProductFilters,
|
||||
} from '../../core/store/actions/process.actions';
|
||||
import { Router } from '@angular/router';
|
||||
import { takeUntil, switchMap, distinctUntilChanged } from 'rxjs/operators';
|
||||
import { SharedSelectors } from '../../core/store/selectors/shared.selectors';
|
||||
@@ -21,8 +24,6 @@ import { AppService } from '../../core/services/app.service';
|
||||
import { ModuleSwitcher } from '../../core/models/app-switcher.enum';
|
||||
import { BranchProcess } from '../../core/models/branch-process.model';
|
||||
import { SetBranchProcessCurrentPath } from '../../core/store/actions/branch-process.actions';
|
||||
import { SetRemissionStarted, ResetRemissionShippingDocument } from '../../core/store/actions/remission.actions';
|
||||
import { RemissionSelectors } from '../../core/store/selectors/remission.selectors';
|
||||
import { AppState } from '../../core/store/state/app.state';
|
||||
import { FILIALE_LANDING_PAGE } from '../../core/utils/app.constants';
|
||||
|
||||
@@ -30,12 +31,17 @@ import { FILIALE_LANDING_PAGE } from '../../core/utils/app.constants';
|
||||
selector: 'app-breadcrumbs',
|
||||
templateUrl: './breadcrumbs.component.html',
|
||||
styleUrls: ['./breadcrumbs.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
@ViewChild('container', { read: ElementRef, static: false }) public container: ElementRef<any>;
|
||||
@Select(SharedSelectors.getBreadcrumbs) breadcrumbs$: Observable<Breadcrumb[]>;
|
||||
@Select(SharedSelectors.getCurrentProcess) currentProcess$: Observable<Process>;
|
||||
@ViewChild('container', { read: ElementRef, static: false })
|
||||
public container: ElementRef<any>;
|
||||
@Select(SharedSelectors.getBreadcrumbs) breadcrumbs$: Observable<
|
||||
Breadcrumb[]
|
||||
>;
|
||||
@Select(SharedSelectors.getCurrentProcess) currentProcess$: Observable<
|
||||
Process
|
||||
>;
|
||||
module: ModuleSwitcher;
|
||||
destroy$ = new Subject();
|
||||
breadcrumbs: Breadcrumb[] = [];
|
||||
@@ -65,7 +71,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
get backArrow() {
|
||||
return this.router.url.substring(0, 16) === '/product/details' && this.breadsCount > 1;
|
||||
return (
|
||||
this.router.url.substring(0, 16) === '/product/details' &&
|
||||
this.breadsCount > 1
|
||||
);
|
||||
}
|
||||
|
||||
get selectedBreadCrumbIndex() {
|
||||
@@ -78,7 +87,7 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
const endOfRouteWithOutParams = this.currentRoute.indexOf('?');
|
||||
route = this.currentRoute.substring(0, endOfRouteWithOutParams);
|
||||
}
|
||||
return this.breadcrumbs.findIndex(t => t && t.path.indexOf(route) >= 0);
|
||||
return this.breadcrumbs.findIndex((t) => t && t.path.indexOf(route) >= 0);
|
||||
}
|
||||
|
||||
get lowerMargin() {
|
||||
@@ -94,7 +103,12 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
return false;
|
||||
}
|
||||
|
||||
constructor(private store: Store, private router: Router, private cdrf: ChangeDetectorRef, private appService: AppService) { }
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private cdrf: ChangeDetectorRef,
|
||||
private appService: AppService
|
||||
) {}
|
||||
|
||||
getBreadcrumbsFromCurentProcess() {
|
||||
this.currentProcess$
|
||||
@@ -106,7 +120,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.container.nativeElement.scrollTo({ left: this.container.nativeElement.scrollWidth, behavior: 'smooth' });
|
||||
this.container.nativeElement.scrollTo({
|
||||
left: this.container.nativeElement.scrollWidth,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}, 400);
|
||||
return this.breadcrumbs$;
|
||||
}),
|
||||
@@ -118,10 +135,18 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.showBackNavigationArrow = false;
|
||||
if (breadcrumbs) {
|
||||
const breadName =
|
||||
breadcrumbs.length > 0 ? (breadcrumbs[breadcrumbs.length - 1] ? breadcrumbs[breadcrumbs.length - 1].name : '') : '';
|
||||
this.showBack = !(breadName === 'Bestellbestätigung' || breadName === 'Remission');
|
||||
breadcrumbs.length > 0
|
||||
? breadcrumbs[breadcrumbs.length - 1]
|
||||
? breadcrumbs[breadcrumbs.length - 1].name
|
||||
: ''
|
||||
: '';
|
||||
this.showBack = !(
|
||||
breadName === 'Bestellbestätigung' || breadName === 'Remission'
|
||||
);
|
||||
this.breadcrumbs = breadcrumbs;
|
||||
this.breadsCount = Object.keys(this.breadcrumbs) ? Object.keys(this.breadcrumbs).length : 0;
|
||||
this.breadsCount = Object.keys(this.breadcrumbs)
|
||||
? Object.keys(this.breadcrumbs).length
|
||||
: 0;
|
||||
this.cdrf.detectChanges();
|
||||
if (!this.isIPad) {
|
||||
setTimeout(() => {
|
||||
@@ -136,16 +161,6 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
selectBreadcrumb(breadcrumb: Breadcrumb) {
|
||||
if (breadcrumb && breadcrumb.path) {
|
||||
if (breadcrumb.path === '/remission/create') {
|
||||
const shipingDocumentProductCount = this.store.selectSnapshot(RemissionSelectors.getRemissionShippingDocumentProductCount);
|
||||
if (shipingDocumentProductCount === 0) {
|
||||
this.store.dispatch(new SetRemissionStarted(false));
|
||||
this.store.dispatch(new ResetRemissionShippingDocument());
|
||||
} else {
|
||||
this.appService.openStartedRemissionLeavingDialog();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (breadcrumb.path === '/product/search') {
|
||||
this.store.dispatch(new ResetFilters());
|
||||
this.store.dispatch(new ResetProcessProductFilters());
|
||||
@@ -153,9 +168,13 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
if (this.module === ModuleSwitcher.Customer) {
|
||||
this.store.dispatch(new ChangeCurrentRoute(breadcrumb.path, true));
|
||||
} else {
|
||||
this.store.dispatch(new SetBranchProcessCurrentPath(breadcrumb.path, true));
|
||||
this.store.dispatch(
|
||||
new SetBranchProcessCurrentPath(breadcrumb.path, true)
|
||||
);
|
||||
}
|
||||
this.router.navigate([breadcrumb.path], { queryParams: breadcrumb.queryParams ? breadcrumb.queryParams : {} });
|
||||
this.router.navigate([breadcrumb.path], {
|
||||
queryParams: breadcrumb.queryParams ? breadcrumb.queryParams : {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +184,9 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
this.store.dispatch(new ChangeCurrentRoute(breadcrumb.path, false));
|
||||
this.router.navigate(['/product/results']);
|
||||
} else {
|
||||
this.store.dispatch(new SetBranchProcessCurrentPath(breadcrumb.path, false));
|
||||
this.store.dispatch(
|
||||
new SetBranchProcessCurrentPath(breadcrumb.path, false)
|
||||
);
|
||||
this.router.navigate([FILIALE_LANDING_PAGE]);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +221,7 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
addOne() {
|
||||
this.breadcrumbs.push({
|
||||
name: 'test' + (this.breadcrumbs ? this.breadcrumbs.length : 0),
|
||||
path: './i'
|
||||
path: './i',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -215,7 +236,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
elementProcessed++;
|
||||
childrenWidth += this.container.nativeElement.children[i].offsetWidth;
|
||||
}
|
||||
if (breadcrumbs === elementProcessed && containerWidth <= childrenWidth) {
|
||||
if (
|
||||
breadcrumbs === elementProcessed &&
|
||||
containerWidth <= childrenWidth
|
||||
) {
|
||||
this.showBackNavigationArrow = true;
|
||||
this.cdrf.detectChanges();
|
||||
} else {
|
||||
@@ -233,7 +257,11 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
hideBackNavArrow(event) {
|
||||
if (event) {
|
||||
const e = event.toElement || event.relatedTarget;
|
||||
if (e && e.classList && (e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')) {
|
||||
if (
|
||||
e &&
|
||||
e.classList &&
|
||||
(e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.showBackArrow = false;
|
||||
@@ -251,7 +279,11 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
hideForwardNavArrow(event) {
|
||||
if (event) {
|
||||
const e = event.toElement || event.relatedTarget;
|
||||
if (e && e.classList && (e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')) {
|
||||
if (
|
||||
e &&
|
||||
e.classList &&
|
||||
(e.classList[0] === 'icon' || e.classList[0] === 'ng-star-inserted')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.showForrwardArrow = false;
|
||||
@@ -267,7 +299,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
if (scrollLeft <= 121) {
|
||||
this.showBackNavigationArrow = false;
|
||||
}
|
||||
this.container.nativeElement.scrollTo({ left: scrollLeft - 120, behavior: 'smooth' });
|
||||
this.container.nativeElement.scrollTo({
|
||||
left: scrollLeft - 120,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
this.showForwardNavigationalArrow = true;
|
||||
this.cdrf.detectChanges();
|
||||
}
|
||||
@@ -279,7 +314,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
if (scrollLeft + 119 + containerWidth <= scrollWidth) {
|
||||
this.showForwardNavigationalArrow = false;
|
||||
}
|
||||
this.container.nativeElement.scrollTo({ left: scrollLeft + 120, behavior: 'smooth' });
|
||||
this.container.nativeElement.scrollTo({
|
||||
left: scrollLeft + 120,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
this.showBackNavigationArrow = true;
|
||||
this.cdrf.detectChanges();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Observable } from 'rxjs';
|
||||
import { RemissionSelectors } from '../../core/store/selectors/remission.selectors';
|
||||
import { Select } from '@ngxs/store';
|
||||
import { ContentHeaderService } from '../../core/services/content-header.service';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-content-header',
|
||||
|
||||
@@ -4,17 +4,13 @@
|
||||
<div class="menu-item-grid align-center active" (click)="routeToMenu('/product/search', 'productsearch')">
|
||||
<div>
|
||||
<lib-icon class="menu-icon" name="{{
|
||||
router.url === '/product/search' ||
|
||||
router.url.startsWith('/product/results') ||
|
||||
router.url.startsWith('/product/details')
|
||||
router.url === '/product/search' || router.url.startsWith('/product/results') || router.url.startsWith('/product/details')
|
||||
? 'Icon_Artikelsuche'
|
||||
: 'Icon_Artikelsuche_inactive'
|
||||
}}" width="34px" height="24px"></lib-icon>
|
||||
</div>
|
||||
<span *ngIf="
|
||||
router.url === '/product/search' ||
|
||||
router.url.startsWith('/product/results') ||
|
||||
router.url.startsWith('/product/details');
|
||||
router.url === '/product/search' || router.url.startsWith('/product/results') || router.url.startsWith('/product/details');
|
||||
else articleSearchLabelElse
|
||||
" class="menu-item selected">Artikelsuche</span>
|
||||
<ng-template #articleSearchLabelElse>
|
||||
@@ -44,19 +40,16 @@
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="menu-item-grid align-center active" (click)="routeToMenu('/shelf/search', 'shelfsearch')">
|
||||
<!-- <div class="menu-item-grid align-center"> -->
|
||||
<div>
|
||||
<lib-icon class="menu-icon" name="{{
|
||||
router.url === '/shelf/search' ||
|
||||
router.url.startsWith('/shelf/results') ||
|
||||
router.url.startsWith('/shelf/details')
|
||||
router.url === '/shelf/search' || router.url.startsWith('/shelf/results') || router.url.startsWith('/shelf/details')
|
||||
? 'Icon_Abholfach'
|
||||
: 'Icon_Abholfach_inactive'
|
||||
}}" width="34px" height="24px"></lib-icon>
|
||||
</div>
|
||||
<span *ngIf="
|
||||
router.url === '/shelf/search' ||
|
||||
router.url.startsWith('/shelf/results') ||
|
||||
router.url.startsWith('/shelf/details');
|
||||
router.url === '/shelf/search' || router.url.startsWith('/shelf/results') || router.url.startsWith('/shelf/details');
|
||||
else shelfSearchLabelElse
|
||||
" class="menu-item selected">Warenausgabe</span>
|
||||
<ng-template #shelfSearchLabelElse>
|
||||
@@ -68,7 +61,8 @@
|
||||
<!-- Branch module menus -->
|
||||
<div class="branch-menu" *ngIf="module === 1">
|
||||
<div class="menu-grid">
|
||||
<div class="menu-item-grid align-center active" (click)="routeToMenu('/branch/main', 'branchmain')">
|
||||
<div class="menu-item-grid align-center">
|
||||
<!-- <div class="menu-item-grid align-center" (click)="routeToMenu('/branch/main', 'branchmain')"> -->
|
||||
<div>
|
||||
<lib-icon class="menu-icon" name="{{
|
||||
router.url === '/branch/main' ||
|
||||
@@ -113,17 +107,11 @@
|
||||
<div class="menu-item-grid align-center active" (click)="routeToMenu('/remission/create', 'remission')">
|
||||
<div>
|
||||
<lib-icon class="menu-icon" name="{{
|
||||
router.url === '/remission/create' ||
|
||||
router.url === '/remission/started'
|
||||
? 'Icon_Remission'
|
||||
: 'Icon_Remission_inactive'
|
||||
router.url === '/remission/create' || router.url === '/remission/started' ? 'Icon_Remission' : 'Icon_Remission_inactive'
|
||||
}}" width="34px" height="24px"></lib-icon>
|
||||
</div>
|
||||
<span *ngIf="
|
||||
router.url === '/remission/create' ||
|
||||
router.url === '/remission/started';
|
||||
else remissionLabelElse
|
||||
" class="branch-menu-item branch-selected">Remission</span>
|
||||
<span *ngIf="router.url === '/remission/create' || router.url === '/remission/started'; else remissionLabelElse"
|
||||
class="branch-menu-item branch-selected">Remission</span>
|
||||
<ng-template #remissionLabelElse>
|
||||
<span class="branch-menu-item">Remission</span>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Output,
|
||||
EventEmitter,
|
||||
OnDestroy,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||
import { PrinterService } from '../../core/services/printer.service';
|
||||
import { ModalService, ButtonComponent } from '@libs/ui';
|
||||
import { Subject } from 'rxjs';
|
||||
@@ -16,7 +9,7 @@ import { PRINT_ERROR_MSG } from './printer-selection.constants';
|
||||
@Component({
|
||||
selector: 'app-printer-selection',
|
||||
templateUrl: './printer-selection.component.html',
|
||||
styleUrls: ['./printer-selection.component.scss']
|
||||
styleUrls: ['./printer-selection.component.scss'],
|
||||
})
|
||||
export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
id = 'printer-modal';
|
||||
@@ -31,20 +24,16 @@ export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
printingRequested = false;
|
||||
submited = false;
|
||||
@Output() print: EventEmitter<string> = new EventEmitter();
|
||||
@Output() closed = new EventEmitter();
|
||||
@ViewChild('printBtn', { static: false }) printBtn: ButtonComponent;
|
||||
|
||||
constructor(
|
||||
private printerService: PrinterService,
|
||||
private modalService: ModalService
|
||||
) { }
|
||||
constructor(private printerService: PrinterService, private modalService: ModalService, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
printerSelected(value: string | number) {
|
||||
this.selected = value;
|
||||
this.selectedPrinterValue = this.printers.find(
|
||||
t => t.text === this.selected
|
||||
).key;
|
||||
this.selectedPrinterValue = this.printers.find((t) => t.text === this.selected).key;
|
||||
}
|
||||
|
||||
emitPrint() {
|
||||
@@ -63,7 +52,7 @@ export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
this.printerService
|
||||
.getAvailablePrinters()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(response => {
|
||||
.subscribe((response) => {
|
||||
if ((response as { error: string }).error) {
|
||||
const errorResponse = response as { error: string };
|
||||
this.error = true;
|
||||
@@ -71,17 +60,13 @@ export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
const result = response as Printer[];
|
||||
this.printers = result.map(t => {
|
||||
this.printers = result.map((t) => {
|
||||
return { key: t.key, text: t.description, selected: t.selected };
|
||||
});
|
||||
const selectedPrinter = this.printers.find(printer => printer.selected);
|
||||
this.options = this.printers.map(t => t.text);
|
||||
this.selectedPrinterValue = selectedPrinter
|
||||
? selectedPrinter.key
|
||||
: this.printers[0].key;
|
||||
this.selected = selectedPrinter
|
||||
? selectedPrinter.text
|
||||
: this.options[0];
|
||||
const selectedPrinter = this.printers.find((printer) => printer.selected);
|
||||
this.options = this.printers.map((t) => t.text);
|
||||
this.selectedPrinterValue = selectedPrinter ? selectedPrinter.key : this.printers[0].key;
|
||||
this.selected = selectedPrinter ? selectedPrinter.text : this.options[0];
|
||||
this.error = false;
|
||||
this.loaded = true;
|
||||
if (this.printBtn) {
|
||||
@@ -91,6 +76,7 @@ export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
this.error = true;
|
||||
this.errorMessage = 'No available printers';
|
||||
}
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,6 +90,7 @@ export class PrinterSelectionComponent implements OnInit, OnDestroy {
|
||||
if (this.printBtn) {
|
||||
this.printBtn.stopLoading();
|
||||
}
|
||||
this.closed.emit();
|
||||
}
|
||||
|
||||
setError(errorMessage?: string) {
|
||||
|
||||
81
apps/sales/src/app/core/mappings/customer.mapping.spec.ts
Normal file
81
apps/sales/src/app/core/mappings/customer.mapping.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { Organisation, User } from '../models/user.model';
|
||||
import { CustomerMapping } from './customer.mapping';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
fdescribe('CustomerMapping', () => {
|
||||
let mapper: CustomerMapping;
|
||||
const mockUser: User = {
|
||||
id: 123,
|
||||
first_name: 'Vorname',
|
||||
last_name: 'Nachname',
|
||||
title: 'Dr',
|
||||
gender: 'Herr',
|
||||
|
||||
email: 'test@test.de',
|
||||
phone_number: '017012345678',
|
||||
mobile_number: '019998765432',
|
||||
|
||||
organisation: {
|
||||
name: 'Mock Org',
|
||||
department: 'Mock Dept.',
|
||||
vatId: '123',
|
||||
},
|
||||
|
||||
delivery_addres: {
|
||||
id: 456,
|
||||
first_name: 'Vorname Addresse',
|
||||
last_name: 'Nachname Addresse',
|
||||
street: 'Test Strasse',
|
||||
streetNo: 12,
|
||||
zip: '80636',
|
||||
city: 'Munich',
|
||||
country: 'Deutschland',
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [DatePipe, CustomerMapping],
|
||||
});
|
||||
|
||||
mapper = TestBed.get(CustomerMapping);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(mapper instanceof CustomerMapping).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('fromOrganisationToOrganisationDTO', () => {
|
||||
const mockOrganisation: Organisation = mockUser.organisation;
|
||||
it('should map the provided organisation (Organisation) to OrganisationDTO', () => {
|
||||
const result = mapper.fromOrganisationToOrganisationDTO(mockOrganisation);
|
||||
|
||||
expect(result.vatId).toEqual(mockOrganisation.vatId);
|
||||
expect(result.department).toEqual(mockOrganisation.department);
|
||||
expect(result.name).toEqual(mockOrganisation.name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromUserToCommunicationDetails', () => {
|
||||
it('should map the provided user (User) to communication details (CommunicationDetailsDTO)', () => {
|
||||
const result = mapper.fromUserToCommunicationDetails(mockUser);
|
||||
|
||||
expect(result.phone).toEqual(mockUser.phone_number);
|
||||
expect(result.mobile).toEqual(mockUser.mobile_number);
|
||||
expect(result.email).toEqual(mockUser.email);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromUserToShippingAddressDTO', () => {
|
||||
it('should map the provided user (User) to shipping address (ShippingAddressDTO)', () => {
|
||||
const result = mapper.fromUserToShippingAddressDTO(mockUser);
|
||||
|
||||
expect(result.title).toEqual(mockUser.delivery_addres.title);
|
||||
expect(result.gender).toEqual(2);
|
||||
expect(result.firstName).toEqual(mockUser.delivery_addres.first_name);
|
||||
expect(result.lastName).toEqual(mockUser.delivery_addres.last_name);
|
||||
expect(result.source).toEqual(mockUser.delivery_addres.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9,12 +9,15 @@ import {
|
||||
CommunicationDetailsDTO,
|
||||
AssignedPayerDTO,
|
||||
CustomerInfoDTO,
|
||||
ShippingAddressDTO,
|
||||
PayerDTO,
|
||||
CustomerDTO,
|
||||
ShippingAddressDTO,
|
||||
} from '@swagger/crm';
|
||||
import { NotificationChannels } from '../models/notification-channels.enum';
|
||||
import { EntityDTOContainerOfBranchDTO } from '@swagger/checkout';
|
||||
import {
|
||||
EntityDTOContainerOfBranchDTO,
|
||||
ShippingAddressDTO as CheckoutShippingAddressDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats';
|
||||
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
|
||||
@@ -60,7 +63,9 @@ export class CustomerMapping {
|
||||
zipCode: customer.delivery_addres.zip + '',
|
||||
street: customer.delivery_addres.street,
|
||||
streetNumber: customer.delivery_addres.streetNo + '',
|
||||
info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
|
||||
info: customer.delivery_addres.note
|
||||
? customer.delivery_addres.note
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,7 +76,9 @@ export class CustomerMapping {
|
||||
zipCode: customer.invoice_address.zip + '',
|
||||
street: customer.invoice_address.street,
|
||||
streetNumber: customer.invoice_address.streetNo + '',
|
||||
info: customer.invoice_address.note ? customer.invoice_address.note : null,
|
||||
info: customer.invoice_address.note
|
||||
? customer.invoice_address.note
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,14 +91,20 @@ export class CustomerMapping {
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(customer.poossible_delivery_addresses) && customer.poossible_delivery_addresses.length > 0) {
|
||||
if (
|
||||
Array.isArray(customer.poossible_delivery_addresses) &&
|
||||
customer.poossible_delivery_addresses.length > 0
|
||||
) {
|
||||
customer.poossible_delivery_addresses.forEach((adrs: Address) => {
|
||||
addresses.push(this.addressesItem(adrs));
|
||||
shippingAddresses.push(this.addressesItem(adrs));
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(customer.poossible_invoice_addresses) && customer.poossible_invoice_addresses.length > 0) {
|
||||
if (
|
||||
Array.isArray(customer.poossible_invoice_addresses) &&
|
||||
customer.poossible_invoice_addresses.length > 0
|
||||
) {
|
||||
customer.poossible_invoice_addresses.forEach((adrs: Address) => {
|
||||
if (adrs) {
|
||||
addresses.push(this.addressesItem(adrs));
|
||||
@@ -178,7 +191,9 @@ export class CustomerMapping {
|
||||
}
|
||||
}
|
||||
|
||||
private addressesItem(address: Address): EntityDTOContainerOfShippingAddressDTO {
|
||||
private addressesItem(
|
||||
address: Address
|
||||
): EntityDTOContainerOfShippingAddressDTO {
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
@@ -214,7 +229,9 @@ export class CustomerMapping {
|
||||
|
||||
customerInfoDTOtoUser(customerInfo: CustomerInfoDTO): User {
|
||||
if (isNullOrUndefined(customerInfo)) {
|
||||
throw new Error('argument customerInfo:CustomerInfoDTO is null or undefined.');
|
||||
throw new Error(
|
||||
'argument customerInfo:CustomerInfoDTO is null or undefined.'
|
||||
);
|
||||
}
|
||||
|
||||
const id = customerInfo.id;
|
||||
@@ -252,18 +269,22 @@ export class CustomerMapping {
|
||||
customerInfo.notificationChannels === NotificationChannels.EmailSms;
|
||||
}
|
||||
|
||||
let address: Address;
|
||||
let baseAddres: Address = {} as Address;
|
||||
if (!!customerInfo.address) {
|
||||
address = {
|
||||
baseAddres = {
|
||||
id: Date.now() + Math.random(),
|
||||
title: customerInfo.title,
|
||||
first_name: customerInfo.firstName,
|
||||
last_name: customerInfo.lastName,
|
||||
city: customerInfo.address.city ? customerInfo.address.city : undefined,
|
||||
zip: customerInfo.address.zipCode ? customerInfo.address.zipCode : undefined,
|
||||
zip: customerInfo.address.zipCode
|
||||
? customerInfo.address.zipCode
|
||||
: undefined,
|
||||
country: customerInfo.address.country,
|
||||
street: customerInfo.address.street,
|
||||
streetNo: customerInfo.address.streetNumber ? +customerInfo.address.streetNumber : undefined,
|
||||
streetNo: customerInfo.address.streetNumber
|
||||
? +customerInfo.address.streetNumber
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -310,7 +331,8 @@ export class CustomerMapping {
|
||||
payement_method: null, // missing info from API
|
||||
tolino: false, // missing info from API
|
||||
title: null, // missing info from API
|
||||
delivery_addres: address,
|
||||
base_addres: baseAddres,
|
||||
hasOnlineAccount: customerInfo.hasOnlineAccount,
|
||||
isGuestAccount: customerInfo.isGuestAccount,
|
||||
features: features,
|
||||
changed: customerInfo.changed,
|
||||
@@ -334,7 +356,9 @@ export class CustomerMapping {
|
||||
|
||||
customerDTOtoUser(customerDto: CustomerDTO): User {
|
||||
if (isNullOrUndefined(customerDto)) {
|
||||
throw new Error('argument customerDto:CustomerInfoDTO is null or undefined.');
|
||||
throw new Error(
|
||||
'argument customerDto:CustomerInfoDTO is null or undefined.'
|
||||
);
|
||||
}
|
||||
|
||||
const id = customerDto.id;
|
||||
@@ -365,15 +389,16 @@ export class CustomerMapping {
|
||||
let notificationEmail = false;
|
||||
if (customerDto.notificationChannels) {
|
||||
notificationSms =
|
||||
customerDto.notificationChannels === NotificationChannels.Sms || customerDto.notificationChannels === NotificationChannels.EmailSms;
|
||||
customerDto.notificationChannels === NotificationChannels.Sms ||
|
||||
customerDto.notificationChannels === NotificationChannels.EmailSms;
|
||||
notificationEmail =
|
||||
customerDto.notificationChannels === NotificationChannels.Email ||
|
||||
customerDto.notificationChannels === NotificationChannels.EmailSms;
|
||||
}
|
||||
|
||||
let address: Address;
|
||||
let baseAddres: Address;
|
||||
if (!!customerDto.address) {
|
||||
address = {
|
||||
baseAddres = {
|
||||
id: Date.now() + Math.random(),
|
||||
title: customerDto.title,
|
||||
first_name: customerDto.firstName,
|
||||
@@ -422,7 +447,9 @@ export class CustomerMapping {
|
||||
mobile_number: mobileNumber,
|
||||
newUser: newUser,
|
||||
customer_card: false,
|
||||
createdInBranch: customerDto.createdInBranch ? customerDto.createdInBranch.id : null,
|
||||
createdInBranch: customerDto.createdInBranch
|
||||
? customerDto.createdInBranch.id
|
||||
: null,
|
||||
newsletter: false, // missing info from API
|
||||
shop: false,
|
||||
notificationSms: notificationSms,
|
||||
@@ -430,21 +457,22 @@ export class CustomerMapping {
|
||||
payement_method: null, // missing info from API
|
||||
tolino: false, // missing info from API
|
||||
title: null, // missing info from API
|
||||
delivery_addres: address,
|
||||
invoice_address: address,
|
||||
poossible_delivery_addresses: [address],
|
||||
poossible_invoice_addresses: [address],
|
||||
features: features,
|
||||
changed: customerDto.changed,
|
||||
created: customerDto.created,
|
||||
customerNumber: customerDto.customerNumber,
|
||||
organisation: organisation,
|
||||
hasOnlineAccount: customerDto.hasOnlineAccount,
|
||||
};
|
||||
}
|
||||
|
||||
fromShippingAddressDtoToAddress(shippingAddress: ShippingAddressDTO): Address {
|
||||
fromShippingAddressDtoToAddress(
|
||||
shippingAddress: ShippingAddressDTO
|
||||
): Address {
|
||||
if (isNullOrUndefined(shippingAddress)) {
|
||||
throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
|
||||
throw new Error(
|
||||
'argument shippingAddress:ShippingAddressDTO is null or undefined.'
|
||||
);
|
||||
}
|
||||
|
||||
let gender = '';
|
||||
@@ -497,7 +525,10 @@ export class CustomerMapping {
|
||||
};
|
||||
}
|
||||
|
||||
fromAddressToShippingAddressDTO(address: Address, forUpdate: boolean): ShippingAddressDTO {
|
||||
fromAddressToShippingAddressDTO(
|
||||
address: Address,
|
||||
forUpdate: boolean
|
||||
): ShippingAddressDTO {
|
||||
if (isNullOrUndefined(address)) {
|
||||
throw new Error('argument address:Address is null or undefined.');
|
||||
}
|
||||
@@ -529,7 +560,10 @@ export class CustomerMapping {
|
||||
lastName: address.last_name,
|
||||
gender: this.getGender(address.gender),
|
||||
status: 1,
|
||||
isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
|
||||
isDefault: this.datePipe.transform(
|
||||
new Date(),
|
||||
BACKEND_API_TIMESTAMP_FORMAT
|
||||
),
|
||||
organisation: <OrganisationDTO>{
|
||||
name: address.company_name,
|
||||
department: address.company_department,
|
||||
@@ -584,7 +618,10 @@ export class CustomerMapping {
|
||||
lastName: address.last_name,
|
||||
gender: this.getGender(address.gender),
|
||||
status: 1,
|
||||
isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
|
||||
isDefault: this.datePipe.transform(
|
||||
new Date(),
|
||||
BACKEND_API_TIMESTAMP_FORMAT
|
||||
),
|
||||
address: _address,
|
||||
payerNumber: address.payer_number,
|
||||
payerStatus: address.payer_status,
|
||||
@@ -599,7 +636,9 @@ export class CustomerMapping {
|
||||
|
||||
fromAssignedPayerDtoToAddress(assignedPayers: AssignedPayerDTO): Address {
|
||||
if (isNullOrUndefined(assignedPayers)) {
|
||||
throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
|
||||
throw new Error(
|
||||
'argument shippingAddress:ShippingAddressDTO is null or undefined.'
|
||||
);
|
||||
}
|
||||
|
||||
let id = null;
|
||||
@@ -612,7 +651,11 @@ export class CustomerMapping {
|
||||
}
|
||||
|
||||
let gender = '';
|
||||
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.gender) {
|
||||
if (
|
||||
!!assignedPayers.payer &&
|
||||
!!assignedPayers.payer.data &&
|
||||
!!assignedPayers.payer.data.gender
|
||||
) {
|
||||
gender = this.getGenderFromCode(assignedPayers.payer.data.gender);
|
||||
}
|
||||
|
||||
@@ -621,7 +664,11 @@ export class CustomerMapping {
|
||||
let street = '';
|
||||
let streetNo = null;
|
||||
let zip: string = null;
|
||||
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.address) {
|
||||
if (
|
||||
!!assignedPayers.payer &&
|
||||
!!assignedPayers.payer.data &&
|
||||
!!assignedPayers.payer.data.address
|
||||
) {
|
||||
country = assignedPayers.payer.data.address.country;
|
||||
city = assignedPayers.payer.data.address.city;
|
||||
street = assignedPayers.payer.data.address.street;
|
||||
@@ -643,7 +690,11 @@ export class CustomerMapping {
|
||||
let company_name = null;
|
||||
let company_department = null;
|
||||
let company_tax_number = null;
|
||||
if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.organisation) {
|
||||
if (
|
||||
!!assignedPayers.payer &&
|
||||
!!assignedPayers.payer.data &&
|
||||
!!assignedPayers.payer.data.organisation
|
||||
) {
|
||||
company_name = assignedPayers.payer.data.organisation.name;
|
||||
company_department = assignedPayers.payer.data.organisation.department;
|
||||
company_tax_number = assignedPayers.payer.data.organisation.vatId;
|
||||
@@ -706,7 +757,9 @@ export class CustomerMapping {
|
||||
zipCode: customer.delivery_addres.zip + '',
|
||||
street: customer.delivery_addres.street,
|
||||
streetNumber: customer.delivery_addres.streetNo + '',
|
||||
info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
|
||||
info: customer.delivery_addres.note
|
||||
? customer.delivery_addres.note
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -767,4 +820,44 @@ export class CustomerMapping {
|
||||
? NotificationChannels.Sms
|
||||
: NotificationChannels.NotSet;
|
||||
}
|
||||
|
||||
fromOrganisationToOrganisationDTO(
|
||||
organisation: Organisation
|
||||
): OrganisationDTO {
|
||||
if (!organisation) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
name: organisation.name,
|
||||
department: organisation.department,
|
||||
vatId: organisation.vatId,
|
||||
};
|
||||
}
|
||||
|
||||
fromUserToCommunicationDetails(user: User): CommunicationDetailsDTO {
|
||||
return {
|
||||
phone: user.phone_number,
|
||||
mobile: user.mobile_number,
|
||||
email: user.email,
|
||||
};
|
||||
}
|
||||
|
||||
fromUserToShippingAddressDTO(user: User): CheckoutShippingAddressDTO {
|
||||
const deliveryDetails = user.delivery_addres;
|
||||
|
||||
if (!deliveryDetails) {
|
||||
throw Error('No Delivery Address set.');
|
||||
}
|
||||
|
||||
return {
|
||||
title: deliveryDetails.title,
|
||||
gender: this.getGender(user.gender),
|
||||
firstName: deliveryDetails.first_name,
|
||||
lastName: deliveryDetails.last_name,
|
||||
organisation: this.fromOrganisationToOrganisationDTO(user.organisation),
|
||||
communicationDetails: this.fromUserToCommunicationDetails(user),
|
||||
address: this.fromAddressToAddressDTO(deliveryDetails),
|
||||
source: deliveryDetails.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
9
apps/sales/src/app/core/models/action-result.model.ts
Normal file
9
apps/sales/src/app/core/models/action-result.model.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface ActionResult<T> {
|
||||
error?: boolean;
|
||||
errorReasons?: { [key: string]: string };
|
||||
http?: {
|
||||
code: number;
|
||||
};
|
||||
message?: string;
|
||||
result?: T;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export interface User {
|
||||
email?: string;
|
||||
mobile_number?: string;
|
||||
phone_number?: string;
|
||||
base_addres?: Address;
|
||||
delivery_addres?: Address;
|
||||
invoice_address?: Address;
|
||||
shop?: boolean;
|
||||
@@ -26,6 +27,7 @@ export interface User {
|
||||
notificationEmail?: boolean;
|
||||
createdInBranch?: number;
|
||||
customerId?: string;
|
||||
hasOnlineAccount?: boolean;
|
||||
isGuestAccount?: boolean;
|
||||
features?: Features[];
|
||||
changed?: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './overlay-close-event.model';
|
||||
export * from './overlay-content.model';
|
||||
export * from './modal-content.model';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type OverlayContent<T> = T;
|
||||
@@ -5,5 +5,5 @@ export * from './isa-overlay-ref';
|
||||
export * from './modal-dialog-data';
|
||||
export * from './modal-service';
|
||||
export * from './overlay.animations';
|
||||
export * from './overlay-content.token';
|
||||
// end:ng42.barrel
|
||||
|
||||
|
||||
6
apps/sales/src/app/core/overlay/overlay-content.token.ts
Normal file
6
apps/sales/src/app/core/overlay/overlay-content.token.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { OverlayContent } from './defs';
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const OVERLAY_DATA = new InjectionToken<
|
||||
OverlayContent<{ queryString: string }>
|
||||
>('OVERLAY_DATA');
|
||||
12
apps/sales/src/app/core/providers/transaction-id.provider.ts
Normal file
12
apps/sales/src/app/core/providers/transaction-id.provider.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class TransactionIdProvider {
|
||||
private transactionId = 0;
|
||||
|
||||
constructor() {}
|
||||
|
||||
next() {
|
||||
return this.transactionId++;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SupplierDTO,
|
||||
QueryTokenDTO,
|
||||
AutocompleteTokenDTO,
|
||||
AbholfachService,
|
||||
} from '@swagger/oms';
|
||||
import { map, filter } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -31,6 +32,7 @@ import { CollectingShelfSelectors } from '../store/selectors/collecting-shelf.se
|
||||
export class CollectingShelfService {
|
||||
constructor(
|
||||
private omsService: OrderService,
|
||||
private abholfach: AbholfachService,
|
||||
private mapper: ShelfMapping,
|
||||
public datepipe: DatePipe,
|
||||
private receiptService: ReceiptService,
|
||||
@@ -52,17 +54,13 @@ export class CollectingShelfService {
|
||||
take: take,
|
||||
};
|
||||
|
||||
return this.omsService.OrderWarenausgabe(params).pipe(
|
||||
return this.abholfach.AbholfachWarenausgabe(params).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return {
|
||||
result: this.mapper.fromOrderItemListItemDTOArrayToCollectingShelfOrder(
|
||||
response.result,
|
||||
this,
|
||||
skip === 0
|
||||
),
|
||||
result: this.mapper.fromOrderItemListItemDTOArrayToCollectingShelfOrder(response.result, this, skip === 0),
|
||||
hits: response.hits,
|
||||
};
|
||||
})
|
||||
@@ -70,22 +68,17 @@ export class CollectingShelfService {
|
||||
}
|
||||
|
||||
searchShelfCached() {
|
||||
return this.store
|
||||
.select(CollectingShelfSelectors.getCachedResults)
|
||||
.pipe(filter((data) => !isNullOrUndefined(data)));
|
||||
return this.store.select(CollectingShelfSelectors.getCachedResults).pipe(filter((data) => !isNullOrUndefined(data)));
|
||||
}
|
||||
|
||||
searchShelfHasResults(
|
||||
input: string,
|
||||
branchNumber: string
|
||||
): Observable<number> {
|
||||
searchShelfHasResults(input: string, branchNumber: string): Observable<number> {
|
||||
const params = <QueryTokenDTO>{
|
||||
input: {
|
||||
qs: input,
|
||||
},
|
||||
branchNumber,
|
||||
};
|
||||
return this.omsService.OrderWarenausgabe(params).pipe(
|
||||
return this.abholfach.AbholfachWarenausgabe(params).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -113,7 +106,7 @@ export class CollectingShelfService {
|
||||
hitsOnly,
|
||||
branchNumber,
|
||||
};
|
||||
return this.omsService.OrderWarenausgabe(params).pipe(
|
||||
return this.abholfach.AbholfachWarenausgabe(params).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -124,7 +117,7 @@ export class CollectingShelfService {
|
||||
}
|
||||
|
||||
searchWarenausgabeAutocomplete(autocompleteTokenDTO: AutocompleteTokenDTO) {
|
||||
return this.omsService.OrderWarenausgabeAutocomplete(autocompleteTokenDTO);
|
||||
return this.abholfach.AbholfachWarenausgabeAutocomplete(autocompleteTokenDTO);
|
||||
}
|
||||
|
||||
getOrderByOrderId(orderId: number): Observable<OrderDTO> {
|
||||
@@ -153,9 +146,7 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
getOrderItemSubsetByOrderItemSubsetId(
|
||||
orderItemSubsetId: number
|
||||
): Observable<OrderItemSubsetDTO> {
|
||||
getOrderItemSubsetByOrderItemSubsetId(orderItemSubsetId: number): Observable<OrderItemSubsetDTO> {
|
||||
return this.omsService.OrderGetOrderItemSubset(orderItemSubsetId).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
@@ -166,18 +157,13 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
getOrdersByCompartmentNumber(
|
||||
compartmentNumbers: string[],
|
||||
orderId: number
|
||||
): Observable<OrderDTO> {
|
||||
getOrdersByCompartmentNumber(compartmentNumbers: string[], orderId: number): Observable<OrderDTO> {
|
||||
return this.omsService.OrderGetOrdersByCompartment(compartmentNumbers).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return response.result
|
||||
? response.result.find((o) => +o.id === +orderId)
|
||||
: null;
|
||||
return response.result ? response.result.find((o) => +o.id === +orderId) : null;
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -236,16 +222,8 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
extendPickUpPeriod(
|
||||
orderId: number,
|
||||
orderItemId: number,
|
||||
orderItemSubsetId: number,
|
||||
date: Date
|
||||
): Observable<OrderItemSubsetDTO> {
|
||||
const compartmentStop = this.datepipe.transform(
|
||||
date,
|
||||
BACKEND_API_TIMESTAMP_FORMAT
|
||||
);
|
||||
extendPickUpPeriod(orderId: number, orderItemId: number, orderItemSubsetId: number, date: Date): Observable<OrderItemSubsetDTO> {
|
||||
const compartmentStop = this.datepipe.transform(date, BACKEND_API_TIMESTAMP_FORMAT);
|
||||
const params = <OrderService.OrderPatchOrderItemSubsetParams>{
|
||||
orderId,
|
||||
orderItemId,
|
||||
@@ -264,16 +242,8 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
updateEstimatedShippingDate(
|
||||
orderId: number,
|
||||
orderItemId: number,
|
||||
orderItemSubsetId: number,
|
||||
date: Date
|
||||
): Observable<OrderItemSubsetDTO> {
|
||||
const estimatedShippingDate = this.datepipe.transform(
|
||||
date,
|
||||
BACKEND_API_TIMESTAMP_FORMAT
|
||||
);
|
||||
updateEstimatedShippingDate(orderId: number, orderItemId: number, orderItemSubsetId: number, date: Date): Observable<OrderItemSubsetDTO> {
|
||||
const estimatedShippingDate = this.datepipe.transform(date, BACKEND_API_TIMESTAMP_FORMAT);
|
||||
const params = <OrderService.OrderPatchOrderItemSubsetParams>{
|
||||
orderId,
|
||||
orderItemId,
|
||||
@@ -292,11 +262,7 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
patchOrderItem(
|
||||
orderId: number,
|
||||
orderItemId: number,
|
||||
orderItem: OrderItemDTO
|
||||
) {
|
||||
patchOrderItem(orderId: number, orderItemId: number, orderItem: OrderItemDTO) {
|
||||
const params = <OrderService.OrderPatchOrderItemParams>{
|
||||
orderId,
|
||||
orderItemId,
|
||||
@@ -381,9 +347,7 @@ export class CollectingShelfService {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return response.result && response.result[0]
|
||||
? response.result[0].supplierDescription
|
||||
: null;
|
||||
return response.result && response.result[0] ? response.result[0].supplierDescription : null;
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -410,23 +374,14 @@ export class CollectingShelfService {
|
||||
);
|
||||
}
|
||||
|
||||
processOrderCompartmentNumber(
|
||||
orders: OrderItemListItemDTO[],
|
||||
order: OrderItemListItemDTO,
|
||||
index: number,
|
||||
firstBatch: boolean
|
||||
) {
|
||||
processOrderCompartmentNumber(orders: OrderItemListItemDTO[], order: OrderItemListItemDTO, index: number, firstBatch: boolean) {
|
||||
const tempOrders = [...orders];
|
||||
const prevOrders = tempOrders.slice(0, index);
|
||||
let hideCompartmentNumber = false;
|
||||
if (index === 0 && !firstBatch) {
|
||||
const olderOrders = this.store.selectSnapshot(
|
||||
CollectingShelfSelectors.getShelfOrders
|
||||
);
|
||||
const olderOrders = this.store.selectSnapshot(CollectingShelfSelectors.getShelfOrders);
|
||||
if (olderOrders && olderOrders.length > 0) {
|
||||
const filteredOrders = olderOrders.filter(
|
||||
(t) => t.orderId === order.orderId
|
||||
);
|
||||
const filteredOrders = olderOrders.filter((t) => t.orderId === order.orderId);
|
||||
if (
|
||||
order.compartmentCode &&
|
||||
order.processingStatus &&
|
||||
@@ -435,10 +390,8 @@ export class CollectingShelfService {
|
||||
filteredOrders[filteredOrders.length - 1] &&
|
||||
filteredOrders[filteredOrders.length - 1].compartmentCode &&
|
||||
filteredOrders[filteredOrders.length - 1].processingStatus &&
|
||||
filteredOrders[filteredOrders.length - 1].compartmentCode ===
|
||||
order.compartmentCode &&
|
||||
filteredOrders[filteredOrders.length - 1].processingStatus ===
|
||||
order.processingStatus
|
||||
filteredOrders[filteredOrders.length - 1].compartmentCode === order.compartmentCode &&
|
||||
filteredOrders[filteredOrders.length - 1].processingStatus === order.processingStatus
|
||||
) {
|
||||
hideCompartmentNumber = true;
|
||||
}
|
||||
@@ -451,10 +404,8 @@ export class CollectingShelfService {
|
||||
prevOrders[prevOrders.length - 1] &&
|
||||
prevOrders[prevOrders.length - 1].compartmentCode &&
|
||||
prevOrders[prevOrders.length - 1].processingStatus &&
|
||||
prevOrders[prevOrders.length - 1].compartmentCode ===
|
||||
order.compartmentCode &&
|
||||
prevOrders[prevOrders.length - 1].processingStatus ===
|
||||
order.processingStatus
|
||||
prevOrders[prevOrders.length - 1].compartmentCode === order.compartmentCode &&
|
||||
prevOrders[prevOrders.length - 1].processingStatus === order.processingStatus
|
||||
) {
|
||||
hideCompartmentNumber = true;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ export class ContentHeaderService {
|
||||
breadcrumbs: [],
|
||||
filter: ['/remission/create', '/remission/started', '/shelf'],
|
||||
};
|
||||
blackList = { breadcrumbs: ['/dashboard', '/branch/main'], filter: [] };
|
||||
blackList = {
|
||||
breadcrumbs: ['/dashboard', '/branch/main'],
|
||||
filter: ['/history', 'edit'],
|
||||
};
|
||||
|
||||
constructor(
|
||||
private locationService: LocationService,
|
||||
@@ -115,7 +118,11 @@ export class ContentHeaderService {
|
||||
(whitelistetUrl) =>
|
||||
whitelistetUrl.includes(url) || url.includes(whitelistetUrl)
|
||||
);
|
||||
return isOnWhiteList;
|
||||
const isOnBlacklist = applicableBlacklist.find(
|
||||
(blackListUrl) =>
|
||||
blackListUrl.includes(url) || url.includes(blackListUrl)
|
||||
);
|
||||
return isOnWhiteList && !isOnBlacklist;
|
||||
}
|
||||
|
||||
if (type === 'breadcrumbs') {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CustomerService } from './customer.service';
|
||||
|
||||
describe('CustomerService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
});
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { QueryTokenDTO } from '@swagger/cat';
|
||||
import { CustomerService as CustomerApiService, PayerService } from '@swagger/crm';
|
||||
import {
|
||||
CustomerService as CustomerApiService,
|
||||
PayerService,
|
||||
} from '@swagger/crm';
|
||||
import { map, switchMap, catchError } from 'rxjs/operators';
|
||||
import { Observable, of, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
import { CustomerMapping } from '../mappings/customer.mapping';
|
||||
@@ -28,7 +31,11 @@ import { CustomerFilters } from '../../modules/customer';
|
||||
})
|
||||
export class CustomerService {
|
||||
addAddressError$ = new BehaviorSubject<any>(null);
|
||||
constructor(private customerService: CustomerApiService, private mapper: CustomerMapping, private payerService: PayerService) {}
|
||||
constructor(
|
||||
private customerService: CustomerApiService,
|
||||
private mapper: CustomerMapping,
|
||||
private payerService: PayerService
|
||||
) {}
|
||||
|
||||
createCustomer(_customer: User): Observable<User> {
|
||||
const customer: CustomerDTO = this.mapper.fromUser(_customer);
|
||||
@@ -97,7 +104,9 @@ export class CustomerService {
|
||||
filter?: CustomerFilters
|
||||
): Observable<CustomerSearchResponse> {
|
||||
const noFilters = filter
|
||||
? isNullOrUndefined(filter.bonuscard) && isNullOrUndefined(filter.guestaccount) && isNullOrUndefined(filter.onlineshop)
|
||||
? isNullOrUndefined(filter.bonuscard) &&
|
||||
isNullOrUndefined(filter.guestaccount) &&
|
||||
isNullOrUndefined(filter.onlineshop)
|
||||
? true
|
||||
: false
|
||||
: false;
|
||||
@@ -112,9 +121,15 @@ export class CustomerService {
|
||||
filter:
|
||||
filter && !noFilters
|
||||
? {
|
||||
['bonuscard']: !isNullOrUndefined(filter.bonuscard) ? String(filter.bonuscard) : undefined,
|
||||
['guestaccount']: !isNullOrUndefined(filter.guestaccount) ? String(filter.guestaccount) : undefined,
|
||||
['onlineshop']: !isNullOrUndefined(filter.onlineshop) ? String(filter.onlineshop) : undefined,
|
||||
['bonuscard']: !isNullOrUndefined(filter.bonuscard)
|
||||
? String(filter.bonuscard)
|
||||
: undefined,
|
||||
['guestaccount']: !isNullOrUndefined(filter.guestaccount)
|
||||
? String(filter.guestaccount)
|
||||
: undefined,
|
||||
['onlineshop']: !isNullOrUndefined(filter.onlineshop)
|
||||
? String(filter.onlineshop)
|
||||
: undefined,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
@@ -125,7 +140,9 @@ export class CustomerService {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return {
|
||||
customers: response.result.map((t) => this.mapper.customerInfoDTOtoUser(t)),
|
||||
customers: response.result.map((t) =>
|
||||
this.mapper.customerInfoDTOtoUser(t)
|
||||
),
|
||||
hits: response.hits,
|
||||
message: response.message,
|
||||
};
|
||||
@@ -133,23 +150,31 @@ export class CustomerService {
|
||||
);
|
||||
}
|
||||
|
||||
getCustomer(customer$: Observable<ResponseArgsOfCustomerDTO>): Observable<User> {
|
||||
getCustomer(
|
||||
customer$: Observable<ResponseArgsOfCustomerDTO>
|
||||
): Observable<User> {
|
||||
return customer$.pipe(
|
||||
map((response: ResponseArgsOfCustomerDTO) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return {
|
||||
shippingAddresses: response.result.shippingAddresses.map((addressEnity) => addressEnity.id),
|
||||
shippingAddresses: response.result.shippingAddresses.map(
|
||||
(addressEnity) => addressEnity.id
|
||||
),
|
||||
user: this.mapper.customerDTOtoUser(response.result),
|
||||
};
|
||||
}),
|
||||
switchMap((customerResult) => {
|
||||
const user = customerResult.user;
|
||||
if (!customerResult.shippingAddresses || customerResult.shippingAddresses.length === 0) {
|
||||
if (
|
||||
!customerResult.shippingAddresses ||
|
||||
customerResult.shippingAddresses.length === 0
|
||||
) {
|
||||
return of(customerResult.user);
|
||||
}
|
||||
const observablesOfShippingAddresses = customerResult.shippingAddresses.map((t) => {
|
||||
const observablesOfShippingAddresses = customerResult.shippingAddresses.map(
|
||||
(t) => {
|
||||
return this.customerService.CustomerGetShippingaddress(t).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
@@ -158,22 +183,28 @@ export class CustomerService {
|
||||
return response.result;
|
||||
})
|
||||
);
|
||||
});
|
||||
const shippingAddresses$ = combineLatest(observablesOfShippingAddresses);
|
||||
}
|
||||
);
|
||||
const shippingAddresses$ = combineLatest(
|
||||
observablesOfShippingAddresses
|
||||
);
|
||||
return shippingAddresses$.pipe(
|
||||
switchMap((address: ShippingAddressDTO[]) => {
|
||||
const updatedUser = user;
|
||||
if (!!address) {
|
||||
const addresses = address.map((t) => {
|
||||
return { ...this.mapper.fromShippingAddressDtoToAddress(t), synced: true };
|
||||
return {
|
||||
...this.mapper.fromShippingAddressDtoToAddress(t),
|
||||
synced: true,
|
||||
};
|
||||
});
|
||||
const defaultAddresses = addresses.reduce((ad1, ad2) => {
|
||||
const defaultAddress = addresses.reduce((ad1, ad2) => {
|
||||
if (ad1.defaultSince >= ad2.defaultSince) {
|
||||
return ad1;
|
||||
}
|
||||
return ad2;
|
||||
});
|
||||
updatedUser.delivery_addres = defaultAddresses;
|
||||
updatedUser.delivery_addres = defaultAddress;
|
||||
updatedUser.poossible_delivery_addresses = addresses;
|
||||
}
|
||||
return of(updatedUser);
|
||||
@@ -182,7 +213,9 @@ export class CustomerService {
|
||||
}),
|
||||
switchMap((customer: User) => {
|
||||
const updateCustomer = customer;
|
||||
return this.customerService.CustomerGetAssignedPayersByCustomerId(customer.id).pipe(
|
||||
return this.customerService
|
||||
.CustomerGetAssignedPayersByCustomerId(customer.id)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -190,19 +223,26 @@ export class CustomerService {
|
||||
return { customer: customer, assignedPayers: response.result };
|
||||
}),
|
||||
switchMap((response) => {
|
||||
if (Array.isArray(response.assignedPayers) && response.assignedPayers.length > 0) {
|
||||
if (
|
||||
Array.isArray(response.assignedPayers) &&
|
||||
response.assignedPayers.length > 0
|
||||
) {
|
||||
const invoiceAddresses = response.assignedPayers.map((t) => {
|
||||
return { ...this.mapper.fromAssignedPayerDtoToAddress(t), synced: true };
|
||||
return {
|
||||
...this.mapper.fromAssignedPayerDtoToAddress(t),
|
||||
synced: true,
|
||||
};
|
||||
});
|
||||
const defaultAddresses = invoiceAddresses.reduce((ad1, ad2) => {
|
||||
const defaultAddress = invoiceAddresses.reduce((ad1, ad2) => {
|
||||
if (ad1.defaultSince >= ad2.defaultSince) {
|
||||
return ad1;
|
||||
}
|
||||
return ad2;
|
||||
});
|
||||
updateCustomer.poossible_invoice_addresses = invoiceAddresses;
|
||||
updateCustomer.invoice_address = defaultAddresses;
|
||||
updateCustomer.invoice_address = defaultAddress;
|
||||
}
|
||||
|
||||
return of(updateCustomer);
|
||||
})
|
||||
);
|
||||
@@ -223,7 +263,12 @@ export class CustomerService {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return { customers: response.result.map((t) => this.mapper.customerInfoDTOtoUser(t)), hits: 0 };
|
||||
return {
|
||||
customers: response.result.map((t) =>
|
||||
this.mapper.customerInfoDTOtoUser(t)
|
||||
),
|
||||
hits: 0,
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -249,7 +294,9 @@ export class CustomerService {
|
||||
isDefault: true,
|
||||
};
|
||||
if (shippingAddress.synced === true) {
|
||||
return this.customerService.CustomerModifyShippingAddressFlag(params).pipe(
|
||||
return this.customerService
|
||||
.CustomerModifyShippingAddressFlag(params)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -258,9 +305,14 @@ export class CustomerService {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return this.addShippingAddress(customerId, { ...shippingAddress, id: null }).pipe(
|
||||
return this.addShippingAddress(customerId, {
|
||||
...shippingAddress,
|
||||
id: null,
|
||||
}).pipe(
|
||||
switchMap(() => {
|
||||
return this.customerService.CustomerModifyShippingAddressFlag(params).pipe(
|
||||
return this.customerService
|
||||
.CustomerModifyShippingAddressFlag(params)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -292,7 +344,10 @@ export class CustomerService {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return this.addInvoiceAddress(customerId, { ...invoiceAddress, id: null }).pipe(
|
||||
return this.addInvoiceAddress(customerId, {
|
||||
...invoiceAddress,
|
||||
id: null,
|
||||
}).pipe(
|
||||
switchMap((payerDto: PayerDTO) => {
|
||||
const params = {
|
||||
payerId: payerDto.id,
|
||||
@@ -316,7 +371,10 @@ export class CustomerService {
|
||||
if (!isNullOrUndefined(shippingAddress.id)) {
|
||||
const params = {
|
||||
shippingAddressId: shippingAddress.id,
|
||||
shippingAddress: this.mapper.fromAddressToShippingAddressDTO(shippingAddress, !isNullOrUndefined(shippingAddress.id)),
|
||||
shippingAddress: this.mapper.fromAddressToShippingAddressDTO(
|
||||
shippingAddress,
|
||||
!isNullOrUndefined(shippingAddress.id)
|
||||
),
|
||||
customerId: customerId,
|
||||
};
|
||||
return this.customerService.CustomerUpdateShippingAddress(params).pipe(
|
||||
@@ -344,7 +402,10 @@ export class CustomerService {
|
||||
);
|
||||
} else {
|
||||
const params = {
|
||||
shippingAddress: this.mapper.fromAddressToShippingAddressDTO(shippingAddress, !isNullOrUndefined(shippingAddress.id)),
|
||||
shippingAddress: this.mapper.fromAddressToShippingAddressDTO(
|
||||
shippingAddress,
|
||||
!isNullOrUndefined(shippingAddress.id)
|
||||
),
|
||||
customerId: customerId,
|
||||
};
|
||||
return this.customerService.CustomerCreateShippingAddress(params).pipe(
|
||||
@@ -404,7 +465,9 @@ export class CustomerService {
|
||||
payerId: payerResponse.result.id,
|
||||
isDefault: true,
|
||||
};
|
||||
return this.customerService.CustomerAddPayerReference(referenceParamsparams).pipe(
|
||||
return this.customerService
|
||||
.CustomerAddPayerReference(referenceParamsparams)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
@@ -450,7 +513,10 @@ export class CustomerService {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return response.result.map((t, index) => <Country>{ index, id: t.id, key: t.isO3166_A_3, value: t.name });
|
||||
return response.result.map(
|
||||
(t, index) =>
|
||||
<Country>{ index, id: t.id, key: t.isO3166_A_3, value: t.name }
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OrderService, QueryTokenDTO } from '@swagger/oms';
|
||||
import { OrderService, QueryTokenDTO, AbholfachService } from '@swagger/oms';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { GoodsInSelectors } from '../store/selectors/goods-in.selectors';
|
||||
@@ -8,14 +8,9 @@ import { GoodsInSelectors } from '../store/selectors/goods-in.selectors';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class GoodsInService {
|
||||
constructor(private omsService: OrderService, private store: Store) {}
|
||||
constructor(private abholfach: AbholfachService, private store: Store) {}
|
||||
|
||||
search(
|
||||
input: string,
|
||||
branchNumber: string,
|
||||
skip: number = 0,
|
||||
take: number = 0
|
||||
) {
|
||||
search(input: string, branchNumber: string, skip: number = 0, take: number = 0) {
|
||||
const params = <QueryTokenDTO>{
|
||||
input: {
|
||||
qs: input,
|
||||
@@ -24,7 +19,7 @@ export class GoodsInService {
|
||||
skip,
|
||||
take,
|
||||
};
|
||||
return this.omsService.OrderWareneingangsuche(params).pipe(
|
||||
return this.abholfach.AbholfachWareneingangsuche(params).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw new Error(response.message);
|
||||
|
||||
24
apps/sales/src/app/core/services/scroll-position.service.ts
Normal file
24
apps/sales/src/app/core/services/scroll-position.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { SessionStorageService } from './session-storage.service';
|
||||
|
||||
export interface ScrollPosition {
|
||||
top?: number;
|
||||
left?: number;
|
||||
}
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ScrollPositionService {
|
||||
constructor(private storageService: SessionStorageService) {}
|
||||
|
||||
getPosition(key: string): ScrollPosition {
|
||||
return this.storageService.get(key);
|
||||
}
|
||||
|
||||
setPosition(key: string, position: ScrollPosition): void {
|
||||
this.storageService.set(key, position);
|
||||
}
|
||||
|
||||
deletePosition(key: string): void {
|
||||
this.storageService.delete(key);
|
||||
}
|
||||
}
|
||||
35
apps/sales/src/app/core/services/session-storage.service.ts
Normal file
35
apps/sales/src/app/core/services/session-storage.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class SessionStorageService extends StorageService {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
get<T>(key: string): T {
|
||||
let result: T;
|
||||
try {
|
||||
result = JSON.parse(sessionStorage.getItem(key));
|
||||
} catch (error) {
|
||||
console.error(`Error getting ${key}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
set<T>(key: string, data: T): void {
|
||||
try {
|
||||
sessionStorage.setItem(key, JSON.stringify(data));
|
||||
} catch (error) {
|
||||
console.error(`Error saving ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key: string): void {
|
||||
try {
|
||||
sessionStorage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.error(`Error deleting ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
apps/sales/src/app/core/services/storage.service.ts
Normal file
7
apps/sales/src/app/core/services/storage.service.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export abstract class StorageService {
|
||||
abstract get<T>(key: string): T;
|
||||
|
||||
abstract set<T>(key: string, data: T): void;
|
||||
|
||||
abstract delete(key: string): void;
|
||||
}
|
||||
24
apps/sales/src/app/core/services/vat.service.ts
Normal file
24
apps/sales/src/app/core/services/vat.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OrderService, VATType, VATDTO } from '@swagger/oms';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { BehaviorSubject, ReplaySubject } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class VatService {
|
||||
private getVatsSubject: ReplaySubject<VATDTO[]>;
|
||||
|
||||
constructor(private orderService: OrderService) {}
|
||||
|
||||
getVats() {
|
||||
if (!this.getVatsSubject) {
|
||||
this.getVatsSubject = new ReplaySubject();
|
||||
|
||||
this.orderService
|
||||
.OrderGetVATs({})
|
||||
.pipe(map((response) => response.result))
|
||||
.subscribe(this.getVatsSubject);
|
||||
}
|
||||
|
||||
return this.getVatsSubject.asObservable();
|
||||
}
|
||||
}
|
||||
@@ -25,17 +25,31 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getCurrentProcess(state: AppStateModel, processState: ProcessStateModel): Process {
|
||||
static getCurrentProcess(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
): Process {
|
||||
return processState.processes[state.currentProcesssId];
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getCurrentProcessId(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
): number {
|
||||
return processState.processes[state.currentProcesssId].id;
|
||||
}
|
||||
|
||||
@Selector([ProcessState])
|
||||
static getRecentProducts(state: ProcessStateModel) {
|
||||
return state.recentArticles;
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getSelectedProduct(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getSelectedProduct(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess) {
|
||||
return currentProcess.selectedItem;
|
||||
@@ -43,7 +57,10 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getCurrentProcessCustomerNotificationFlag(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getCurrentProcessCustomerNotificationFlag(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess) {
|
||||
return currentProcess.customerNotificationFlag;
|
||||
@@ -51,7 +68,10 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getScrollPositionForProduct(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getScrollPositionForProduct(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess) {
|
||||
return currentProcess.productScrollTo;
|
||||
@@ -59,7 +79,10 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getActiveUserId(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getActiveUserId(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess) {
|
||||
return currentProcess.activeCustomer;
|
||||
@@ -67,15 +90,25 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getDetailsUserId(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getDetailsUserId(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess && (!currentProcess.onlineCustomerCreationError || currentProcess.onlineCustomerCreationError.error === false)) {
|
||||
if (
|
||||
currentProcess &&
|
||||
(!currentProcess.onlineCustomerCreationError ||
|
||||
currentProcess.onlineCustomerCreationError.error === false)
|
||||
) {
|
||||
return currentProcess.detailsCustomer;
|
||||
}
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getUniqueIdentifier(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getUniqueIdentifier(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
const currentProcess = processState.processes[state.currentProcesssId];
|
||||
if (currentProcess) {
|
||||
return currentProcess.uniqueIdentifier;
|
||||
@@ -83,7 +116,10 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getCustomerSearch(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getCustomerSearch(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return processState.processes[state.currentProcesssId].customerSearch;
|
||||
}
|
||||
|
||||
@@ -98,27 +134,46 @@ export class ProcessSelectors {
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getFinishedOrders(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getFinishedOrders(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return processState.processes[state.currentProcesssId].finishedOrder;
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getArticleSearchErrorStatus(state: AppStateModel, processState: ProcessStateModel) {
|
||||
return processState.processes[state.currentProcesssId].articleSearchErrorStatus;
|
||||
static getArticleSearchErrorStatus(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return processState.processes[state.currentProcesssId]
|
||||
.articleSearchErrorStatus;
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getPreviousRoute(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getPreviousRoute(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return processState.processes[state.currentProcesssId].previousRoute;
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getCurrentRoute(state: AppStateModel, processState: ProcessStateModel) {
|
||||
return getProperty(processState, `processes.${state.currentProcesssId}.currentRoute`);
|
||||
static getCurrentRoute(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return getProperty(
|
||||
processState,
|
||||
`processes.${state.currentProcesssId}.currentRoute`
|
||||
);
|
||||
}
|
||||
|
||||
@Selector([AppState, ProcessState])
|
||||
static getProductFilters(state: AppStateModel, processState: ProcessStateModel) {
|
||||
static getProductFilters(
|
||||
state: AppStateModel,
|
||||
processState: ProcessStateModel
|
||||
) {
|
||||
return processState.processes[state.currentProcesssId].productSearchFilters;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ import {
|
||||
import { LoadBranches, LoadUserBranch } from '../actions/branch.actions';
|
||||
import { BranchSelectors } from '../selectors/branch.selector';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { RemoveProcessNewState, ReloadProcessData } from '../actions/process.actions';
|
||||
import {
|
||||
RemoveProcessNewState,
|
||||
ReloadProcessData,
|
||||
} from '../actions/process.actions';
|
||||
import { UserStateService } from '../../services/user-state.service';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { ReloadCustomersData } from '../actions/customer.actions';
|
||||
@@ -20,7 +23,7 @@ import { ReloadProductsData } from '../actions/product.actions';
|
||||
import { ReloadBreadcrumbsData } from '../actions/breadcrumb.actions';
|
||||
import { Router } from '@angular/router';
|
||||
import { Subject } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
|
||||
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
|
||||
import { ModuleSwitcher } from '../../models/app-switcher.enum';
|
||||
import { ModuleSwitcherService } from '../../services/module-switcher.service';
|
||||
import { ReloadFiltersData } from '../actions/filter.actions';
|
||||
@@ -31,7 +34,7 @@ import { ReloadRemission } from '../actions/remission.actions';
|
||||
import { ReloadFormState } from '../actions/forms.actions';
|
||||
import { FILIALE_LANDING_PAGE } from '../../utils/app.constants';
|
||||
|
||||
export const SYNC_DATA_VERSION = 210;
|
||||
export const SYNC_DATA_VERSION = 211;
|
||||
|
||||
export class AppStateModel {
|
||||
currentProcesssId: number;
|
||||
@@ -101,9 +104,13 @@ export class AppState {
|
||||
}
|
||||
|
||||
@Action(AppSetCurrentProcess)
|
||||
appSetCurrentProcess(ctx: StateContext<AppStateModel>, { payload }: AppSetCurrentProcess) {
|
||||
appSetCurrentProcess(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppSetCurrentProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const processExists = state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
const processExists =
|
||||
state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
if (processExists) {
|
||||
ctx.patchState({
|
||||
currentProcesssId: payload,
|
||||
@@ -115,18 +122,23 @@ export class AppState {
|
||||
@Action(AppAddProcess)
|
||||
appAddProcess(ctx: StateContext<AppStateModel>, { payload }: AppAddProcess) {
|
||||
const state = ctx.getState();
|
||||
const processExists = state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
const processExists =
|
||||
state.processIds.findIndex((t) => t === payload) !== -1;
|
||||
if (!processExists) {
|
||||
const processIds = [...state.processIds, payload];
|
||||
const currentProcesssId = payload;
|
||||
ctx.patchState({ currentProcesssId, processIds });
|
||||
|
||||
const branchesLoaded = this.store.selectSnapshot(BranchSelectors.getBranches);
|
||||
const branchesLoaded = this.store.selectSnapshot(
|
||||
BranchSelectors.getBranches
|
||||
);
|
||||
if (!branchesLoaded || Object.keys(branchesLoaded).length === 0) {
|
||||
this.store.dispatch(new LoadBranches());
|
||||
}
|
||||
|
||||
const userBranch = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const userBranch = this.store.selectSnapshot(
|
||||
BranchSelectors.getUserBranch
|
||||
);
|
||||
if (isNullOrUndefined(userBranch)) {
|
||||
this.store.dispatch(new LoadUserBranch());
|
||||
}
|
||||
@@ -136,7 +148,10 @@ export class AppState {
|
||||
}
|
||||
|
||||
@Action(AppDeleteProcess)
|
||||
appDeleteProcess(ctx: StateContext<AppStateModel>, { payload }: AppDeleteProcess) {
|
||||
appDeleteProcess(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppDeleteProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentIds = state.processIds;
|
||||
if (currentIds) {
|
||||
@@ -147,7 +162,10 @@ export class AppState {
|
||||
}
|
||||
|
||||
@Action(AppSwitchModule)
|
||||
appSwitchModule(ctx: StateContext<AppStateModel>, { payload }: AppSwitchModule) {
|
||||
appSwitchModule(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ payload }: AppSwitchModule
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({ activeModule: payload });
|
||||
this.syncApiState(state.processIds, payload);
|
||||
@@ -157,7 +175,10 @@ export class AppState {
|
||||
* Save store data on backend
|
||||
*/
|
||||
@Action(AppUserDataSync)
|
||||
appUserDataSynced(ctx: StateContext<AppStateModel>, { data, sync }: AppUserDataSync) {
|
||||
appUserDataSynced(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ data, sync }: AppUserDataSync
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
|
||||
let currentUserData: UserStateSyncData = {};
|
||||
@@ -184,7 +205,10 @@ export class AppState {
|
||||
* Initial store loading from API, triggered once on page load
|
||||
*/
|
||||
@Action(ReloadSavedState)
|
||||
reloadSavedState(ctx: StateContext<AppStateModel>, { data, sync }: ReloadSavedState) {
|
||||
reloadSavedState(
|
||||
ctx: StateContext<AppStateModel>,
|
||||
{ data, sync }: ReloadSavedState
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
|
||||
let currentUserData: UserStateSyncData = {};
|
||||
@@ -208,7 +232,10 @@ export class AppState {
|
||||
if (sync && syncedData.version === SYNC_DATA_VERSION) {
|
||||
this.reloadDataFromAPI(syncedData);
|
||||
|
||||
if (syncedData.currentProcesssId || syncedData.activeModule === ModuleSwitcher.Branch) {
|
||||
if (
|
||||
syncedData.currentProcesssId ||
|
||||
syncedData.activeModule === ModuleSwitcher.Branch
|
||||
) {
|
||||
ctx.patchState({
|
||||
...state,
|
||||
synced: true,
|
||||
@@ -235,11 +262,19 @@ export class AppState {
|
||||
return;
|
||||
}
|
||||
if (data.customers) {
|
||||
this.store.dispatch(new ReloadCustomersData(data.customers, data.lastCreatedCustomerId, data.cachedCustomerSearch));
|
||||
this.store.dispatch(
|
||||
new ReloadCustomersData(
|
||||
data.customers,
|
||||
data.lastCreatedCustomerId,
|
||||
data.cachedCustomerSearch
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.products) {
|
||||
this.store.dispatch(new ReloadProductsData(data.products, data.cachedProductResults));
|
||||
this.store.dispatch(
|
||||
new ReloadProductsData(data.products, data.cachedProductResults)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.carts) {
|
||||
@@ -259,7 +294,9 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.processes) {
|
||||
this.store.dispatch(new ReloadProcessData(data.processes, data.recentArticles));
|
||||
this.store.dispatch(
|
||||
new ReloadProcessData(data.processes, data.recentArticles)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.formsState) {
|
||||
@@ -267,11 +304,20 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.activeModule === ModuleSwitcher.Customer) {
|
||||
if (data.processes && data.currentProcesssId && data.processes[data.currentProcesssId]) {
|
||||
if (
|
||||
data.processes &&
|
||||
data.currentProcesssId &&
|
||||
data.processes[data.currentProcesssId]
|
||||
) {
|
||||
const currentProcesssId = data.currentProcesssId;
|
||||
const currentRoute = data.processes[currentProcesssId].currentRoute;
|
||||
if (currentRoute && currentRoute.length > 0) {
|
||||
this.routingAvailableAction(data, currentProcesssId, currentRoute, data.activeModule);
|
||||
this.routingAvailableAction(
|
||||
data,
|
||||
currentProcesssId,
|
||||
currentRoute,
|
||||
data.activeModule
|
||||
);
|
||||
} else {
|
||||
this.router.navigate(['/dashboard']);
|
||||
}
|
||||
@@ -283,7 +329,12 @@ export class AppState {
|
||||
const currentProcesssId = -1;
|
||||
const currentRoute = data.branchProcess.currentRoute;
|
||||
if (currentRoute && currentRoute.length > 0) {
|
||||
this.routingAvailableAction(data, currentProcesssId, currentRoute, data.activeModule);
|
||||
this.routingAvailableAction(
|
||||
data,
|
||||
currentProcesssId,
|
||||
currentRoute,
|
||||
data.activeModule
|
||||
);
|
||||
} else {
|
||||
this.router.navigate([FILIALE_LANDING_PAGE]);
|
||||
}
|
||||
@@ -297,11 +348,24 @@ export class AppState {
|
||||
}
|
||||
|
||||
if (data.activeModule === ModuleSwitcher.Branch) {
|
||||
this.moduleSwitcherService.switch(ModuleSwitcher.Branch, data.branchProcess);
|
||||
this.moduleSwitcherService.switch(
|
||||
ModuleSwitcher.Branch,
|
||||
data.branchProcess
|
||||
);
|
||||
}
|
||||
|
||||
if (data.processesBreadcrumbs && data.activeCrumbs && (data.currentProcesssId || data.activeModule === ModuleSwitcher.Branch)) {
|
||||
this.store.dispatch(new ReloadBreadcrumbsData(data.processesBreadcrumbs, data.activeCrumbs, data.previusMenuPath));
|
||||
if (
|
||||
data.processesBreadcrumbs &&
|
||||
data.activeCrumbs &&
|
||||
(data.currentProcesssId || data.activeModule === ModuleSwitcher.Branch)
|
||||
) {
|
||||
this.store.dispatch(
|
||||
new ReloadBreadcrumbsData(
|
||||
data.processesBreadcrumbs,
|
||||
data.activeCrumbs,
|
||||
data.previusMenuPath
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (data.filters && data.processesSelectedFilters && data.dropdownFilters) {
|
||||
@@ -327,16 +391,28 @@ export class AppState {
|
||||
);
|
||||
}
|
||||
|
||||
private routingAvailableAction(data: UserStateSyncData, currentProcesssId: number, currentRoute: string, module: ModuleSwitcher) {
|
||||
const hasActiveCrumbsAvailableForProcess = data.activeCrumbs && data.activeCrumbs[currentProcesssId];
|
||||
private routingAvailableAction(
|
||||
data: UserStateSyncData,
|
||||
currentProcesssId: number,
|
||||
currentRoute: string,
|
||||
module: ModuleSwitcher
|
||||
) {
|
||||
const hasActiveCrumbsAvailableForProcess =
|
||||
data.activeCrumbs && data.activeCrumbs[currentProcesssId];
|
||||
const activeCrumbs = data.activeCrumbs[currentProcesssId];
|
||||
const hasProcessBreadcrumbsAvailableForProcess = data.processesBreadcrumbs && data.processesBreadcrumbs[activeCrumbs];
|
||||
const hasProcessBreadcrumbsAvailableForProcess =
|
||||
data.processesBreadcrumbs && data.processesBreadcrumbs[activeCrumbs];
|
||||
|
||||
const breadcrumb = hasProcessBreadcrumbsAvailableForProcess
|
||||
? data.processesBreadcrumbs[activeCrumbs].find((t) => t.processId === currentProcesssId)
|
||||
? data.processesBreadcrumbs[activeCrumbs].find(
|
||||
(t) => t.processId === currentProcesssId
|
||||
)
|
||||
: null;
|
||||
|
||||
const breadcrumbPath = breadcrumb && breadcrumb.breadcrumbs ? breadcrumb.breadcrumbs.find((t) => t && t.path === currentRoute) : null;
|
||||
const breadcrumbPath =
|
||||
breadcrumb && breadcrumb.breadcrumbs
|
||||
? breadcrumb.breadcrumbs.find((t) => t && t.path === currentRoute)
|
||||
: null;
|
||||
|
||||
if (
|
||||
hasActiveCrumbsAvailableForProcess &&
|
||||
|
||||
@@ -1,21 +1,8 @@
|
||||
import {
|
||||
State,
|
||||
Action,
|
||||
StateContext,
|
||||
Store,
|
||||
StateOperator,
|
||||
Selector,
|
||||
} from '@ngxs/store';
|
||||
import { State, Action, StateContext, Store, StateOperator, Selector } from '@ngxs/store';
|
||||
import { ProcessBreadcrumbs } from '../../models/process-breadcrumbs.model';
|
||||
import * as actions from '../actions/breadcrumb.actions';
|
||||
import { Breadcrumb } from '../../models/breadcrumb.model';
|
||||
import {
|
||||
patch,
|
||||
removeItem,
|
||||
iif,
|
||||
updateItem,
|
||||
append,
|
||||
} from '@ngxs/store/operators';
|
||||
import { patch, removeItem, iif, updateItem, append } from '@ngxs/store/operators';
|
||||
import { AppState } from './app.state';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { AppUserDataSync } from '../actions/app.actions';
|
||||
@@ -59,10 +46,7 @@ export class BreadcrumbsStateModel {
|
||||
export class BreadcrumbsState {
|
||||
@Selector()
|
||||
static hasRemissionBreadcrumbs(breadcrumbsState: BreadcrumbsStateModel) {
|
||||
return (
|
||||
breadcrumbsState.processesBreadcrumbs.remission &&
|
||||
breadcrumbsState.processesBreadcrumbs.remission.length > 0
|
||||
);
|
||||
return breadcrumbsState.processesBreadcrumbs.remission && breadcrumbsState.processesBreadcrumbs.remission.length > 0;
|
||||
}
|
||||
|
||||
editAddaddressPageFilter = (path: string) =>
|
||||
@@ -73,66 +57,37 @@ export class BreadcrumbsState {
|
||||
// tslint:disable-next-line: semicolon
|
||||
path.includes('/delivery/add/');
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private moduleSwitcherService: ModuleSwitcherService
|
||||
) {}
|
||||
constructor(private store: Store, private router: Router, private moduleSwitcherService: ModuleSwitcherService) {}
|
||||
|
||||
@Action(actions.AddBreadcrumb)
|
||||
addBreadcrumb(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ payload, breadcrumbsId, isMainBreadcrumb }: actions.AddBreadcrumb
|
||||
) {
|
||||
addBreadcrumb(ctx: StateContext<BreadcrumbsStateModel>, { payload, breadcrumbsId, isMainBreadcrumb }: actions.AddBreadcrumb) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
|
||||
const currentProcessBreadcrumbs: ProcessBreadcrumbs[] =
|
||||
state.processesBreadcrumbs[breadcrumbsId];
|
||||
const currentProcessBreadcrumbs: ProcessBreadcrumbs[] = state.processesBreadcrumbs[breadcrumbsId];
|
||||
const pathData = payload.path.split('/');
|
||||
|
||||
if (!currentProcessBreadcrumbs || !currentProcessId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const processBreadcrumbsFound =
|
||||
currentProcessBreadcrumbs.find(
|
||||
(t) => t.processId === currentProcessId
|
||||
) !== undefined;
|
||||
const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
|
||||
let processesBreadcrumbs: ProcessBreadcrumbs[];
|
||||
if (processBreadcrumbsFound) {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map(
|
||||
(pb: ProcessBreadcrumbs) => {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
|
||||
if (pb.processId === currentProcessId) {
|
||||
return this.updateProcessBreadcrumb(
|
||||
payload,
|
||||
isMainBreadcrumb,
|
||||
pathData.length > 0 ? pathData[pathData.length - 1] : ''
|
||||
)(pb);
|
||||
return this.updateProcessBreadcrumb(payload, isMainBreadcrumb, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
|
||||
} else {
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
processesBreadcrumbs = [
|
||||
...currentProcessBreadcrumbs,
|
||||
{ processId: currentProcessId, breadcrumbs: [payload] },
|
||||
];
|
||||
processesBreadcrumbs = [...currentProcessBreadcrumbs, { processId: currentProcessId, breadcrumbs: [payload] }];
|
||||
}
|
||||
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(
|
||||
state.processesBreadcrumbs,
|
||||
breadcrumbsId,
|
||||
processesBreadcrumbs
|
||||
);
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);
|
||||
|
||||
if (
|
||||
(pathData.length > 0 && pathData[pathData.length - 1] === 'billing') ||
|
||||
pathData[pathData.length - 1] === 'delivery'
|
||||
) {
|
||||
if ((pathData.length > 0 && pathData[pathData.length - 1] === 'billing') || pathData[pathData.length - 1] === 'delivery') {
|
||||
pathData.pop();
|
||||
const newPath = pathData.join('/');
|
||||
this.store.dispatch(new SetPreviusPath(newPath));
|
||||
@@ -152,12 +107,7 @@ export class BreadcrumbsState {
|
||||
}
|
||||
}
|
||||
|
||||
const previousMenuPath = this.updatePreviousMenuPath(
|
||||
currentProcessId,
|
||||
state.previusMenuPath,
|
||||
state.activeCrumbs,
|
||||
payload
|
||||
);
|
||||
const previousMenuPath = this.updatePreviousMenuPath(currentProcessId, state.previusMenuPath, state.activeCrumbs, payload);
|
||||
|
||||
ctx.setState({
|
||||
processesBreadcrumbs: {
|
||||
@@ -173,64 +123,39 @@ export class BreadcrumbsState {
|
||||
activeCrumbs: currentProcessActiveCrumbs,
|
||||
previusMenuPath: previousMenuPath,
|
||||
});
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
currentProcessActiveCrumbs,
|
||||
previousMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, previousMenuPath);
|
||||
}
|
||||
|
||||
@Action(actions.AddBreadcrumbNoDub)
|
||||
addBreadcrumbNoDub(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ payload, breadcrumbsId }: actions.AddBreadcrumbNoDub
|
||||
) {
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
addBreadcrumbNoDub(ctx: StateContext<BreadcrumbsStateModel>, { payload, breadcrumbsId }: actions.AddBreadcrumbNoDub) {
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const state = ctx.getState();
|
||||
const currentProcessBreadcrumbs: ProcessBreadcrumbs[] =
|
||||
state.processesBreadcrumbs[breadcrumbsId];
|
||||
const currentProcessBreadcrumbs: ProcessBreadcrumbs[] = state.processesBreadcrumbs[breadcrumbsId];
|
||||
const pathData = payload.path.split('/');
|
||||
|
||||
if (!currentProcessBreadcrumbs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const processBreadcrumbsFound =
|
||||
currentProcessBreadcrumbs.find(
|
||||
(t) => t.processId === currentProcessId
|
||||
) !== undefined;
|
||||
const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
|
||||
let processesBreadcrumbs: ProcessBreadcrumbs[];
|
||||
if (processBreadcrumbsFound) {
|
||||
const currentBreadcrumb = currentProcessBreadcrumbs.find(
|
||||
(t) => t.processId === currentProcessId
|
||||
).breadcrumbs;
|
||||
const currentBreadcrumb = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId).breadcrumbs;
|
||||
if (currentBreadcrumb) {
|
||||
const lastPath = currentBreadcrumb[currentBreadcrumb.length - 1];
|
||||
if (lastPath && lastPath.path !== payload.path) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map(
|
||||
(pb: ProcessBreadcrumbs) => {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
|
||||
if (pb.processId === currentProcessId) {
|
||||
return this.updateProcessBreadcrumbFoNoDub(
|
||||
payload,
|
||||
false,
|
||||
pathData.length > 0 ? pathData[pathData.length - 1] : ''
|
||||
)(pb);
|
||||
return this.updateProcessBreadcrumbFoNoDub(payload, false, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
|
||||
} else {
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(
|
||||
state.processesBreadcrumbs,
|
||||
breadcrumbsId,
|
||||
processesBreadcrumbs
|
||||
);
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);
|
||||
|
||||
ctx.patchState({
|
||||
processesBreadcrumbs: {
|
||||
@@ -244,20 +169,14 @@ export class BreadcrumbsState {
|
||||
// taskCalendar: updatedCrumbs['tasksCalendar']
|
||||
},
|
||||
});
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
state.activeCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Action(actions.DeleteLastPreviousPath)
|
||||
deleteLastPreviousPath(ctx: StateContext<BreadcrumbsStateModel>) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
if (!state || !currentProcessId) {
|
||||
return;
|
||||
}
|
||||
@@ -266,10 +185,7 @@ export class BreadcrumbsState {
|
||||
if (previousMenuPath && previousMenuPath[currentProcessId]) {
|
||||
const updatedPrevousPaths = {
|
||||
...previousMenuPath,
|
||||
[currentProcessId]: previousMenuPath[currentProcessId].slice(
|
||||
0,
|
||||
previousMenuPath[currentProcessId].length - 1
|
||||
),
|
||||
[currentProcessId]: previousMenuPath[currentProcessId].slice(0, previousMenuPath[currentProcessId].length - 1),
|
||||
};
|
||||
ctx.patchState({ previusMenuPath: updatedPrevousPaths });
|
||||
}
|
||||
@@ -278,16 +194,9 @@ export class BreadcrumbsState {
|
||||
@Action(actions.ResetBreadcrumbsTo)
|
||||
resetBreadCrumbs(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{
|
||||
payload,
|
||||
breadcrumbsId,
|
||||
isMainBreadcrumb,
|
||||
isBranchView,
|
||||
}: actions.ResetBreadcrumbsTo
|
||||
{ payload, breadcrumbsId, isMainBreadcrumb, isBranchView }: actions.ResetBreadcrumbsTo
|
||||
) {
|
||||
const currentProcessId = isBranchView
|
||||
? -1
|
||||
: this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const currentProcessId = isBranchView ? -1 : this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const state = ctx.getState();
|
||||
|
||||
const currentProcessBreadcrumbs = state.processesBreadcrumbs[breadcrumbsId];
|
||||
@@ -297,42 +206,24 @@ export class BreadcrumbsState {
|
||||
return;
|
||||
}
|
||||
|
||||
const processBreadcrumbsFound =
|
||||
currentProcessBreadcrumbs.find(
|
||||
(t) => t.processId === currentProcessId
|
||||
) !== undefined;
|
||||
const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
|
||||
let processesBreadcrumbs: ProcessBreadcrumbs[];
|
||||
if (processBreadcrumbsFound) {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map(
|
||||
(pb: ProcessBreadcrumbs) => {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
|
||||
if (pb.processId === currentProcessId) {
|
||||
return this.updateProcessBreadcrumb(
|
||||
payload,
|
||||
isMainBreadcrumb,
|
||||
pathData.length > 0 ? pathData[pathData.length - 1] : ''
|
||||
)(pb);
|
||||
return this.updateProcessBreadcrumb(payload, isMainBreadcrumb, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
|
||||
} else {
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
processesBreadcrumbs = [
|
||||
...currentProcessBreadcrumbs,
|
||||
{ processId: currentProcessId, breadcrumbs: [payload] },
|
||||
];
|
||||
processesBreadcrumbs = [...currentProcessBreadcrumbs, { processId: currentProcessId, breadcrumbs: [payload] }];
|
||||
}
|
||||
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(
|
||||
state.processesBreadcrumbs,
|
||||
breadcrumbsId,
|
||||
processesBreadcrumbs
|
||||
);
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);
|
||||
|
||||
if (updatedCrumbs[breadcrumbsId].length === 0) {
|
||||
updatedCrumbs[breadcrumbsId] = [
|
||||
{ processId: currentProcessId, breadcrumbs: [payload] },
|
||||
];
|
||||
updatedCrumbs[breadcrumbsId] = [{ processId: currentProcessId, breadcrumbs: [payload] }];
|
||||
}
|
||||
|
||||
let currentProcessActiveCrumbs = {};
|
||||
@@ -349,12 +240,7 @@ export class BreadcrumbsState {
|
||||
}
|
||||
}
|
||||
|
||||
const previousMenuPath = this.updatePreviousMenuPath(
|
||||
currentProcessId,
|
||||
state.previusMenuPath,
|
||||
state.activeCrumbs,
|
||||
payload
|
||||
);
|
||||
const previousMenuPath = this.updatePreviousMenuPath(currentProcessId, state.previusMenuPath, state.activeCrumbs, payload);
|
||||
|
||||
ctx.setState({
|
||||
processesBreadcrumbs: {
|
||||
@@ -371,11 +257,7 @@ export class BreadcrumbsState {
|
||||
previusMenuPath: previousMenuPath,
|
||||
});
|
||||
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
currentProcessActiveCrumbs,
|
||||
previousMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, previousMenuPath);
|
||||
}
|
||||
|
||||
private updatePreviousMenuPath(
|
||||
@@ -388,26 +270,19 @@ export class BreadcrumbsState {
|
||||
[id: number]: { crumb: string; path: string }[];
|
||||
} = {};
|
||||
const activeModule = this.store.selectSnapshot(AppState.activeModule);
|
||||
const defaultCrumb =
|
||||
activeModule === ModuleSwitcher.Customer ? 'dashboard' : 'goodsin';
|
||||
const defaultPath =
|
||||
activeModule === ModuleSwitcher.Customer
|
||||
? '/dashboard'
|
||||
: FILIALE_LANDING_PAGE;
|
||||
const defaultCrumb = activeModule === ModuleSwitcher.Customer ? 'dashboard' : 'goodsin';
|
||||
const defaultPath = activeModule === ModuleSwitcher.Customer ? '/dashboard' : FILIALE_LANDING_PAGE;
|
||||
if (currentProcessId) {
|
||||
const route = this.router.url;
|
||||
const currentProcessCrumb = crumbs[currentProcessId];
|
||||
const isCustomeOrderDetails = /^[/]customer[/][0-9]{1,}[/]order[/][0-9]{1,}$/;
|
||||
if (route) {
|
||||
if (
|
||||
(route === '/cart/confirmation' &&
|
||||
breadcrumb &&
|
||||
!isCustomeOrderDetails.test(breadcrumb.path)) ||
|
||||
(route === '/cart/confirmation' && breadcrumb && !isCustomeOrderDetails.test(breadcrumb.path)) ||
|
||||
(menuPath &&
|
||||
menuPath[currentProcessId] &&
|
||||
menuPath[currentProcessId][menuPath[currentProcessId].length - 1] &&
|
||||
menuPath[currentProcessId][menuPath[currentProcessId].length - 1]
|
||||
.path === '/cart/confirmation')
|
||||
menuPath[currentProcessId][menuPath[currentProcessId].length - 1].path === '/cart/confirmation')
|
||||
) {
|
||||
previousMenuPath = {
|
||||
...menuPath,
|
||||
@@ -415,21 +290,15 @@ export class BreadcrumbsState {
|
||||
};
|
||||
} else {
|
||||
if (menuPath[currentProcessId]) {
|
||||
const lastPath =
|
||||
menuPath[currentProcessId][menuPath[currentProcessId].length - 1];
|
||||
const isOntheSamePage = lastPath
|
||||
? lastPath.crumb === currentProcessCrumb &&
|
||||
lastPath.path === route
|
||||
: false;
|
||||
const lastPath = menuPath[currentProcessId][menuPath[currentProcessId].length - 1];
|
||||
const isOntheSamePage = lastPath ? lastPath.crumb === currentProcessCrumb && lastPath.path === route : false;
|
||||
if (!isOntheSamePage) {
|
||||
previousMenuPath = {
|
||||
...menuPath,
|
||||
[currentProcessId]: [
|
||||
...menuPath[currentProcessId],
|
||||
{
|
||||
crumb: currentProcessCrumb
|
||||
? currentProcessCrumb
|
||||
: defaultCrumb,
|
||||
crumb: currentProcessCrumb ? currentProcessCrumb : defaultCrumb,
|
||||
path: currentProcessCrumb ? route : defaultPath,
|
||||
},
|
||||
],
|
||||
@@ -442,9 +311,7 @@ export class BreadcrumbsState {
|
||||
...menuPath,
|
||||
[currentProcessId]: [
|
||||
{
|
||||
crumb: currentProcessCrumb
|
||||
? currentProcessCrumb
|
||||
: defaultCrumb,
|
||||
crumb: currentProcessCrumb ? currentProcessCrumb : defaultCrumb,
|
||||
path: defaultPath,
|
||||
},
|
||||
],
|
||||
@@ -478,8 +345,7 @@ export class BreadcrumbsState {
|
||||
const updatedBreadcrumbs = updateItem<Breadcrumb>(
|
||||
(breadcrumb) =>
|
||||
breadcrumb.name === payload.name ||
|
||||
(breadcrumb.path.substring(0, 16) ===
|
||||
payload.path.substring(0, 16) &&
|
||||
(breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16) &&
|
||||
pathEnding !== 'billing' &&
|
||||
pathEnding !== 'delivery' &&
|
||||
this.editAddaddressPageFilter(payload.path)),
|
||||
@@ -495,11 +361,7 @@ export class BreadcrumbsState {
|
||||
(item: Breadcrumb[]) => append<Breadcrumb>([payload])(item)
|
||||
)(updatedBreadcrumbs);
|
||||
const isCustomeOrderDetails = /^[/]customer[/][0-9]{1,}[/]order[/][0-9]{1,}$/;
|
||||
if (
|
||||
breadcrumbs &&
|
||||
breadcrumbs[0].path === 'cart/confirmation' &&
|
||||
!isCustomeOrderDetails.test(payload.path)
|
||||
) {
|
||||
if (breadcrumbs && breadcrumbs[0].path === 'cart/confirmation' && !isCustomeOrderDetails.test(payload.path)) {
|
||||
breadcrumbs = [breadcrumbs[0]];
|
||||
}
|
||||
|
||||
@@ -523,14 +385,11 @@ export class BreadcrumbsState {
|
||||
breadcrumbs: [payload],
|
||||
};
|
||||
} else {
|
||||
const updatedBreadcrumbs = updateItem<Breadcrumb>(
|
||||
(breadcrumb) => breadcrumb.path === payload.path,
|
||||
{
|
||||
const updatedBreadcrumbs = updateItem<Breadcrumb>((breadcrumb) => breadcrumb.path === payload.path, {
|
||||
name: payload.name,
|
||||
path: payload.path,
|
||||
queryParams: payload.queryParams,
|
||||
}
|
||||
)(processBreadcrumbs.breadcrumbs);
|
||||
})(processBreadcrumbs.breadcrumbs);
|
||||
|
||||
return <ProcessBreadcrumbs>{
|
||||
...processBreadcrumbs,
|
||||
@@ -555,10 +414,8 @@ export class BreadcrumbsState {
|
||||
const updatedBreadcrumbs = updateItem<Breadcrumb>(
|
||||
(breadcrumb) =>
|
||||
breadcrumb.name === payload.name ||
|
||||
((breadcrumb.path.substring(0, 16) ===
|
||||
payload.path.substring(0, 16) ||
|
||||
breadcrumb.path.substring(0, 14) ===
|
||||
payload.path.substring(0, 14)) &&
|
||||
((breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16) ||
|
||||
breadcrumb.path.substring(0, 14) === payload.path.substring(0, 14)) &&
|
||||
pathEnding !== 'billing' &&
|
||||
pathEnding !== 'delivery' &&
|
||||
!this.editAddaddressPageFilter(payload.path)),
|
||||
@@ -578,33 +435,20 @@ export class BreadcrumbsState {
|
||||
}
|
||||
|
||||
@Action(actions.PopBreadcrumbsAfterCurrent)
|
||||
popBreadcrumbs(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ path }: actions.PopBreadcrumbsAfterCurrent
|
||||
) {
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
popBreadcrumbs(ctx: StateContext<BreadcrumbsStateModel>, { path }: actions.PopBreadcrumbsAfterCurrent) {
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const state = ctx.getState();
|
||||
const previusMenuPath = { ...state.previusMenuPath };
|
||||
|
||||
if (previusMenuPath && currentProcessId) {
|
||||
const currentPreviousMenuPaths = previusMenuPath[currentProcessId];
|
||||
if (currentPreviousMenuPaths) {
|
||||
const pathIndex = currentPreviousMenuPaths.findIndex(
|
||||
(p) => p && p.path === path
|
||||
);
|
||||
const pathIndex = currentPreviousMenuPaths.findIndex((p) => p && p.path === path);
|
||||
if (pathIndex !== -1) {
|
||||
const updatedMenuPaths = [
|
||||
...currentPreviousMenuPaths.slice(0, pathIndex),
|
||||
];
|
||||
const updatedMenuPaths = [...currentPreviousMenuPaths.slice(0, pathIndex)];
|
||||
previusMenuPath[currentProcessId] = updatedMenuPaths;
|
||||
ctx.patchState({ previusMenuPath });
|
||||
this.syncApiState(
|
||||
state.processesBreadcrumbs,
|
||||
state.activeCrumbs,
|
||||
previusMenuPath
|
||||
);
|
||||
this.syncApiState(state.processesBreadcrumbs, state.activeCrumbs, previusMenuPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,9 +456,7 @@ export class BreadcrumbsState {
|
||||
|
||||
deleteBreadcrumbsByIndex(index: number): StateOperator<ProcessBreadcrumbs> {
|
||||
return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
|
||||
const breadcrumbs = removeItem<Breadcrumb>(
|
||||
(b) => processBreadcrumbs.breadcrumbs.indexOf(b) > index
|
||||
)(processBreadcrumbs.breadcrumbs);
|
||||
const breadcrumbs = removeItem<Breadcrumb>((b) => processBreadcrumbs.breadcrumbs.indexOf(b) > index)(processBreadcrumbs.breadcrumbs);
|
||||
return <ProcessBreadcrumbs>{
|
||||
...processBreadcrumbs,
|
||||
breadcrumbs: breadcrumbs,
|
||||
@@ -623,14 +465,9 @@ export class BreadcrumbsState {
|
||||
}
|
||||
|
||||
@Action(actions.PopBreadcrumbsBeforeCurrent)
|
||||
popBreadcrumbsBefore(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ breadcrumbsId }: actions.PopBreadcrumbsBeforeCurrent
|
||||
) {
|
||||
popBreadcrumbsBefore(ctx: StateContext<BreadcrumbsStateModel>, { breadcrumbsId }: actions.PopBreadcrumbsBeforeCurrent) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const currentProcessBreadcrumbs = state.processesBreadcrumbs[breadcrumbsId];
|
||||
|
||||
let currentProcessActiveCrumbs = {};
|
||||
@@ -662,20 +499,12 @@ export class BreadcrumbsState {
|
||||
previusMenuPath: {},
|
||||
});
|
||||
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
currentProcessActiveCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, state.previusMenuPath);
|
||||
}
|
||||
|
||||
leaveOnlyLastBreadcrumb(): StateOperator<ProcessBreadcrumbs> {
|
||||
return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
|
||||
const breadcrumbs = [
|
||||
processBreadcrumbs.breadcrumbs[
|
||||
processBreadcrumbs.breadcrumbs.length - 1
|
||||
],
|
||||
];
|
||||
const breadcrumbs = [processBreadcrumbs.breadcrumbs[processBreadcrumbs.breadcrumbs.length - 1]];
|
||||
return <ProcessBreadcrumbs>{
|
||||
...processBreadcrumbs,
|
||||
breadcrumbs: breadcrumbs,
|
||||
@@ -693,14 +522,9 @@ export class BreadcrumbsState {
|
||||
}
|
||||
|
||||
@Action(actions.PopLastBreadcrumbs)
|
||||
popLastBreadcrumb(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ path }: actions.PopLastBreadcrumbs
|
||||
) {
|
||||
popLastBreadcrumb(ctx: StateContext<BreadcrumbsStateModel>, { path }: actions.PopLastBreadcrumbs) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const activeCrumbs = state.activeCrumbs[currentProcessId];
|
||||
if (!activeCrumbs) {
|
||||
return;
|
||||
@@ -708,9 +532,7 @@ export class BreadcrumbsState {
|
||||
if (
|
||||
state.previusMenuPath[currentProcessId] &&
|
||||
state.previusMenuPath[currentProcessId].length > 0 &&
|
||||
state.previusMenuPath[currentProcessId][
|
||||
state.previusMenuPath[currentProcessId].length - 1
|
||||
].crumb !== activeCrumbs
|
||||
state.previusMenuPath[currentProcessId][state.previusMenuPath[currentProcessId].length - 1].crumb !== activeCrumbs
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -718,19 +540,10 @@ export class BreadcrumbsState {
|
||||
|
||||
const processesBreadcrumbs = updateItem<ProcessBreadcrumbs>(
|
||||
(item) => item.processId === currentProcessId,
|
||||
(item) =>
|
||||
this.deleteLastBreadcrumbForProcess(
|
||||
path,
|
||||
currentProcessId,
|
||||
state.previusMenuPath
|
||||
)(item)
|
||||
(item) => this.deleteLastBreadcrumbForProcess(path, currentProcessId, state.previusMenuPath)(item)
|
||||
)(currentProcessBreadcrumbs);
|
||||
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(
|
||||
state.processesBreadcrumbs,
|
||||
activeCrumbs,
|
||||
processesBreadcrumbs
|
||||
);
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, activeCrumbs, processesBreadcrumbs);
|
||||
|
||||
ctx.setState({
|
||||
processesBreadcrumbs: {
|
||||
@@ -746,11 +559,7 @@ export class BreadcrumbsState {
|
||||
activeCrumbs: state.activeCrumbs,
|
||||
previusMenuPath: state.previusMenuPath,
|
||||
});
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
state.activeCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
|
||||
}
|
||||
|
||||
deleteLastBreadcrumbForProcess(
|
||||
@@ -772,13 +581,7 @@ export class BreadcrumbsState {
|
||||
previusMenuPath[processId].length &&
|
||||
previusMenuPath[processId][previusMenuPath[processId].length - 1]
|
||||
) {
|
||||
this.store.dispatch(
|
||||
new ChangeCurrentRoute(
|
||||
previusMenuPath[processId][
|
||||
previusMenuPath[processId].length - 1
|
||||
].path
|
||||
)
|
||||
);
|
||||
this.store.dispatch(new ChangeCurrentRoute(previusMenuPath[processId][previusMenuPath[processId].length - 1].path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,11 +605,8 @@ export class BreadcrumbsState {
|
||||
if (!currentProcessBreadcrumbs) {
|
||||
return;
|
||||
}
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const breadcrumbs: ProcessBreadcrumbs[] =
|
||||
currentProcessBreadcrumbs[breadcrumbId];
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const breadcrumbs: ProcessBreadcrumbs[] = currentProcessBreadcrumbs[breadcrumbId];
|
||||
if (!currentProcessId || !breadcrumbs) {
|
||||
return;
|
||||
}
|
||||
@@ -838,11 +638,7 @@ export class BreadcrumbsState {
|
||||
ctx.patchState({
|
||||
processesBreadcrumbs,
|
||||
});
|
||||
this.syncApiState(
|
||||
processesBreadcrumbs,
|
||||
state.activeCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
|
||||
}
|
||||
|
||||
@Action(actions.UpdateCurrentBreadcrumbName)
|
||||
@@ -854,9 +650,7 @@ export class BreadcrumbsState {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
if (!currentProcessId) {
|
||||
return;
|
||||
}
|
||||
@@ -867,41 +661,24 @@ export class BreadcrumbsState {
|
||||
const currentProcessBreadcrumbs = state.processesBreadcrumbs[activeCrumbs];
|
||||
const pathData = payload.split('/');
|
||||
|
||||
const processBreadcrumbsFound =
|
||||
currentProcessBreadcrumbs.find(
|
||||
(t) => t.processId === currentProcessId
|
||||
) !== undefined;
|
||||
const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
|
||||
let processesBreadcrumbs: ProcessBreadcrumbs[];
|
||||
if (processBreadcrumbsFound) {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map(
|
||||
(pb: ProcessBreadcrumbs) => {
|
||||
if (
|
||||
pb.processId === currentProcessId &&
|
||||
pb.breadcrumbs &&
|
||||
pb.breadcrumbs.length > 0
|
||||
) {
|
||||
processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
|
||||
if (pb.processId === currentProcessId && pb.breadcrumbs && pb.breadcrumbs.length > 0) {
|
||||
const newBreadcrumb = {
|
||||
...pb.breadcrumbs[pb.breadcrumbs.length - 1],
|
||||
name: payload,
|
||||
queryParams: queryParams,
|
||||
};
|
||||
return this.updateNameAndQueryParams(
|
||||
newBreadcrumb,
|
||||
false,
|
||||
pathData.length > 0 ? pathData[pathData.length - 1] : ''
|
||||
)(pb);
|
||||
return this.updateNameAndQueryParams(newBreadcrumb, false, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
|
||||
} else {
|
||||
return pb;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(
|
||||
state.processesBreadcrumbs,
|
||||
activeCrumbs,
|
||||
processesBreadcrumbs
|
||||
);
|
||||
const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, activeCrumbs, processesBreadcrumbs);
|
||||
|
||||
ctx.setState({
|
||||
processesBreadcrumbs: {
|
||||
@@ -917,18 +694,10 @@ export class BreadcrumbsState {
|
||||
activeCrumbs: state.activeCrumbs,
|
||||
previusMenuPath: state.previusMenuPath,
|
||||
});
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
state.activeCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
|
||||
}
|
||||
|
||||
updateBreadcrumbsForProcess(
|
||||
payload: string,
|
||||
currentPath: string,
|
||||
queryParams: any
|
||||
): StateOperator<Readonly<ProcessBreadcrumbs>> {
|
||||
updateBreadcrumbsForProcess(payload: string, currentPath: string, queryParams: any): StateOperator<Readonly<ProcessBreadcrumbs>> {
|
||||
return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
|
||||
const breadcrumbs = updateItem<Breadcrumb>(
|
||||
(item) => item.path === currentPath,
|
||||
@@ -946,48 +715,17 @@ export class BreadcrumbsState {
|
||||
}
|
||||
|
||||
@Action(actions.DeleteBreadcrumbsForProcess)
|
||||
deleteBreadcrumbsForProcess(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ payload }: actions.DeleteBreadcrumbsForProcess
|
||||
) {
|
||||
deleteBreadcrumbsForProcess(ctx: StateContext<BreadcrumbsStateModel>, { payload }: actions.DeleteBreadcrumbsForProcess) {
|
||||
const state = ctx.getState();
|
||||
|
||||
if (state.processesBreadcrumbs) {
|
||||
const customer = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'customer',
|
||||
payload
|
||||
);
|
||||
const product = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'product',
|
||||
payload
|
||||
);
|
||||
const shelf = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'shelf',
|
||||
payload
|
||||
);
|
||||
const shoppingCart = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'shoppingCart',
|
||||
payload
|
||||
);
|
||||
const checkout = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'checkout',
|
||||
payload
|
||||
);
|
||||
const goodsin = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'goodsin',
|
||||
payload
|
||||
);
|
||||
const remission = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'remission',
|
||||
payload
|
||||
);
|
||||
const customer = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'customer', payload);
|
||||
const product = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'product', payload);
|
||||
const shelf = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'shelf', payload);
|
||||
const shoppingCart = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'shoppingCart', payload);
|
||||
const checkout = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'checkout', payload);
|
||||
const goodsin = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'goodsin', payload);
|
||||
const remission = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'remission', payload);
|
||||
/* const taskCalendar = this.deleteBreadsForMenu(
|
||||
state.processesBreadcrumbs,
|
||||
'taskCalendar',
|
||||
@@ -1019,35 +757,20 @@ export class BreadcrumbsState {
|
||||
activeCrumbs: activeCrumbs,
|
||||
previusMenuPath: previousMenuPath,
|
||||
});
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
activeCrumbs,
|
||||
previousMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, activeCrumbs, previousMenuPath);
|
||||
}
|
||||
}
|
||||
|
||||
private deleteBreadsForMenu(
|
||||
breds: ProcessBreadcrumb,
|
||||
menu: string,
|
||||
processId: number
|
||||
) {
|
||||
private deleteBreadsForMenu(breds: ProcessBreadcrumb, menu: string, processId: number) {
|
||||
const menuBreadcrumbs = breds[menu];
|
||||
const processesBreadcrumbs = removeItem<ProcessBreadcrumbs>(
|
||||
(item) => item.processId === processId
|
||||
)(menuBreadcrumbs);
|
||||
const processesBreadcrumbs = removeItem<ProcessBreadcrumbs>((item) => item.processId === processId)(menuBreadcrumbs);
|
||||
return processesBreadcrumbs;
|
||||
}
|
||||
|
||||
@Action(actions.UpdateActiveCrumb)
|
||||
updateActiveCrumb(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{ crumb }: actions.UpdateActiveCrumb
|
||||
) {
|
||||
updateActiveCrumb(ctx: StateContext<BreadcrumbsStateModel>, { crumb }: actions.UpdateActiveCrumb) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
|
||||
if (state && currentProcessId) {
|
||||
let currentProcessActiveCrumbs = {};
|
||||
@@ -1065,22 +788,14 @@ export class BreadcrumbsState {
|
||||
}
|
||||
|
||||
ctx.patchState({ activeCrumbs: currentProcessActiveCrumbs });
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
currentProcessActiveCrumbs,
|
||||
state.previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, state.previusMenuPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Action(actions.ReloadBreadcrumbsData)
|
||||
reloadBreadcrumbs(
|
||||
ctx: StateContext<BreadcrumbsStateModel>,
|
||||
{
|
||||
processesBreadcrumbs,
|
||||
activeCrumbs,
|
||||
previusMenuPath,
|
||||
}: actions.ReloadBreadcrumbsData
|
||||
{ processesBreadcrumbs, activeCrumbs, previusMenuPath }: actions.ReloadBreadcrumbsData
|
||||
) {
|
||||
const crumbs = {
|
||||
processesBreadcrumbs: {
|
||||
@@ -1097,14 +812,8 @@ export class BreadcrumbsState {
|
||||
previusMenuPath: previusMenuPath,
|
||||
};
|
||||
|
||||
console.log('reloadBreadcrumbs [ctxState]', crumbs);
|
||||
|
||||
ctx.setState({ ...crumbs });
|
||||
this.syncApiState(
|
||||
ctx.getState().processesBreadcrumbs,
|
||||
activeCrumbs,
|
||||
previusMenuPath
|
||||
);
|
||||
this.syncApiState(ctx.getState().processesBreadcrumbs, activeCrumbs, previusMenuPath);
|
||||
}
|
||||
|
||||
private syncApiState(
|
||||
@@ -1118,17 +827,11 @@ export class BreadcrumbsState {
|
||||
previusMenuPath,
|
||||
};
|
||||
|
||||
console.log('[syncApiState] userSyncData', userSyncData);
|
||||
|
||||
const userData = JSON.stringify(userSyncData);
|
||||
this.store.dispatch(new AppUserDataSync(userData));
|
||||
}
|
||||
|
||||
private udpatedBreadcrumbsObject(
|
||||
data: ProcessBreadcrumb,
|
||||
breadcrumbsId: string,
|
||||
newCrumbs: ProcessBreadcrumbs[]
|
||||
) {
|
||||
private udpatedBreadcrumbsObject(data: ProcessBreadcrumb, breadcrumbsId: string, newCrumbs: ProcessBreadcrumbs[]) {
|
||||
const obj = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
if (key === breadcrumbsId) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
DestinationDTO,
|
||||
AddressDTO,
|
||||
UpdateShoppingCartItemDTO,
|
||||
ShippingAddressDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { ProcessSelectors } from '../selectors/process.selectors';
|
||||
@@ -24,7 +25,7 @@ import { tap, map, flatMap, concatMap, switchMap } from 'rxjs/operators';
|
||||
import { Observable, of, from } from 'rxjs';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { AppUserDataSync } from '../actions/app.actions';
|
||||
import { SupplierDTO, ShippingAddressDTO } from '@swagger/oms';
|
||||
import { SupplierDTO } from '@swagger/oms';
|
||||
import { isArray } from 'util';
|
||||
import { User } from '../../models/user.model';
|
||||
import { CustomerMapping } from '../../mappings/customer.mapping';
|
||||
@@ -529,11 +530,11 @@ export class CartEntryState {
|
||||
logistician: { id: availability.logisticianId },
|
||||
shippingAddress:
|
||||
customer && addressDto
|
||||
? <ShippingAddressDTO>{
|
||||
? ({
|
||||
firstName: customer.first_name,
|
||||
lastName: customer.last_name,
|
||||
address: addressDto,
|
||||
}
|
||||
} as ShippingAddressDTO)
|
||||
: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
import { State, Action, StateContext, Store, Selector } from '@ngxs/store';
|
||||
import * as actions from '../actions/cart.actions';
|
||||
import { AddCartToProcess, SetFinishedOrderData, ChangeCurrentRoute } from '../actions/process.actions';
|
||||
import {
|
||||
AddCartToProcess,
|
||||
SetFinishedOrderData,
|
||||
ChangeCurrentRoute,
|
||||
} from '../actions/process.actions';
|
||||
import { DeleteCartEntry } from '../actions/cart-entry.actions';
|
||||
import { ProcessSelectors } from '../selectors/process.selectors';
|
||||
import { CheckoutService } from '../../services/checkout.service';
|
||||
import { tap, catchError } from 'rxjs/operators';
|
||||
import { ShoppingCartDTO, PaymentDTO, CheckoutDTO, BuyerDTO, PayerDTO } from '@swagger/checkout';
|
||||
import {
|
||||
ShoppingCartDTO,
|
||||
PaymentDTO,
|
||||
CheckoutDTO,
|
||||
BuyerDTO,
|
||||
PayerDTO,
|
||||
DestinationDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { DisplayOrderDTO, EnvironmentChannel } from '@swagger/oms';
|
||||
import { User } from '../../models/user.model';
|
||||
import { CustomerMapping } from '../../mappings/customer.mapping';
|
||||
import { OrganisationDTO } from '@swagger/crm';
|
||||
import { DestinationDTO } from '@swagger/checkout';
|
||||
import { UserStateSyncData } from '../../models/user-state-sync.model';
|
||||
import { AppUserDataSync } from '../actions/app.actions';
|
||||
import { of } from 'rxjs';
|
||||
@@ -48,7 +58,10 @@ export class CartState {
|
||||
}
|
||||
|
||||
@Action(actions.SetCartToProcess)
|
||||
setCartToProcess(ctx: StateContext<CartStateModel>, {}: actions.SetCartToProcess) {
|
||||
setCartToProcess(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{}: actions.SetCartToProcess
|
||||
) {
|
||||
return this.checkoutService.createCart().pipe(
|
||||
tap((cart: ShoppingCartDTO) => {
|
||||
const state = ctx.getState();
|
||||
@@ -62,28 +75,41 @@ export class CartState {
|
||||
}
|
||||
|
||||
@Action(actions.SetCartData)
|
||||
addItemToCart(ctx: StateContext<CartStateModel>, { cartEntryId }: actions.SetCartData) {
|
||||
addItemToCart(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ cartEntryId }: actions.SetCartData
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentProcess = this.store.selectSnapshot(ProcessSelectors.getCurrentProcess);
|
||||
const currentProcess = this.store.selectSnapshot(
|
||||
ProcessSelectors.getCurrentProcess
|
||||
);
|
||||
if (!currentProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const carts = { ...state.carts };
|
||||
carts[currentProcess.cartId] = [...carts[currentProcess.cartId], cartEntryId];
|
||||
carts[currentProcess.cartId] = [
|
||||
...carts[currentProcess.cartId],
|
||||
cartEntryId,
|
||||
];
|
||||
|
||||
ctx.patchState({ carts });
|
||||
this.syncApiState(carts);
|
||||
}
|
||||
|
||||
@Action(actions.DeleteProductFromCart)
|
||||
deleteProductFromCart(ctx: StateContext<CartStateModel>, { cartId, cartEntryId }: actions.DeleteProductFromCart) {
|
||||
deleteProductFromCart(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ cartId, cartEntryId }: actions.DeleteProductFromCart
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const allCarts = { ...state.carts };
|
||||
const cartEntries = allCarts[cartId];
|
||||
|
||||
// Remove product from
|
||||
const productToDelete = cartEntries.find((entryId: number) => entryId === cartEntryId);
|
||||
const productToDelete = cartEntries.find(
|
||||
(entryId: number) => entryId === cartEntryId
|
||||
);
|
||||
if (productToDelete) {
|
||||
this.store.dispatch(new DeleteCartEntry(cartEntryId, true));
|
||||
}
|
||||
@@ -99,7 +125,10 @@ export class CartState {
|
||||
}
|
||||
|
||||
@Action(actions.DeleteCart)
|
||||
deleteCart(ctx: StateContext<CartStateModel>, { cartId }: actions.DeleteCart) {
|
||||
deleteCart(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ cartId }: actions.DeleteCart
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const carts = { ...state.carts };
|
||||
const cartEntries = carts[cartId];
|
||||
@@ -122,16 +151,26 @@ export class CartState {
|
||||
}
|
||||
|
||||
@Action(actions.CreateOrder)
|
||||
createOrder(ctx: StateContext<CartStateModel>, { checkoutId }: actions.CreateOrder) {
|
||||
createOrder(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId }: actions.CreateOrder
|
||||
) {
|
||||
return this.checkoutService.createOrder(checkoutId).pipe(
|
||||
tap((order: { failedItemIds: { id: number; type: number }[]; displayOrder: DisplayOrderDTO[] }) => {
|
||||
tap(
|
||||
(order: {
|
||||
failedItemIds: { id: number; type: number }[];
|
||||
displayOrder: DisplayOrderDTO[];
|
||||
}) => {
|
||||
this.store.dispatch(new SetFinishedOrderData(order));
|
||||
}),
|
||||
}
|
||||
),
|
||||
catchError((error) => {
|
||||
const status = error.status;
|
||||
const message = error && error.error ? error.error.message : undefined;
|
||||
const invalidProperties =
|
||||
error && error.error && error.error.invalidProperties ? JSON.stringify(error.error.invalidProperties) : undefined;
|
||||
error && error.error && error.error.invalidProperties
|
||||
? JSON.stringify(error.error.invalidProperties)
|
||||
: undefined;
|
||||
this.errorService.addErrors(status, message, invalidProperties);
|
||||
this.openCart();
|
||||
return of(undefined);
|
||||
@@ -151,40 +190,67 @@ export class CartState {
|
||||
}
|
||||
|
||||
@Action(actions.SetPaymentTypeToCheckout)
|
||||
getPaymentToCheckout(ctx: StateContext<CartStateModel>, { checkoutId, customer, isDelivery }: actions.SetPaymentTypeToCheckout) {
|
||||
getPaymentToCheckout(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, customer, isDelivery }: actions.SetPaymentTypeToCheckout
|
||||
) {
|
||||
return this.checkoutService.getPayments(checkoutId).pipe(
|
||||
tap((payment: PaymentDTO) => {
|
||||
const type = payment.availablePaymentTypes[0].key;
|
||||
this.store.dispatch(new actions.SetPaymentToCheckout(checkoutId, type, customer, isDelivery));
|
||||
this.store.dispatch(
|
||||
new actions.SetPaymentToCheckout(
|
||||
checkoutId,
|
||||
type,
|
||||
customer,
|
||||
isDelivery
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetPaymentToCheckout)
|
||||
setPaymentToCheckout(ctx: StateContext<CartStateModel>, { checkoutId, type, customer, isDelivery }: actions.SetPaymentToCheckout) {
|
||||
setPaymentToCheckout(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, type, customer, isDelivery }: actions.SetPaymentToCheckout
|
||||
) {
|
||||
return this.checkoutService.setPaymentType(checkoutId, type).pipe(
|
||||
tap((checkout: CheckoutDTO) => {
|
||||
this.store.dispatch(new actions.SetBuyerToCheckout(checkout.id, customer, isDelivery));
|
||||
this.store.dispatch(
|
||||
new actions.SetBuyerToCheckout(checkout.id, customer, isDelivery)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetBuyerToCheckout)
|
||||
setBuyerToCheckout(ctx: StateContext<CartStateModel>, { checkoutId, customer, isDelivery }: actions.SetBuyerToCheckout) {
|
||||
return this.checkoutService.setBuyer(checkoutId, this.buyerObject(customer)).pipe(
|
||||
setBuyerToCheckout(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, customer, isDelivery }: actions.SetBuyerToCheckout
|
||||
) {
|
||||
return this.checkoutService
|
||||
.setBuyer(checkoutId, this.buyerObject(customer))
|
||||
.pipe(
|
||||
tap((checkout: CheckoutDTO) => {
|
||||
if (!isDelivery) {
|
||||
this.store.dispatch(new actions.CreateOrder(checkout.id));
|
||||
} else {
|
||||
this.store.dispatch(new actions.SetPayerToCheckout(checkout.id, customer));
|
||||
this.store.dispatch(
|
||||
new actions.SetPayerToCheckout(checkout.id, customer)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetPayerToCheckout)
|
||||
setPayerToCheckout(ctx: StateContext<CartStateModel>, { checkoutId, customer }: actions.SetPayerToCheckout) {
|
||||
return this.checkoutService.setPayer(checkoutId, this.payerObject(customer)).pipe(
|
||||
setPayerToCheckout(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, customer }: actions.SetPayerToCheckout
|
||||
) {
|
||||
return this.checkoutService
|
||||
.setPayer(checkoutId, this.payerObject(customer))
|
||||
.pipe(
|
||||
tap((checkout: CheckoutDTO) => {
|
||||
this.store.dispatch(new actions.CreateOrder(checkout.id));
|
||||
})
|
||||
@@ -194,20 +260,34 @@ export class CartState {
|
||||
@Action(actions.SetDestinationToCheckout)
|
||||
setDestinationToCheckout(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, destinationId, customer, target }: actions.SetDestinationToCheckout
|
||||
{
|
||||
checkoutId,
|
||||
destinationId,
|
||||
customer,
|
||||
target,
|
||||
}: actions.SetDestinationToCheckout
|
||||
) {
|
||||
const dest = this.destinationObject(customer, destinationId, target);
|
||||
return this.checkoutService.setDestination(checkoutId, destinationId, dest);
|
||||
}
|
||||
|
||||
@Action(actions.ReloadCartData)
|
||||
reloadCarts(ctx: StateContext<CartStateModel>, { carts }: actions.ReloadCartData) {
|
||||
reloadCarts(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ carts }: actions.ReloadCartData
|
||||
) {
|
||||
ctx.setState({ carts: { ...carts } });
|
||||
}
|
||||
|
||||
@Action(actions.SetNotificationChannel)
|
||||
setNotificationChannel(ctx: StateContext<CartStateModel>, { checkoutId, notificationChannel }: actions.SetNotificationChannel) {
|
||||
return this.checkoutService.setNotificationChannel(checkoutId, notificationChannel);
|
||||
setNotificationChannel(
|
||||
ctx: StateContext<CartStateModel>,
|
||||
{ checkoutId, notificationChannel }: actions.SetNotificationChannel
|
||||
) {
|
||||
return this.checkoutService.setNotificationChannel(
|
||||
checkoutId,
|
||||
notificationChannel
|
||||
);
|
||||
}
|
||||
|
||||
private syncApiState(carts: { [key: number]: number[] }) {
|
||||
@@ -239,7 +319,9 @@ export class CartState {
|
||||
}
|
||||
|
||||
private payerObject(customer: User) {
|
||||
const invoice = customer.invoice_address ? customer.invoice_address : customer.delivery_addres;
|
||||
const invoice = customer.invoice_address
|
||||
? customer.invoice_address
|
||||
: customer.delivery_addres;
|
||||
const transformed = this.customerMapper.fromUser(customer);
|
||||
let organisation: OrganisationDTO = {};
|
||||
|
||||
@@ -274,25 +356,27 @@ export class CartState {
|
||||
return payerDTO;
|
||||
}
|
||||
|
||||
private destinationObject(customer: User, destinationId: number, target?: EnvironmentChannel) {
|
||||
private destinationObject(
|
||||
customer: User,
|
||||
destinationId: number,
|
||||
target?: EnvironmentChannel
|
||||
) {
|
||||
const delivery = customer.delivery_addres;
|
||||
if (!delivery) {
|
||||
this.cartService.addOrderErrorEvent();
|
||||
throw new Error(
|
||||
`Delvery address is missing for customer: ${customer.first_name + ' ' + customer.last_name} when trying to set destination object.
|
||||
`Delvery address is missing for customer: ${
|
||||
customer.first_name + ' ' + customer.last_name
|
||||
} when trying to set destination object.
|
||||
Source => cart.state.ts.destinationObject. Line 238`
|
||||
);
|
||||
}
|
||||
const transformed = this.customerMapper.fromUser(customer);
|
||||
const shippingAddress = this.customerMapper.fromUserToShippingAddressDTO(
|
||||
customer
|
||||
);
|
||||
|
||||
const destinationDTO: DestinationDTO = {
|
||||
shippingAddress: {
|
||||
gender: this.customerMapper.getGender(delivery.gender),
|
||||
communicationDetails: transformed.communicationDetails,
|
||||
firstName: delivery.first_name,
|
||||
lastName: delivery.last_name,
|
||||
address: transformed.address,
|
||||
source: delivery.id,
|
||||
},
|
||||
shippingAddress,
|
||||
target: target ? target : 2,
|
||||
id: destinationId,
|
||||
};
|
||||
|
||||
@@ -40,7 +40,10 @@ export class CustomerState {
|
||||
constructor(private store: Store, private customerService: CustomerService) {}
|
||||
|
||||
@Action(actions.SearchUser)
|
||||
searchUser(ctx: StateContext<CustomerStateModel>, { payload }: actions.SearchUser): Observable<User[]> {
|
||||
searchUser(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload }: actions.SearchUser
|
||||
): Observable<User[]> {
|
||||
return this.customerService.searchCustomer(payload).pipe(
|
||||
map((response: CustomerSearchResponse) => response.customers),
|
||||
tap((_customers: User[]) => {
|
||||
@@ -55,21 +58,30 @@ export class CustomerState {
|
||||
customers = { ...customers, [t.id]: t };
|
||||
});
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
this.store.dispatch(new AddCustomerIds(customerIds));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.AddCustomers)
|
||||
addCustomers(ctx: StateContext<CustomerStateModel>, { payload }: actions.AddCustomers) {
|
||||
addCustomers(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload }: actions.AddCustomers
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
const currentCustomers = state.customers;
|
||||
const newCustomers = payload.filter((t) => t !== undefined);
|
||||
const currentCustomerIds = this.store.selectSnapshot(ProcessSelectors.getCustomerIds);
|
||||
const currentCustomerIds = this.store.selectSnapshot(
|
||||
ProcessSelectors.getCustomerIds
|
||||
);
|
||||
const customerIds = [];
|
||||
let customers = currentCustomers;
|
||||
if (!!currentCustomerIds) {
|
||||
@@ -84,12 +96,19 @@ export class CustomerState {
|
||||
}
|
||||
});
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
this.store.dispatch(new AddCustomerIds(customerIds));
|
||||
}
|
||||
|
||||
@Action(actions.SetActiveUser)
|
||||
setActiveUser(ctx: StateContext<CustomerStateModel>, { payload, cartHasItemForDownload, cartEntriesIds }: actions.SetActiveUser) {
|
||||
setActiveUser(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload, cartHasItemForDownload, cartEntriesIds }: actions.SetActiveUser
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -101,14 +120,21 @@ export class CustomerState {
|
||||
ctx.patchState({ customers });
|
||||
this.store.dispatch(new SetActiveCustomer(_customer.id));
|
||||
this.store.dispatch(new UpdateProcessName(_customer.last_name));
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.AddUser)
|
||||
addUser(ctx: StateContext<CustomerStateModel>, { payload, setActiveUser }: actions.AddUser) {
|
||||
addUser(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload, setActiveUser }: actions.AddUser
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
let customerApi$: Observable<User>;
|
||||
let isOnline = false;
|
||||
@@ -126,10 +152,21 @@ export class CustomerState {
|
||||
}
|
||||
return customerApi$.pipe(
|
||||
tap((addedCustomer: User) => {
|
||||
this.store.dispatch(new SetOnlineCustomerCreationStatus({ error: false, invalidProperties: null }));
|
||||
this.store.dispatch(
|
||||
new SetOnlineCustomerCreationStatus({
|
||||
error: false,
|
||||
invalidProperties: null,
|
||||
})
|
||||
);
|
||||
if (state.customers[addedCustomer.id] === undefined) {
|
||||
const customers = { ...state.customers, [addedCustomer.id]: addedCustomer };
|
||||
ctx.patchState({ customers, lastCreatedCustomerId: addedCustomer.id });
|
||||
const customers = {
|
||||
...state.customers,
|
||||
[addedCustomer.id]: addedCustomer,
|
||||
};
|
||||
ctx.patchState({
|
||||
customers,
|
||||
lastCreatedCustomerId: addedCustomer.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (setActiveUser) {
|
||||
@@ -153,7 +190,8 @@ export class CustomerState {
|
||||
this.store.dispatch(
|
||||
new SetOnlineCustomerCreationStatus({
|
||||
error: true,
|
||||
invalidProperties: error && error.error ? error.error.invalidProperties : undefined,
|
||||
invalidProperties:
|
||||
error && error.error ? error.error.invalidProperties : undefined,
|
||||
})
|
||||
);
|
||||
console.error(error);
|
||||
@@ -163,24 +201,40 @@ export class CustomerState {
|
||||
}
|
||||
|
||||
@Action(actions.SetUserDetails)
|
||||
editUser(ctx: StateContext<CustomerStateModel>, { payload, addBreadcrumb }: actions.SetUserDetails) {
|
||||
editUser(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload, addBreadcrumb }: actions.SetUserDetails
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const id = +payload.id;
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.features && payload.features.findIndex((v) => v.key === 'onlineshop') >= 0) {
|
||||
const customers = { ...state.customers, [id]: { ...payload, id: id, error: null } };
|
||||
if (
|
||||
payload.features &&
|
||||
payload.features.findIndex((v) => v.key === 'onlineshop') >= 0
|
||||
) {
|
||||
const customers = {
|
||||
...state.customers,
|
||||
[id]: { ...payload, id: id, error: null },
|
||||
};
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
return this.customerService.updateCustomer(payload).pipe(
|
||||
catchError((error) => {
|
||||
console.error(error);
|
||||
const customers = { ...state.customers, [id]: { ...state.customers[id], id: id, error: error } };
|
||||
const customers = {
|
||||
...state.customers,
|
||||
[id]: { ...state.customers[id], id: id, error: error },
|
||||
};
|
||||
ctx.patchState({ customers });
|
||||
return of(undefined);
|
||||
}),
|
||||
@@ -188,7 +242,10 @@ export class CustomerState {
|
||||
if (!addedCustomer) {
|
||||
return;
|
||||
}
|
||||
const customers = { ...state.customers, [id]: { ...payload, id: id, error: null } };
|
||||
const customers = {
|
||||
...state.customers,
|
||||
[id]: { ...payload, id: id, error: null },
|
||||
};
|
||||
ctx.patchState({ customers });
|
||||
if (addBreadcrumb) {
|
||||
this.store.dispatch(
|
||||
@@ -201,13 +258,20 @@ export class CustomerState {
|
||||
)
|
||||
);
|
||||
}
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetUserDetailsById)
|
||||
getUserDetails(ctx: StateContext<CustomerStateModel>, { payload, addBreadcrumb }: actions.SetUserDetailsById) {
|
||||
getUserDetails(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload, addBreadcrumb }: actions.SetUserDetailsById
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -215,7 +279,12 @@ export class CustomerState {
|
||||
return this.customerService.getCustomerById(payload).pipe(
|
||||
tap((user: User) => {
|
||||
if (user) {
|
||||
ctx.setState(this.updateCustomer({ ...user, id: +payload }, state.lastCreatedCustomerId));
|
||||
ctx.setState(
|
||||
this.updateCustomer(
|
||||
{ ...user, id: +payload },
|
||||
state.lastCreatedCustomerId
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -229,7 +298,10 @@ export class CustomerState {
|
||||
}
|
||||
|
||||
@Action(actions.DeleteCustomerForProcess)
|
||||
deleteCustomerForProcess(ctx: StateContext<CustomerStateModel>, { payload }: actions.DeleteCustomerForProcess) {
|
||||
deleteCustomerForProcess(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ payload }: actions.DeleteCustomerForProcess
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -238,19 +310,29 @@ export class CustomerState {
|
||||
delete customers[payload];
|
||||
ctx.patchState({ customers });
|
||||
this.store.dispatch(new RemoveCustomerId(payload));
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
|
||||
return this.customerService.deleteCustomer(payload);
|
||||
}
|
||||
|
||||
@Action(actions.SetDefaultShippingAddress)
|
||||
setDefaultShippingAddress(ctx: StateContext<CustomerStateModel>, { customerId, shippingAddress }: actions.SetDefaultShippingAddress) {
|
||||
setDefaultShippingAddress(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customerId, shippingAddress }: actions.SetDefaultShippingAddress
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
const currentCustomers = state.customers;
|
||||
const shipping$ = this.customerService.setShippingAddressAsDefault(shippingAddress, customerId);
|
||||
const shipping$ = this.customerService.setShippingAddressAsDefault(
|
||||
shippingAddress,
|
||||
customerId
|
||||
);
|
||||
if (!shipping$) {
|
||||
return;
|
||||
}
|
||||
@@ -259,23 +341,38 @@ export class CustomerState {
|
||||
const customer = currentCustomers[customerId];
|
||||
const updatedCustomer = {
|
||||
...customer,
|
||||
delivery_addres: customer.poossible_delivery_addresses.find((t) => t.id === shippingAddress.id),
|
||||
delivery_addres: customer.poossible_delivery_addresses.find(
|
||||
(t) => t.id === shippingAddress.id
|
||||
),
|
||||
};
|
||||
const customers = {
|
||||
...currentCustomers,
|
||||
[updatedCustomer.id]: updatedCustomer,
|
||||
};
|
||||
const customers = { ...currentCustomers, [updatedCustomer.id]: updatedCustomer };
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetDefaultInvoiceAddress)
|
||||
setDefaultInvoiceAddress(ctx: StateContext<CustomerStateModel>, { customerId, invoiceAddress }: actions.SetDefaultInvoiceAddress) {
|
||||
setDefaultInvoiceAddress(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customerId, invoiceAddress }: actions.SetDefaultInvoiceAddress
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
const currentCustomers = state.customers;
|
||||
const invoice$ = this.customerService.setInvoiceAddressAsDefault(invoiceAddress, customerId);
|
||||
const invoice$ = this.customerService.setInvoiceAddressAsDefault(
|
||||
invoiceAddress,
|
||||
customerId
|
||||
);
|
||||
if (!invoice$) {
|
||||
return;
|
||||
}
|
||||
@@ -284,17 +381,29 @@ export class CustomerState {
|
||||
const customer = currentCustomers[customerId];
|
||||
const updatedCustomer = {
|
||||
...customer,
|
||||
invoice_address: customer.poossible_invoice_addresses.find((t) => t.id === invoiceAddress.id),
|
||||
invoice_address: customer.poossible_invoice_addresses.find(
|
||||
(t) => t.id === invoiceAddress.id
|
||||
),
|
||||
};
|
||||
const customers = {
|
||||
...currentCustomers,
|
||||
[updatedCustomer.id]: updatedCustomer,
|
||||
};
|
||||
const customers = { ...currentCustomers, [updatedCustomer.id]: updatedCustomer };
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.AddNewShippingAddress)
|
||||
addNewShippingAddress(ctx: StateContext<CustomerStateModel>, { customerId, shippingAddress }: actions.AddNewShippingAddress) {
|
||||
addNewShippingAddress(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customerId, shippingAddress }: actions.AddNewShippingAddress
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -310,27 +419,53 @@ export class CustomerState {
|
||||
updatedCustomer = <User>{
|
||||
...customer,
|
||||
delivery_addres: address,
|
||||
poossible_delivery_addresses: [...customer.poossible_delivery_addresses.filter((t) => t.id !== address.id), address],
|
||||
poossible_invoice_addresses: [...customer.poossible_invoice_addresses.filter((t) => t.id !== address.id), address],
|
||||
poossible_delivery_addresses: [
|
||||
...(customer.poossible_delivery_addresses.filter(
|
||||
(t) => t.id !== address.id
|
||||
) || []),
|
||||
address,
|
||||
],
|
||||
poossible_invoice_addresses: [
|
||||
...(customer.poossible_invoice_addresses.filter(
|
||||
(t) => t.id !== address.id
|
||||
) || []),
|
||||
address,
|
||||
],
|
||||
};
|
||||
} else {
|
||||
updatedCustomer = <User>{
|
||||
...customer,
|
||||
delivery_addres: address,
|
||||
poossible_delivery_addresses: [...customer.poossible_delivery_addresses, address],
|
||||
poossible_invoice_addresses: [...customer.poossible_invoice_addresses, address],
|
||||
poossible_delivery_addresses: [
|
||||
...(customer.poossible_delivery_addresses || []),
|
||||
address,
|
||||
],
|
||||
poossible_invoice_addresses: [
|
||||
...(customer.poossible_invoice_addresses || []),
|
||||
address,
|
||||
],
|
||||
};
|
||||
}
|
||||
this.store.dispatch(new actions.SetUserDetailsById(customerId));
|
||||
const customers = { ...currentCustomers, [updatedCustomer.id]: updatedCustomer };
|
||||
const customers = {
|
||||
...currentCustomers,
|
||||
[updatedCustomer.id]: updatedCustomer,
|
||||
};
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.AddNewInvoiceAddress)
|
||||
addNewInvoiceAddress(ctx: StateContext<CustomerStateModel>, { customerId, invoiceAddress }: actions.AddNewInvoiceAddress) {
|
||||
addNewInvoiceAddress(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customerId, invoiceAddress }: actions.AddNewInvoiceAddress
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -346,27 +481,53 @@ export class CustomerState {
|
||||
updatedCustomer = <User>{
|
||||
...customer,
|
||||
invoice_address: address,
|
||||
poossible_delivery_addresses: [...customer.poossible_delivery_addresses.filter((t) => t && t.id !== address.id), address],
|
||||
poossible_invoice_addresses: [...customer.poossible_invoice_addresses.filter((t) => t && t.id !== address.id), address],
|
||||
poossible_delivery_addresses: [
|
||||
...(customer.poossible_delivery_addresses.filter(
|
||||
(t) => t && t.id !== address.id
|
||||
) || []),
|
||||
address,
|
||||
],
|
||||
poossible_invoice_addresses: [
|
||||
...(customer.poossible_invoice_addresses.filter(
|
||||
(t) => t && t.id !== address.id
|
||||
) || []),
|
||||
address,
|
||||
],
|
||||
};
|
||||
} else {
|
||||
updatedCustomer = <User>{
|
||||
...customer,
|
||||
invoice_address: address,
|
||||
poossible_delivery_addresses: [...customer.poossible_delivery_addresses, address],
|
||||
poossible_invoice_addresses: [...customer.poossible_invoice_addresses, address],
|
||||
poossible_delivery_addresses: [
|
||||
...(customer.poossible_delivery_addresses || []),
|
||||
address,
|
||||
],
|
||||
poossible_invoice_addresses: [
|
||||
...(customer.poossible_invoice_addresses || []),
|
||||
address,
|
||||
],
|
||||
};
|
||||
}
|
||||
this.store.dispatch(new actions.SetUserDetailsById(customerId));
|
||||
const customers = { ...currentCustomers, [updatedCustomer.id]: updatedCustomer };
|
||||
const customers = {
|
||||
...currentCustomers,
|
||||
[updatedCustomer.id]: updatedCustomer,
|
||||
};
|
||||
ctx.patchState({ customers });
|
||||
this.syncApiState(customers, state.lastCreatedCustomerId, state.cachedCustomerSearch);
|
||||
this.syncApiState(
|
||||
customers,
|
||||
state.lastCreatedCustomerId,
|
||||
state.cachedCustomerSearch
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Action(actions.SetLastCreatedCustomerId)
|
||||
setLastCreatedCustomerId(ctx: StateContext<CustomerStateModel>, { customerId }: actions.SetLastCreatedCustomerId) {
|
||||
setLastCreatedCustomerId(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customerId }: actions.SetLastCreatedCustomerId
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
if (!state) {
|
||||
return;
|
||||
@@ -376,30 +537,45 @@ export class CustomerState {
|
||||
}
|
||||
|
||||
@Action(actions.SetCachedCustomers)
|
||||
setCachedCustomers(ctx: StateContext<CustomerStateModel>, { cachedCustomerSearch }: actions.SetCachedCustomers) {
|
||||
setCachedCustomers(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ cachedCustomerSearch }: actions.SetCachedCustomers
|
||||
) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
|
||||
if (currentProcessId) {
|
||||
const cachedCustomers = { ...state.cachedCustomerSearch };
|
||||
cachedCustomers[currentProcessId] = cachedCustomerSearch;
|
||||
|
||||
ctx.patchState({ cachedCustomerSearch: cachedCustomers });
|
||||
this.syncApiState(state.customers, state.lastCreatedCustomerId, cachedCustomers);
|
||||
this.syncApiState(
|
||||
state.customers,
|
||||
state.lastCreatedCustomerId,
|
||||
cachedCustomers
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Action(actions.ClearCachedCustomers)
|
||||
clearCachedCustomers(ctx: StateContext<CustomerStateModel>) {
|
||||
const state = ctx.getState();
|
||||
const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
|
||||
const currentProcessId = this.store.selectSnapshot(
|
||||
AppState.getCurrentProcessId
|
||||
);
|
||||
|
||||
if (currentProcessId) {
|
||||
const cachedCustomers = { ...state.cachedCustomerSearch };
|
||||
cachedCustomers[currentProcessId] = undefined;
|
||||
|
||||
ctx.patchState({ cachedCustomerSearch: cachedCustomers });
|
||||
this.syncApiState(state.customers, state.lastCreatedCustomerId, cachedCustomers);
|
||||
this.syncApiState(
|
||||
state.customers,
|
||||
state.lastCreatedCustomerId,
|
||||
cachedCustomers
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +590,11 @@ export class CustomerState {
|
||||
@Action(actions.ReloadCustomersData)
|
||||
reloadProcesses(
|
||||
ctx: StateContext<CustomerStateModel>,
|
||||
{ customers, lastCreatedCustomerId, cachedCustomerSearch }: actions.ReloadCustomersData
|
||||
{
|
||||
customers,
|
||||
lastCreatedCustomerId,
|
||||
cachedCustomerSearch,
|
||||
}: actions.ReloadCustomersData
|
||||
) {
|
||||
ctx.patchState({ customers, lastCreatedCustomerId, cachedCustomerSearch });
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ const errorWhiteList: { url: string; codes: number[] }[] = [
|
||||
-1,
|
||||
],
|
||||
},
|
||||
{ url: '/customer', codes: [400, 404, 409, 500] },
|
||||
{ url: '/customer', codes: [400, 409, 500] },
|
||||
{ url: '/bonuscard', codes: [404] },
|
||||
{ url: '/order/', codes: [400, 409, 500] },
|
||||
{ url: '/order/', codes: [400, 404, 409, 500] },
|
||||
{ url: '/store/customer/emailexists', codes: [400, 409, 500] },
|
||||
{
|
||||
url: '/receipt/order/',
|
||||
|
||||
6
apps/sales/src/app/core/utils/price.util.ts
Normal file
6
apps/sales/src/app/core/utils/price.util.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export function toDecimalPlaces(
|
||||
value: number,
|
||||
decimalPlaces: number = 2
|
||||
): string {
|
||||
return Number(value).toFixed(decimalPlaces);
|
||||
}
|
||||
@@ -32,6 +32,10 @@ export function getFormatedPublicationDate(publicationDate: Date): string {
|
||||
);
|
||||
}
|
||||
|
||||
export function allowedAvailabilityStatusCodes(code: number) {
|
||||
return [2, 32, 256, 1024, 2048, 4096].findIndex(allowedCode => allowedCode === code) !== -1;
|
||||
export function allowedAvailabilityStatusCodes(code: number): boolean {
|
||||
return (
|
||||
[2, 32, 256, 1024, 2048, 4096].findIndex(
|
||||
(allowedCode) => allowedCode === code
|
||||
) !== -1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
OnDestroy,
|
||||
Output,
|
||||
@@ -25,8 +24,12 @@ import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions
|
||||
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { ProcessCart } from '../../../../core/models/process-cart.model';
|
||||
import { map, filter, take, takeUntil } from 'rxjs/operators';
|
||||
import { deliveryFilter, CartReviewItem, cartToCartReviewArray } from '../../pages/cart-review/cart-review.component';
|
||||
import { map, filter, takeUntil } from 'rxjs/operators';
|
||||
import {
|
||||
deliveryFilter,
|
||||
CartReviewItem,
|
||||
cartToCartReviewArray,
|
||||
} from '../../pages/cart-review/cart-review.component';
|
||||
import { Address } from '../../../../core/models/user.model';
|
||||
import { isArrayMinLength } from '../../../../core/utils/app.utils';
|
||||
import { DeleteProductFromCart } from 'apps/sales/src/app/core/store/actions/cart.actions';
|
||||
@@ -49,7 +52,11 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
lastDeletedItemIndex = -1;
|
||||
destroy$ = new Subject();
|
||||
|
||||
constructor(private store: Store, private router: Router, private cdr: ChangeDetectorRef) {}
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.delivery$ = this.cartData$.pipe(
|
||||
@@ -87,7 +94,14 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
const customer = cart.customer;
|
||||
if (customer.delivery_addres) {
|
||||
const { first_name, last_name, street, streetNo, zip, city } = customer.delivery_addres;
|
||||
const {
|
||||
first_name,
|
||||
last_name,
|
||||
street,
|
||||
streetNo,
|
||||
zip,
|
||||
city,
|
||||
} = customer.delivery_addres;
|
||||
this.deliveryAddress = `${first_name} ${last_name} | ${street} ${streetNo}, ${zip} ${city}`;
|
||||
}
|
||||
this.detectChanges();
|
||||
@@ -111,7 +125,9 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(new SetDetailsCustomer(customerId));
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.store.dispatch(new AddBreadcrumb(newBread, 'shoppingCart'));
|
||||
this.router.navigate([currentRoute], { queryParams: { cart: true, notEditable: true } });
|
||||
this.router.navigate([currentRoute], {
|
||||
queryParams: { cart: true, notEditable: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +136,10 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
name: item.product.name ? item.product.name.substring(0, 12) + (item.product.name.length > 12 ? '...' : '') : '',
|
||||
name: item.product.name
|
||||
? item.product.name.substring(0, 12) +
|
||||
(item.product.name.length > 12 ? '...' : '')
|
||||
: '',
|
||||
path: '/product/details/' + item.id,
|
||||
},
|
||||
'product'
|
||||
@@ -133,12 +152,18 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
validateDeliveryAddress(processCart) {
|
||||
const deliveryExist = processCart.cart.some((item: Cart) => item.deliveryType === DeliveryOption.DELIVERY);
|
||||
const deliveryExist = processCart.cart.some(
|
||||
(item: Cart) => item.deliveryType === DeliveryOption.DELIVERY
|
||||
);
|
||||
const missingAddress =
|
||||
processCart.customer && (!this.isAddressSet(processCart.customer.delivery_addres) || !processCart.customer.email);
|
||||
processCart.customer &&
|
||||
(!this.isAddressSet(processCart.customer.delivery_addres) ||
|
||||
!processCart.customer.email);
|
||||
|
||||
if (deliveryExist && missingAddress) {
|
||||
this.store.dispatch(new UpdateCustomerFormState(CustomerFormState.MISSING_DELIVERY));
|
||||
this.store.dispatch(
|
||||
new UpdateCustomerFormState(CustomerFormState.MISSING_DELIVERY)
|
||||
);
|
||||
const currentRoute = `customer/search/missing-data/${processCart.customer.id}`;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute));
|
||||
this.router.navigate([currentRoute], { queryParams: { card: 'create' } });
|
||||
@@ -169,20 +194,29 @@ export class DeliveryCartComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
getDeleteAnimation(index: number) {
|
||||
const animation = index >= this.lastDeletedItemIndex && this.lastDeletedItemIndex !== -1 ? 'delete' : '';
|
||||
const animation =
|
||||
index >= this.lastDeletedItemIndex && this.lastDeletedItemIndex !== -1
|
||||
? 'delete'
|
||||
: '';
|
||||
return animation;
|
||||
}
|
||||
|
||||
deleteItem(item: CartReviewItem, index: number) {
|
||||
this.lastDeletedItemIndex = index;
|
||||
if (item) {
|
||||
this.store.dispatch(new DeleteProductFromCart(item.cartId, item.cartEntryId));
|
||||
this.store.dispatch(
|
||||
new DeleteProductFromCart(item.cartId, item.cartEntryId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
detectChanges() {
|
||||
setTimeout(() => {
|
||||
if (this.cdr !== null && this.cdr !== undefined && !(this.cdr as ViewRef).destroyed) {
|
||||
if (
|
||||
this.cdr !== null &&
|
||||
this.cdr !== undefined &&
|
||||
!(this.cdr as ViewRef).destroyed
|
||||
) {
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}, 0);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
|
||||
import { ModalService, ButtonComponent } from '@libs/ui';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { ChangeCurrentRoute, EmptyFinishedOrders } from '../../../../core/store/actions/process.actions';
|
||||
import {
|
||||
ChangeCurrentRoute,
|
||||
EmptyFinishedOrders,
|
||||
} from '../../../../core/store/actions/process.actions';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { Router } from '@angular/router';
|
||||
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
@@ -10,8 +13,15 @@ import { ProcessSelectors } from '../../../../core/store/selectors/process.selec
|
||||
import { Observable, Subject, of, forkJoin } from 'rxjs';
|
||||
import { takeUntil, switchMap, filter, map } from 'rxjs/operators';
|
||||
import { CheckoutService } from '../../../../core/services/checkout.service';
|
||||
import { CheckoutDTO, EntityDTOContainerOfDestinationDTO } from '@swagger/checkout';
|
||||
import { SetPaymentTypeToCheckout, SetDestinationToCheckout, SetNotificationChannel } from '../../../../core/store/actions/cart.actions';
|
||||
import {
|
||||
CheckoutDTO,
|
||||
EntityDTOContainerOfDestinationDTO,
|
||||
} from '@swagger/checkout';
|
||||
import {
|
||||
SetPaymentTypeToCheckout,
|
||||
SetDestinationToCheckout,
|
||||
SetNotificationChannel,
|
||||
} from '../../../../core/store/actions/cart.actions';
|
||||
import { User } from '../../../../core/models/user.model';
|
||||
import { CustomerSelectors } from 'apps/sales/src/app/core/store/selectors/customer.selectors';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
@@ -99,19 +109,31 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
return this.checkoutService.createCheckout(this.cartId).pipe(
|
||||
takeUntil(this.destroy$),
|
||||
switchMap((checkout: CheckoutDTO) => {
|
||||
return this.setNotificationChannel(checkout, this.customer, this.delivery || this.download);
|
||||
return this.setNotificationChannel(
|
||||
checkout,
|
||||
this.customer,
|
||||
this.delivery || this.download
|
||||
);
|
||||
}),
|
||||
switchMap((response) => {
|
||||
if (this.delivery || this.download) {
|
||||
const deliveryShippingDestionation = response.checkout.destinations.find(
|
||||
(dest: EntityDTOContainerOfDestinationDTO) => this.delivery && dest.data.target === 2
|
||||
(dest: EntityDTOContainerOfDestinationDTO) =>
|
||||
this.delivery && dest.data.target === 2
|
||||
);
|
||||
const downloadShippingDestionation = response.checkout.destinations.find(
|
||||
(dest: EntityDTOContainerOfDestinationDTO) => this.download && dest.data.target === 16
|
||||
(dest: EntityDTOContainerOfDestinationDTO) =>
|
||||
this.download && dest.data.target === 16
|
||||
);
|
||||
if (!deliveryShippingDestionation && !downloadShippingDestionation) {
|
||||
return forkJoin(
|
||||
this.store.dispatch(new SetPaymentTypeToCheckout(response.checkout.id, this.customer, response.delivery)),
|
||||
this.store.dispatch(
|
||||
new SetPaymentTypeToCheckout(
|
||||
response.checkout.id,
|
||||
this.customer,
|
||||
response.delivery
|
||||
)
|
||||
),
|
||||
of({
|
||||
checkout: response.checkout,
|
||||
delivery: response.delivery,
|
||||
@@ -127,7 +149,7 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
new SetDestinationToCheckout(
|
||||
response.checkout.id,
|
||||
deliveryShippingDestionation.id,
|
||||
response.customer,
|
||||
this.customer,
|
||||
deliveryShippingDestionation.data.target
|
||||
)
|
||||
),
|
||||
@@ -139,13 +161,16 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
downloadDestinationDTO: undefined,
|
||||
})
|
||||
);
|
||||
} else if (!deliveryShippingDestionation && downloadShippingDestionation) {
|
||||
} else if (
|
||||
!deliveryShippingDestionation &&
|
||||
downloadShippingDestionation
|
||||
) {
|
||||
return forkJoin(
|
||||
this.store.dispatch(
|
||||
new SetDestinationToCheckout(
|
||||
response.checkout.id,
|
||||
downloadShippingDestionation.id,
|
||||
response.customer,
|
||||
this.customer,
|
||||
downloadShippingDestionation.data.target
|
||||
)
|
||||
),
|
||||
@@ -157,13 +182,16 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
downloadDestinationDTO: undefined,
|
||||
})
|
||||
);
|
||||
} else if (deliveryShippingDestionation && downloadShippingDestionation) {
|
||||
} else if (
|
||||
deliveryShippingDestionation &&
|
||||
downloadShippingDestionation
|
||||
) {
|
||||
return forkJoin(
|
||||
this.store.dispatch(
|
||||
new SetDestinationToCheckout(
|
||||
response.checkout.id,
|
||||
deliveryShippingDestionation.id,
|
||||
response.customer,
|
||||
this.customer,
|
||||
deliveryShippingDestionation.data.target
|
||||
)
|
||||
),
|
||||
@@ -177,7 +205,13 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
} else {
|
||||
return forkJoin(
|
||||
this.store.dispatch(new SetPaymentTypeToCheckout(response.checkout.id, this.customer, response.delivery)),
|
||||
this.store.dispatch(
|
||||
new SetPaymentTypeToCheckout(
|
||||
response.checkout.id,
|
||||
this.customer,
|
||||
response.delivery
|
||||
)
|
||||
),
|
||||
of({
|
||||
checkout: response.checkout,
|
||||
delivery: response.delivery,
|
||||
@@ -189,7 +223,13 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
} else {
|
||||
return forkJoin(
|
||||
this.store.dispatch(new SetPaymentTypeToCheckout(response.checkout.id, this.customer, response.delivery)),
|
||||
this.store.dispatch(
|
||||
new SetPaymentTypeToCheckout(
|
||||
response.checkout.id,
|
||||
this.customer,
|
||||
response.delivery
|
||||
)
|
||||
),
|
||||
of({
|
||||
checkout: response.checkout,
|
||||
delivery: response.delivery,
|
||||
@@ -202,8 +242,19 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
}),
|
||||
switchMap(([, data]) => {
|
||||
if (data.finished) {
|
||||
return forkJoin(of({}), of({ checkout: data.checkout, delivery: data.delivery, finished: true }));
|
||||
} else if (!data.finished && data.downloadDestinationNedded && !isNullOrUndefined(data.downloadDestinationDTO)) {
|
||||
return forkJoin(
|
||||
of({}),
|
||||
of({
|
||||
checkout: data.checkout,
|
||||
delivery: data.delivery,
|
||||
finished: true,
|
||||
})
|
||||
);
|
||||
} else if (
|
||||
!data.finished &&
|
||||
data.downloadDestinationNedded &&
|
||||
!isNullOrUndefined(data.downloadDestinationDTO)
|
||||
) {
|
||||
return forkJoin(
|
||||
this.store.dispatch(
|
||||
new SetDestinationToCheckout(
|
||||
@@ -213,12 +264,26 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
data.downloadDestinationDTO.target
|
||||
)
|
||||
),
|
||||
of({ checkout: data.checkout, delivery: data.delivery, finished: false })
|
||||
of({
|
||||
checkout: data.checkout,
|
||||
delivery: data.delivery,
|
||||
finished: false,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return forkJoin(
|
||||
this.store.dispatch(new SetPaymentTypeToCheckout(data.checkout.id, this.customer, data.delivery)),
|
||||
of({ checkout: data.checkout, delivery: data.delivery, finished: true })
|
||||
this.store.dispatch(
|
||||
new SetPaymentTypeToCheckout(
|
||||
data.checkout.id,
|
||||
this.customer,
|
||||
data.delivery
|
||||
)
|
||||
),
|
||||
of({
|
||||
checkout: data.checkout,
|
||||
delivery: data.delivery,
|
||||
finished: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}),
|
||||
@@ -226,7 +291,13 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
if (data.finished) {
|
||||
return of({});
|
||||
} else {
|
||||
return this.store.dispatch(new SetPaymentTypeToCheckout(data.checkout.id, this.customer, data.delivery));
|
||||
return this.store.dispatch(
|
||||
new SetPaymentTypeToCheckout(
|
||||
data.checkout.id,
|
||||
this.customer,
|
||||
data.delivery
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -251,9 +322,13 @@ export class PayMethodComponent implements OnInit, OnDestroy {
|
||||
customer: User,
|
||||
delivery: boolean
|
||||
): Observable<{ checkout: CheckoutDTO; customer: User; delivery: boolean }> {
|
||||
const user: User = this.store.selectSnapshot(CustomerSelectors.getCurrentProcessActiveUser);
|
||||
const user: User = this.store.selectSnapshot(
|
||||
CustomerSelectors.getCurrentProcessActiveUser
|
||||
);
|
||||
const notificationChannel = this.customerMap.getNotificationChannels(user);
|
||||
return this.store.dispatch(new SetNotificationChannel(checkout.id, notificationChannel)).pipe(
|
||||
return this.store
|
||||
.dispatch(new SetNotificationChannel(checkout.id, notificationChannel))
|
||||
.pipe(
|
||||
map(() => {
|
||||
return { checkout, customer, delivery };
|
||||
})
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
<div class="cart-content">
|
||||
<div class="cart-list">
|
||||
<hr />
|
||||
<app-invoice-address [disableAddressChanging]="disableAddressChanging"></app-invoice-address>
|
||||
<app-invoice-address
|
||||
[disableAddressChanging]="disableAddressChanging"
|
||||
></app-invoice-address>
|
||||
<hr />
|
||||
<app-notification-settings
|
||||
*ngIf="showNotificationSettings"
|
||||
@@ -22,33 +24,65 @@
|
||||
></app-notification-settings>
|
||||
<div *ngIf="takeNowCount$ | async" @removeItemCartTrigger>
|
||||
<hr />
|
||||
<app-take-now-cart (editItem)="enableDisableOrder($event)"></app-take-now-cart>
|
||||
<app-take-now-cart
|
||||
(editItem)="enableDisableOrder($event)"
|
||||
></app-take-now-cart>
|
||||
</div>
|
||||
<div *ngIf="deliveryCount$ | async" @removeItemCartTrigger [@cartPaymentType]="getDeliveryDeleteAnimation()">
|
||||
<div
|
||||
*ngIf="deliveryCount$ | async"
|
||||
@removeItemCartTrigger
|
||||
[@cartPaymentType]="getDeliveryDeleteAnimation()"
|
||||
>
|
||||
<hr />
|
||||
<app-delivery-cart [disableAddressChanging]="disableAddressChanging" (editItem)="enableDisableOrder($event)"></app-delivery-cart>
|
||||
<app-delivery-cart
|
||||
[disableAddressChanging]="disableAddressChanging"
|
||||
(editItem)="enableDisableOrder($event)"
|
||||
></app-delivery-cart>
|
||||
</div>
|
||||
<div *ngIf="pickUpCount$ | async" @removeItemCartTrigger [@cartPaymentType]="getPickUpDeleteAnimation()">
|
||||
<div
|
||||
*ngIf="pickUpCount$ | async"
|
||||
@removeItemCartTrigger
|
||||
[@cartPaymentType]="getPickUpDeleteAnimation()"
|
||||
>
|
||||
<hr />
|
||||
<app-pick-up-cart (editItem)="enableDisableOrder($event)"></app-pick-up-cart>
|
||||
<app-pick-up-cart
|
||||
(editItem)="enableDisableOrder($event)"
|
||||
></app-pick-up-cart>
|
||||
</div>
|
||||
<div *ngIf="downloadCount$ | async" @removeItemCartTrigger>
|
||||
<hr />
|
||||
<app-download-cart (editItem)="enableDisableOrder($event)"></app-download-cart>
|
||||
<app-download-cart
|
||||
(editItem)="enableDisableOrder($event)"
|
||||
></app-download-cart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-footer">
|
||||
<div class="overview-container">
|
||||
<span class="items">{{ total.items }} Artikel I {{ (cartData$ | async)?.promotionPoints }} Lesepunkte</span>
|
||||
<span class="items"
|
||||
>{{ total.items }} Artikel I
|
||||
{{ (cartData$ | async)?.promotionPoints }} Lesepunkte</span
|
||||
>
|
||||
<div class="overview">
|
||||
<div class="overview-price-container">
|
||||
<span class="overview-price">Zwischensumme {{ total.price | bookPrice }} EUR</span>
|
||||
<span *ngIf="(deliveryCount$ | async) > 0" class="overview-tax">ohne Versandkosten</span>
|
||||
<span *ngIf="(deliveryCount$ | async) === 0" class="overview-tax"> </span>
|
||||
<span class="overview-price"
|
||||
>Zwischensumme {{ total.price | bookPrice }} EUR</span
|
||||
>
|
||||
<span *ngIf="(deliveryCount$ | async) > 0" class="overview-tax"
|
||||
>ohne Versandkosten</span
|
||||
>
|
||||
<span *ngIf="(deliveryCount$ | async) === 0" class="overview-tax"
|
||||
> </span
|
||||
>
|
||||
</div>
|
||||
<app-button (action)="next()" [disabled]="orderDisabled" [primary]="true" [load]="true" [stayOnPage]="true" #continue
|
||||
<app-button
|
||||
(action)="next()"
|
||||
[disabled]="orderDisabled"
|
||||
[primary]="true"
|
||||
[load]="true"
|
||||
[stayOnPage]="true"
|
||||
#continue
|
||||
>Bestellen</app-button
|
||||
>
|
||||
</div>
|
||||
@@ -58,18 +92,25 @@
|
||||
|
||||
<ng-template #emptyCart>
|
||||
<div class="cart-container-empty">
|
||||
<div class="image-empty"><img src="assets/images/shopping_cart_white.svg" /></div>
|
||||
<div class="image-empty">
|
||||
<img src="assets/images/shopping_cart_white.svg" />
|
||||
</div>
|
||||
<div class="header-empty"><span>Ihr Warenkorb ist leer.</span></div>
|
||||
<div class="content-empty"><span>Sie haben alle Artikel aus dem</span></div>
|
||||
<div class="content-empty"><span>Warenkorb entfernt oder noch</span></div>
|
||||
<div class="content-empty"><span>Artikel hinzugefügt.</span></div>
|
||||
<div class="btn-empty">
|
||||
<app-button [primary]="true" (action)="search()">Artikel suchen</app-button>
|
||||
<app-button [primary]="true" (action)="search()"
|
||||
>Artikel suchen</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<app-printer-selection #printModal (print)="print($event)"></app-printer-selection>
|
||||
<app-printer-selection
|
||||
#printModal
|
||||
(print)="print($event)"
|
||||
></app-printer-selection>
|
||||
|
||||
<app-pay-method
|
||||
#payMethod
|
||||
|
||||
@@ -133,7 +133,7 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
cartHasItemsForDownload = false;
|
||||
guestChecked = false;
|
||||
onlineChecked = false;
|
||||
guestDisabled = true;
|
||||
guestDisabled = false;
|
||||
customerCardChecked = false;
|
||||
deliveryAndGuestOrOnline = false;
|
||||
pickUpOrTakeNowAndOnline = false;
|
||||
@@ -261,31 +261,48 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.cartHasItemsForTakeNow = cartHasItemsFor(DeliveryOption.TAKE_NOW);
|
||||
this.cartHasItemsForDownload = cartHasItemsFor(DeliveryOption.DOWNLOAD);
|
||||
|
||||
(this.cartHasItemsForDownload || this.cartHasItemsForDelivery).ifTrue(
|
||||
() => {
|
||||
this.cartHasItemsForDownload.ifTrue(() => {
|
||||
this.onlineChecked = true;
|
||||
this.guestDisabled = true;
|
||||
this.guestChecked = false;
|
||||
this.flagsSetByConditions = true;
|
||||
|
||||
if (this.cartHasItemsForDownload) {
|
||||
this.headerText =
|
||||
'Um Ihnen das ebook bereitstellen zu können, brauchen Sie ein Onlinekonto bei Hugendubel. Wir richten es Ihnen gerne sofort ein.';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(
|
||||
this.cartHasItemsForDelivery &&
|
||||
!this.cartHasItemsForDownload &&
|
||||
!this.onlineChecked
|
||||
).ifTrue(this.guestCheckboxInitialization);
|
||||
return;
|
||||
});
|
||||
|
||||
// Ruecklage
|
||||
if (this.cartHasItemsForTakeNow) {
|
||||
this.disableGuestCheckbox();
|
||||
}
|
||||
|
||||
// Abholung
|
||||
if (this.cartHasItemsForPickUp) {
|
||||
this.disableGuestCheckbox();
|
||||
}
|
||||
|
||||
// Versand
|
||||
if (this.cartHasItemsForDelivery) {
|
||||
this.guestCheckboxInitialization();
|
||||
}
|
||||
|
||||
this.detectChanges();
|
||||
};
|
||||
|
||||
disableGuestCheckbox = () => {
|
||||
this.guestDisabled = true;
|
||||
this.flagsSetByConditions = true;
|
||||
};
|
||||
|
||||
guestCheckboxInitialization = () => {
|
||||
this.guestChecked = true;
|
||||
this.onlineChecked = false;
|
||||
this.guestDisabled = false;
|
||||
this.flagsSetByConditions = true;
|
||||
|
||||
if (!this.guestChecked && !this.onlineChecked) {
|
||||
this.guestChecked = true;
|
||||
}
|
||||
};
|
||||
|
||||
initializationSubscriptionHandler = (process: Process) => {
|
||||
@@ -308,7 +325,11 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.formState = process.customerFormState;
|
||||
this.canBeSetToActiveUser = !process.closeDirectlyTab;
|
||||
this.customerFormState = process.customerFormState;
|
||||
if (process.customerFormState === CustomerFormState.MISSING_DELIVERY) {
|
||||
if (
|
||||
process.customerFormState === CustomerFormState.MISSING_DELIVERY &&
|
||||
this.customer.customer &&
|
||||
this.customer.customer.hasOnlineAccount
|
||||
) {
|
||||
this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
this.customerHelper.customerUpdateRedirect();
|
||||
this.id = this.customer.customer.id;
|
||||
@@ -316,7 +337,9 @@ export class CreateCustomerCardComponent implements OnInit, OnDestroy {
|
||||
this.btnText = 'Weiter';
|
||||
this.create = false;
|
||||
} else if (
|
||||
process.customerFormState === CustomerFormState.MISSING_ONLINE
|
||||
process.customerFormState === CustomerFormState.MISSING_ONLINE ||
|
||||
(process.customerFormState === CustomerFormState.MISSING_DELIVERY &&
|
||||
!this.customer.customer.hasOnlineAccount)
|
||||
) {
|
||||
this.buildSetDeliveryAddressForm(this.fb, this.customer.customer);
|
||||
this.customerHelper.customerUpdateRedirect();
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { Component, OnInit, OnDestroy, Input, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||
import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Input,
|
||||
ViewChild,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
FormGroup,
|
||||
FormBuilder,
|
||||
Validators,
|
||||
AbstractControl,
|
||||
} from '@angular/forms';
|
||||
import { Router, ActivatedRoute } from '@angular/router';
|
||||
import { Subject, Observable, of } from 'rxjs';
|
||||
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
|
||||
import { EditCustomerData } from '../../../../core/models/edit-customer.model';
|
||||
import { takeUntil, distinctUntilChanged, take, map, filter, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
takeUntil,
|
||||
distinctUntilChanged,
|
||||
take,
|
||||
map,
|
||||
filter,
|
||||
switchMap,
|
||||
} from 'rxjs/operators';
|
||||
import { User, Address } from '../../../../core/models/user.model';
|
||||
import { ChangeCurrentRoute } from 'apps/sales/src/app/core/store/actions/process.actions';
|
||||
import { AddNewShippingAddress, AddNewInvoiceAddress } from 'apps/sales/src/app/core/store/actions/customer.actions';
|
||||
import {
|
||||
AddNewShippingAddress,
|
||||
AddNewInvoiceAddress,
|
||||
} from 'apps/sales/src/app/core/store/actions/customer.actions';
|
||||
import { GENDERS, TIITLES } from '../../dropdown-values';
|
||||
import { CountrySelector } from 'apps/sales/src/app/core/store/selectors/countries.selector';
|
||||
import { Country } from 'apps/sales/src/app/core/models/country.model';
|
||||
@@ -35,9 +57,14 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
@Input() title: string;
|
||||
@Input() addressId: string;
|
||||
@Input() isBillingForm = true;
|
||||
@Select(SharedSelectors.getCustomerEditData) customerEditData$: Observable<EditCustomerData>;
|
||||
@Select(CountrySelector.getCountriesIterable) countries$: Observable<Country[]>;
|
||||
@ViewChild('suggestions', { static: false }) suggestions: AddressSugestionsComponent;
|
||||
@Select(SharedSelectors.getCustomerEditData) customerEditData$: Observable<
|
||||
EditCustomerData
|
||||
>;
|
||||
@Select(CountrySelector.getCountriesIterable) countries$: Observable<
|
||||
Country[]
|
||||
>;
|
||||
@ViewChild('suggestions', { static: false })
|
||||
suggestions: AddressSugestionsComponent;
|
||||
@ViewChild('postBtn', { static: false }) postBtn: ButtonComponent;
|
||||
form: FormGroup;
|
||||
submitted = false;
|
||||
@@ -82,9 +109,13 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
this.customer = this.customerInput;
|
||||
|
||||
if (this.isBillingForm) {
|
||||
this.selecetedAddress = this.customerInput.poossible_invoice_addresses.find((address: Address) => address.id === +this.addressId);
|
||||
this.selecetedAddress = this.customerInput.poossible_invoice_addresses.find(
|
||||
(address: Address) => address.id === +this.addressId
|
||||
);
|
||||
} else {
|
||||
this.selecetedAddress = this.customerInput.poossible_delivery_addresses.find((address: Address) => address.id === +this.addressId);
|
||||
this.selecetedAddress = this.customerInput.poossible_delivery_addresses.find(
|
||||
(address: Address) => address.id === +this.addressId
|
||||
);
|
||||
}
|
||||
this.buildForm(this.fb, this.selecetedAddress);
|
||||
this.onFormChange();
|
||||
@@ -113,7 +144,9 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
this.countryList = data;
|
||||
});
|
||||
|
||||
this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((queryParams) => {
|
||||
this.route.queryParams
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((queryParams) => {
|
||||
if (queryParams && queryParams['notEditable']) {
|
||||
this.notEditable = true;
|
||||
}
|
||||
@@ -126,7 +159,9 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
// store address form data on changes
|
||||
this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((v) => {
|
||||
const formStateString = JSON.stringify(v);
|
||||
this.store.dispatch(new SaveFormState(ADDRESS_FORM_STATE_KEY, formStateString));
|
||||
this.store.dispatch(
|
||||
new SaveFormState(ADDRESS_FORM_STATE_KEY, formStateString)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -155,8 +190,15 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
if (key !== 'FirstName' && key !== 'LastName') {
|
||||
showErrorPopUp = true;
|
||||
}
|
||||
if (keysChecked === Object.keys(invalidProperties).length && showErrorPopUp) {
|
||||
this.errorService.addErrors(409, null, JSON.stringify(invalidProperties));
|
||||
if (
|
||||
keysChecked === Object.keys(invalidProperties).length &&
|
||||
showErrorPopUp
|
||||
) {
|
||||
this.errorService.addErrors(
|
||||
409,
|
||||
null,
|
||||
JSON.stringify(invalidProperties)
|
||||
);
|
||||
}
|
||||
});
|
||||
scrollToFirstInvalidElement();
|
||||
@@ -216,7 +258,12 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
if (this.isBillingForm) {
|
||||
this.store
|
||||
.dispatch(new AddNewInvoiceAddress(updatedCustomer.id, updatedCustomer.invoice_address))
|
||||
.dispatch(
|
||||
new AddNewInvoiceAddress(
|
||||
updatedCustomer.id,
|
||||
updatedCustomer.invoice_address
|
||||
)
|
||||
)
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
switchMap(() => {
|
||||
@@ -228,16 +275,25 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
this.addAddressErrorHandler(invalidProperties);
|
||||
this.postBtn.stopLoading();
|
||||
} else {
|
||||
const queryParams = this.notEditable ? { notEditable: true } : undefined;
|
||||
const queryParams = this.notEditable
|
||||
? { notEditable: true }
|
||||
: undefined;
|
||||
const currentRoute = `customer/edit/${this.customer.id}/billing`;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute, false, queryParams));
|
||||
this.store.dispatch(
|
||||
new ChangeCurrentRoute(currentRoute, false, queryParams)
|
||||
);
|
||||
this.postBtn.stopLoading();
|
||||
this.router.navigate([currentRoute], { queryParams: queryParams });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.store
|
||||
.dispatch(new AddNewShippingAddress(updatedCustomer.id, updatedCustomer.delivery_addres))
|
||||
.dispatch(
|
||||
new AddNewShippingAddress(
|
||||
updatedCustomer.id,
|
||||
updatedCustomer.delivery_addres
|
||||
)
|
||||
)
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
switchMap(() => {
|
||||
@@ -249,9 +305,13 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
this.addAddressErrorHandler(invalidProperties);
|
||||
this.postBtn.stopLoading();
|
||||
} else {
|
||||
const queryParams = this.notEditable ? { notEditable: true } : undefined;
|
||||
const queryParams = this.notEditable
|
||||
? { notEditable: true }
|
||||
: undefined;
|
||||
const currentRoute = `customer/edit/${this.customer.id}/delivery`;
|
||||
this.store.dispatch(new ChangeCurrentRoute(currentRoute, false, queryParams));
|
||||
this.store.dispatch(
|
||||
new ChangeCurrentRoute(currentRoute, false, queryParams)
|
||||
);
|
||||
this.postBtn.stopLoading();
|
||||
this.router.navigate([currentRoute], { queryParams: queryParams });
|
||||
}
|
||||
@@ -324,7 +384,9 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
countryKey(country: string) {
|
||||
return this.store.selectSnapshot(CountrySelector.getCountriesIterable).find((t) => t.value === country).key;
|
||||
return this.store
|
||||
.selectSnapshot(CountrySelector.getCountriesIterable)
|
||||
.find((t) => t.value === country).key;
|
||||
}
|
||||
|
||||
private updateUserData(): User {
|
||||
@@ -332,25 +394,29 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
const updatedCustomer: User = { ...this.customer };
|
||||
|
||||
if (this.isBillingForm) {
|
||||
updatedCustomer.poossible_invoice_addresses = updatedCustomer.poossible_invoice_addresses.map((address: Address) => {
|
||||
updatedCustomer.poossible_invoice_addresses = updatedCustomer.poossible_invoice_addresses.map(
|
||||
(address: Address) => {
|
||||
if (address.id === +this.addressId) {
|
||||
return updateAddress;
|
||||
}
|
||||
|
||||
return address;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (updatedCustomer.invoice_address.id === updateAddress.id) {
|
||||
updatedCustomer.invoice_address = updateAddress;
|
||||
}
|
||||
} else {
|
||||
updatedCustomer.poossible_delivery_addresses = updatedCustomer.poossible_delivery_addresses.map((address: Address) => {
|
||||
updatedCustomer.poossible_delivery_addresses = updatedCustomer.poossible_delivery_addresses.map(
|
||||
(address: Address) => {
|
||||
if (address.id === +this.addressId) {
|
||||
return updateAddress;
|
||||
}
|
||||
|
||||
return address;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (updatedCustomer.delivery_addres.id === updateAddress.id) {
|
||||
updatedCustomer.delivery_addres = updateAddress;
|
||||
@@ -365,10 +431,16 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
const updatedCustomer: User = { ...this.customer };
|
||||
|
||||
if (this.isBillingForm) {
|
||||
updatedCustomer.poossible_invoice_addresses = [...updatedCustomer.poossible_invoice_addresses, newAddress];
|
||||
updatedCustomer.poossible_invoice_addresses = [
|
||||
...(updatedCustomer.poossible_invoice_addresses || []),
|
||||
newAddress,
|
||||
];
|
||||
updatedCustomer.invoice_address = newAddress;
|
||||
} else {
|
||||
updatedCustomer.poossible_delivery_addresses = [...updatedCustomer.poossible_delivery_addresses, newAddress];
|
||||
updatedCustomer.poossible_delivery_addresses = [
|
||||
...(updatedCustomer.poossible_delivery_addresses || []),
|
||||
newAddress,
|
||||
];
|
||||
updatedCustomer.delivery_addres = newAddress;
|
||||
}
|
||||
|
||||
@@ -417,9 +489,13 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
this.form.get('city').value,
|
||||
this.countryKey(this.form.get('country').value),
|
||||
this.form.get('company').value ? this.form.get('company').value : null,
|
||||
this.form.get('department').value ? this.form.get('department').value : null,
|
||||
this.form.get('department').value
|
||||
? this.form.get('department').value
|
||||
: null,
|
||||
this.form.get('note').value ? this.form.get('note').value : null,
|
||||
this.form.get('tax_number').value ? this.form.get('tax_number').value : null,
|
||||
this.form.get('tax_number').value
|
||||
? this.form.get('tax_number').value
|
||||
: null,
|
||||
this.form.get('title').value ? this.form.get('title').value : null,
|
||||
this.form.get('gender').value ? this.form.get('gender').value : null
|
||||
);
|
||||
@@ -427,7 +503,11 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
return data;
|
||||
}
|
||||
|
||||
private updateDataForSelectedOption(field: string, value: string, defaultValue: string) {
|
||||
private updateDataForSelectedOption(
|
||||
field: string,
|
||||
value: string,
|
||||
defaultValue: string
|
||||
) {
|
||||
if (value !== defaultValue) {
|
||||
this.form.get(field).patchValue(value);
|
||||
} else {
|
||||
@@ -448,33 +528,95 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
.subscribe((value) => {
|
||||
const savedAddress = value ? JSON.parse(value) : undefined;
|
||||
this.form = fb.group({
|
||||
id: [address ? address.id : this.getProperty(savedAddress, 'id') || ''],
|
||||
firstName: [address ? address.first_name : this.getProperty(savedAddress, 'firstName') || '', Validators.required],
|
||||
lastName: [address ? address.last_name : this.getProperty(savedAddress, 'lastName') || '', Validators.required],
|
||||
id: [
|
||||
address ? address.id : this.getProperty(savedAddress, 'id') || '',
|
||||
],
|
||||
firstName: [
|
||||
address
|
||||
? address.first_name
|
||||
: this.getProperty(savedAddress, 'firstName') || '',
|
||||
Validators.required,
|
||||
],
|
||||
lastName: [
|
||||
address
|
||||
? address.last_name
|
||||
: this.getProperty(savedAddress, 'lastName') || '',
|
||||
Validators.required,
|
||||
],
|
||||
address: [
|
||||
address ? (address.street ? address.street : '') : this.getProperty(savedAddress, 'address') || '',
|
||||
address
|
||||
? address.street
|
||||
? address.street
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'address') || '',
|
||||
Validators.required,
|
||||
],
|
||||
streetNo: [
|
||||
address ? (address.streetNo ? address.streetNo : '') : this.getProperty(savedAddress, 'streetNo') || '',
|
||||
address
|
||||
? address.streetNo
|
||||
? address.streetNo
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'streetNo') || '',
|
||||
Validators.required,
|
||||
],
|
||||
zipCode: [
|
||||
address
|
||||
? address.zip
|
||||
? address.zip
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'zipCode') || '',
|
||||
Validators.required,
|
||||
],
|
||||
city: [
|
||||
address
|
||||
? address.city
|
||||
? address.city
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'city') || '',
|
||||
Validators.required,
|
||||
],
|
||||
country: [
|
||||
this.getProperty(savedAddress, 'country') ||
|
||||
this.getCountryFromAddress(address),
|
||||
Validators.required,
|
||||
],
|
||||
zipCode: [address ? (address.zip ? address.zip : '') : this.getProperty(savedAddress, 'zipCode') || '', Validators.required],
|
||||
city: [address ? (address.city ? address.city : '') : this.getProperty(savedAddress, 'city') || '', Validators.required],
|
||||
country: [this.getProperty(savedAddress, 'country') || this.getCountryFromAddress(address), Validators.required],
|
||||
gender: [
|
||||
address ? address.gender : this.getProperty(savedAddress, 'gender') || '',
|
||||
address
|
||||
? address.gender
|
||||
: this.getProperty(savedAddress, 'gender') || '',
|
||||
[Validators.required, CustomValidators.validateGender],
|
||||
],
|
||||
company: [address ? (address.company_name ? address.company_name : '') : this.getProperty(savedAddress, 'company') || ''],
|
||||
company: [
|
||||
address
|
||||
? address.company_name
|
||||
? address.company_name
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'company') || '',
|
||||
],
|
||||
department: [
|
||||
address ? (address.company_department ? address.company_department : '') : this.getProperty(savedAddress, 'department') || '',
|
||||
address
|
||||
? address.company_department
|
||||
? address.company_department
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'department') || '',
|
||||
],
|
||||
tax_number: [
|
||||
address ? (address.company_tax_number ? address.company_tax_number : '') : this.getProperty(savedAddress, 'tax_number') || '',
|
||||
address
|
||||
? address.company_tax_number
|
||||
? address.company_tax_number
|
||||
: ''
|
||||
: this.getProperty(savedAddress, 'tax_number') || '',
|
||||
],
|
||||
title: [
|
||||
address
|
||||
? address.title
|
||||
: this.getProperty(savedAddress, 'title') || '',
|
||||
],
|
||||
note: [
|
||||
address
|
||||
? address.note
|
||||
: this.getProperty(savedAddress, 'note') || '',
|
||||
],
|
||||
title: [address ? address.title : this.getProperty(savedAddress, 'title') || ''],
|
||||
note: [address ? address.note : this.getProperty(savedAddress, 'note') || ''],
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -484,7 +626,9 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
return '';
|
||||
}
|
||||
|
||||
const countries = this.store.selectSnapshot(CountrySelector.getCountriesIterable);
|
||||
const countries = this.store.selectSnapshot(
|
||||
CountrySelector.getCountriesIterable
|
||||
);
|
||||
const country = countries.find((t) => t.key === address.country);
|
||||
if (isNullOrUndefined(country)) {
|
||||
return address.country;
|
||||
@@ -502,11 +646,21 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
const addressesLength = address.length;
|
||||
address.forEach((add) => {
|
||||
processedItems++;
|
||||
const street = add.street ? add.street : (this.form.get('address').value as string);
|
||||
const streetNo = add.streetNumber ? add.streetNumber : (this.form.get('streetNo').value as string);
|
||||
const zipCode = add.zipCode ? add.zipCode : (this.form.get('zipCode').value as string);
|
||||
const city = add.city ? add.city : (this.form.get('city').value as string);
|
||||
const countryValue = add.country ? add.country : (this.form.get('country').value as string);
|
||||
const street = add.street
|
||||
? add.street
|
||||
: (this.form.get('address').value as string);
|
||||
const streetNo = add.streetNumber
|
||||
? add.streetNumber
|
||||
: (this.form.get('streetNo').value as string);
|
||||
const zipCode = add.zipCode
|
||||
? add.zipCode
|
||||
: (this.form.get('zipCode').value as string);
|
||||
const city = add.city
|
||||
? add.city
|
||||
: (this.form.get('city').value as string);
|
||||
const countryValue = add.country
|
||||
? add.country
|
||||
: (this.form.get('country').value as string);
|
||||
|
||||
if (city && street && streetNo && zipCode && countryValue) {
|
||||
this.addressSuggestions.push({
|
||||
@@ -520,7 +674,10 @@ export class CustomerAddressFormComponent implements OnInit, OnDestroy {
|
||||
zip: zipCode,
|
||||
});
|
||||
}
|
||||
if (processedItems === addressesLength && this.addressSuggestions.length === addressesLength) {
|
||||
if (
|
||||
processedItems === addressesLength &&
|
||||
this.addressSuggestions.length === addressesLength
|
||||
) {
|
||||
this.cdrf.detectChanges();
|
||||
this.openSuggestions();
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ import { SetEditOrder } from 'apps/sales/src/app/core/store/actions/collecting-s
|
||||
import { OrderDTO, ReceiptListItemDTO } from '@swagger/oms';
|
||||
import { OrderPaymentReceipt } from 'apps/sales/src/app/core/models/order-payment-receipt.model';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { receiptType, orderPaymentType } from 'apps/sales/src/app/core/mappings/shelf.mapping';
|
||||
import {
|
||||
receiptType,
|
||||
orderPaymentType,
|
||||
} from 'apps/sales/src/app/core/mappings/shelf.mapping';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ShelfNavigationService } from '../../../shelf/shared/services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-customer-order-item-row',
|
||||
@@ -39,7 +43,12 @@ export class CustomerOrderItemRowComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private store: Store, private router: Router, private datePipe: DatePipe) {}
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private datePipe: DatePipe,
|
||||
private shelfNavigationService: ShelfNavigationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadPreorderedTag();
|
||||
@@ -55,13 +64,20 @@ export class CustomerOrderItemRowComponent implements OnInit {
|
||||
this.store.dispatch(new SetEditOrder(this.order));
|
||||
let breadcrumbName = '';
|
||||
if (this.order.buyer) {
|
||||
breadcrumbName = this.order.buyer.firstName + ' ' + this.order.buyer.lastName + ' Bearbeiten';
|
||||
breadcrumbName =
|
||||
this.order.buyer.firstName +
|
||||
' ' +
|
||||
this.order.buyer.lastName +
|
||||
' Bearbeiten';
|
||||
}
|
||||
|
||||
const subItem = this.order.items.find((t) => t.id === id).data.subsetItems[0].data;
|
||||
const path = `/shelf/edit/${subItem.compartmentCode ? subItem.compartmentCode : this.order.id}/${this.order.id}/${
|
||||
subItem.processingStatus
|
||||
}/${subItem.compartmentCode ? 'c' : 'o'}/customer/${this.customerId}/${subItem.id}`;
|
||||
const subItem = this.order.items.find((t) => t.id === id).data
|
||||
.subsetItems[0].data;
|
||||
const path = `/shelf/edit/${
|
||||
subItem.compartmentCode ? subItem.compartmentCode : this.order.id
|
||||
}/${this.order.id}/${subItem.processingStatus}/${
|
||||
subItem.compartmentCode ? 'c' : 'o'
|
||||
}/customer/${this.customerId}/${subItem.id}`;
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(
|
||||
<Breadcrumb>{
|
||||
@@ -86,15 +102,23 @@ export class CustomerOrderItemRowComponent implements OnInit {
|
||||
const receiptTp = receiptType[this.paymentDataDTO.receiptType];
|
||||
let printedDate: string;
|
||||
if (this.paymentDataDTO.printedDate) {
|
||||
printedDate = this.datePipe.transform(this.paymentDataDTO.printedDate, 'dd.MM.yy | HH:mm') + ' Uhr';
|
||||
printedDate =
|
||||
this.datePipe.transform(
|
||||
this.paymentDataDTO.printedDate,
|
||||
'dd.MM.yy | HH:mm'
|
||||
) + ' Uhr';
|
||||
}
|
||||
const placeOfPayment = this.paymentDataDTO.placeOfPayment;
|
||||
if (this.paymentDataDTO.paidAt && !isNullOrUndefined(this.paymentDataDTO.paidAt)) {
|
||||
if (
|
||||
this.paymentDataDTO.paidAt &&
|
||||
!isNullOrUndefined(this.paymentDataDTO.paidAt)
|
||||
) {
|
||||
this.isPayed = true;
|
||||
} else {
|
||||
this.isPayed = false;
|
||||
}
|
||||
const invoiceText = this.item && this.item.invoiceText ? this.item.invoiceText : '';
|
||||
const invoiceText =
|
||||
this.item && this.item.invoiceText ? this.item.invoiceText : '';
|
||||
this.paymentData = <OrderPaymentReceipt>{
|
||||
receiptNumber,
|
||||
placeOfPayment,
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Component, OnInit, Input, AfterViewInit, ViewChild, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
AfterViewInit,
|
||||
ViewChild,
|
||||
OnDestroy,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
@@ -8,34 +15,50 @@ import { SearchInputComponent } from '@libs/ui';
|
||||
import { Process } from '../../../../core/models/process.model';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { AddProcess, ChangeCurrentRoute, AddCustomerSearch } from '../../../../core/store/actions/process.actions';
|
||||
import {
|
||||
AddProcess,
|
||||
ChangeCurrentRoute,
|
||||
AddCustomerSearch,
|
||||
} from '../../../../core/store/actions/process.actions';
|
||||
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
|
||||
import { CustomerSearchResult } from '../../../../core/models/customer-search-result.model';
|
||||
import { AppState } from 'apps/sales/src/app/core/store/state/app.state';
|
||||
import { CustomerService } from 'apps/sales/src/app/core/services/customer.service';
|
||||
import { CustomerSearchResponse } from 'apps/sales/src/app/core/models/customer-search-response.model';
|
||||
import { CUSTOMER_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants';
|
||||
import { ClearCachedCustomers, SetCachedCustomers } from 'apps/sales/src/app/core/store/actions/customer.actions';
|
||||
import {
|
||||
ClearCachedCustomers,
|
||||
SetCachedCustomers,
|
||||
} from 'apps/sales/src/app/core/store/actions/customer.actions';
|
||||
import { CustomerFeatures } from 'apps/sales/src/app/core/models/customer-features.model';
|
||||
import { CustomerSearchEmptyModalComponent } from '../customer-search-empty-modal/customer-search-empty-modal.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-customer-card',
|
||||
templateUrl: './customer-search-card.component.html',
|
||||
styleUrls: ['./customer-search-card.component.scss']
|
||||
styleUrls: ['./customer-search-card.component.scss'],
|
||||
})
|
||||
export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
@Select(SharedSelectors.getSearchedCustomers) customers$: Observable<CustomerSearchResult>;
|
||||
export class CustomerSearchCardComponent
|
||||
implements OnInit, AfterViewInit, OnDestroy {
|
||||
@Select(SharedSelectors.getSearchedCustomers) customers$: Observable<
|
||||
CustomerSearchResult
|
||||
>;
|
||||
|
||||
destroy$ = new Subject();
|
||||
processes: Process[];
|
||||
searchError = '';
|
||||
|
||||
@Input() searchParams = '';
|
||||
@ViewChild('searchInput', { static: false }) searchInput: SearchInputComponent;
|
||||
@ViewChild('searchEmptyModal', { static: false }) searchEmptyModal: CustomerSearchEmptyModalComponent;
|
||||
@ViewChild('searchInput', { static: false })
|
||||
searchInput: SearchInputComponent;
|
||||
@ViewChild('searchEmptyModal', { static: false })
|
||||
searchEmptyModal: CustomerSearchEmptyModalComponent;
|
||||
|
||||
constructor(private store: Store, private router: Router, private customerService: CustomerService) { }
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
private customerService: CustomerService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.dispatch(new ClearCachedCustomers());
|
||||
@@ -62,8 +85,8 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
.pipe(take(1), takeUntil(this.destroy$))
|
||||
.subscribe((r: CustomerSearchResponse) => {
|
||||
if (r.customers.length > 0) {
|
||||
const filteredCustomer = r.customers.filter(data => {
|
||||
if (!data.features) {
|
||||
const filteredCustomer = r.customers.filter((data) => {
|
||||
if (isNullOrUndefined(data) || !data.features) {
|
||||
return true;
|
||||
}
|
||||
const customerFeatures = new CustomerFeatures(data.features);
|
||||
@@ -72,12 +95,19 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
}
|
||||
return true;
|
||||
});
|
||||
const response = { ...r, customers: filteredCustomer, hits: filteredCustomer ? filteredCustomer.length : 0 };
|
||||
const response = {
|
||||
...r,
|
||||
customers: filteredCustomer,
|
||||
hits: r.hits,
|
||||
};
|
||||
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
name:
|
||||
(searchParams.length > 12 ? searchParams.substring(0, 12) + '...' : searchParams) +
|
||||
(searchParams.length > 12
|
||||
? searchParams.substring(0, 12) + '...'
|
||||
: searchParams) +
|
||||
` (${
|
||||
!isNullOrUndefined(response)
|
||||
? !isNullOrUndefined(response.hits)
|
||||
@@ -87,7 +117,7 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
: 0
|
||||
: 0
|
||||
} Ergebnisse)`,
|
||||
path: '/customer/results'
|
||||
path: '/customer/results',
|
||||
},
|
||||
'customer'
|
||||
)
|
||||
@@ -97,7 +127,7 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
query: searchParams,
|
||||
skip: 0,
|
||||
take: 5,
|
||||
firstLoad: true
|
||||
firstLoad: true,
|
||||
})
|
||||
);
|
||||
sessionStorage.removeItem(CUSTOMER_SCROLL_INDEX);
|
||||
@@ -119,7 +149,7 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
const newProcess = <Process>{
|
||||
id: 1,
|
||||
name: 'Vorgang 1',
|
||||
currentRoute: '/product/search'
|
||||
currentRoute: '/product/search',
|
||||
};
|
||||
|
||||
this.store.dispatch(new AddProcess(newProcess));
|
||||
@@ -127,7 +157,7 @@ export class CustomerSearchCardComponent implements OnInit, AfterViewInit, OnDes
|
||||
new AddBreadcrumb(
|
||||
<Breadcrumb>{
|
||||
name: 'Artikelsuche',
|
||||
path: '/product/search'
|
||||
path: '/product/search',
|
||||
},
|
||||
'customer'
|
||||
)
|
||||
|
||||
@@ -3,12 +3,14 @@ import { User } from '../../../../core/models/user.model';
|
||||
import { Observable, BehaviorSubject, Subject } from 'rxjs';
|
||||
import { takeUntil, debounceTime, take } from 'rxjs/operators';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { CurrentCustomerPageLoaded, AddCustomers } from '../../../../core/store/actions/customer.actions';
|
||||
import {
|
||||
CurrentCustomerPageLoaded,
|
||||
AddCustomers,
|
||||
} from '../../../../core/store/actions/customer.actions';
|
||||
import { CustomerService } from '../../../../core/services/customer.service';
|
||||
import { CustomerSearchResponse } from '../../../../core/models/customer-search-response.model';
|
||||
import { CustomerFilters } from './customer-search-result.component';
|
||||
import { AddBreadcrumb, AddBreadcrumbNoDub } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { isArrayMinLength } from 'apps/sales/src/app/core/utils/app.utils';
|
||||
import { AddBreadcrumbNoDub } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { CustomerSelectors } from 'apps/sales/src/app/core/store/selectors/customer.selectors';
|
||||
|
||||
@@ -16,7 +18,9 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
private pageSize = 10;
|
||||
public cachedData = Array.from<User>({ length: 0 });
|
||||
private fetchedPages = new Set<number>();
|
||||
private dataStream = new BehaviorSubject<(User | undefined)[]>(this.cachedData);
|
||||
private dataStream = new BehaviorSubject<(User | undefined)[]>(
|
||||
this.cachedData
|
||||
);
|
||||
destroy$ = new Subject();
|
||||
public loading = true;
|
||||
public results = false;
|
||||
@@ -35,8 +39,12 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
super();
|
||||
}
|
||||
|
||||
connect(collectionViewer: CollectionViewer): Observable<(User | undefined)[]> {
|
||||
collectionViewer.viewChange.pipe(takeUntil(this.destroy$)).subscribe(range => {
|
||||
connect(
|
||||
collectionViewer: CollectionViewer
|
||||
): Observable<(User | undefined)[]> {
|
||||
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++) {
|
||||
@@ -45,11 +53,8 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
});
|
||||
|
||||
this.dataStream
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
debounceTime(1000)
|
||||
)
|
||||
.subscribe(i => {
|
||||
.pipe(takeUntil(this.destroy$), debounceTime(1000))
|
||||
.subscribe((i) => {
|
||||
this.store.dispatch(new AddCustomers([...i]));
|
||||
});
|
||||
|
||||
@@ -79,7 +84,14 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
const customerSource$ = !this.scan
|
||||
? page === 0 && this.first
|
||||
? this.store.select(CustomerSelectors.getCachedCustomers)
|
||||
: this.customerService.searchCustomer(this.search, false, page * this.pageSize, this.pageSize, false, this.customerFilters)
|
||||
: this.customerService.searchCustomer(
|
||||
this.search,
|
||||
false,
|
||||
page * this.pageSize,
|
||||
this.pageSize,
|
||||
false,
|
||||
this.customerFilters
|
||||
)
|
||||
: this.customerService.scannCustomer(this.search);
|
||||
customerSource$.pipe(take(1)).subscribe((data: CustomerSearchResponse) => {
|
||||
if (!data) {
|
||||
@@ -92,8 +104,14 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
new AddBreadcrumbNoDub(
|
||||
{
|
||||
name:
|
||||
(this.search.length > 12 ? this.search.substring(0, 12) + '...' : this.search) +
|
||||
` (${!isNullOrUndefined(data.hits) ? data.hits : data.customers.length} Ergebnisse)`,
|
||||
(this.search.length > 12
|
||||
? this.search.substring(0, 12) + '...'
|
||||
: this.search) +
|
||||
` (${
|
||||
!isNullOrUndefined(data.hits)
|
||||
? data.hits
|
||||
: data.customers.length
|
||||
} Ergebnisse)`,
|
||||
path: '/customer/results',
|
||||
},
|
||||
'customer'
|
||||
@@ -106,7 +124,11 @@ export class CustomerSearchDataSource extends DataSource<User | undefined> {
|
||||
this.allHits = data.hits;
|
||||
}
|
||||
|
||||
this.cachedData.splice(page * this.pageSize, this.pageSize, ...data.customers);
|
||||
this.cachedData.splice(
|
||||
page * this.pageSize,
|
||||
this.pageSize,
|
||||
...data.customers
|
||||
);
|
||||
this.dataStream.next(this.cachedData);
|
||||
|
||||
if (page === 0) {
|
||||
|
||||
@@ -9,17 +9,29 @@
|
||||
</div>
|
||||
<div class="filters" *ngIf="ds">
|
||||
<app-checkbox
|
||||
[option]="{ value: 'Gastkunde', disabled: false, selected: filters.guestaccount !== undefined }"
|
||||
[option]="{
|
||||
value: 'Gastkunde',
|
||||
disabled: false,
|
||||
selected: filters.guestaccount !== undefined
|
||||
}"
|
||||
[negative]="selectedFilterMode === 1"
|
||||
(valueChanges)="updateFilter($event, 'guestaccount')"
|
||||
></app-checkbox>
|
||||
<app-checkbox
|
||||
[option]="{ value: 'Kundenkarte', disabled: false, selected: filters.bonuscard !== undefined }"
|
||||
[option]="{
|
||||
value: 'Kundenkarte',
|
||||
disabled: false,
|
||||
selected: filters.bonuscard !== undefined
|
||||
}"
|
||||
[negative]="selectedFilterMode === 1"
|
||||
(valueChanges)="updateFilter($event, 'bonuscard')"
|
||||
></app-checkbox>
|
||||
<app-checkbox
|
||||
[option]="{ value: 'Onlinekonto', disabled: false, selected: filters.onlineshop !== undefined }"
|
||||
[option]="{
|
||||
value: 'Onlinekonto',
|
||||
disabled: false,
|
||||
selected: filters.onlineshop !== undefined
|
||||
}"
|
||||
[negative]="selectedFilterMode === 1"
|
||||
(valueChanges)="updateFilter($event, 'onlineshop')"
|
||||
></app-checkbox>
|
||||
@@ -36,23 +48,39 @@
|
||||
<span>
|
||||
Kunde nicht gefunden?
|
||||
</span>
|
||||
<app-button (action)="redirectToCustomerCreate()">Neue Kundendaten erfassen</app-button>
|
||||
<app-button (action)="redirectToCustomerCreate()"
|
||||
>Neue Kundendaten erfassen</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<cdk-virtual-scroll-viewport itemSize="144" #viewport
|
||||
[perfectScrollbar]="{minScrollbarLength: 20}">
|
||||
<div *cdkVirtualFor="let customer of ds; let i = index; let last = last" class="virtual-item">
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="144"
|
||||
#viewport
|
||||
[perfectScrollbar]="{ minScrollbarLength: 20 }"
|
||||
>
|
||||
<div
|
||||
*cdkVirtualFor="let customer of ds; let i = index; let last = last"
|
||||
class="virtual-item"
|
||||
>
|
||||
<div class="user">
|
||||
<div class="user-info-container" *ngIf="customer != null; else loadingComponent" (click)="details(customer, i)">
|
||||
<div
|
||||
class="user-info-container"
|
||||
*ngIf="customer != null; else loadingComponent"
|
||||
(click)="details(customer, i)"
|
||||
>
|
||||
<div class="info">
|
||||
<div class="user-info">
|
||||
<div class="name-created-date">
|
||||
<h1 class="user-title">{{ customer.first_name }} {{ customer.last_name }}</h1>
|
||||
<h1 class="user-title">
|
||||
{{ customer.first_name }} {{ customer.last_name }}
|
||||
</h1>
|
||||
<span>{{ customer.created | date: 'dd.MM.yy' }}</span>
|
||||
</div>
|
||||
<div class="user-location">
|
||||
<span class="user-label">PLZ und Wohnort</span>
|
||||
<span class="user-data">{{ getCustomerDeliveryAddress(customer) }}</span>
|
||||
<span class="user-data">{{
|
||||
getCustomerDeliveryAddress(customer)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="user-email">
|
||||
<span class="user-label">E-Mail</span>
|
||||
@@ -61,13 +89,25 @@
|
||||
</div>
|
||||
<div
|
||||
class="user-actions"
|
||||
[ngClass]="{ 'additional-visible': customer.customer_card || customer.shop || customer.newsletter || customer.tolino }"
|
||||
[ngClass]="{
|
||||
'additional-visible':
|
||||
customer.customer_card ||
|
||||
customer.shop ||
|
||||
customer.newsletter ||
|
||||
customer.tolino
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<div class="additional">
|
||||
<ng-container>
|
||||
<div class="data" *ngFor="let features of customer.features">
|
||||
<lib-icon name="Check_2" alt="check" height="11px" width="15px" pr="5px"></lib-icon>
|
||||
<lib-icon
|
||||
name="Check_2"
|
||||
alt="check"
|
||||
height="11px"
|
||||
width="15px"
|
||||
pr="5px"
|
||||
></lib-icon>
|
||||
<span>{{ features.description }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -93,13 +133,16 @@
|
||||
</div>
|
||||
<div class="note" *ngIf="last">
|
||||
<span class="note-text">
|
||||
Hinweis: Aus Datenschutzgründen werden nur Teilinformationen dargestellt. Tab auf einen Kunden um mehr zu erfahren.
|
||||
Hinweis: Aus Datenschutzgründen werden nur Teilinformationen
|
||||
dargestellt. Tab auf einen Kunden um mehr zu erfahren.
|
||||
</span>
|
||||
<div class="new-customer-container">
|
||||
<span>
|
||||
Kunde nicht gefunden?
|
||||
</span>
|
||||
<app-button (action)="redirectToCustomerCreate()">Neue Kundendaten erfassen</app-button>
|
||||
<app-button (action)="redirectToCustomerCreate()"
|
||||
>Neue Kundendaten erfassen</app-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -170,20 +170,7 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
}
|
||||
|
||||
hasAllRequiredData = (user: User): boolean => {
|
||||
if (
|
||||
!user.gender ||
|
||||
!user.first_name ||
|
||||
!user.last_name ||
|
||||
!user.email ||
|
||||
!!(
|
||||
isNullOrUndefined(user.delivery_addres) &&
|
||||
isNullOrUndefined(user.invoice_address)
|
||||
) ||
|
||||
!(
|
||||
(user.delivery_addres && user.delivery_addres.country) ||
|
||||
(user.invoice_address && user.invoice_address.country)
|
||||
)
|
||||
) {
|
||||
if (!user.gender || !user.first_name || !user.last_name || !user.email) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -275,10 +262,10 @@ export class CustomerSearchResultComponent implements OnInit {
|
||||
if (!user) {
|
||||
return '';
|
||||
}
|
||||
if (!user.delivery_addres) {
|
||||
if (!user.base_addres) {
|
||||
return '';
|
||||
}
|
||||
const address = user.delivery_addres;
|
||||
const address = user.base_addres;
|
||||
const plz = address
|
||||
? isNullOrUndefined(address.zip)
|
||||
? ''
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
<div class="filter-wrapper">
|
||||
<div class="filter-nav">
|
||||
<button
|
||||
*ngFor="let filter of value; trackBy: trackByKeyOrName; let first = first"
|
||||
type="button"
|
||||
(click)="selected = filter"
|
||||
class="isa-btn isa-btn-block isa-btn-default isa-btn-l"
|
||||
[class.customer]="module === 'Customer'"
|
||||
[class.isa-mt-10]="!first"
|
||||
[class.is-active]="filter === selected"
|
||||
[class.isa-font-weight-emphasis]="filter !== selected"
|
||||
[class.isa-font-weight-bold]="filter === selected"
|
||||
>
|
||||
<button *ngFor="let filter of value; trackBy: trackByKeyOrName; let first = first" type="button"
|
||||
(click)="selected = filter" class="isa-btn isa-btn-block isa-btn-default-bg isa-btn-large"
|
||||
[class.customer]="module === 'Customer'" [class.isa-mt-10]="!first" [class.is-active]="filter === selected"
|
||||
[class.isa-font-weight-emphasis]="filter !== selected" [class.isa-font-weight-bold]="filter === selected">
|
||||
<span>{{ filter.name }}</span>
|
||||
<lib-icon
|
||||
[name]="
|
||||
<lib-icon [name]="
|
||||
filter === selected
|
||||
? 'Arrow_Next_Dark'
|
||||
: module === 'Customer'
|
||||
? 'Arrow_Next'
|
||||
: 'Arrow_Next_Grey'
|
||||
"
|
||||
height="17px"
|
||||
></lib-icon>
|
||||
" height="17px"></lib-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="filter-content isa-ml-10" [ngSwitch]="selected?.type">
|
||||
<app-select-filter
|
||||
*ngSwitchCase="'select'"
|
||||
[options]="selected.options"
|
||||
[module]="module"
|
||||
[max]="selected.max"
|
||||
(optionsChanged)="emitOnChange($event)"
|
||||
>
|
||||
<app-select-filter *ngSwitchCase="'select'" [options]="selected.options" [module]="module" [max]="selected.max"
|
||||
(optionsChanged)="emitOnChange($event)">
|
||||
</app-select-filter>
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,8 +5,9 @@
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="isa-btn isa-p-0 isa-font-lightgrey isa-font-weight-emphasis"
|
||||
class="isa-btn isa-p-0 isa-font-weight-emphasis"
|
||||
[class.isa-font-color-customer]="module === 'Customer'"
|
||||
[class.isa-font-lightgrey]="module !== 'Customer'"
|
||||
*ngIf="!max"
|
||||
(click)="selectAll()"
|
||||
>
|
||||
@@ -34,15 +35,16 @@
|
||||
|
||||
<button
|
||||
*ngIf="canScroll"
|
||||
class="scroll-btn isa-btn isa-btn-circle isa-btn-default"
|
||||
class="scroll-btn isa-btn isa-btn-circle isa-btn-default-bg"
|
||||
[class.up]="scrollPositionPersantage >= 20"
|
||||
[class.down]="scrollPositionPersantage < 20"
|
||||
[class.no-shadow]="module === 'Customer'"
|
||||
(click)="scroll()"
|
||||
>
|
||||
<lib-icon
|
||||
width="18px"
|
||||
height="20px"
|
||||
mt="3px"
|
||||
name="Arrow_back_branch"
|
||||
[name]="module === 'Customer' ? 'Arrow_back' : 'Arrow_back_branch'"
|
||||
></lib-icon>
|
||||
</button>
|
||||
|
||||
@@ -28,6 +28,20 @@
|
||||
right: 6px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(89, 100, 112, 0.5);
|
||||
|
||||
&.no-shadow {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@include mq-desktop() {
|
||||
bottom: 28px;
|
||||
right: 16vw;
|
||||
}
|
||||
|
||||
@include mq-big-desktop() {
|
||||
bottom: 28px;
|
||||
right: 26vw;
|
||||
}
|
||||
|
||||
transition: all 100ms linear;
|
||||
&.up {
|
||||
transform: rotate(90deg);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SelectFilterComponent } from './select-filter.component';
|
||||
import { SelectFilterOptionComponent } from './select-filter-option.component';
|
||||
import {
|
||||
selectFilterOptionsAllSelected,
|
||||
selectFilterOptionsAllDeselected,
|
||||
selectFilterOptionsMixedSelected,
|
||||
} from '../../../../shelf/shared/mockdata';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
fdescribe('SelectFilterComponent', () => {
|
||||
let fixture: ComponentFixture<SelectFilterComponent>;
|
||||
let component: SelectFilterComponent;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [],
|
||||
declarations: [SelectFilterComponent, SelectFilterOptionComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SelectFilterComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
spyOn(component, 'ngAfterViewInit').and.callFake(() => {});
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('setAllSelected', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'setAllSelected').and.callThrough();
|
||||
spyOn(component.allSelected$, 'next').and.callFake(() => {});
|
||||
});
|
||||
|
||||
it('should set allSelected with true', () => {
|
||||
const mockFilters = selectFilterOptionsAllSelected;
|
||||
component.setAllSelected(mockFilters);
|
||||
|
||||
expect(component.allSelected$.next).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should set allSelected to false if only some options are selected', () => {
|
||||
const mockFilters = selectFilterOptionsMixedSelected;
|
||||
component.setAllSelected(mockFilters);
|
||||
|
||||
expect(component.allSelected$.next).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should set allSelected with false', () => {
|
||||
const mockFilters = selectFilterOptionsAllDeselected;
|
||||
component.setAllSelected(mockFilters);
|
||||
|
||||
expect(component.allSelected$.next).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -146,7 +146,7 @@ export class SelectFilterComponent
|
||||
}
|
||||
|
||||
return options.every((option) => {
|
||||
if (!option.options) {
|
||||
if (!option.options || !option.options.length) {
|
||||
return option.selected;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,4 +5,5 @@ export interface SelectFilterOption extends FilterOptionBase {
|
||||
expanded?: boolean;
|
||||
options?: SelectFilterOption[];
|
||||
id?: string;
|
||||
initial_selected_state?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type FilterResetStrategy = 'all' | 'default';
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './filter-reset-strategy.model';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<div class="container" *ngIf="value">
|
||||
<div class="clear-all">
|
||||
<div class="clear-all" *ngIf="showResetButton">
|
||||
<button
|
||||
class="isa-btn isa-btn-primary-link remove-all isa-pt-0 isa-pb-0 isa-pl-15 isa-pr-15"
|
||||
(click)="clearAllFilters()"
|
||||
(click)="resetFilters()"
|
||||
>
|
||||
Alle Filter Entfernen
|
||||
{{ resetStrategy | filterResetWording }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@ $font-size: 18px;
|
||||
}
|
||||
|
||||
.clear-all {
|
||||
&:only-child {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.filter-name {
|
||||
padding-right: $filter-padding-right;
|
||||
padding-left: 2rem;
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
import { SelectedFilterOptionsComponent } from './selected-filter-options.component';
|
||||
import { ChangeDetectorRef, Renderer2 } from '@angular/core';
|
||||
import { IconModule } from '@libs/ui';
|
||||
import {
|
||||
FilterResetWordingPipe,
|
||||
SelectedFiltersPipe,
|
||||
CheckAllChildOptionsSelectedPipe,
|
||||
} from '../pipe';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { PrimaryFilterOption } from '../../../shelf/defs';
|
||||
import {
|
||||
mockFilters,
|
||||
primaryFiltersMock,
|
||||
} from '../../../shelf/shared/mockdata';
|
||||
import { SelectFilter } from '../../models';
|
||||
|
||||
fdescribe('SelectedFilterOptionsComponent', () => {
|
||||
let fixture: ComponentFixture<SelectedFilterOptionsComponent>;
|
||||
let component: SelectedFilterOptionsComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [IconModule, IconModule],
|
||||
providers: [ChangeDetectorRef, Renderer2, SelectedFilterOptionsComponent],
|
||||
declarations: [
|
||||
SelectedFilterOptionsComponent,
|
||||
SelectedFiltersPipe,
|
||||
FilterResetWordingPipe,
|
||||
CheckAllChildOptionsSelectedPipe,
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SelectedFilterOptionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.value = [];
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('#ResetFilters', () => {
|
||||
let button: HTMLButtonElement;
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'resetFilters').and.callThrough();
|
||||
component.resetStrategy = 'all';
|
||||
component.showResetButton = true;
|
||||
|
||||
button = fixture.debugElement.query(
|
||||
By.css('.isa-btn.isa-btn-primary-link.remove-all')
|
||||
).nativeElement;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be shown by default', () => {
|
||||
expect(button).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not be shwon if showResetButton is false', async () => {
|
||||
component.showResetButton = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.whenStable();
|
||||
|
||||
const debugButtonEl = fixture.debugElement.query(
|
||||
By.css('.isa-btn.isa-btn-primary-link.remove-all')
|
||||
);
|
||||
button = debugButtonEl && debugButtonEl.nativeElement;
|
||||
|
||||
expect(button).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should deselect all filters when resetStrategy is all', () => {
|
||||
spyOn(component, 'deselectAllFilters').and.callThrough();
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.resetFilters).toHaveBeenCalled();
|
||||
expect(component.deselectAllFilters).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should revert to the default selection of filters when resetStrategy is default', () => {
|
||||
spyOn(component, 'resetToDefault').and.callThrough();
|
||||
component.resetStrategy = 'default';
|
||||
fixture.detectChanges();
|
||||
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.resetFilters).toHaveBeenCalled();
|
||||
expect(component.resetToDefault).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetToDefault', () => {
|
||||
let primaryFilters: PrimaryFilterOption[];
|
||||
let selectFilters: SelectFilter[];
|
||||
|
||||
beforeEach(() => {
|
||||
primaryFilters = primaryFiltersMock;
|
||||
selectFilters = mockFilters;
|
||||
});
|
||||
|
||||
it('should reset the filters to its initial state', () => {
|
||||
selectFilters[0].options[0].selected = true;
|
||||
selectFilters[0].options[0].initial_selected_state = false;
|
||||
|
||||
spyOn(component, 'resetToDefault').and.callThrough();
|
||||
|
||||
const result = component.resetToDefault(selectFilters);
|
||||
|
||||
expect(result[0].options[0].selected).toBe(false);
|
||||
expect(result[0].options[0].initial_selected_state).toBe(false);
|
||||
expect(result.length).toEqual(selectFilters.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deselectAllFilters', () => {
|
||||
let primaryFilters: PrimaryFilterOption[];
|
||||
let selectFilters: SelectFilter[];
|
||||
|
||||
beforeEach(() => {
|
||||
primaryFilters = primaryFiltersMock;
|
||||
selectFilters = mockFilters;
|
||||
});
|
||||
|
||||
it('should deselect all filters', () => {
|
||||
spyOn(component, 'deselectAllFilters').and.callThrough();
|
||||
|
||||
const result = component.deselectAllFilters(selectFilters);
|
||||
|
||||
expect(
|
||||
result.every((filter) => {
|
||||
if (filter.options) {
|
||||
return filter.options.every((o) => !o.selected);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
);
|
||||
expect(result.length).toEqual(selectFilters.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import { Filter, SelectFilterOption } from '../../models';
|
||||
import { cloneFilter } from '../../utils';
|
||||
import { FilterResetStrategy } from './defs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-selected-filter-options',
|
||||
@@ -21,11 +22,13 @@ import { cloneFilter } from '../../utils';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
@Input()
|
||||
value: Filter[];
|
||||
@Input() value: Filter[];
|
||||
@Input() module: 'Customer' | 'Branch' = 'Branch';
|
||||
@Input() resetStrategy: FilterResetStrategy = 'all';
|
||||
@Input() showResetButton = true;
|
||||
|
||||
@Output() filterChanged = new EventEmitter<Filter[]>();
|
||||
@Output() reset = new EventEmitter<void>();
|
||||
|
||||
@ViewChildren('filteredItem') items: QueryList<ElementRef>;
|
||||
|
||||
@@ -61,8 +64,18 @@ export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
clearAllFilters() {
|
||||
resetFilters() {
|
||||
switch (this.resetStrategy) {
|
||||
case 'default':
|
||||
this.filterChanged.emit(this.resetToDefault(this.value));
|
||||
break;
|
||||
|
||||
case 'all':
|
||||
this.filterChanged.emit(this.deselectAllFilters(this.value));
|
||||
break;
|
||||
}
|
||||
|
||||
this.reset.emit();
|
||||
}
|
||||
|
||||
clearFilter(filter: SelectFilterOption) {
|
||||
@@ -106,7 +119,7 @@ export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
this.filterChanged.emit(clonedFilter);
|
||||
}
|
||||
|
||||
private deselectAllFilters(filters: Filter[]): Filter[] {
|
||||
deselectAllFilters(filters: Filter[]): Filter[] {
|
||||
return filters.reduce((acc, curr) => {
|
||||
if (Array.isArray(curr.options)) {
|
||||
curr.options.forEach((option) => this.deselect(option));
|
||||
@@ -115,6 +128,21 @@ export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
}, []);
|
||||
}
|
||||
|
||||
resetToDefault(filters: Filter[]): Filter[] {
|
||||
const updatedFilters = filters.map((filter) => {
|
||||
let updatedOptions = filter.options || [];
|
||||
if (filter.options) {
|
||||
updatedOptions = filter.options.map((f) => ({
|
||||
...this.resetToDefaultValue(f),
|
||||
}));
|
||||
}
|
||||
|
||||
return { ...filter, options: updatedOptions };
|
||||
});
|
||||
|
||||
return updatedFilters;
|
||||
}
|
||||
|
||||
private deselectAllMatches(filter: SelectFilterOption, match?: string) {
|
||||
if (filter.name === match) {
|
||||
filter.selected = false;
|
||||
@@ -142,6 +170,18 @@ export class SelectedFilterOptionsComponent implements AfterViewInit {
|
||||
return filter;
|
||||
}
|
||||
|
||||
private resetToDefaultValue(filter: SelectFilterOption): SelectFilterOption {
|
||||
return {
|
||||
...filter,
|
||||
selected: filter.initial_selected_state,
|
||||
options: [
|
||||
...(filter.options
|
||||
? filter.options.map((option) => this.resetToDefaultValue(option))
|
||||
: []),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private hasNestedOptions(filter: SelectFilterOption): boolean {
|
||||
return filter && Array.isArray(filter.options) && !!filter.options.length;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { FilterResetWordingPipe } from './filter-reset-wording.pipe';
|
||||
|
||||
describe('#FilterResetWordingPipe', () => {
|
||||
let pipe: FilterResetWordingPipe;
|
||||
|
||||
beforeEach(() => {
|
||||
pipe = new FilterResetWordingPipe();
|
||||
});
|
||||
|
||||
it('should show Alle Filter Entfernen for the default strategy', () => {
|
||||
const result = pipe.transform('all');
|
||||
expect(result).toEqual('Alle Filter Entfernen');
|
||||
});
|
||||
|
||||
it('should show Filter zurücksetzen for the default strategy', () => {
|
||||
const result = pipe.transform('default');
|
||||
expect(result).toEqual('Filter zurücksetzen');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FilterResetStrategy } from '../components/defs';
|
||||
|
||||
@Pipe({
|
||||
name: 'filterResetWording',
|
||||
})
|
||||
export class FilterResetWordingPipe implements PipeTransform {
|
||||
RESET_TO_DEFAULT_WORDING = 'Filter zurücksetzen';
|
||||
RESET_All_WORDING = 'Alle Filter Entfernen';
|
||||
|
||||
transform(strategy: FilterResetStrategy): string {
|
||||
if (strategy === 'default') {
|
||||
return this.RESET_TO_DEFAULT_WORDING;
|
||||
}
|
||||
|
||||
return this.RESET_All_WORDING;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './check-all-selected.pipe';
|
||||
export * from './selected-filters.pipe';
|
||||
export * from './filter-reset-wording.pipe';
|
||||
// end:ng42.barrel
|
||||
|
||||
|
||||
@@ -2,15 +2,25 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { IconModule } from '@libs/ui';
|
||||
import { SelectedFilterOptionsComponent } from './components';
|
||||
import { SelectedFiltersPipe, CheckAllChildOptionsSelectedPipe} from './pipe';
|
||||
import {
|
||||
SelectedFiltersPipe,
|
||||
CheckAllChildOptionsSelectedPipe,
|
||||
FilterResetWordingPipe,
|
||||
} from './pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IconModule],
|
||||
exports: [SelectedFilterOptionsComponent, CheckAllChildOptionsSelectedPipe],
|
||||
declarations:
|
||||
[SelectedFilterOptionsComponent,
|
||||
exports: [
|
||||
SelectedFilterOptionsComponent,
|
||||
CheckAllChildOptionsSelectedPipe,
|
||||
FilterResetWordingPipe,
|
||||
],
|
||||
declarations: [
|
||||
SelectedFilterOptionsComponent,
|
||||
SelectedFiltersPipe,
|
||||
CheckAllChildOptionsSelectedPipe],
|
||||
FilterResetWordingPipe,
|
||||
CheckAllChildOptionsSelectedPipe,
|
||||
],
|
||||
providers: [],
|
||||
})
|
||||
export class SelectedFilterOptionsModule {}
|
||||
|
||||
@@ -1,20 +1,380 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ProductCheckoutComponent } from './product-checkout.component';
|
||||
import { DeliveryOption } from 'apps/sales/src/app/core/models/delivery-option.model';
|
||||
import {
|
||||
ProductAvailabilityService,
|
||||
BranchService,
|
||||
} from '@sales/core-services';
|
||||
import { of } from 'rxjs';
|
||||
import { CheckoutType } from 'apps/sales/src/app/core/models/checkout-type.enum';
|
||||
import { AvailabilityDTO } from '@swagger/availability';
|
||||
import { NO_ERRORS_SCHEMA, ChangeDetectorRef } from '@angular/core';
|
||||
import { BookPricePipe } from 'apps/sales/src/app/pipes/book-price.pipe';
|
||||
import { ModalService } from '@libs/ui';
|
||||
import { NgxsModule } from '@ngxs/store';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { BranchDTO } from '@swagger/checkout';
|
||||
import { ProductAvailability } from 'apps/sales/src/app/core/models/product-availability.model';
|
||||
|
||||
describe('ProductCheckoutComponent', () => {
|
||||
const mockAvailabilityResponse: {
|
||||
branchId: number;
|
||||
type: CheckoutType;
|
||||
av: AvailabilityDTO[];
|
||||
} = {
|
||||
branchId: 1,
|
||||
type: CheckoutType.store,
|
||||
av: [
|
||||
{
|
||||
itemId: 42455413,
|
||||
requestReference: '0',
|
||||
ean: '9783957260376',
|
||||
shop: 3,
|
||||
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
|
||||
supplier: 'L',
|
||||
supplierId: 3,
|
||||
ssc: '999',
|
||||
sscText: 'Lieferbar in 1-2 Tagen',
|
||||
qty: 9.0,
|
||||
isPrebooked: false,
|
||||
at: '2020-07-16T14:00:00',
|
||||
status: 1024,
|
||||
preferred: 1,
|
||||
requested: '2020-07-15T08:28:39.5751342Z',
|
||||
requestStatusCode: '1',
|
||||
requestMessage: 'OK',
|
||||
},
|
||||
{
|
||||
itemId: 42455413,
|
||||
requestReference: '0',
|
||||
ean: '9783957260376',
|
||||
shop: 3,
|
||||
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
|
||||
supplier: 'K',
|
||||
supplierId: 4,
|
||||
ssc: '999',
|
||||
sscText: 'Lieferbar in 1-2 Tagen',
|
||||
qty: 6.0,
|
||||
isPrebooked: false,
|
||||
at: '2020-07-16T14:00:00',
|
||||
status: 1024,
|
||||
preferred: 0,
|
||||
requested: '2020-07-15T08:28:39.5754585Z',
|
||||
requestStatusCode: '1',
|
||||
requestMessage: 'OK',
|
||||
},
|
||||
{
|
||||
itemId: 42455413,
|
||||
requestReference: '0',
|
||||
ean: '9783957260376',
|
||||
shop: 3,
|
||||
price: { value: { value: 16.8, currency: 'EUR' }, vat: { vatType: 8 } },
|
||||
supplier: 'LV3',
|
||||
supplierId: 13,
|
||||
ssc: '999',
|
||||
sscText: 'Lieferbar in 10-14Tagen',
|
||||
isPrebooked: false,
|
||||
at: '2020-07-29T00:00:00',
|
||||
status: 1024,
|
||||
preferred: 0,
|
||||
requested: '2020-07-15T08:28:39.5757159Z',
|
||||
requestStatusCode: '1',
|
||||
requestMessage: 'OK',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockProductAvailabilities: ProductAvailability[] = [
|
||||
{
|
||||
av: {
|
||||
at: '2020-07-15T14:00:00+02:00',
|
||||
ean: '9783957260291',
|
||||
isPrebooked: false,
|
||||
itemId: 38219742,
|
||||
logisticianId: 1002,
|
||||
preferred: 1,
|
||||
price: { value: { value: 9.99, currency: 'EUR' }, vat: { vatType: 2 } },
|
||||
qty: 1,
|
||||
requestMessage: 'Artikel ist lieferbar.',
|
||||
requestReference: '0',
|
||||
requestStatusCode: '1',
|
||||
requested: '2020-07-15T15:19:50.8364336Z',
|
||||
ssc: '005',
|
||||
sscText: 'Sofort lieferbar (Download)',
|
||||
status: 1024,
|
||||
supplier: 'DIG',
|
||||
supplierId: 41,
|
||||
supplierProductNumber: '33161986',
|
||||
},
|
||||
itemId: 38219742,
|
||||
branchId: 123,
|
||||
quantity: 1,
|
||||
status: 1024,
|
||||
type: 3,
|
||||
},
|
||||
{
|
||||
av: {
|
||||
at: '2020-08-15T14:00:00+02:00',
|
||||
ean: '9783957270291',
|
||||
isPrebooked: false,
|
||||
itemId: 38229742,
|
||||
logisticianId: 1002,
|
||||
preferred: 0,
|
||||
price: { value: { value: 3.99, currency: 'EUR' }, vat: { vatType: 2 } },
|
||||
qty: 1,
|
||||
requestMessage: 'Artikel ist lieferbar.',
|
||||
requestReference: '0',
|
||||
requestStatusCode: '1',
|
||||
requested: '2020-07-15T15:19:50.8364336Z',
|
||||
ssc: '005',
|
||||
sscText: 'Sofort lieferbar (Download)',
|
||||
status: 1024,
|
||||
supplier: 'DIG',
|
||||
supplierId: 41,
|
||||
supplierProductNumber: '33161986',
|
||||
},
|
||||
itemId: 38219742,
|
||||
branchId: 456,
|
||||
quantity: 1,
|
||||
status: 1024,
|
||||
type: 3,
|
||||
},
|
||||
];
|
||||
|
||||
fdescribe('ProductCheckoutComponent', () => {
|
||||
let component: ProductCheckoutComponent;
|
||||
let fixture: ComponentFixture<ProductCheckoutComponent>;
|
||||
let productAvailabilityService: jasmine.SpyObj<ProductAvailabilityService>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ProductCheckoutComponent]
|
||||
}).compileComponents();
|
||||
imports: [NgxsModule.forRoot([]), RouterTestingModule],
|
||||
declarations: [ProductCheckoutComponent, BookPricePipe],
|
||||
providers: [
|
||||
{
|
||||
provide: ProductAvailabilityService,
|
||||
useValue: jasmine.createSpyObj('productAvailabilityService', [
|
||||
'getStoreAvailability',
|
||||
]),
|
||||
},
|
||||
{
|
||||
provide: ModalService,
|
||||
useValue: jasmine.createSpy('modalService'),
|
||||
},
|
||||
{
|
||||
provide: BranchService,
|
||||
useValue: jasmine.createSpy('branchService'),
|
||||
},
|
||||
DatePipe,
|
||||
ChangeDetectorRef,
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
})
|
||||
.overrideTemplate(ProductCheckoutComponent, '<div>Test</div>')
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProductCheckoutComponent);
|
||||
component = fixture.componentInstance;
|
||||
productAvailabilityService = TestBed.get(ProductAvailabilityService);
|
||||
|
||||
spyOn(component, 'ngOnInit').and.callFake(() => {});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('get preferredDownloadAvailability', () => {
|
||||
let availabilities: ProductAvailability[];
|
||||
|
||||
beforeEach(() => {
|
||||
availabilities = mockProductAvailabilities;
|
||||
|
||||
component.isDownload = true;
|
||||
component.availability = availabilities;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should return the preferred availability', () => {
|
||||
const expected = mockProductAvailabilities[0].av;
|
||||
|
||||
const result = component.preferredDownloadAvailability;
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return undefined if availability is undefined', () => {
|
||||
component.availability = undefined;
|
||||
|
||||
fixture.detectChanges();
|
||||
const result = component.preferredDownloadAvailability;
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return undefined if no matching availabilities exist', () => {
|
||||
const availabilitiesWithTakeNowCheckoutType = mockProductAvailabilities.map(
|
||||
(av) => ({ ...av, type: 0 })
|
||||
);
|
||||
|
||||
component.availability = availabilitiesWithTakeNowCheckoutType;
|
||||
fixture.detectChanges();
|
||||
|
||||
const result = component.preferredDownloadAvailability;
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setQuantityError', () => {
|
||||
let numberOfItems: number;
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'setQuantityError').and.callThrough();
|
||||
numberOfItems = 16;
|
||||
});
|
||||
|
||||
it('should return Exemplar if only 1 item is available', () => {
|
||||
const limit = 1;
|
||||
const expected = `${limit} Exemplar sofort lieferbar.`;
|
||||
component.setQuantityError(numberOfItems, limit);
|
||||
|
||||
expect(component.quantityError).toEqual(expected);
|
||||
});
|
||||
it('should return Exemplar if more than 1 item is available', () => {
|
||||
const limit = 10;
|
||||
const expected = `${limit} Exemplare sofort lieferbar.`;
|
||||
|
||||
component.setQuantityError(numberOfItems, limit);
|
||||
|
||||
expect(component.quantityError).toEqual(expected);
|
||||
});
|
||||
it('should return a error message if requested items is larger than 999', () => {
|
||||
const limit = 10;
|
||||
numberOfItems = 1000;
|
||||
|
||||
const expected = `Achtung, Maximal ${limit} Exemplare bestellbar.`;
|
||||
component.setQuantityError(numberOfItems, limit);
|
||||
|
||||
expect(component.quantityError).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateAvailability', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'validateAvailability').and.callThrough();
|
||||
|
||||
component.book = {
|
||||
product: { ean: 123 },
|
||||
};
|
||||
|
||||
component.selectedBranch = { id: 789 } as BranchDTO;
|
||||
});
|
||||
|
||||
it('should set current availability and pick up price if a preferred supplier is present', async () => {
|
||||
component.deliveryType = DeliveryOption.PICK_UP;
|
||||
productAvailabilityService.getStoreAvailability.and.returnValue(
|
||||
of(mockAvailabilityResponse)
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const result = await component.validateAvailability(9);
|
||||
const expectedCurrentAvailability = mockAvailabilityResponse.av.find(
|
||||
(av) => av && av.preferred === 1
|
||||
);
|
||||
const expectedPickUpPrice = expectedCurrentAvailability.price.value.value;
|
||||
|
||||
expect(component.currentAvailability).toBe(expectedCurrentAvailability);
|
||||
expect(component._pikUpPrice).toBe(expectedPickUpPrice);
|
||||
});
|
||||
|
||||
it('should not set current availability and pick up price if no preferred supplier is present', () => {
|
||||
component.deliveryType = DeliveryOption.PICK_UP;
|
||||
const mockAvailabilityResponseWithoutPreferred = {
|
||||
...mockAvailabilityResponse,
|
||||
av: mockAvailabilityResponse.av.filter(
|
||||
(av) => av && av.preferred !== 1
|
||||
),
|
||||
};
|
||||
productAvailabilityService.getStoreAvailability.and.returnValue(
|
||||
of(mockAvailabilityResponseWithoutPreferred)
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.currentAvailability).toBeFalsy();
|
||||
expect(component._pikUpPrice).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should provide the sum of available quantities across all suppliers', async () => {
|
||||
component.deliveryType = DeliveryOption.PICK_UP;
|
||||
productAvailabilityService.getStoreAvailability.and.returnValue(
|
||||
of(mockAvailabilityResponse)
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
const expected = 15;
|
||||
const result = await component.validateAvailability(9);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return 0 if no supplier has any quantity available', async () => {
|
||||
component.deliveryType = DeliveryOption.PICK_UP;
|
||||
const responseWithOutQuantity = setMockAvailablityResponse(
|
||||
mockAvailabilityResponse,
|
||||
'qty',
|
||||
0
|
||||
);
|
||||
productAvailabilityService.getStoreAvailability.and.returnValue(
|
||||
of(responseWithOutQuantity)
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
const expected = 0;
|
||||
const result = await component.validateAvailability(9);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should return 0 if no availabliliy has an eligible (allowed) status code', async () => {
|
||||
component.deliveryType = DeliveryOption.PICK_UP;
|
||||
|
||||
const responseWithFakeStatusCode = setMockAvailablityResponse(
|
||||
mockAvailabilityResponse,
|
||||
'status',
|
||||
5
|
||||
);
|
||||
|
||||
productAvailabilityService.getStoreAvailability.and.returnValue(
|
||||
of(responseWithFakeStatusCode)
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
const expected = 0;
|
||||
const result = await component.validateAvailability(9);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setMockAvailablityResponse(
|
||||
mr: {
|
||||
branchId: number;
|
||||
type: CheckoutType;
|
||||
av: AvailabilityDTO[];
|
||||
},
|
||||
property: keyof AvailabilityDTO,
|
||||
value: string | number
|
||||
): {
|
||||
branchId: number;
|
||||
type: CheckoutType;
|
||||
av: AvailabilityDTO[];
|
||||
} {
|
||||
return {
|
||||
...mr,
|
||||
av: (mr.av.map(({ [property]: prop, ...rest }) => ({
|
||||
rest,
|
||||
property: value,
|
||||
})) as unknown) as AvailabilityDTO[],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import { Component, OnInit, Output, EventEmitter, Input, OnDestroy, ViewChild, ChangeDetectorRef, ViewRef } from '@angular/core';
|
||||
import { ModalService, ButtonComponent, DeleteDropdownComponent } from '@libs/ui';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Output,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnDestroy,
|
||||
ViewChild,
|
||||
ChangeDetectorRef,
|
||||
ViewRef,
|
||||
} from '@angular/core';
|
||||
import { ModalService, DeleteDropdownComponent } from '@libs/ui';
|
||||
import { Router } from '@angular/router';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
@@ -7,7 +17,7 @@ import { DeliveryOption } from '../../../../core/models/delivery-option.model';
|
||||
import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actions';
|
||||
import { SetCartEntry } from '../../../../core/store/actions/cart-entry.actions';
|
||||
import { Subject, Observable, of } from 'rxjs';
|
||||
import { takeUntil, tap, map, filter, take } from 'rxjs/operators';
|
||||
import { tap, map, filter, take } from 'rxjs/operators';
|
||||
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
|
||||
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
|
||||
import { BranchSelectors } from '../../../../core/store/selectors/branch.selector';
|
||||
@@ -15,7 +25,11 @@ import { ProductAvailability } from '../../../../core/models/product-availabilit
|
||||
import { CheckoutType } from '../../../../core/models/checkout-type.enum';
|
||||
import { BranchDTO } from '@swagger/checkout';
|
||||
import { AvailabilityDTO } from '@swagger/availability';
|
||||
import { mapToIterable, distance, objectNotNull } from '../../../../core/utils/app.utils';
|
||||
import {
|
||||
mapToIterable,
|
||||
distance,
|
||||
objectNotNull,
|
||||
} from '../../../../core/utils/app.utils';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { ProductAvailabilityService } from '../../../../core/services/product-availability.service';
|
||||
import { BranchService } from '../../../../core/services/branch.service';
|
||||
@@ -26,7 +40,6 @@ import { SearchDropdownComponent } from '@libs/ui/lib/search-dropdown';
|
||||
import { AddBranchesIfNotLoaded } from 'apps/sales/src/app/core/store/actions/branch.actions';
|
||||
import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/product.util';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-checkout',
|
||||
templateUrl: './product-checkout.component.html',
|
||||
@@ -34,11 +47,17 @@ import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/pr
|
||||
})
|
||||
export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
@Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
|
||||
@Select(SharedSelectors.getBreadcrumbs) getBreadcrumbs$: Observable<Breadcrumb[]>;
|
||||
@Select(BranchSelectors.getBranches) branches$: Observable<{ [key: number]: BranchDTO }>;
|
||||
@Select(SharedSelectors.getBreadcrumbs) getBreadcrumbs$: Observable<
|
||||
Breadcrumb[]
|
||||
>;
|
||||
@Select(BranchSelectors.getBranches) branches$: Observable<{
|
||||
[key: number]: BranchDTO;
|
||||
}>;
|
||||
|
||||
@ViewChild('deleteDropdown', { static: false }) deleteDropdown: DeleteDropdownComponent;
|
||||
@ViewChild('branchesdropdown', { static: false }) branchesdd: SearchDropdownComponent;
|
||||
@ViewChild('deleteDropdown', { static: false })
|
||||
deleteDropdown: DeleteDropdownComponent;
|
||||
@ViewChild('branchesdropdown', { static: false })
|
||||
branchesdd: SearchDropdownComponent;
|
||||
|
||||
id = 'checkout-modal';
|
||||
stepOne = true;
|
||||
@@ -87,7 +106,10 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
@Input() availability: ProductAvailability[];
|
||||
|
||||
get takeNowAvailable() {
|
||||
if (this.availability.filter((t) => t.type === CheckoutType.takeNow).length > 0) {
|
||||
if (
|
||||
this.availability.filter((t) => t.type === CheckoutType.takeNow).length >
|
||||
0
|
||||
) {
|
||||
return of(true);
|
||||
}
|
||||
return of(false);
|
||||
@@ -98,7 +120,8 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.availability.filter(
|
||||
(t) =>
|
||||
t.type === CheckoutType.store &&
|
||||
(allowedAvailabilityStatusCodes(t.status) || (t.av && t.av.ssc === '830' && t.av.supplier === 'G'))
|
||||
(allowedAvailabilityStatusCodes(t.status) ||
|
||||
(t.av && t.av.ssc === '830' && t.av.supplier === 'G'))
|
||||
).length > 0
|
||||
) {
|
||||
return of(true);
|
||||
@@ -107,7 +130,13 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
get shippingAvailable() {
|
||||
if (this.availability.filter((t) => t.type === CheckoutType.delivery && allowedAvailabilityStatusCodes(t.status)).length > 0) {
|
||||
if (
|
||||
this.availability.filter(
|
||||
(t) =>
|
||||
t.type === CheckoutType.delivery &&
|
||||
allowedAvailabilityStatusCodes(t.status)
|
||||
).length > 0
|
||||
) {
|
||||
return of(true);
|
||||
}
|
||||
return of(false);
|
||||
@@ -188,6 +217,25 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
get preferredDownloadAvailability(): AvailabilityDTO {
|
||||
if (this.availability) {
|
||||
const downloadAvailabilites = this.availability.filter(
|
||||
(availability) => availability.type === CheckoutType.donwload
|
||||
);
|
||||
|
||||
if (downloadAvailabilites) {
|
||||
const preferredProductAvailability = downloadAvailabilites.find(
|
||||
(productAvailability) =>
|
||||
!!productAvailability.av && !!productAvailability.av.preferred
|
||||
);
|
||||
|
||||
if (preferredProductAvailability) {
|
||||
return preferredProductAvailability.av;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private modalService: ModalService,
|
||||
private store: Store,
|
||||
@@ -203,15 +251,14 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.stepTwoImgType = 'download-type';
|
||||
this.possibleItems = [1];
|
||||
this.deliveryType = DeliveryOption.DOWNLOAD;
|
||||
if (this.availability && this.availability.length > 0) {
|
||||
const currAv = this.availability.filter((t) => t && t.type === CheckoutType.donwload);
|
||||
if (currAv && currAv[0] && currAv[0].av) {
|
||||
this.currentAvailability = currAv[0].av;
|
||||
}
|
||||
if (this.preferredDownloadAvailability) {
|
||||
this.currentAvailability = this.preferredDownloadAvailability;
|
||||
}
|
||||
}
|
||||
|
||||
this.currency = this.book.catalogAvailability.price ? this.book.catalogAvailability.price.value.currency : null;
|
||||
this.currency = this.book.catalogAvailability.price
|
||||
? this.book.catalogAvailability.price.value.currency
|
||||
: null;
|
||||
this.currentPoints = this.book.promoPoints + '';
|
||||
|
||||
this.branches$
|
||||
@@ -255,8 +302,12 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
this.branchList = branches;
|
||||
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const userBranch = branches.find((t) => t.branchNumber === userBranchNumber);
|
||||
const userBranchNumber = this.store.selectSnapshot(
|
||||
BranchSelectors.getUserBranch
|
||||
);
|
||||
const userBranch = branches.find(
|
||||
(t) => t.branchNumber === userBranchNumber
|
||||
);
|
||||
if (!!userBranch) {
|
||||
this.selectedBranch = userBranch;
|
||||
this.currentLocation = userBranch.name;
|
||||
@@ -277,29 +328,49 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.filteredBranches = this.branches;
|
||||
if (!!userBranch) {
|
||||
this.filteredBranches = this.branchesData
|
||||
.sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch))
|
||||
.sort((a: BranchDTO, b: BranchDTO) =>
|
||||
this.branchSorterFn(a, b, userBranch)
|
||||
)
|
||||
.map((t) => t.name);
|
||||
}
|
||||
}
|
||||
|
||||
private branchSorterFn(a: BranchDTO, b: BranchDTO, userBranch: BranchDTO) {
|
||||
const userLat = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.latitude : 0;
|
||||
const userLong = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.longitude : 0;
|
||||
const aLat = a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
|
||||
const aLong = a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
|
||||
const bLat = b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
|
||||
const bLong = b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
|
||||
const userLat =
|
||||
userBranch.address && userBranch.address.geoLocation
|
||||
? userBranch.address.geoLocation.latitude
|
||||
: 0;
|
||||
const userLong =
|
||||
userBranch.address && userBranch.address.geoLocation
|
||||
? userBranch.address.geoLocation.longitude
|
||||
: 0;
|
||||
const aLat =
|
||||
a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
|
||||
const aLong =
|
||||
a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
|
||||
const bLat =
|
||||
b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
|
||||
const bLong =
|
||||
b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
|
||||
const aDist = distance(userLat, userLong, aLat, aLong);
|
||||
const bDist = distance(userLat, userLong, bLat, bLong);
|
||||
return aDist - bDist;
|
||||
}
|
||||
|
||||
branchSearch(term: string) {
|
||||
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const userBranchNumber = this.store.selectSnapshot(
|
||||
BranchSelectors.getUserBranch
|
||||
);
|
||||
const branchList = [...this.branchesData];
|
||||
const userBranch = branchList.find((t) => t.branchNumber === userBranchNumber);
|
||||
const userBranch = branchList.find(
|
||||
(t) => t.branchNumber === userBranchNumber
|
||||
);
|
||||
if (isNullOrUndefined(term) || term.length < 1) {
|
||||
this.filteredBranches = branchList.sort((a: BranchDTO, b: BranchDTO) => this.branchSorterFn(a, b, userBranch)).map((t) => t.name);
|
||||
this.filteredBranches = branchList
|
||||
.sort((a: BranchDTO, b: BranchDTO) =>
|
||||
this.branchSorterFn(a, b, userBranch)
|
||||
)
|
||||
.map((t) => t.name);
|
||||
return;
|
||||
}
|
||||
this.branchService
|
||||
@@ -334,7 +405,9 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.store.dispatch(new AddBranchesIfNotLoaded(branches));
|
||||
if (branches) {
|
||||
branches.forEach((br) => {
|
||||
const existsInBranchData = this.branchesData.find((t) => t.branchNumber === br.branchNumber);
|
||||
const existsInBranchData = this.branchesData.find(
|
||||
(t) => t.branchNumber === br.branchNumber
|
||||
);
|
||||
if (!existsInBranchData) {
|
||||
this.branches.push(br.name);
|
||||
this.branchesData.push(br);
|
||||
@@ -351,26 +424,40 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
storeBranches(): Observable<number[]> {
|
||||
return of(this.availability.filter((t) => t.type === CheckoutType.store).map((t) => t.branchId));
|
||||
return of(
|
||||
this.availability
|
||||
.filter((t) => t.type === CheckoutType.store)
|
||||
.map((t) => t.branchId)
|
||||
);
|
||||
}
|
||||
|
||||
// STEP ONE
|
||||
selectLocation(location: string) {
|
||||
this.branchLoad = true;
|
||||
this.searchingNewBranch = true;
|
||||
const branch = this.branchesData.find((b: BranchDTO) => b.name === location);
|
||||
const branch = this.branchesData.find(
|
||||
(b: BranchDTO) => b.name === location
|
||||
);
|
||||
// reset store availability
|
||||
this.availability = [...this.availability.filter((t) => t.type !== CheckoutType.store)];
|
||||
this.availability = [
|
||||
...this.availability.filter((t) => t.type !== CheckoutType.store),
|
||||
];
|
||||
this.productAvailabilityService
|
||||
.getStoreAvailability(this.book, this.book.product.ean, branch.id)
|
||||
.pipe(
|
||||
tap((response) => {
|
||||
if (response.av && response.av.length > 0) {
|
||||
this.currentLocation = location;
|
||||
this.filteredBranches = [location, ...this.branches.filter((t) => t !== location)];
|
||||
this.filteredBranches = [
|
||||
location,
|
||||
...this.branches.filter((t) => t !== location),
|
||||
];
|
||||
this.selectedBranch = branch;
|
||||
const preferedAv = response.av.find((t) => t.preferred === 1);
|
||||
this.currentPickUpDate = this.datePipe.transform(new Date(preferedAv.at), 'dd.MM.yy');
|
||||
this.currentPickUpDate = this.datePipe.transform(
|
||||
new Date(preferedAv.at),
|
||||
'dd.MM.yy'
|
||||
);
|
||||
response.av.forEach((t) => {
|
||||
this.availability.push({
|
||||
itemId: t.itemId,
|
||||
@@ -395,13 +482,19 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
if (action === DeliveryOption.TAKE_NOW) {
|
||||
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.takeNow).av;
|
||||
this.currentAvailability = this.availability.find(
|
||||
(t) => t.type === CheckoutType.takeNow
|
||||
).av;
|
||||
this.stepTwoImgType = 'Take_now';
|
||||
this.deliveryType = DeliveryOption.TAKE_NOW;
|
||||
this.confirmationBtnText = 'Reservieren';
|
||||
// Set user branch as selected branch
|
||||
const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
|
||||
const userBranch = this.branchList.find((t) => t.branchNumber === userBranchNumber);
|
||||
const userBranchNumber = this.store.selectSnapshot(
|
||||
BranchSelectors.getUserBranch
|
||||
);
|
||||
const userBranch = this.branchList.find(
|
||||
(t) => t.branchNumber === userBranchNumber
|
||||
);
|
||||
if (!!userBranch) {
|
||||
this.selectedBranch = userBranch;
|
||||
this.currentLocation = userBranch.name;
|
||||
@@ -410,12 +503,16 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
if (!this.availability.find((t) => t.type === CheckoutType.store)) {
|
||||
return;
|
||||
}
|
||||
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.store).av;
|
||||
this.currentAvailability = this.availability.find(
|
||||
(t) => t.type === CheckoutType.store
|
||||
).av;
|
||||
this.stepTwoImgType = 'Package_Icon';
|
||||
this.deliveryType = DeliveryOption.PICK_UP;
|
||||
this.confirmationBtnText = 'Fortfahren';
|
||||
} else {
|
||||
this.currentAvailability = this.availability.find((t) => t.type === CheckoutType.delivery).av;
|
||||
this.currentAvailability = this.availability.find(
|
||||
(t) => t.type === CheckoutType.delivery
|
||||
).av;
|
||||
this.stepTwoImgType = 'truck_Icon';
|
||||
this.deliveryType = DeliveryOption.DELIVERY;
|
||||
this.confirmationBtnText = 'Fortfahren';
|
||||
@@ -442,7 +539,8 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
this.loading = true;
|
||||
const av = await this.validateAvailability(numberOfItems);
|
||||
this.setQuantityError(numberOfItems, av);
|
||||
const isTakeNowAndSelectedMoreThanInStock = this.deliveryType === DeliveryOption.TAKE_NOW && numberOfItems > av;
|
||||
const isTakeNowAndSelectedMoreThanInStock =
|
||||
this.deliveryType === DeliveryOption.TAKE_NOW && numberOfItems > av;
|
||||
isTakeNowAndSelectedMoreThanInStock.ifFalse(() => {
|
||||
this.addItemsToCartDisabled = false;
|
||||
});
|
||||
@@ -458,7 +556,9 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
if (numberOfItems > 999) {
|
||||
this.quantityError = `Achtung, Maximal ${limit} Exemplare bestellbar.`;
|
||||
} else if (numberOfItems > limit) {
|
||||
this.quantityError = `${limit} Exemplar sofort lieferbar.`;
|
||||
this.quantityError = `${limit} ${
|
||||
limit > 1 ? 'Exemplare' : 'Exemplar'
|
||||
} sofort lieferbar.`;
|
||||
} else {
|
||||
this.quantityError = null;
|
||||
}
|
||||
@@ -478,24 +578,39 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
).toPromise();
|
||||
} else if (this.deliveryType === DeliveryOption.PICK_UP) {
|
||||
const storeAvailability = await this.productAvailabilityService
|
||||
.getStoreAvailability(this.book, this.book.product.ean, this.selectedBranch.id, numberOfItems)
|
||||
.getStoreAvailability(
|
||||
this.book,
|
||||
this.book.product.ean,
|
||||
this.selectedBranch.id,
|
||||
numberOfItems
|
||||
)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response && response.av) {
|
||||
const preferedAv = response.av.find((t) => t && t.preferred === 1);
|
||||
let preferredQty = 0;
|
||||
if (preferedAv) {
|
||||
this.currentAvailability = preferedAv;
|
||||
this._pikUpPrice = preferedAv.price.value.value;
|
||||
if (allowedAvailabilityStatusCodes(preferedAv.status)) {
|
||||
preferredQty = preferedAv.qty;
|
||||
const preferredSupplier = response.av.find(
|
||||
(av) => av && av.preferred === 1
|
||||
);
|
||||
if (preferredSupplier) {
|
||||
this.currentAvailability = preferredSupplier;
|
||||
this._pikUpPrice = preferredSupplier.price.value.value;
|
||||
}
|
||||
|
||||
const eligibleAvailabilities = response.av
|
||||
.filter(
|
||||
(availability) =>
|
||||
allowedAvailabilityStatusCodes(availability.status) &&
|
||||
availability.qty
|
||||
)
|
||||
.map((availability) => availability.qty);
|
||||
|
||||
const totalAvailableQuantity = eligibleAvailabilities.reduce(
|
||||
(acc, curr) => acc + curr,
|
||||
0
|
||||
);
|
||||
|
||||
this.detectChanges();
|
||||
}
|
||||
if (preferredQty) {
|
||||
return preferredQty;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return totalAvailableQuantity;
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
@@ -505,11 +620,18 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
return storeAvailability;
|
||||
} else if (this.deliveryType === DeliveryOption.DELIVERY) {
|
||||
const shippingAvailability = await this.productAvailabilityService
|
||||
.getShippingAvailability(this.book, this.book.product.ean, +this.selectedBranch.branchNumber, numberOfItems)
|
||||
.getShippingAvailability(
|
||||
this.book,
|
||||
this.book.product.ean,
|
||||
+this.selectedBranch.branchNumber,
|
||||
numberOfItems
|
||||
)
|
||||
.pipe(
|
||||
map((response) => {
|
||||
if (response && response.av) {
|
||||
const preferedAv = response.av.find((t) => t && t.preferred === 1);
|
||||
const preferedAv = response.av.find(
|
||||
(t) => t && t.preferred === 1
|
||||
);
|
||||
let preferredQty = 0;
|
||||
if (preferedAv) {
|
||||
this.currentAvailability = preferedAv;
|
||||
@@ -572,11 +694,15 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
|
||||
private redirectRoute() {
|
||||
if (this.customerAlredySet) {
|
||||
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true));
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(this.generateBreadcrumb(), 'shoppingCart', true)
|
||||
);
|
||||
this.store.dispatch(new ChangeCurrentRoute('/cart/review'));
|
||||
this.router.navigate(['/cart/review']);
|
||||
} else {
|
||||
this.store.dispatch(new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true));
|
||||
this.store.dispatch(
|
||||
new AddBreadcrumb(this.generateBreadcrumb(), 'customer', true)
|
||||
);
|
||||
this.store.dispatch(new ChangeCurrentRoute('/customer/search'));
|
||||
this.router.navigate(['/customer/search']);
|
||||
}
|
||||
@@ -630,7 +756,11 @@ export class ProductCheckoutComponent implements OnInit, OnDestroy {
|
||||
|
||||
detectChanges() {
|
||||
setTimeout(() => {
|
||||
if (this.cdrf !== null && this.cdrf !== undefined && !(this.cdrf as ViewRef).destroyed) {
|
||||
if (
|
||||
this.cdrf !== null &&
|
||||
this.cdrf !== undefined &&
|
||||
!(this.cdrf as ViewRef).destroyed
|
||||
) {
|
||||
this.cdrf.detectChanges();
|
||||
}
|
||||
}, 0);
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.headers {
|
||||
display: block;
|
||||
min-height: 60px;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<div class="start-remission">
|
||||
<button
|
||||
class="isa-btn isa-btn-l isa-btn-primary isa-btn-shadow isa-btn-pill"
|
||||
(click)="startRemission.emit()"
|
||||
>
|
||||
<button class="isa-btn isa-btn-xl isa-btn-primary isa-btn-shadow isa-btn-pill" (click)="startRemission.emit()">
|
||||
Remission starten
|
||||
</button>
|
||||
</div>
|
||||
@@ -22,6 +22,7 @@
|
||||
<app-selected-filter-options
|
||||
[value]="filters"
|
||||
(filterChanged)="onFiltersChange($event)"
|
||||
[showResetButton]="filters | hasSelectedFilters"
|
||||
>
|
||||
</app-selected-filter-options>
|
||||
</div>
|
||||
@@ -38,7 +39,12 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="apply-filter-wrapper">
|
||||
<div
|
||||
class="apply-filter-wrapper"
|
||||
centerBottomDuringTransition
|
||||
[inTransition]="inTransition"
|
||||
className="in-transition"
|
||||
>
|
||||
<button
|
||||
class="isa-btn isa-btn-primary isa-btn-pill btn-apply-filters text-nowrap"
|
||||
(click)="applyFilters()"
|
||||
|
||||
@@ -13,6 +13,13 @@
|
||||
.apply-filter-wrapper {
|
||||
text-align: center;
|
||||
padding-bottom: 5px;
|
||||
|
||||
&.in-transition {
|
||||
position: absolute;
|
||||
transform: translate(-50%);
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-apply-filters {
|
||||
|
||||
@@ -1,15 +1,47 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RemissionListFilterComponent } from './remission-list-filter.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { IsaOverlayRef } from 'apps/sales/src/app/core/overlay';
|
||||
import { RemissionFilterService } from '../../services/remission-filter.service';
|
||||
import { RemissionService } from '@isa/remission';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgxsModule } from '@ngxs/store';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { HasSelectedFiltersPipe } from '../../pipes';
|
||||
|
||||
describe('RemissionListFilterComponent', () => {
|
||||
fdescribe('RemissionListFilterComponent', () => {
|
||||
let fixture: ComponentFixture<RemissionListFilterComponent>;
|
||||
let component: RemissionListFilterComponent;
|
||||
let filterService: RemissionFilterService;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NoopAnimationsModule, NgxsModule.forRoot([])],
|
||||
providers: [
|
||||
{
|
||||
provide: IsaOverlayRef,
|
||||
useValue: jasmine.createSpyObj('isaOverlayRef', ['close']),
|
||||
},
|
||||
{
|
||||
provide: RemissionFilterService,
|
||||
useValue: jasmine.createSpy('remissionFilterService'),
|
||||
},
|
||||
{
|
||||
provide: RemissionService,
|
||||
useValue: jasmine.createSpy('remissionService'),
|
||||
},
|
||||
],
|
||||
declarations: [RemissionListFilterComponent, HasSelectedFiltersPipe],
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [RemissionListFilterComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RemissionListFilterComponent);
|
||||
component = fixture.componentInstance;
|
||||
filterService = TestBed.get(RemissionFilterService);
|
||||
|
||||
spyOn(component, 'ngOnInit').and.callFake(() => {});
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -17,4 +49,47 @@ describe('RemissionListFilterComponent', () => {
|
||||
it('should create the component', () => {
|
||||
expect(fixture).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('close', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'close').and.callThrough();
|
||||
filterService.resetFilters$ = new BehaviorSubject(null);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should set inTransition to true', () => {
|
||||
component.close();
|
||||
fixture.detectChanges();
|
||||
|
||||
const result = component.inTransition;
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('closeAnimationDone', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(component, 'closeAnimationDone').and.callThrough();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
it('should set inTransition to false if animation is complete (in)', () => {
|
||||
component.animate = 'in';
|
||||
component.inTransition = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.closeAnimationDone();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.inTransition).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not set in transition if animation out is already complete', () => {
|
||||
component.animate = 'out';
|
||||
component.inTransition = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.closeAnimationDone();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.inTransition).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,7 +40,8 @@ import { slideIn } from 'apps/sales/src/app/core/overlay';
|
||||
animations: [slideIn],
|
||||
})
|
||||
export class RemissionListFilterComponent implements OnInit, OnDestroy {
|
||||
animate = 'in';
|
||||
animate: 'in' | 'out' = 'in';
|
||||
inTransition = true;
|
||||
|
||||
@ViewChild('selectionContainer', { static: false })
|
||||
selectionContainer: ElementRef;
|
||||
@@ -205,17 +206,27 @@ export class RemissionListFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
updateLastGroupChanged(updatedGroup: Filter) {
|
||||
this.filterService.lastFilterGroupChanged$.next(updatedGroup);
|
||||
this.filterService.lastFilterGroupChanged = updatedGroup;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.animate = 'out';
|
||||
this.filterService.resetFilters$.next();
|
||||
|
||||
this.setTransition(true);
|
||||
}
|
||||
|
||||
closeAnimationDone() {
|
||||
if (this.animate === 'in') {
|
||||
this.setTransition(false);
|
||||
}
|
||||
|
||||
if (this.animate === 'out') {
|
||||
this.overlayRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
private setTransition(isTransitioning: boolean) {
|
||||
this.inTransition = isTransitioning;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RemissionUeberlaufCapacitiesModule } from '../../pages/remission-list-create/ueberlauf-capacities';
|
||||
import { SharedModule } from 'apps/sales/src/app/shared/shared.module';
|
||||
import { RemissionRequiredCapacitiesWidgetModule } from '../../components/remission-required-capacities-widget';
|
||||
import { HasSelectedFiltersPipe } from '../../pipes';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -21,8 +22,8 @@ import { RemissionRequiredCapacitiesWidgetModule } from '../../components/remiss
|
||||
RemissionUeberlaufCapacitiesModule,
|
||||
RemissionRequiredCapacitiesWidgetModule,
|
||||
],
|
||||
exports: [RemissionListFilterComponent],
|
||||
declarations: [RemissionListFilterComponent],
|
||||
exports: [RemissionListFilterComponent, HasSelectedFiltersPipe],
|
||||
declarations: [RemissionListFilterComponent, HasSelectedFiltersPipe],
|
||||
entryComponents: [RemissionListFilterComponent],
|
||||
})
|
||||
export class RemissionListFilterModule {}
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectorRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { RemissionFinishingProcessStatus } from '../../models/remission-finishing-process-status.enum';
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors';
|
||||
import { RemissionSupplier, RemissionProcess, RemissionService, ShippingDocument, Printer } from '@isa/remission';
|
||||
import { filter, takeUntil, take, switchMap, map } from 'rxjs/operators';
|
||||
import {
|
||||
RemissionSupplier,
|
||||
RemissionProcess,
|
||||
RemissionService,
|
||||
ShippingDocument,
|
||||
Printer,
|
||||
} from '@isa/remission';
|
||||
import { filter, takeUntil, take, switchMap, map, tap } from 'rxjs/operators';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { Subject, Observable, merge } from 'rxjs';
|
||||
import { SUPPLIER_PLACEHOLDER } from '../../constants/remission.constants';
|
||||
import { AddBreadcrumb, ResetBreadcrumbsTo } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
|
||||
import {
|
||||
AddBreadcrumb,
|
||||
ResetBreadcrumbsTo,
|
||||
} from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
|
||||
import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions';
|
||||
import { Router } from '@angular/router';
|
||||
import {
|
||||
@@ -17,7 +32,7 @@ import {
|
||||
SetRemissionStarted,
|
||||
SetRemissionProcess,
|
||||
ResetRemissionShippingDocument,
|
||||
CompleteRemissionShippingDocument
|
||||
CompleteRemissionShippingDocument,
|
||||
} from 'apps/sales/src/app/core/store/actions/remission.actions';
|
||||
import { Breadcrumb } from 'apps/sales/src/app/core/models/breadcrumb.model';
|
||||
import { AppService } from '@sales/core-services';
|
||||
@@ -33,10 +48,12 @@ import { RemissionScanConfirmationDialogComponent } from '../../components/remis
|
||||
@Component({
|
||||
selector: 'app-remission-finish',
|
||||
templateUrl: './remission-finish.component.html',
|
||||
styleUrls: ['./remission-finish.component.scss']
|
||||
styleUrls: ['./remission-finish.component.scss'],
|
||||
})
|
||||
export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<RemissionProcess>;
|
||||
@Select(RemissionSelectors.getRemissionProcess) remissionProcess$: Observable<
|
||||
RemissionProcess
|
||||
>;
|
||||
@Select(RemissionSelectors.getRemissionShippingDocument)
|
||||
shippingDocument$: Observable<ShippingDocument>;
|
||||
@Select(RemissionSelectors.getRemissionShippingDocumentstatus)
|
||||
@@ -49,8 +66,10 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
invalidBarcodeDialog: RemissionScanProductInvalidBarcodeComponent;
|
||||
@ViewChild('invalidDocumentDialog', { static: false })
|
||||
invalidDocumentDialog: RemissionScanShippingDocumentClosedComponent;
|
||||
@ViewChild('printModal', { static: false }) printModal: PrinterSelectionComponent;
|
||||
@ViewChild('confirmScanModal', { static: false }) confirmScanModal: RemissionScanConfirmationDialogComponent;
|
||||
@ViewChild('printModal', { static: false })
|
||||
printModal: PrinterSelectionComponent;
|
||||
@ViewChild('confirmScanModal', { static: false })
|
||||
confirmScanModal: RemissionScanConfirmationDialogComponent;
|
||||
pageStatus: RemissionFinishingProcessStatus;
|
||||
suppliers: {
|
||||
leftSupplier: RemissionSupplier;
|
||||
@@ -61,13 +80,14 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
destroy$ = new Subject();
|
||||
containerId: string;
|
||||
showError = false;
|
||||
errorMsg = 'Leider haben Sie den falschen Barcode gescannt. Scannen Sie bitte den Barcode seitlich an der Wanne.';
|
||||
errorMsg =
|
||||
'Leider haben Sie den falschen Barcode gescannt. Scannen Sie bitte den Barcode seitlich an der Wanne.';
|
||||
supplier: RemissionSupplier;
|
||||
startNewRemission = false;
|
||||
title = {
|
||||
0: 'Wannennummer scannen',
|
||||
1: 'Wanne abgeschlossen',
|
||||
2: ``
|
||||
2: ``,
|
||||
};
|
||||
isSafari = false;
|
||||
isNative = false;
|
||||
@@ -78,17 +98,17 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
0: 'Scannen Sie die Wannennummer um den Warenbegleitschein abzuschließen.',
|
||||
// tslint:disable-next-line: max-line-length
|
||||
1: 'Legen Sie abschließend den "Blank Beizettel" in die abgeschlossene Wanne. Der Warenbegleitschein wird nach Abschluss der Remission versendet. Zum Öffnen eines neuen Warenbegleitscheins setzen Sie die Remission fort.',
|
||||
2: ''
|
||||
2: '',
|
||||
};
|
||||
|
||||
private finishPocessTitles = {
|
||||
1: `${SUPPLIER_PLACEHOLDER} Warenbegleitschein gedruckt`,
|
||||
2: `Warenbegleitschein versendet`
|
||||
2: `Warenbegleitschein versendet`,
|
||||
};
|
||||
|
||||
private finishPocessSubTitles = {
|
||||
1: `Die Warenbegleitscheine für ${SUPPLIER_PLACEHOLDER} werden gedruckt. Bitte in die Wanne legen.`,
|
||||
2: `Der Warenbegleitschein wurde digital an ${SUPPLIER_PLACEHOLDER} versendet.`
|
||||
2: `Der Warenbegleitschein wurde digital an ${SUPPLIER_PLACEHOLDER} versendet.`,
|
||||
};
|
||||
|
||||
constructor(
|
||||
@@ -116,21 +136,25 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
.select(RemissionSelectors.getRemissionFinishingProcessStatus)
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
take(1)
|
||||
)
|
||||
.subscribe(status => {
|
||||
.subscribe((status) => {
|
||||
this.pageStatus = status;
|
||||
this.cdrf.detectChanges();
|
||||
if (this.pageStatus === RemissionFinishingProcessStatus.containerScanned) {
|
||||
this.containerId = this.store.selectSnapshot(RemissionSelectors.getRemissionContainerId);
|
||||
if (
|
||||
this.pageStatus === RemissionFinishingProcessStatus.containerScanned
|
||||
) {
|
||||
this.containerId = this.store.selectSnapshot(
|
||||
RemissionSelectors.getRemissionContainerId
|
||||
);
|
||||
} else if (this.pageStatus === RemissionFinishingProcessStatus.notSet) {
|
||||
this.navigateToNewRemissionList();
|
||||
}
|
||||
});
|
||||
this.remissionProcess$
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
take(1),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
@@ -138,7 +162,7 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.shippingDocument$
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(this.shippingDocumentSubscriptionHandler);
|
||||
@@ -146,21 +170,22 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
this.loadCurrentRemissionProcessStatuses().then(() => {
|
||||
this.continueProcess()
|
||||
.pipe(takeUntil(this.destroy$), take(1))
|
||||
.subscribe(remission => {
|
||||
.subscribe((remission) => {
|
||||
if (this.shippingDocument && this.shippingDocument.id) {
|
||||
this.loadSupplier(remission.filter.target);
|
||||
this.remissionService
|
||||
.getShippingDocuments({
|
||||
remissionProcessId: this.remissionProcess.id,
|
||||
shippingDocumentId: this.shippingDocument.id
|
||||
shippingDocumentId: this.shippingDocument.id,
|
||||
})
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
takeUntil(this.destroy$)
|
||||
)
|
||||
.subscribe(({ isCompleted }) => {
|
||||
if (isCompleted) {
|
||||
this.pageStatus = RemissionFinishingProcessStatus.containerScanned;
|
||||
this.pageStatus =
|
||||
RemissionFinishingProcessStatus.containerScanned;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -171,32 +196,42 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
loadCurrentRemissionProcessStatuses() {
|
||||
return this.remissionProcessStatuses$
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
take(1)
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
loadCurrentRemissionProcess() {
|
||||
return this.currentRemissionProcess$.pipe(filter(data => !isNullOrUndefined(data), take(1)));
|
||||
return this.currentRemissionProcess$.pipe(
|
||||
filter((data) => !isNullOrUndefined(data), take(1))
|
||||
);
|
||||
}
|
||||
|
||||
continueProcess() {
|
||||
return this.loadCurrentRemissionProcess().pipe(
|
||||
take(1),
|
||||
switchMap(remissionProcess => {
|
||||
switchMap((remissionProcess) => {
|
||||
return this.remissionService.continueProcess(remissionProcess);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
shippingDocumentSubscriptionHandler = (shippingDocument: ShippingDocument) => {
|
||||
shippingDocumentSubscriptionHandler = (
|
||||
shippingDocument: ShippingDocument
|
||||
) => {
|
||||
this.shippingDocument = shippingDocument;
|
||||
};
|
||||
|
||||
remissionProcessSubscriptionHandler = (remissionProcess: RemissionProcess) => {
|
||||
remissionProcessSubscriptionHandler = (
|
||||
remissionProcess: RemissionProcess
|
||||
) => {
|
||||
this.remissionProcess = remissionProcess;
|
||||
if (remissionProcess && remissionProcess.filter && remissionProcess.filter.target) {
|
||||
if (
|
||||
remissionProcess &&
|
||||
remissionProcess.filter &&
|
||||
remissionProcess.filter.target
|
||||
) {
|
||||
this.loadSupplier(remissionProcess.filter.target);
|
||||
}
|
||||
};
|
||||
@@ -210,11 +245,15 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
if (this.supplier) {
|
||||
const selectedSupplierName = remissionSuplier.name;
|
||||
this.title[RemissionFinishingProcessStatus.finished] = this.finishPocessTitles[this.supplier.id].replace(
|
||||
this.title[
|
||||
RemissionFinishingProcessStatus.finished
|
||||
] = this.finishPocessTitles[this.supplier.id].replace(
|
||||
SUPPLIER_PLACEHOLDER,
|
||||
selectedSupplierName
|
||||
);
|
||||
this.subTitle[RemissionFinishingProcessStatus.finished] = this.finishPocessSubTitles[this.supplier.id].replace(
|
||||
this.subTitle[
|
||||
RemissionFinishingProcessStatus.finished
|
||||
] = this.finishPocessSubTitles[this.supplier.id].replace(
|
||||
SUPPLIER_PLACEHOLDER,
|
||||
selectedSupplierName
|
||||
);
|
||||
@@ -222,7 +261,7 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private openConfirmationDialog(barcode: string) {
|
||||
this.confirmScanModal.openDialog(barcode)
|
||||
this.confirmScanModal.openDialog(barcode);
|
||||
}
|
||||
|
||||
retryScan() {
|
||||
@@ -239,26 +278,31 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
|
||||
abandonScan() {
|
||||
this.store.dispatch(new SetBranchProcessCurrentPath('remission', false));
|
||||
this.store.dispatch(
|
||||
this.store
|
||||
.dispatch(
|
||||
new SetRemissionFinishedProcessStatus(
|
||||
RemissionFinishingProcessStatus.notSet
|
||||
)
|
||||
).toPromise().then(_ => {
|
||||
)
|
||||
.toPromise()
|
||||
.then((_) => {
|
||||
this.router.navigate(['remission/started']);
|
||||
});
|
||||
}
|
||||
|
||||
handleScanResult(barcode: string) {
|
||||
if (!barcode) {
|
||||
return this.showInvalidBarcodeDialog()
|
||||
return this.showInvalidBarcodeDialog();
|
||||
}
|
||||
|
||||
return this.openConfirmationDialog(barcode)
|
||||
return this.openConfirmationDialog(barcode);
|
||||
}
|
||||
|
||||
finishRemission() {
|
||||
const remissionProcessId = this.remissionProcess.id;
|
||||
this.remissionService.isPrintingRequired({ remissionProcessId }).subscribe(isPrintingRequired => {
|
||||
this.remissionService
|
||||
.isPrintingRequired({ remissionProcessId })
|
||||
.subscribe((isPrintingRequired) => {
|
||||
if (isPrintingRequired) {
|
||||
this.printModal.openDialog();
|
||||
} else {
|
||||
@@ -272,7 +316,7 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
this.remissionService
|
||||
.printShippingDocument({
|
||||
remissionProcessId: this.remissionProcess.id,
|
||||
printerKey: selectedPrinter
|
||||
printerKey: selectedPrinter,
|
||||
})
|
||||
.subscribe(
|
||||
() => {
|
||||
@@ -282,18 +326,24 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
},
|
||||
() => {
|
||||
this.pageStatus = RemissionFinishingProcessStatus.containerScanned;
|
||||
this.store.dispatch(new SetRemissionFinishedProcessStatus(this.pageStatus));
|
||||
this.store.dispatch(
|
||||
new SetRemissionFinishedProcessStatus(this.pageStatus)
|
||||
);
|
||||
this.isPrinting = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
postFinishRemission(remissionProcessId: number) {
|
||||
this.remissionService.completeRemissions({ remissionProcessId }).subscribe(status => {
|
||||
this.remissionService
|
||||
.completeRemissions({ remissionProcessId })
|
||||
.subscribe((status) => {
|
||||
if (status) {
|
||||
this.appService.clearIdleReminder();
|
||||
this.pageStatus = RemissionFinishingProcessStatus.finished;
|
||||
this.store.dispatch(new SetRemissionFinishedProcessStatus(this.pageStatus));
|
||||
this.store.dispatch(
|
||||
new SetRemissionFinishedProcessStatus(this.pageStatus)
|
||||
);
|
||||
this.store.dispatch(new ResetRemissionState());
|
||||
}
|
||||
});
|
||||
@@ -310,7 +360,7 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
new ResetBreadcrumbsTo(
|
||||
<Breadcrumb>{
|
||||
name: 'Remission',
|
||||
path: path
|
||||
path: path,
|
||||
},
|
||||
'remission',
|
||||
true
|
||||
@@ -321,13 +371,21 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
startRemission() {
|
||||
try {
|
||||
throw new Error('[Remission Finished] Start Remission');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.remissionService
|
||||
.startRemission({ remissionProcessId: this.remissionProcess.id })
|
||||
.pipe(
|
||||
filter(data => !isNullOrUndefined(data)),
|
||||
take(1)
|
||||
filter((data) => !isNullOrUndefined(data)),
|
||||
take(1),
|
||||
tap((process) =>
|
||||
console.log('[Remission Finished] Start Remission II', process)
|
||||
)
|
||||
.subscribe(remissionProcess => {
|
||||
)
|
||||
.subscribe((remissionProcess) => {
|
||||
this.store.dispatch(new SetRemissionStarted(true));
|
||||
this.store.dispatch(new SetRemissionProcess(remissionProcess));
|
||||
this.navigateToStartedRemission();
|
||||
@@ -341,7 +399,7 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
new AddBreadcrumb(
|
||||
{
|
||||
path: path,
|
||||
name: 'Warenbegleitschein'
|
||||
name: 'Warenbegleitschein',
|
||||
},
|
||||
'remission'
|
||||
)
|
||||
@@ -355,21 +413,25 @@ export class RemissionFinishComponent implements OnInit, OnDestroy {
|
||||
.completeShippingDocument({
|
||||
remissionProcessId: this.remissionProcess.id,
|
||||
shippingDocumentId: this.shippingDocument.id,
|
||||
containerId: this.containerId
|
||||
containerId: this.containerId,
|
||||
})
|
||||
.subscribe(response => {
|
||||
.subscribe((response) => {
|
||||
if (response.error) {
|
||||
if (response.errorReasons.PackageCode) {
|
||||
this.emptyOrInvalidBarcode = false;
|
||||
this.pageStatus = RemissionFinishingProcessStatus.start;
|
||||
this.store.dispatch(new SetRemissionFinishedProcessStatus(this.pageStatus));
|
||||
this.showInvalidBarcodeDialog()
|
||||
this.store.dispatch(
|
||||
new SetRemissionFinishedProcessStatus(this.pageStatus)
|
||||
);
|
||||
this.showInvalidBarcodeDialog();
|
||||
} else {
|
||||
this.invalidDocumentDialog.openDialog();
|
||||
}
|
||||
} else {
|
||||
this.pageStatus = RemissionFinishingProcessStatus.containerScanned;
|
||||
this.store.dispatch(new CompleteRemissionShippingDocument(this.shippingDocument));
|
||||
this.store.dispatch(
|
||||
new CompleteRemissionShippingDocument(this.shippingDocument)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
first,
|
||||
map,
|
||||
withLatestFrom,
|
||||
tap,
|
||||
} from 'rxjs/operators';
|
||||
import { RemissionResourceType } from '../../models/remission-resource-type.model';
|
||||
import {
|
||||
@@ -316,6 +317,11 @@ export class RemissionListCreateComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
startRemission() {
|
||||
try {
|
||||
throw new Error('[Remission Create] Start Remission');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
this.currentRemissionProcess$
|
||||
.pipe(
|
||||
switchMap((process) =>
|
||||
@@ -323,7 +329,10 @@ export class RemissionListCreateComponent implements OnInit, OnDestroy {
|
||||
.startRemission({ remissionProcessId: process.id })
|
||||
.pipe(filter((data) => !isNullOrUndefined(data)))
|
||||
),
|
||||
first()
|
||||
first(),
|
||||
tap((process) =>
|
||||
console.log('[Remission Create] Start Remission II', process)
|
||||
)
|
||||
)
|
||||
.subscribe((process) => {
|
||||
this.store.dispatch(new SetRemissionStarted(true));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { HasSelectedFiltersPipe } from './has-selected-filters.pipe';
|
||||
import { filters } from '../mocks';
|
||||
|
||||
fdescribe('Pipe: HasSelectedFiltersPipe', () => {
|
||||
let pipe: HasSelectedFiltersPipe;
|
||||
|
||||
beforeEach(() => {
|
||||
pipe = new HasSelectedFiltersPipe();
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
const filterWithSelected = filters;
|
||||
|
||||
const result = pipe.transform(filterWithSelected);
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
const filterWithSelected = filters.map((f) => ({
|
||||
...f,
|
||||
options: f.options.map((o) => ({ ...o, selected: false })),
|
||||
}));
|
||||
|
||||
const result = pipe.transform(filterWithSelected);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { SelectFilter } from '../../filter';
|
||||
|
||||
@Pipe({
|
||||
name: 'hasSelectedFilters',
|
||||
})
|
||||
export class HasSelectedFiltersPipe implements PipeTransform {
|
||||
hasSelectedFilters = (filter: SelectFilter[]) => {
|
||||
return filter.some((f) => f.options.some((o) => o.selected));
|
||||
};
|
||||
|
||||
transform(filters: SelectFilter[]): boolean {
|
||||
return this.hasSelectedFilters(filters);
|
||||
}
|
||||
}
|
||||
4
apps/sales/src/app/modules/remission/pipes/index.ts
Normal file
4
apps/sales/src/app/modules/remission/pipes/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './has-selected-filters.pipe';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { RemissionFilterService } from './remission-filter.service';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RemissionResourceType } from '../models';
|
||||
import { RemissionService } from '@isa/remission';
|
||||
import { NgxsModule } from '@ngxs/store';
|
||||
import { hot, cold } from 'jasmine-marbles';
|
||||
import { Filter } from '../../filter';
|
||||
|
||||
fdescribe('#RemissionFilterService', () => {
|
||||
let remissionFilterService: RemissionFilterService;
|
||||
|
||||
const mockSetFilterGroup: Filter = {
|
||||
type: 'select',
|
||||
name: 'Mock',
|
||||
options: [],
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [NgxsModule.forRoot([])],
|
||||
providers: [
|
||||
RemissionFilterService,
|
||||
{
|
||||
provide: RemissionService,
|
||||
useValue: jasmine.createSpy('remissionService'),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
remissionFilterService = TestBed.get(RemissionFilterService);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(remissionFilterService, 'getFilters').and.callFake(() => {});
|
||||
spyOn(remissionFilterService, 'getFlattenedFilters').and.callFake(() => {});
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(
|
||||
remissionFilterService instanceof RemissionFilterService
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('get lastFilterGroupChanged$', () => {
|
||||
beforeEach(() => {
|
||||
const remissionSource$ = hot('-a', { a: RemissionResourceType.Central });
|
||||
spyOnProperty(remissionFilterService, 'remissionSource$').and.returnValue(
|
||||
remissionSource$
|
||||
);
|
||||
|
||||
const remissionTarget$ = hot('-b', { b: 'libri' });
|
||||
spyOnProperty(remissionFilterService, 'remissionTarget$').and.returnValue(
|
||||
remissionTarget$
|
||||
);
|
||||
});
|
||||
it('should return the last filter group changed for the currently selected remisison filter and target', () => {
|
||||
spyOnProperty(
|
||||
remissionFilterService,
|
||||
'lastFilterGroupChanged$'
|
||||
).and.callThrough();
|
||||
|
||||
remissionFilterService.lastFilterGroupChanged = mockSetFilterGroup;
|
||||
|
||||
const expected$ = cold('-c', { c: mockSetFilterGroup });
|
||||
|
||||
expect(remissionFilterService.lastFilterGroupChanged$).toBeObservable(
|
||||
expected$
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set lastFilterGroupChanged', () => {
|
||||
beforeEach(() => {
|
||||
const remissionSource$ = hot('-a--b', {
|
||||
a: RemissionResourceType.Central,
|
||||
b: RemissionResourceType.Central,
|
||||
});
|
||||
spyOnProperty(remissionFilterService, 'remissionSource$').and.returnValue(
|
||||
remissionSource$
|
||||
);
|
||||
|
||||
const remissionTarget$ = hot('-b---', { b: 'libri' });
|
||||
spyOnProperty(remissionFilterService, 'remissionTarget$').and.returnValue(
|
||||
remissionTarget$
|
||||
);
|
||||
});
|
||||
it('should set the last filter group changed for the currently selected remisison filter and target', () => {
|
||||
spyOnProperty(
|
||||
remissionFilterService,
|
||||
'lastFilterGroupChanged$'
|
||||
).and.callThrough();
|
||||
|
||||
remissionFilterService.lastFilterGroupChanged = mockSetFilterGroup;
|
||||
|
||||
const expected$ = cold('-a--b', {
|
||||
a: mockSetFilterGroup,
|
||||
b: mockSetFilterGroup,
|
||||
});
|
||||
expect(remissionFilterService.lastFilterGroupChanged$).toBeObservable(
|
||||
expected$
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { RequestUpdateRemissionFilter } from '../../../core/store/actions/remission.actions';
|
||||
import { flatten } from '../../../shared/utils';
|
||||
import { RemissionResourceType } from '../models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class RemissionFilterService {
|
||||
@@ -33,13 +34,40 @@ export class RemissionFilterService {
|
||||
getRemissionActiveFilters$: Observable<{
|
||||
[filterId: string]: string[];
|
||||
}>;
|
||||
@Select(RemissionSelectors.getRemissiontarget)
|
||||
remissionTarget$: Observable<string>;
|
||||
@Select(RemissionSelectors.getRemissionSource)
|
||||
remissionSource$: Observable<RemissionResourceType>;
|
||||
|
||||
public filters$: Observable<SelectFilter[]>;
|
||||
private lastFilterGroupChangedState = new BehaviorSubject<
|
||||
Map<string, Filter>
|
||||
>(new Map());
|
||||
public filtersByGroup$: Observable<SelectFilter[]>;
|
||||
public lastFilterGroupChanged$ = new BehaviorSubject<Filter>(undefined);
|
||||
public filters$: Observable<SelectFilter[]>;
|
||||
public pendingFilters$ = new BehaviorSubject<Filter[]>(null);
|
||||
public resetFilters$ = new Subject<void>();
|
||||
|
||||
public set lastFilterGroupChanged(filterGroup: Filter) {
|
||||
this.getSupplierAndFilter$()
|
||||
.pipe(take(1))
|
||||
.subscribe(([target, supplier]) =>
|
||||
this.lastFilterGroupChangedState.value.set(
|
||||
this.getFilterGroupKey(target, supplier),
|
||||
filterGroup
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public get lastFilterGroupChanged$(): Observable<Filter> {
|
||||
return this.getSupplierAndFilter$().pipe(
|
||||
map(([target, supplier]) =>
|
||||
this.lastFilterGroupChangedState.value.get(
|
||||
this.getFilterGroupKey(target, supplier)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private remissionService: RemissionService,
|
||||
private store: Store
|
||||
@@ -49,14 +77,13 @@ export class RemissionFilterService {
|
||||
}
|
||||
|
||||
getFilters(): Observable<SelectFilter[]> {
|
||||
// tslint:disable-next-line: deprecation
|
||||
return combineLatest(
|
||||
return combineLatest([
|
||||
this.remissionProcess$,
|
||||
this.resetFilters$.pipe(
|
||||
startWith(true),
|
||||
tap(() => this.pendingFilters$.next(null))
|
||||
)
|
||||
).pipe(
|
||||
),
|
||||
]).pipe(
|
||||
debounceTime(100),
|
||||
filter(([process, _]) => !isNullOrUndefined(process)),
|
||||
map(([process, _]) => process.id),
|
||||
@@ -250,9 +277,29 @@ export class RemissionFilterService {
|
||||
}
|
||||
|
||||
private setInitialFilterGroupLastChanged(filters: SelectFilter[]) {
|
||||
if (!this.lastFilterGroupChanged$.value) {
|
||||
this.lastFilterGroupChanged$.next(filters[0]);
|
||||
this.lastFilterGroupChanged$
|
||||
.pipe(take(1))
|
||||
.subscribe((lastFilterGroupChanged) => {
|
||||
if (!lastFilterGroupChanged) {
|
||||
this.lastFilterGroupChanged = filters[0];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getSupplierAndFilter$(): Observable<[string, RemissionResourceType]> {
|
||||
return combineLatest([this.remissionTarget$, this.remissionSource$]).pipe(
|
||||
filter(
|
||||
([target, supplier]) =>
|
||||
!isNullOrUndefined(target) && !isNullOrUndefined(supplier)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private getFilterGroupKey(
|
||||
target: string,
|
||||
supplier: RemissionResourceType
|
||||
): string {
|
||||
return target.toLowerCase() + supplier.toLowerCase();
|
||||
}
|
||||
|
||||
getRemissionFilters(
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { AutocompleteResultsComponent } from './autocomplete-results.component';
|
||||
import { ResultItemComponent } from './result-item';
|
||||
import { ActiveDescendantKeyManager, Highlightable } from '@angular/cdk/a11y';
|
||||
import { QueryList } from '@angular/core';
|
||||
|
||||
fdescribe('#AutocompleteResultsComponent', () => {
|
||||
let fixture: ComponentFixture<AutocompleteResultsComponent>;
|
||||
let component: AutocompleteResultsComponent;
|
||||
|
||||
let items: QueryList<Highlightable & ResultItemComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AutocompleteResultsComponent, ResultItemComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AutocompleteResultsComponent);
|
||||
component = fixture.componentInstance;
|
||||
items = new QueryList();
|
||||
component['keyManager'] = new ActiveDescendantKeyManager<
|
||||
ResultItemComponent
|
||||
>(items).withWrap();
|
||||
|
||||
spyOn(component, 'ngAfterViewInit').and.callFake(() => {});
|
||||
component.results = [];
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component instanceof AutocompleteResultsComponent).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Deselect Items on keypress', () => {
|
||||
let activeItemMock: Highlightable & ResultItemComponent;
|
||||
beforeEach(() => {
|
||||
activeItemMock = ({
|
||||
result: 'Test Result',
|
||||
} as unknown) as Highlightable & ResultItemComponent;
|
||||
spyOn(component, 'onKeyDown').and.callThrough();
|
||||
spyOn(component.selectItem, 'emit').and.callFake(() => {});
|
||||
spyOnProperty(component['keyManager'], 'activeItem').and.returnValue(
|
||||
activeItemMock
|
||||
);
|
||||
spyOn(component['keyManager'], 'setActiveItem').and.callFake(() => {});
|
||||
});
|
||||
|
||||
it('should deselect the current active item if a letter is typed', () => {
|
||||
const event = new KeyboardEvent('keyup', { key: 'a' });
|
||||
window.dispatchEvent(event);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.onKeyDown).toHaveBeenCalled();
|
||||
expect(component['keyManager'].setActiveItem).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should deselect the current active item if backspace is typed', () => {
|
||||
const event = new KeyboardEvent('keyup', { key: 'Backspace' });
|
||||
window.dispatchEvent(event);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.onKeyDown).toHaveBeenCalled();
|
||||
expect(component['keyManager'].setActiveItem).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should not deselect if ArrowUp is pressed', () => {
|
||||
const event = new KeyboardEvent('keyup', { key: 'ArrowUp' });
|
||||
window.dispatchEvent(event);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.onKeyDown).toHaveBeenCalled();
|
||||
expect(component['keyManager'].setActiveItem).not.toHaveBeenCalledWith(
|
||||
null
|
||||
);
|
||||
expect(component.selectItem.emit).toHaveBeenCalledWith(
|
||||
activeItemMock.result
|
||||
);
|
||||
});
|
||||
|
||||
it('should not deselect if ArrowDown is pressed', () => {
|
||||
const event = new KeyboardEvent('keyup', { key: 'ArrowDown' });
|
||||
window.dispatchEvent(event);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.onKeyDown).toHaveBeenCalled();
|
||||
expect(component['keyManager'].setActiveItem).not.toHaveBeenCalledWith(
|
||||
null
|
||||
);
|
||||
expect(component.selectItem.emit).toHaveBeenCalledWith(
|
||||
activeItemMock.result
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -33,12 +33,16 @@ export class AutocompleteResultsComponent implements AfterViewInit {
|
||||
constructor() {}
|
||||
|
||||
@HostListener('window:keyup', ['$event']) onKeyDown(event: KeyboardEvent) {
|
||||
if (event.key === 'ArrowDown' || 'ArrowUp') {
|
||||
if (this.isUpOrDownKey(event.key)) {
|
||||
this.keyManager.onKeydown(event);
|
||||
if (this.keyManager.activeItem) {
|
||||
this.selectItem.emit(this.keyManager.activeItem.result);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldDeselectItem(event.key)) {
|
||||
this.keyManager.setActiveItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@@ -57,4 +61,16 @@ export class AutocompleteResultsComponent implements AfterViewInit {
|
||||
private getItemIndex(item: AutocompleteDTO): number {
|
||||
return this.results.indexOf(item);
|
||||
}
|
||||
|
||||
private shouldDeselectItem(key: string): boolean {
|
||||
const isBackspace = key === 'Backspace';
|
||||
const isLetter = key.length === 1 && /^[a-zA-Z]+$/.test(key);
|
||||
const isNumber = key.length === 1 && /^[0-9]+$/.test(key);
|
||||
|
||||
return isBackspace || isLetter || isNumber;
|
||||
}
|
||||
|
||||
private isUpOrDownKey(key: string): boolean {
|
||||
return key === 'ArrowDown' || key === 'ArrowUp';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './shelf-edit-actions.component';
|
||||
export * from './shelf-edit-actions.module';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="actions">
|
||||
<button (click)="cancel.emit()"
|
||||
class="isa-btn isa-btn-pill isa-btn-secondary isa-btn-shadow isa-btn-red-border isa-btn-medium">
|
||||
Abbrechen</button><button (click)="save.emit()"
|
||||
class="isa-btn isa-btn-pill isa-btn-primary isa-btn-shadow isa-btn-medium" [disabled]="disabled">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
:host {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
& > .isa-btn {
|
||||
margin: 0 31px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Output,
|
||||
EventEmitter,
|
||||
Input,
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shelf-edit-actions',
|
||||
templateUrl: 'shelf-edit-actions.component.html',
|
||||
styleUrls: ['./shelf-edit-actions.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShelfEditActionsComponent {
|
||||
@Input() disabled = false;
|
||||
|
||||
@Output() save = new EventEmitter<void>();
|
||||
@Output() cancel = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ShelfEditActionsComponent } from './shelf-edit-actions.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
exports: [ShelfEditActionsComponent],
|
||||
declarations: [ShelfEditActionsComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class ShelfEditActionsModule {}
|
||||
@@ -0,0 +1,35 @@
|
||||
<ng-container *ngIf="orderItemForm">
|
||||
<app-shelf-edit-order-item-header [src]="orderItemForm.get('ean')?.value | createImageSourceFromEan"
|
||||
[title]="orderItemForm.get('title')?.value" (expand)="expandGroup($event)" [isOpen]="isGroupOpen">
|
||||
</app-shelf-edit-order-item-header>
|
||||
|
||||
<hr class="isa-content-spacer last" />
|
||||
<form [formGroup]="orderItemForm" *ngIf="isGroupOpen">
|
||||
<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>
|
||||
<hr class="isa-content-spacer" />
|
||||
|
||||
<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>
|
||||
<hr class="isa-content-spacer" />
|
||||
|
||||
<app-ui-text-input formControlName="supplier" label="Lieferant"></app-ui-text-input>
|
||||
<hr class="isa-content-spacer" />
|
||||
|
||||
<app-ui-text-input formControlName="ssc" label="Meldenummer"></app-ui-text-input>
|
||||
<hr class="isa-content-spacer" />
|
||||
|
||||
<app-ui-select-input *ngIf="vatLabelPipe" formControlName="vat" [options]="vatOptions"
|
||||
[optionsPipe]="vatOptionsPipe" [labelPipe]="{ transform: vatLabelTransform }" [valuePipe]="valuePipe"
|
||||
label="MwSt">
|
||||
</app-ui-select-input>
|
||||
<hr class="isa-content-spacer" />
|
||||
|
||||
<app-ui-text-input formControlName="comment" label="Anmerkung"></app-ui-text-input>
|
||||
<hr class="isa-content-spacer" />
|
||||
</form>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ShelfEditOrderItemComponent } from '.';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { CreateImageSourceFromEanPipe } from '../../pipes';
|
||||
import { ShelfEditOrderItemHeaderComponent } from './header';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
|
||||
fdescribe('ShelfEditOrderItemComponent', () => {
|
||||
let fixture: ComponentFixture<ShelfEditOrderItemComponent>;
|
||||
let component: ShelfEditOrderItemComponent;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [CommonModule, ReactiveFormsModule],
|
||||
declarations: [
|
||||
ShelfEditOrderItemComponent,
|
||||
ShelfEditOrderItemHeaderComponent,
|
||||
CreateImageSourceFromEanPipe,
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShelfEditOrderItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component instanceof ShelfEditOrderItemComponent).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Input,
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
OnChanges,
|
||||
ChangeDetectorRef,
|
||||
SimpleChange,
|
||||
} from '@angular/core';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { VATDTO } from '@swagger/oms';
|
||||
import { VatTypeToDisplayNamePipe } from '../../pipes/vat-type-to-display-name.pipe';
|
||||
import { VatDtoToVatValuePipe } from '../../pipes/vat-dto-to-vat-value.pipe';
|
||||
import { VatDtoToVatTypePipe } from '../../pipes/vat-dto-to-vat-type.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shelf-edit-order-item',
|
||||
templateUrl: 'edit-order-item.component.html',
|
||||
styleUrls: ['./edit-order-item.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShelfEditOrderItemComponent implements OnInit, OnChanges {
|
||||
@Input() orderItemForm: FormGroup;
|
||||
@Input() onlyChild: boolean;
|
||||
@Input() firstChild: boolean;
|
||||
@Input() vatOptions: VATDTO[];
|
||||
|
||||
vatLabelPipe: VatTypeToDisplayNamePipe;
|
||||
vatOptionsPipe = new VatDtoToVatValuePipe();
|
||||
valuePipe = new VatDtoToVatTypePipe();
|
||||
isGroupOpen: boolean;
|
||||
|
||||
vatLabelTransform = (value) => {
|
||||
return this.vatLabelPipe.transform(value, this.vatOptions);
|
||||
};
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnChanges({ vatOptions }: SimpleChanges) {
|
||||
if (vatOptions.currentValue) {
|
||||
this.vatLabelPipe = new VatTypeToDisplayNamePipe();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.isGroupOpen = this.onlyChild || this.firstChild;
|
||||
}
|
||||
|
||||
expandGroup(shouldExpand: boolean) {
|
||||
this.isGroupOpen = shouldExpand;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { IconModule } from '@libs/ui';
|
||||
import { UiTextInputModule } from '@isa-ui/text-input';
|
||||
import { ShelfEditOrderItemComponent } from './edit-order-item.component';
|
||||
import { ShelfEditOrderItemHeaderComponent } from './header';
|
||||
import { UiSelectInputModule } from '@isa-ui/select-input';
|
||||
import { ShelfPipesModule } from '../../pipes';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
IconModule,
|
||||
UiTextInputModule,
|
||||
UiSelectInputModule,
|
||||
ShelfPipesModule,
|
||||
],
|
||||
exports: [ShelfEditOrderItemComponent, ShelfEditOrderItemHeaderComponent],
|
||||
declarations: [
|
||||
ShelfEditOrderItemComponent,
|
||||
ShelfEditOrderItemHeaderComponent,
|
||||
],
|
||||
providers: [],
|
||||
})
|
||||
export class ShelfEditOrderItemModule {}
|
||||
@@ -0,0 +1,24 @@
|
||||
<div class="header">
|
||||
<div>
|
||||
<img
|
||||
*ngIf="src"
|
||||
class="isa-thumbnail"
|
||||
[src]="src"
|
||||
alt="item.product.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="title isa-font-weight-bold">{{ title }}</div>
|
||||
<div
|
||||
class="collapse"
|
||||
*ngIf="showCollapseArrow"
|
||||
(click)="expand.emit(!isOpen)"
|
||||
>
|
||||
<lib-icon
|
||||
class="isa-accordion-arrow"
|
||||
[class.arrow-up]="!isOpen"
|
||||
[class.arrow-down]="isOpen"
|
||||
name="Arrow_right"
|
||||
[height]="'16px'"
|
||||
></lib-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 30px 0;
|
||||
|
||||
.title {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shelf-edit-order-item-header',
|
||||
templateUrl: 'edit-order-item-header.component.html',
|
||||
styleUrls: ['./edit-order-item-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShelfEditOrderItemHeaderComponent {
|
||||
@Input() showCollapseArrow = true;
|
||||
@Input() isOpen = false;
|
||||
|
||||
@Input() src: string;
|
||||
@Input() title: string;
|
||||
|
||||
@Output() expand = new EventEmitter<boolean>();
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './edit-order-item-header.component';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// start:ng42.barrel
|
||||
export * from './edit-order-item.component';
|
||||
export * from './edit-order-item.module';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="log-entry mt-40" [class.last]="last" [class.first]="first">
|
||||
<span class="timeline-dot"></span>
|
||||
<app-shelf-history-log-header
|
||||
[infoText]="history | historyDtoToInfoText"
|
||||
[statusText]="history | historyDtoToStatusText"
|
||||
></app-shelf-history-log-header>
|
||||
<app-shelf-history-log-body
|
||||
*ngFor="let changes of history.values"
|
||||
[oldStatus]="changes | getStatusFromHistory: 'old'"
|
||||
[newStatus]="changes | getStatusFromHistory: 'new'"
|
||||
[changedBy]="history.changedBy"
|
||||
[caption]="changes.caption"
|
||||
></app-shelf-history-log-body>
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
@import 'variables';
|
||||
|
||||
$offset: 10px;
|
||||
$line-top-offset: 3px;
|
||||
$dot-size: 16px;
|
||||
$dot-size-lg: 20px;
|
||||
$border-size: 1px;
|
||||
$border-size-cover: 2px;
|
||||
|
||||
.log-entry {
|
||||
position: relative;
|
||||
// calculate offset relative to container offset (22px)
|
||||
padding-left: calc(70px - 22px);
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 130%;
|
||||
width: 1px;
|
||||
top: $line-top-offset;
|
||||
left: $offset;
|
||||
border: $border-size solid $isa-light-blue-platinum;
|
||||
background: $isa-light-blue-platinum;
|
||||
}
|
||||
|
||||
&.last:before {
|
||||
height: 30%;
|
||||
border: $border-size-cover solid #fff;
|
||||
background: #fff;
|
||||
left: calc(#{$offset} - #{$border-size-cover});
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: $offset;
|
||||
background: #fff;
|
||||
width: 2px;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background: $isa-light-blue-platinum;
|
||||
|
||||
top: $line-top-offset;
|
||||
left: calc((#{$dot-size} - #{$line-top-offset}) / -2);
|
||||
border-radius: 50%;
|
||||
width: $dot-size;
|
||||
height: $dot-size;
|
||||
}
|
||||
}
|
||||
|
||||
&.first {
|
||||
.timeline-dot:before {
|
||||
width: $dot-size-lg;
|
||||
height: $dot-size-lg;
|
||||
background: $isa-neutral-info;
|
||||
left: calc(#{$dot-size} / -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
import { ShelfHistoryLogComponent } from './history-log.component';
|
||||
import { ShelfHistoryLogHeaderComponent } from './log-header';
|
||||
import { ShelfHistoryLogBodyComponent } from './log-body';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { historiesDtoMock, historyDtoMock } from '../../shared/mockdata';
|
||||
import {
|
||||
HistoryDtoToInfoTextPipe,
|
||||
HistoryDtoToStatusTextPipe,
|
||||
GetStatusFromHistoryPipe,
|
||||
} from '../../pages/shelf-history/pipes';
|
||||
import { HistoryDTO } from '@cmf/trade-api';
|
||||
|
||||
fdescribe('HistoryLogComponent', () => {
|
||||
let fixture: ComponentFixture<ShelfHistoryLogComponent>;
|
||||
let component: ShelfHistoryLogComponent;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
ShelfHistoryLogComponent,
|
||||
ShelfHistoryLogHeaderComponent,
|
||||
ShelfHistoryLogBodyComponent,
|
||||
HistoryDtoToInfoTextPipe,
|
||||
HistoryDtoToStatusTextPipe,
|
||||
GetStatusFromHistoryPipe,
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ShelfHistoryLogComponent);
|
||||
component = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
|
||||
const historyMockWithMultipleLogEntries: HistoryDTO = historiesDtoMock.find(
|
||||
(history) => history.values && history.values.length > 2
|
||||
);
|
||||
component.history = historyMockWithMultipleLogEntries;
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component instanceof ShelfHistoryLogComponent).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show a header for all history values (log entries)', () => {
|
||||
const header = debugElement.queryAll(
|
||||
By.css('app-shelf-history-log-header')
|
||||
);
|
||||
|
||||
expect(header.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show an entry for each history value (log entry)', () => {
|
||||
const historyMockWithMultipleLogEntries: HistoryDTO = historiesDtoMock.find(
|
||||
(history) => history.values && history.values.length > 2
|
||||
);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const entries = debugElement.queryAll(By.css('app-shelf-history-log-body'));
|
||||
|
||||
expect(entries.length).toBe(
|
||||
historyMockWithMultipleLogEntries.values.length
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { HistoryDTO } from '@cmf/trade-api';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shelf-history-log',
|
||||
templateUrl: './history-log.component.html',
|
||||
styleUrls: ['./history-log.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShelfHistoryLogComponent {
|
||||
@Input() first = false;
|
||||
@Input() last = false;
|
||||
@Input() history: HistoryDTO;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
HistoryDtoToInfoTextPipe,
|
||||
HistoryDtoToStatusTextPipe,
|
||||
GetStatusFromHistoryPipe,
|
||||
} from '../../pages/shelf-history/pipes';
|
||||
import { IconModule } from '@libs/ui';
|
||||
import { ShelfHistoryLogComponent } from './history-log.component';
|
||||
import { ShelfHistoryLogHeaderComponent } from './log-header';
|
||||
import { ShelfHistoryLogBodyComponent } from './log-body';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, IconModule],
|
||||
declarations: [
|
||||
ShelfHistoryLogComponent,
|
||||
ShelfHistoryLogHeaderComponent,
|
||||
ShelfHistoryLogBodyComponent,
|
||||
HistoryDtoToInfoTextPipe,
|
||||
HistoryDtoToStatusTextPipe,
|
||||
GetStatusFromHistoryPipe,
|
||||
],
|
||||
exports: [
|
||||
ShelfHistoryLogComponent,
|
||||
ShelfHistoryLogHeaderComponent,
|
||||
ShelfHistoryLogBodyComponent,
|
||||
],
|
||||
providers: [],
|
||||
})
|
||||
export class ShelfHistoryLogModule {}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user