mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
57 Commits
feature/45
...
fix/4715-B
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3fc87af0a | ||
|
|
f1bdba5d10 | ||
|
|
c4134e7f99 | ||
|
|
b7a16f5d30 | ||
|
|
4105709286 | ||
|
|
0c3b322fbd | ||
|
|
12096754c7 | ||
|
|
453d921a99 | ||
|
|
bad05fd098 | ||
|
|
363daf1e35 | ||
|
|
e0cb0974cf | ||
|
|
c3d9274766 | ||
|
|
bc16b841fb | ||
|
|
d5dc4e053d | ||
|
|
28fb4ebb48 | ||
|
|
2118bd996a | ||
|
|
8a6448cc17 | ||
|
|
f2c7d57ad6 | ||
|
|
9c9ddfaeec | ||
|
|
6eaa347de5 | ||
|
|
a16f355396 | ||
|
|
8b8db6e335 | ||
|
|
06e248d615 | ||
|
|
c68706b54f | ||
|
|
b271ce9711 | ||
|
|
94888213b1 | ||
|
|
1041d92486 | ||
|
|
43d8d220c9 | ||
|
|
e0993d9c46 | ||
|
|
82656d9b27 | ||
|
|
df36d0934d | ||
|
|
920b8eb8e3 | ||
|
|
4db28b1aa7 | ||
|
|
3a9820aa54 | ||
|
|
30ad99332e | ||
|
|
4b48275910 | ||
|
|
d3e3316459 | ||
|
|
4ef1bd4df6 | ||
|
|
0c2a23e5d2 | ||
|
|
36bd2c1eba | ||
|
|
a38d2eede6 | ||
|
|
ed7dc10246 | ||
|
|
f5251d9069 | ||
|
|
2bd21e168a | ||
|
|
3661bf7580 | ||
|
|
9f2a6633f7 | ||
|
|
3c4d0ea56c | ||
|
|
56bb784c83 | ||
|
|
c687570b1f | ||
|
|
afe5d3468a | ||
|
|
65f43d22ee | ||
|
|
67203a8506 | ||
|
|
92e522dedf | ||
|
|
fb46d329dc | ||
|
|
8f47163627 | ||
|
|
a209d59ea9 | ||
|
|
b838f4c475 |
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@@ -0,0 +1 @@
|
||||
npm run pretty-quick
|
||||
4
TASKS.md
4
TASKS.md
@@ -1,4 +0,0 @@
|
||||
- Neue Icon Module (z.B. mit SVG sprites)
|
||||
- Breadcrumb Navigation (Neu)
|
||||
- Remissions Produkt Liste (Refactoring / Neu)
|
||||
- Angular Version (Upgrade)
|
||||
39
angular.json
39
angular.json
@@ -959,10 +959,10 @@
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "isa-app:build:production"
|
||||
"buildTarget": "isa-app:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "isa-app:build:development"
|
||||
"buildTarget": "isa-app:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
@@ -970,7 +970,7 @@
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "isa-app:build"
|
||||
"buildTarget": "isa-app:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@@ -1470,39 +1470,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shell": {
|
||||
"projectType": "library",
|
||||
"root": "apps/shell",
|
||||
"sourceRoot": "apps/shell/src",
|
||||
"prefix": "shell",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"project": "apps/shell/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "apps/shell/tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "apps/shell/tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"tsConfig": "apps/shell/tsconfig.spec.json",
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
|
||||
.scanner-container {
|
||||
width: 100vw;
|
||||
max-width: 95vw;
|
||||
max-height: calc(95vh - 120px);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.close-scanner {
|
||||
@apply block px-6 py-4 bg-white text-brand border-2 border-solid border-brand rounded-full text-lg font-bold mx-auto mt-4;
|
||||
@apply absolute bottom-12 left-[50%] -translate-x-[50%] block px-6 py-4 bg-white text-brand border-2 border-solid border-brand rounded-full text-lg font-bold mx-auto mt-4;
|
||||
}
|
||||
|
||||
@screen desktop {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Directive, HostListener, Input } from '@angular/core';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
|
||||
@Directive({
|
||||
selector: '[productImageNavigation]',
|
||||
standalone: true,
|
||||
})
|
||||
export class NavigateOnClickDirective {
|
||||
@Input('productImageNavigation') ean: string;
|
||||
|
||||
constructor(private readonly _productCatalogNavigation: ProductCatalogNavigationService) {}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
async onClick(event: MouseEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (this.ean) {
|
||||
await this._navigateToProductSearchDetails();
|
||||
}
|
||||
}
|
||||
|
||||
private async _navigateToProductSearchDetails() {
|
||||
await this._productCatalogNavigation
|
||||
.getArticleDetailsPathByEan({
|
||||
processId: Date.now(),
|
||||
ean: this.ean,
|
||||
extras: { queryParams: { main_qs: this.ean } },
|
||||
})
|
||||
.navigate();
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,5 @@
|
||||
export * from './lib/product-image.service';
|
||||
export * from './lib/product-image.module';
|
||||
export * from './lib/product-image.pipe';
|
||||
export * from './lib/product-image-navigation.directive';
|
||||
export * from './lib/tokens';
|
||||
|
||||
@@ -36,7 +36,7 @@ export class DomainAvailabilityService {
|
||||
private _logisticanService: LogisticianService,
|
||||
private _stockService: StockService,
|
||||
private _supplierService: StoreCheckoutSupplierService,
|
||||
private _branchService: StoreCheckoutBranchService
|
||||
private _branchService: StoreCheckoutBranchService,
|
||||
) {}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
@@ -48,7 +48,7 @@ export class DomainAvailabilityService {
|
||||
getSuppliers(): Observable<SupplierDTO[]> {
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class DomainAvailabilityService {
|
||||
getTakeAwaySupplier(): Observable<SupplierDTO> {
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map(({ result }) => result?.find((supplier) => supplier?.supplierNumber === 'F')),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class DomainAvailabilityService {
|
||||
getBranches(): Observable<BranchDTO[]> {
|
||||
return this._branchService.StoreCheckoutBranchGetBranches({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export class DomainAvailabilityService {
|
||||
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
|
||||
map((response) => response.result),
|
||||
map((result) => result?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ export class DomainAvailabilityService {
|
||||
getDefaultStock(): Observable<StockDTO> {
|
||||
return this._stockService.StockCurrentStock().pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export class DomainAvailabilityService {
|
||||
status: response.result.status,
|
||||
version: response.result.version,
|
||||
})),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ export class DomainAvailabilityService {
|
||||
getLogisticians(): Observable<LogisticianDTO> {
|
||||
return this._logisticanService.LogisticianGetLogisticians({}).pipe(
|
||||
map((response) => response.result?.find((l) => l.logisticianNumber === '2470')),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ export class DomainAvailabilityService {
|
||||
});
|
||||
return availabilities;
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -167,13 +167,13 @@ export class DomainAvailabilityService {
|
||||
this._stockService.StockInStock({ articleIds: [item.itemId], stockId: s.id }),
|
||||
this.getTakeAwaySupplier(),
|
||||
this.getDefaultBranch(),
|
||||
])
|
||||
]),
|
||||
),
|
||||
map(([response, supplier, defaultBranch]) => {
|
||||
const price = item?.price;
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ export class DomainAvailabilityService {
|
||||
map(([response, supplier]) => {
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ export class DomainAvailabilityService {
|
||||
map(([response, supplier, defaultBranch]) => {
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branchId ?? defaultBranch.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ export class DomainAvailabilityService {
|
||||
switchMap((s) => this._stockService.StockInStockByEAN({ eans: eansFiltered, stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
|
||||
map((response) => response[0].result),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ export class DomainAvailabilityService {
|
||||
])
|
||||
.pipe(
|
||||
map((r) => this._mapToPickUpAvailability(r.result)?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ export class DomainAvailabilityService {
|
||||
]).pipe(
|
||||
timeout(5000),
|
||||
map((r) => this._mapToShippingAvailability(r.result)?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ export class DomainAvailabilityService {
|
||||
priceMaintained: preferred?.priceMaintained,
|
||||
};
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -329,12 +329,12 @@ export class DomainAvailabilityService {
|
||||
this.getPickUpAvailability({ item, quantity, branch: branch ?? defaultBranch }).pipe(
|
||||
mergeMap((availability) =>
|
||||
logistician$.pipe(
|
||||
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } }))
|
||||
)
|
||||
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } })),
|
||||
),
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
)
|
||||
shareReplay(1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ export class DomainAvailabilityService {
|
||||
priceMaintained: preferred?.priceMaintained,
|
||||
};
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ export class DomainAvailabilityService {
|
||||
switchMap((stockId) =>
|
||||
stockId
|
||||
? this._stockService.StockInStock({ articleIds: items.map((i) => i.id), stockId })
|
||||
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO)
|
||||
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO),
|
||||
),
|
||||
timeout(20000),
|
||||
withLatestFrom(this.getTakeAwaySupplier()),
|
||||
@@ -389,10 +389,10 @@ export class DomainAvailabilityService {
|
||||
supplier,
|
||||
quantity: 1,
|
||||
price: items?.find((i) => i.id === stockInfo.itemId)?.price,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -400,7 +400,7 @@ export class DomainAvailabilityService {
|
||||
getPickUpAvailabilities(payload: AvailabilityRequestDTO[], preferred?: boolean) {
|
||||
return this._availabilityService.AvailabilityStoreAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result))
|
||||
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -408,7 +408,7 @@ export class DomainAvailabilityService {
|
||||
getDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
map((response) => this._mapToShippingAvailability(response.result)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ export class DomainAvailabilityService {
|
||||
getDigDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
map((response) => this._mapToShippingAvailability(response.result)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -427,16 +427,16 @@ export class DomainAvailabilityService {
|
||||
return this.getPickUpAvailabilities(payload, true).pipe(
|
||||
timeout(20000),
|
||||
switchMap((availability) =>
|
||||
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } })))
|
||||
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } }))),
|
||||
),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
getPriceForAvailability(
|
||||
purchasingOption: string,
|
||||
catalogAvailability: CatAvailabilityDTO | AvailabilityDTO,
|
||||
availability: AvailabilityDTO
|
||||
availability: AvailabilityDTO,
|
||||
): PriceDTO {
|
||||
switch (purchasingOption) {
|
||||
case 'take-away':
|
||||
@@ -567,12 +567,12 @@ export class DomainAvailabilityService {
|
||||
if (!params.branchId) {
|
||||
branchId$ = this.getDefaultBranch().pipe(
|
||||
first(),
|
||||
map((b) => b.id)
|
||||
map((b) => b.id),
|
||||
);
|
||||
}
|
||||
|
||||
const stock$ = branchId$.pipe(
|
||||
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0])))
|
||||
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0]))),
|
||||
);
|
||||
|
||||
return stock$.pipe(
|
||||
@@ -589,17 +589,17 @@ export class DomainAvailabilityService {
|
||||
acc[stockInfo.ean] = stockInfo;
|
||||
return acc;
|
||||
}, {});
|
||||
})
|
||||
)
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
getInStock({ itemIds, branchId }: { itemIds: number[]; branchId: number }): Observable<StockInfoDTO[]> {
|
||||
return this.getStockByBranch(branchId).pipe(
|
||||
mergeMap((stock) =>
|
||||
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result))
|
||||
)
|
||||
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@ export class ThumbnailUrlPipe implements PipeTransform, OnDestroy {
|
||||
private input$ = new BehaviorSubject<{ width?: number; height?: number; ean?: string }>(undefined);
|
||||
private result: string;
|
||||
|
||||
private onDestroy$ = new Subject();
|
||||
private onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(private domainCatalogThumbnailService: DomainCatalogThumbnailService, private cdr: ChangeDetectorRef) {}
|
||||
constructor(
|
||||
private domainCatalogThumbnailService: DomainCatalogThumbnailService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
) {}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next();
|
||||
@@ -27,7 +30,7 @@ export class ThumbnailUrlPipe implements PipeTransform, OnDestroy {
|
||||
this.input$
|
||||
.pipe(
|
||||
takeUntil(this.onDestroy$),
|
||||
switchMap((input) => this.domainCatalogThumbnailService.getThumnaulUrl(input))
|
||||
switchMap((input) => this.domainCatalogThumbnailService.getThumnaulUrl(input)),
|
||||
)
|
||||
.subscribe((result) => {
|
||||
this.result = result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { inject, isDevMode, NgModule } from '@angular/core';
|
||||
import { isDevMode, NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {
|
||||
CanActivateCartGuard,
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
CanActivateCustomerOrdersWithProcessIdGuard,
|
||||
CanActivateCustomerWithProcessIdGuard,
|
||||
CanActivateGoodsInGuard,
|
||||
CanActivateGoodsOutGuard,
|
||||
CanActivateGoodsOutWithProcessIdGuard,
|
||||
CanActivateProductGuard,
|
||||
CanActivateProductWithProcessIdGuard,
|
||||
CanActivateRemissionGuard,
|
||||
@@ -22,7 +20,6 @@ import { MainComponent } from './main.component';
|
||||
import { PreviewComponent } from './preview';
|
||||
import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers';
|
||||
import { TokenLoginComponent, TokenLoginModule } from './token-login';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ProcessIdGuard } from './guards/process-id.guard';
|
||||
import { ActivateProcessIdGuard, ActivateProcessIdWithConfigKeyGuard } from './guards/activate-process-id.guard';
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const metaReducers: MetaReducer<RootState>[] = !environment.production ?
|
||||
imports: [
|
||||
StoreModule.forRoot(rootReducer, { metaReducers }),
|
||||
EffectsModule.forRoot([]),
|
||||
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Application Store' }),
|
||||
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Application Store', connectInZone: true }),
|
||||
],
|
||||
})
|
||||
export class AppStoreModule {}
|
||||
|
||||
@@ -1 +1,28 @@
|
||||
@if ($offlineBannerVisible()) {
|
||||
<div [@fadeInOut] class="bg-brand text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
||||
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
||||
<div>
|
||||
<ng-icon name="matWifiOff"></ng-icon>
|
||||
</div>
|
||||
|
||||
<div>Sie sind offline, keine Verbindung zum Netzwerk.</div>
|
||||
</h3>
|
||||
<p>Bereits geladene Ihnalte werden angezeigt, Interaktionen sind aktuell nicht möglich.</p>
|
||||
</div>
|
||||
}
|
||||
@if ($onlineBannerVisible()) {
|
||||
<div [@fadeInOut] class="bg-green-500 text-white text-center fixed inset-x-0 top-0 z-tooltip p-4">
|
||||
<h3 class="font-bold grid grid-flow-col items-center justify-center text-xl gap-4">
|
||||
<div>
|
||||
<ng-icon name="matWifi"></ng-icon>
|
||||
</div>
|
||||
|
||||
<div>Sie sind wieder online.</div>
|
||||
</h3>
|
||||
<button class="fixed top-2 right-4 text-3xl w-12 h-12" type="button" (click)="$onlineBannerVisible.set(false)">
|
||||
<ng-icon name="matClose"></ng-icon>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
@@ -1,30 +1,71 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Component, HostListener, Inject, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Component, effect, HostListener, Inject, OnInit, Renderer2, signal, untracked } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { Config } from '@core/config';
|
||||
import { NotificationsHub } from '@hub/notifications';
|
||||
import packageInfo from 'package';
|
||||
import { asapScheduler, interval, Observable, Subscription } from 'rxjs';
|
||||
import { asapScheduler, interval, Subscription } from 'rxjs';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
import { IsaLogProvider } from './providers';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { injectOnline$ } from './services/network-status.service';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
animations: [
|
||||
trigger('fadeInOut', [
|
||||
transition(':enter', [
|
||||
// :enter wird ausgelöst, wenn das Element zum DOM hinzugefügt wird
|
||||
style({ opacity: 0, transform: 'translateY(-100%)' }),
|
||||
animate('300ms', style({ opacity: 1, transform: 'translateY(0)' })),
|
||||
]),
|
||||
transition(':leave', [
|
||||
// :leave wird ausgelöst, wenn das Element aus dem DOM entfernt wird
|
||||
animate('300ms', style({ opacity: 0, transform: 'translateY(-100%)' })),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
$online = toSignal(injectOnline$());
|
||||
|
||||
$offlineBannerVisible = signal(false);
|
||||
|
||||
$onlineBannerVisible = signal(false);
|
||||
|
||||
private onlineBannerDismissTimeout: any;
|
||||
|
||||
onlineEffects = effect(() => {
|
||||
const online = this.$online();
|
||||
const offlineBannerVisible = this.$offlineBannerVisible();
|
||||
|
||||
untracked(() => {
|
||||
this.$offlineBannerVisible.set(!online);
|
||||
|
||||
if (!online) {
|
||||
this.$onlineBannerVisible.set(false);
|
||||
clearTimeout(this.onlineBannerDismissTimeout);
|
||||
}
|
||||
|
||||
if (offlineBannerVisible && online) {
|
||||
this.$onlineBannerVisible.set(true);
|
||||
this.onlineBannerDismissTimeout = setTimeout(() => this.$onlineBannerVisible.set(false), 5000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
private _checkForUpdates: number = this._config.get('checkForUpdates');
|
||||
updateAvailableObs: Observable<UpdateAvailableEvent>;
|
||||
|
||||
get checkForUpdates(): number {
|
||||
return this._checkForUpdates;
|
||||
return this._checkForUpdates ?? 60 * 60 * 1000; // default 1 hour
|
||||
}
|
||||
|
||||
// For Unit Testing
|
||||
@@ -45,7 +86,7 @@ export class AppComponent implements OnInit {
|
||||
private infoService: UserStateService,
|
||||
private readonly _environment: EnvironmentService,
|
||||
private readonly _authService: AuthService,
|
||||
private readonly _modal: UiModalService
|
||||
private readonly _modal: UiModalService,
|
||||
) {
|
||||
this.updateClient();
|
||||
IsaLogProvider.InfoService = this.infoService;
|
||||
|
||||
@@ -37,6 +37,10 @@ import { NativeContainerService } from 'native-container';
|
||||
import { ShellModule } from '@shared/shell';
|
||||
import { MainComponent } from './main.component';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { NgIconsModule } from '@ng-icons/core';
|
||||
import { matClose, matWifi, matWifiOff } from '@ng-icons/material-icons/baseline';
|
||||
import { NetworkStatusService } from './services/network-status.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
@@ -46,26 +50,68 @@ export function _appInitializerFactory(
|
||||
auth: AuthService,
|
||||
injector: Injector,
|
||||
scanAdapter: ScanAdapterService,
|
||||
nativeContainer: NativeContainerService
|
||||
nativeContainer: NativeContainerService,
|
||||
networkStatus: NetworkStatusService,
|
||||
) {
|
||||
return async () => {
|
||||
const statusElement = document.querySelector('#init-status');
|
||||
statusElement.innerHTML = 'Konfigurationen werden geladen...';
|
||||
await config.init();
|
||||
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
|
||||
await auth.init();
|
||||
const laoderElement = document.querySelector('#init-loader');
|
||||
|
||||
if (auth.isAuthenticated()) {
|
||||
statusElement.innerHTML = 'App wird initialisiert...';
|
||||
const state = injector.get(RootStateService);
|
||||
await state.init();
|
||||
try {
|
||||
let online = false;
|
||||
|
||||
while (!online) {
|
||||
online = await firstValueFrom(networkStatus.online$);
|
||||
|
||||
if (!online) {
|
||||
statusElement.innerHTML =
|
||||
'<b>Warte auf Netzwerkverbindung (WLAN)</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br>Sobald eine Netzwerkverbindung besteht, wird die App automatisch neu geladen.';
|
||||
await new Promise((resolve) => setTimeout(resolve, 250));
|
||||
}
|
||||
}
|
||||
|
||||
statusElement.innerHTML = 'Konfigurationen werden geladen...';
|
||||
await config.init();
|
||||
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
|
||||
await auth.init();
|
||||
|
||||
if (auth.isAuthenticated()) {
|
||||
statusElement.innerHTML = 'App wird initialisiert...';
|
||||
const state = injector.get(RootStateService);
|
||||
await state.init();
|
||||
}
|
||||
|
||||
statusElement.innerHTML = 'Native Container wird initialisiert...';
|
||||
await nativeContainer.init();
|
||||
|
||||
statusElement.innerHTML = 'Scanner wird initialisiert...';
|
||||
await scanAdapter.init();
|
||||
} catch (error) {
|
||||
laoderElement.remove();
|
||||
statusElement.classList.add('text-xl');
|
||||
statusElement.innerHTML = '<b>Fehler bei der Initialisierung</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br><br>';
|
||||
|
||||
const reload = document.createElement('button');
|
||||
reload.classList.add('bg-brand', 'text-white', 'p-2', 'rounded', 'cursor-pointer');
|
||||
reload.innerHTML = 'App neu laden';
|
||||
reload.onclick = () => window.location.reload();
|
||||
statusElement.appendChild(reload);
|
||||
|
||||
const preLabel = document.createElement('div');
|
||||
preLabel.classList.add('mt-12');
|
||||
preLabel.innerHTML = 'Fehlermeldung:';
|
||||
|
||||
statusElement.appendChild(preLabel);
|
||||
|
||||
const pre = document.createElement('pre');
|
||||
pre.classList.add('mt-4', 'text-wrap');
|
||||
pre.innerHTML = error.message;
|
||||
|
||||
statusElement.appendChild(pre);
|
||||
|
||||
console.error('Error during app initialization', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
statusElement.innerHTML = 'Native Container wird initialisiert...';
|
||||
await nativeContainer.init();
|
||||
|
||||
statusElement.innerHTML = 'Scanner wird initialisiert...';
|
||||
await scanAdapter.init();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,13 +153,14 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
ScanditScanAdapterModule.forRoot(),
|
||||
PlatformModule,
|
||||
IconModule.forRoot(),
|
||||
NgIconsModule.withIcons({ matWifiOff, matClose, matWifi }),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: _appInitializerFactory,
|
||||
multi: true,
|
||||
deps: [Config, AuthService, Injector, ScanAdapterService, NativeContainerService],
|
||||
deps: [Config, AuthService, Injector, ScanAdapterService, NativeContainerService, NetworkStatusService],
|
||||
},
|
||||
{
|
||||
provide: NOTIFICATIONS_HUB_OPTIONS,
|
||||
|
||||
@@ -2,30 +2,31 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
|
||||
import { NEVER, Observable, throwError } from 'rxjs';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { catchError, mergeMap, tap } from 'rxjs/operators';
|
||||
import { catchError, filter, mergeMap, takeUntil, tap } from 'rxjs/operators';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { IsaLogProvider } from '../providers';
|
||||
import { LogLevel } from '@core/logger';
|
||||
import { injectOnline$ } from '../services/network-status.service';
|
||||
|
||||
@Injectable()
|
||||
export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
constructor(private _modal: UiModalService, private _auth: AuthService, private _isaLogProvider: IsaLogProvider) {}
|
||||
readonly offline$ = injectOnline$().pipe(filter((online) => !online));
|
||||
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
private _auth: AuthService,
|
||||
private _isaLogProvider: IsaLogProvider,
|
||||
) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req).pipe(catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)));
|
||||
return next.handle(req).pipe(
|
||||
takeUntil(this.offline$),
|
||||
catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)),
|
||||
);
|
||||
}
|
||||
|
||||
handleError(error: HttpErrorResponse): Observable<any> {
|
||||
if (error.status === 0) {
|
||||
return this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Sie sind offline, keine Verbindung zum Netzwerk',
|
||||
data: { message: 'Bereits geladene Inhalte werden angezeigt. Interaktionen sind aktuell nicht möglich.' },
|
||||
})
|
||||
.afterClosed$.pipe(mergeMap(() => throwError(error)));
|
||||
} else if (error.status === 401) {
|
||||
console.log('401', error);
|
||||
if (error.status === 401) {
|
||||
return this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
@@ -36,11 +37,13 @@ export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
tap(() => {
|
||||
this._auth.login();
|
||||
}),
|
||||
mergeMap(() => NEVER)
|
||||
mergeMap(() => NEVER),
|
||||
);
|
||||
}
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Http Error', error);
|
||||
if (!error.url.endsWith('/isa/logging')) {
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Http Error', error);
|
||||
}
|
||||
|
||||
return throwError(error);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ import { LogLevel } from '@core/logger';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IsaErrorHandler implements ErrorHandler {
|
||||
constructor(private _modal: UiModalService, private _authService: AuthService, private _isaLogProvider: IsaLogProvider) {}
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
private _authService: AuthService,
|
||||
private _isaLogProvider: IsaLogProvider,
|
||||
) {}
|
||||
|
||||
async handleError(error: any): Promise<void> {
|
||||
console.error(error);
|
||||
@@ -37,13 +41,13 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Client Error', error);
|
||||
|
||||
this._modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
title:
|
||||
!navigator.onLine || (error instanceof HttpErrorResponse && error?.status === 0)
|
||||
? 'Sie sind offline, keine Verbindung zum Netzwerk'
|
||||
: 'Unbekannter Fehler',
|
||||
data: error,
|
||||
});
|
||||
// this._modal.open({
|
||||
// content: UiErrorModalComponent,
|
||||
// title:
|
||||
// !navigator.onLine || (error instanceof HttpErrorResponse && error?.status === 0)
|
||||
// ? 'Sie sind offline, keine Verbindung zum Netzwerk'
|
||||
// : 'Unbekannter Fehler',
|
||||
// data: error,
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
25
apps/isa-app/src/app/services/network-status.service.ts
Normal file
25
apps/isa-app/src/app/services/network-status.service.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { map, Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class NetworkStatusService {
|
||||
online$ = new Observable<boolean>((subscriber) => {
|
||||
const handler = () => subscriber.next(navigator.onLine);
|
||||
|
||||
window.addEventListener('online', handler);
|
||||
window.addEventListener('offline', handler);
|
||||
|
||||
handler();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handler);
|
||||
window.removeEventListener('offline', handler);
|
||||
};
|
||||
});
|
||||
|
||||
status$ = this.online$.pipe(map((online) => (online ? 'online' : 'offline')));
|
||||
}
|
||||
|
||||
export const injectNetworkStatus$ = () => inject(NetworkStatusService).status$;
|
||||
|
||||
export const injectOnline$ = () => inject(NetworkStatusService).online$;
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
},
|
||||
"checkForUpdates": 900000,
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
},
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
|
||||
"scandit": "AZ70hgtZLmFWHbYP+BDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YfzKaR2xbfjsImjXJN7C/TE1CYXsv3DeqIzgDRA/kII4LzqoICWm9mnwejER1kMu1Vf+1NWbwUuYJjsQVQXYkwkWgnv53obevBZtjpgZqIzlStufIU+sPNEWSd9BlrLmiSEu6nXC5lbstyPJVaqihsFoBPNJ0Q+IgJUjqaxhTf6hGtNB3Rqcpv0ZT349NdK9mQ+9lAQYCwXdmYbELdlbZVSja0aJQlkf0TaAjixDbNcR32/VMIw98F2YxVRVWPnADUaJ9Bn8oa7NkOJIUfB7VAg1KtUl8IIwSXvRk436UtPZGxg+PQM7UXX/TgiBuqCQWdUChoH/QAoY2fyKRKgXJHnGdt+pBRQ2OYs7TZWCJLZsLKgnHV+eWh3Y3EB1hN9cmcszdnmYiyD1gCY8pSETFbWV01nVPvIuUTc+jMipv3NN81VQDS5/eU1VJNpRuDiufdBKJv0jjHQk+hTdqe+2GSmMD24ZG8FQXJZZPBCodWR1A81T8toCaIAGHnOfZeO7nxZu9Uo+6ohrVxc5F4szSizra+M3zfuFnm5FGgxCKt5uibLYgRyNcmR8/9vqTfMZgalTklbKHEFw1i+xOwrFrqRMh7FoovDRoI3QuaHLpV+ZUpZo3zTOQofdXPthKAmSLc11cjn509oTnnXUBBoBn/hErrwC/v8dZep1YEKM3wjfuIFsHRrsECswqNjwN/yOZCirm2VvFJEPMuKIwy+8jdNGncm8v4R9Br3c+wYJ8696L7Dg0iFShoyoP4OqIvBNY+dYyy935yGkIF2KI6l01xQZcWoHSVKPd6/78Iwy4lTKhtOqtu7ETJqOE53gQpR9jaAY0RiBB1SLm+Jbwt3ipSJiCDgKmkVmk5AU9HC0XYR/erjg13HF4hIcpLPW1ZWMKSxTqZ8z8FMJilInBgfcBwvjNE5seWvdFKltvlItnGhSh7BLUZ5UInDhl00NJBE2PdstDvRvQjLKvYUoFly3jONVsUfuQpzrcjT0g6gEgL8ZtlmaF11owcCvqhNDxWuTAJTX/xXf6WehxEB0qy3xCdxahbuneS3DPI0z2kAQwrx19i5r+RKQ8bWDaQ/OGY47sLPJgDgijEz16CvAMBSN0PvOso6FstsT/ynR3LLTPcY0QDw21Dv3wNHNa9HbanJcb3/MuhZGlrKp7"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "AVljxT/dG+TAIDDL2jTxm843juR2OtZ6lHLxRpYR7x9uYiSvY2IAHdRx8tjsf9KU7wK0F5cAeb/nLMHF6Vor9ps79wvuBQw6G3N0IW978b78ZUgPOFzxHUAMuD8dbkDZlX8r9y1cOd9sT3UNEwGrQ4siUt2oCkigyTxJAgYs1ijnjQid7q42hHk3tMXywrAYeu5MhF0TV1H77DRDMxPHD/xiR0zhFQRB2Dtnm1+e3LHKCyQjZ/zknEpQB6HS7UbCBoEDj4tohb83E6oqmQFWwt85/Jk9f49gxXakIcNODnQI5H63kSqpEmV9Al1a5L+WGZ6Bq1gwBbnD8FBXlVqxoooiFXW7jzzBa9LNmQiQ5J8yEkIsPeyOHec7F4ERvVONSMYwWyH39ZweSiRsZRM1UsFPhN96bCT5MEwkjPFn4gji6TPGEceJZvV3HwsiCT5Bgjla4bvDsZ2jYvAr9tSij8kIii9dHvsWlrimt+szHJLSz+8uNI6jAvXyr2f3oRxZD/F9osZHVWkgtAc+vVWqkxVJCqmpmoHOXI6TFSqSjYHddhZyU5r2lgQt0+NI6k/bV3iN7Le1RJCP/wuSDCTZjzsU1igB7UnIN2Y70CqCjIeVH9qlxaI1YAC9lwFv1FZvsiueYeJP1n39mmXCSELVtzxgIBEX5yaIHNbbGXd+e8JUgcO8vJ2JA2kJudaU+xfYR5SY//+J1kPsNSbnBnM25LL+LjeRB3QTfqV5sFq8ORWcIMITvkEaRfP3PVcOzb+hO4Ren4ezhJuyADulmvG8a9Kxxk6ymzBbE7a93SGVbxp7OQNEmvTn5+B9wJ7/l1mtvZL2TilrDZBQVMYWrGuUGpA="
|
||||
"scandit": "AZ70hgtZLmFWHbYP+BDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YfzKaR2xbfjsImjXJN7C/TE1CYXsv3DeqIzgDRA/kII4LzqoICWm9mnwejER1kMu1Vf+1NWbwUuYJjsQVQXYkwkWgnv53obevBZtjpgZqIzlStufIU+sPNEWSd9BlrLmiSEu6nXC5lbstyPJVaqihsFoBPNJ0Q+IgJUjqaxhTf6hGtNB3Rqcpv0ZT349NdK9mQ+9lAQYCwXdmYbELdlbZVSja0aJQlkf0TaAjixDbNcR32/VMIw98F2YxVRVWPnADUaJ9Bn8oa7NkOJIUfB7VAg1KtUl8IIwSXvRk436UtPZGxg+PQM7UXX/TgiBuqCQWdUChoH/QAoY2fyKRKgXJHnGdt+pBRQ2OYs7TZWCJLZsLKgnHV+eWh3Y3EB1hN9cmcszdnmYiyD1gCY8pSETFbWV01nVPvIuUTc+jMipv3NN81VQDS5/eU1VJNpRuDiufdBKJv0jjHQk+hTdqe+2GSmMD24ZG8FQXJZZPBCodWR1A81T8toCaIAGHnOfZeO7nxZu9Uo+6ohrVxc5F4szSizra+M3zfuFnm5FGgxCKt5uibLYgRyNcmR8/9vqTfMZgalTklbKHEFw1i+xOwrFrqRMh7FoovDRoI3QuaHLpV+ZUpZo3zTOQofdXPthKAmSLc11cjn509oTnnXUBBoBn/hErrwC/v8dZep1YEKM3wjfuIFsHRrsECswqNjwN/yOZCirm2VvFJEPMuKIwy+8jdNGncm8v4R9Br3c+wYJ8696L7Dg0iFShoyoP4OqIvBNY+dYyy935yGkIF2KI6l01xQZcWoHSVKPd6/78Iwy4lTKhtOqtu7ETJqOE53gQpR9jaAY0RiBB1SLm+Jbwt3ipSJiCDgKmkVmk5AU9HC0XYR/erjg13HF4hIcpLPW1ZWMKSxTqZ8z8FMJilInBgfcBwvjNE5seWvdFKltvlItnGhSh7BLUZ5UInDhl00NJBE2PdstDvRvQjLKvYUoFly3jONVsUfuQpzrcjT0g6gEgL8ZtlmaF11owcCvqhNDxWuTAJTX/xXf6WehxEB0qy3xCdxahbuneS3DPI0z2kAQwrx19i5r+RKQ8bWDaQ/OGY47sLPJgDgijEz16CvAMBSN0PvOso6FstsT/ynR3LLTPcY0QDw21Dv3wNHNa9HbanJcb3/MuhZGlrKp7"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
},
|
||||
"checkForUpdates": 3600000,
|
||||
"licence": {
|
||||
"scandit": "Ae7z0WDdRDFqG6oYuAXzesYGJpDLDqt+xWtQHOESiOjaSkB7IEIDJAk534U+cg1zGnk++4hOEK9hXEGR01NLTjh76w1fDL0U63OUjo50EHBXIUvzAVSur3pRY+1ER7SvSEWaT0hDOLYvYrTpdECtt1graN9yMvJzXD38VJKUfssT92p+YENV2Hul3eXIvaVjHqXE/yvupF+MlOMMUMhX0/Km/yTU9H9SjBdsXYihZmYWbt2JotO3Zs1ojXb0+3La10xb01S1q0XdDN6El3XMVilEtdmrP3WoGois8vpQBvOCEvduxCfILFAqjeWXTZvXSut9u+kQKpK8uHW4rVV6iVClpZfPYqKJqTh78AI9gpnfb/zO9GfQEDS3g7wI5WbQKqaNRzhTVowFRri4Ep9R5TRC1bnd00RC4zVaMkbu5kBOA7YoRjgUiYWHKJpi/VokZWyN6u1lsi5mTUbQkm1ZWfX5I/iUVYBgyHZYl+8kfFkwLPXGZNrF4xqubjKiCZRQj0oyNjHOBeHqvAekzhk7scX2g/NN+liRQv4ur413b+uXacSiiYIrLhtGgzrz1KRrtu19uB5odk3LoerDoiYXat7wEg9zUYT/+uBfO2X+uS7L5LW0PMI3hV+joQVpDk5SlA2868Nx0KWtPWmMf7xCuFIhDskfBsXZNRTblqxkk0RzzSqtjx9ihGr+/Tuzm8Pm0s4OQqV7b+++/Zn+Vo4rCqMTwutjOO7dqhah5hbOT1MqY/6VcjCXyDad3BXXr+WYU4GtYTe8Ytjkm/ZTG3fImoDbMchEcqnCw3oxG5e/gkdurE8g/mZlFOtzAN7KkqIsg6qLaC5COjfLPXsi/A=="
|
||||
"scandit": "Ac2kvx5ZOzjvFl/LuAd6wds3C30YJ4g8Cm6PX4sgUnKPePVMuH+rFQIyVNn1YdS3myORojEOBsIZWhMw2nRUGBtOnQ5FO+cRHgQu0pkP+VG6OYvt8ETUTn8Aa2f9bmfqclO3LI8WN8psWr+adkZEtqNTvCgyDLZaICh8S7RfmwJVVWaOPX4LDagGhLDPS2YQdg+ibpR0l0ZlX2h/3GttofE64HOlBN3QtDB8yihHJNgVcUVy2UQVS+BXOyvIfZEFbFgPYVt5HZ0aQgcISlYVAmJvRsHyeKYRUnIi3ZN73EQmHzWcOV4/HWVoTs1MTW0mxV074vNwqExYW9LYmz9zgLUD7FMnKMFClkzRBHRN1CC7brosCnFkZWQp3CV4Ua48Fn9GW0Zpn/4MIZgzSU26inl5ZnT3dCc2+3BH3Us1uugTUrOPwFuwsPpr4NZYXWJMOmcm4kBBVJd0Uwk28GyZM7x1hXADcQgcc23+gDtbbUZWVmr3TE7GBcV3j+XUeOk8nHZw3DsYJ46MT2sSfks3QXRl4tBkBad6M+UxW4tb2IQC/4K9IXKhDN8VYXpzrrRHJNvOiH5+NrixthDZHHd/MGNuBDrJFOuXq8L7O6PxBoVZ0NPXmCO5vKyYdhBz5gJ5u3vNkSke7p+cDcvFicsVyNWRaaoFL0UN9gxFVMY8hkJKBZyGxZL2LEDXD44PxPsDnygpDC1Jyc7pkhEA0fKSc9aXZb1HDpwe1hqyyRVBtHkgPiN78GmJImbaUlj8XsK3yVyAjs2XvPR0/3ASHS53ViwzWKL3Oi8I515IJDtDhlvrkeh7MrWMaEu9k6ZQXJi1uJsh3JGfyS+yDlPjePVdsVqnZ4uw9pKMXvjlJeNcIiGM2Cf5S89nk2Qe/56MrKB9Frm6Q5wQHai8TNdCZYBi67dmAX8KHPXvEU4K6KyyW89YGcabZQ3eOJDr4oqW9ZcYPBttREdH3WI/HxvpEq6bqoDhT9AxpIWEMVb6y/DcDHSEresepibug4qOr9xOPq0yk2uiWYhPubFnCk7thQCXOGv9crWnQoOrt9c1qoaWXM4YmKqfcaQ67Tn+uFQmYTZyqw4jlJU4GgKe5/GNCVQM5aNUg1J0Px1NlFCS+rrIDyMQp0byFcgTd/E9sA5d1+YZKHKmJiQwEAz6oU9yyoUlxntSI42GHB/UttPc7Hj14V5+oJ+Yz+CZodmkXFg57Vx4NuxveNtO"
|
||||
},
|
||||
"gender": {
|
||||
"0": "Keine Anrede",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -15,7 +15,7 @@
|
||||
<app-root>
|
||||
<div class="grid place-items-center h-screen">
|
||||
<div class="grid grid-flow-row gap-4 items-center justify-center">
|
||||
<div class="flex flex-col items-center">
|
||||
<div id="init-loader" class="flex flex-col items-center">
|
||||
<img class="animate-spin" src="/assets/images/spinner.svg" alt="spinner animation" />
|
||||
</div>
|
||||
<div id="init-status" class="text-center">App wird geladen</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
@@ -57,4 +57,3 @@ import 'zone.js'; // Included with Angular CLI.
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
import 'hammerjs';
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
Observable,
|
||||
of,
|
||||
BehaviorSubject,
|
||||
Subject,
|
||||
merge,
|
||||
combineLatest,
|
||||
} from 'rxjs';
|
||||
import { Observable, of, BehaviorSubject, Subject, merge, combineLatest } from 'rxjs';
|
||||
import { delay, distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { createRandomStringId, createRandomId } from './util';
|
||||
import {
|
||||
product,
|
||||
removeSourcePriceAndProductFromProduct,
|
||||
} from './data/product';
|
||||
import { product, removeSourcePriceAndProductFromProduct } from './data/product';
|
||||
import { RemissionService } from '../services/remission.service';
|
||||
import { RemissionProcess } from '../models/remission-process';
|
||||
import { RemissionProduct } from '../models/remission-product';
|
||||
import {
|
||||
remissionProducts,
|
||||
createRemissionProduct,
|
||||
} from './data/remission-products';
|
||||
import { remissionProducts, createRemissionProduct } from './data/remission-products';
|
||||
import { remissionProcessTemplateMock } from './data/remission-process-template';
|
||||
import { ShippingDocument } from '../models/shipping-document';
|
||||
import { shippingDocument } from './data/shipping-document';
|
||||
@@ -41,33 +28,21 @@ import { ActionResult, CapacityType } from '../models';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MockRemissionService extends RemissionService {
|
||||
private delayInMs = 100;
|
||||
private remissionSubjectStore = new Map<
|
||||
number,
|
||||
BehaviorSubject<RemissionProcess>
|
||||
>();
|
||||
private remissionSubjectStore = new Map<number, BehaviorSubject<RemissionProcess>>();
|
||||
private remissionSubjectIdRef = new Map<number, number>();
|
||||
|
||||
private reloadProductsSubject = new Subject();
|
||||
private productSubject = new BehaviorSubject<RemissionProduct[]>(
|
||||
remissionProducts
|
||||
);
|
||||
private reloadProductsSubject = new Subject<void>();
|
||||
private productSubject = new BehaviorSubject<RemissionProduct[]>(remissionProducts);
|
||||
|
||||
private getRemissionFromStore = (
|
||||
remissionProcessId: number
|
||||
): BehaviorSubject<RemissionProcess> => {
|
||||
private getRemissionFromStore = (remissionProcessId: number): BehaviorSubject<RemissionProcess> => {
|
||||
const remission =
|
||||
this.remissionSubjectStore.get(remissionProcessId) ||
|
||||
this.remissionSubjectStore.get(
|
||||
this.remissionSubjectIdRef.get(remissionProcessId)
|
||||
);
|
||||
this.remissionSubjectStore.get(this.remissionSubjectIdRef.get(remissionProcessId));
|
||||
|
||||
return remission;
|
||||
};
|
||||
|
||||
private saveRemissionInStore = (
|
||||
remissionProcessId: number,
|
||||
remissionSubject: BehaviorSubject<RemissionProcess>
|
||||
) => {
|
||||
private saveRemissionInStore = (remissionProcessId: number, remissionSubject: BehaviorSubject<RemissionProcess>) => {
|
||||
this.remissionSubjectStore.set(remissionProcessId, remissionSubject);
|
||||
};
|
||||
|
||||
@@ -80,27 +55,20 @@ export class MockRemissionService extends RemissionService {
|
||||
return processSubject.pipe(distinctUntilChanged(), delay(this.delayInMs));
|
||||
}
|
||||
|
||||
getRemissionProducts(params: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<{
|
||||
getRemissionProducts(params: { remissionProcessId: number }): Observable<{
|
||||
skip?: number;
|
||||
take?: number;
|
||||
hits?: number;
|
||||
items: RemissionProduct[];
|
||||
completed: boolean;
|
||||
}> {
|
||||
const processSubject = this.getRemissionFromStore(
|
||||
params.remissionProcessId
|
||||
);
|
||||
const processSubject = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!processSubject) {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
}
|
||||
|
||||
const process$ = merge(
|
||||
processSubject,
|
||||
this.reloadProductsSubject.pipe(map((_) => processSubject.value))
|
||||
);
|
||||
const process$ = merge(processSubject, this.reloadProductsSubject.pipe(map((_) => processSubject.value)));
|
||||
|
||||
const products$ = this.productSubject.asObservable();
|
||||
|
||||
@@ -126,24 +94,19 @@ export class MockRemissionService extends RemissionService {
|
||||
const productGroupFilter = filter.filter['421228ce'] || [];
|
||||
|
||||
if (productGroupFilter.length > 0) {
|
||||
result.items = result.items.filter((product) =>
|
||||
productGroupFilter.some((s) => product.productGroup === s)
|
||||
);
|
||||
result.items = result.items.filter((product) => productGroupFilter.some((s) => product.productGroup === s));
|
||||
}
|
||||
|
||||
const actionFilter = filter.filter['ce9ee0be'] || [];
|
||||
|
||||
result.hits = result.items.length;
|
||||
|
||||
result.items = result.items.slice(
|
||||
result.skip,
|
||||
result.skip + result.take
|
||||
);
|
||||
result.items = result.items.slice(result.skip, result.skip + result.take);
|
||||
|
||||
result.completed = result.skip + result.take >= result.hits;
|
||||
|
||||
return result;
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,9 +116,7 @@ export class MockRemissionService extends RemissionService {
|
||||
createShippingDocument?: boolean;
|
||||
};
|
||||
}): Observable<RemissionProcess> {
|
||||
const processSubject = this.remissionSubjectStore.get(
|
||||
params.remissionProcessId
|
||||
);
|
||||
const processSubject = this.remissionSubjectStore.get(params.remissionProcessId);
|
||||
|
||||
if (!processSubject) {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
@@ -214,12 +175,8 @@ export class MockRemissionService extends RemissionService {
|
||||
}
|
||||
}
|
||||
|
||||
completeRemission(params: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<boolean> {
|
||||
let processSubject = this.remissionSubjectStore.get(
|
||||
params.remissionProcessId
|
||||
);
|
||||
completeRemission(params: { remissionProcessId: number }): Observable<boolean> {
|
||||
let processSubject = this.remissionSubjectStore.get(params.remissionProcessId);
|
||||
|
||||
if (!processSubject) {
|
||||
const refId = this.remissionSubjectIdRef.get(params.remissionProcessId);
|
||||
@@ -236,9 +193,7 @@ export class MockRemissionService extends RemissionService {
|
||||
}
|
||||
}
|
||||
|
||||
getRemission(params: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<RemissionProcess> {
|
||||
getRemission(params: { remissionProcessId: number }): Observable<RemissionProcess> {
|
||||
const remission = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!remission) {
|
||||
@@ -248,11 +203,7 @@ export class MockRemissionService extends RemissionService {
|
||||
return remission.pipe(distinctUntilChanged(), delay(this.delayInMs));
|
||||
}
|
||||
|
||||
isPrintingRequired({
|
||||
remissionProcessId,
|
||||
}: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<boolean> {
|
||||
isPrintingRequired({ remissionProcessId }: { remissionProcessId: number }): Observable<boolean> {
|
||||
const remission = this.getRemissionFromStore(remissionProcessId);
|
||||
|
||||
if (!remission) {
|
||||
@@ -272,10 +223,7 @@ export class MockRemissionService extends RemissionService {
|
||||
return of(printers).pipe(delay(this.delayInMs));
|
||||
}
|
||||
|
||||
printRemissionList(params: {
|
||||
remissionProcessId: number;
|
||||
printerKey: string;
|
||||
}): Observable<boolean> {
|
||||
printRemissionList(params: { remissionProcessId: number; printerKey: string }): Observable<boolean> {
|
||||
const remission = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!remission) {
|
||||
@@ -285,11 +233,7 @@ export class MockRemissionService extends RemissionService {
|
||||
return of(true).pipe(delay(this.delayInMs));
|
||||
}
|
||||
|
||||
printShippingDocument(params: {
|
||||
remissionProcessId: number;
|
||||
shippingDocumentId?: number;
|
||||
printerKey: string;
|
||||
}): Observable<boolean> {
|
||||
printShippingDocument(params: { remissionProcessId: number; shippingDocumentId?: number; printerKey: string }): Observable<boolean> {
|
||||
const remission = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!remission || !params.printerKey) {
|
||||
@@ -307,8 +251,7 @@ export class MockRemissionService extends RemissionService {
|
||||
getFilters(params: { remissionProcessId: number }): Observable<Filter[]> {
|
||||
const remission = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
const filterForGivenSourceType =
|
||||
remission.value.filter.source === 'zentral' ? filter : filterUeberlauf;
|
||||
const filterForGivenSourceType = remission.value.filter.source === 'zentral' ? filter : filterUeberlauf;
|
||||
return of(filterForGivenSourceType).pipe(delay(this.delayInMs));
|
||||
}
|
||||
|
||||
@@ -327,38 +270,24 @@ export class MockRemissionService extends RemissionService {
|
||||
return of(remissionSources).pipe(delay(this.delayInMs));
|
||||
}
|
||||
|
||||
getShippingDocuments(params: {
|
||||
remissionProcessId: number;
|
||||
shippingDocumentId: number;
|
||||
}): Observable<ShippingDocument> {
|
||||
getShippingDocuments(params: { remissionProcessId: number; shippingDocumentId: number }): Observable<ShippingDocument> {
|
||||
const remissionSub = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
return remissionSub.pipe(
|
||||
map((remission) =>
|
||||
remission.shippingDocuments.find(
|
||||
(shippingDoc) => shippingDoc.id === params.shippingDocumentId
|
||||
)
|
||||
),
|
||||
map((remission) => remission.shippingDocuments.find((shippingDoc) => shippingDoc.id === params.shippingDocumentId)),
|
||||
distinctUntilChanged(),
|
||||
delay(this.delayInMs)
|
||||
delay(this.delayInMs),
|
||||
);
|
||||
}
|
||||
|
||||
deleteProductFromRemissionList(input: {
|
||||
remissionProcessId: number;
|
||||
remissionProductId: number;
|
||||
}): Observable<ActionResult<boolean>> {
|
||||
deleteProductFromRemissionList(input: { remissionProcessId: number; remissionProductId: number }): Observable<ActionResult<boolean>> {
|
||||
return of({
|
||||
error: false,
|
||||
result: true,
|
||||
});
|
||||
}
|
||||
|
||||
addProductToRemit(input: {
|
||||
product: Product;
|
||||
remissionReason: string;
|
||||
remissionQuantity: number;
|
||||
}): Observable<RemissionProduct> {
|
||||
addProductToRemit(input: { product: Product; remissionReason: string; remissionQuantity: number }): Observable<RemissionProduct> {
|
||||
const productToAdd = removeSourcePriceAndProductFromProduct({ ...product });
|
||||
|
||||
const newProduct = createRemissionProduct({
|
||||
@@ -382,9 +311,7 @@ export class MockRemissionService extends RemissionService {
|
||||
quantity?: number;
|
||||
inStock?: number;
|
||||
}): Observable<ActionResult<boolean>> {
|
||||
const processSubject = this.getRemissionFromStore(
|
||||
params.remissionProcessId
|
||||
);
|
||||
const processSubject = this.getRemissionFromStore(params.remissionProcessId);
|
||||
const process = processSubject.value;
|
||||
const products = this.productSubject.value;
|
||||
|
||||
@@ -392,22 +319,15 @@ export class MockRemissionService extends RemissionService {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
}
|
||||
|
||||
let productToAdd = products.find(
|
||||
(product) => product.id === params.remissionProductId
|
||||
);
|
||||
let newProducts = products.filter(
|
||||
(product) => product.id !== params.remissionProductId
|
||||
);
|
||||
let productToAdd = products.find((product) => product.id === params.remissionProductId);
|
||||
let newProducts = products.filter((product) => product.id !== params.remissionProductId);
|
||||
|
||||
if (!!params.placementType || !!params.quantity) {
|
||||
const productToUpdateInProductList = {
|
||||
...productToAdd,
|
||||
remissionQuantity: productToAdd.remissionQuantity - params.quantity,
|
||||
};
|
||||
newProducts =
|
||||
productToAdd.remissionQuantity - params.quantity < 1
|
||||
? newProducts
|
||||
: [...newProducts, productToUpdateInProductList];
|
||||
newProducts = productToAdd.remissionQuantity - params.quantity < 1 ? newProducts : [...newProducts, productToUpdateInProductList];
|
||||
|
||||
productToAdd = {
|
||||
...productToAdd,
|
||||
@@ -415,9 +335,7 @@ export class MockRemissionService extends RemissionService {
|
||||
};
|
||||
}
|
||||
|
||||
const shippingDocumentToUpdate = process.shippingDocuments.find(
|
||||
(document) => document.id === params.shippingDocumentId
|
||||
);
|
||||
const shippingDocumentToUpdate = process.shippingDocuments.find((document) => document.id === params.shippingDocumentId);
|
||||
const updatedShippingDocument = {
|
||||
...shippingDocumentToUpdate,
|
||||
products: [...shippingDocumentToUpdate.products, productToAdd],
|
||||
@@ -426,9 +344,7 @@ export class MockRemissionService extends RemissionService {
|
||||
const updatedProcess = {
|
||||
...process,
|
||||
shippingDocuments: [
|
||||
...process.shippingDocuments.filter(
|
||||
(document) => document.id !== params.shippingDocumentId
|
||||
),
|
||||
...process.shippingDocuments.filter((document) => document.id !== params.shippingDocumentId),
|
||||
updatedShippingDocument,
|
||||
],
|
||||
};
|
||||
@@ -448,9 +364,7 @@ export class MockRemissionService extends RemissionService {
|
||||
remissionProductId: number;
|
||||
placementType?: string;
|
||||
}): Observable<boolean> {
|
||||
const processSubject = this.getRemissionFromStore(
|
||||
params.remissionProcessId
|
||||
);
|
||||
const processSubject = this.getRemissionFromStore(params.remissionProcessId);
|
||||
const process = processSubject.value;
|
||||
const products = this.productSubject.value;
|
||||
|
||||
@@ -458,44 +372,30 @@ export class MockRemissionService extends RemissionService {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
}
|
||||
|
||||
const shippingDocumentToUpdate = process.shippingDocuments.find(
|
||||
(document) => document.id === params.shippingDocumentId
|
||||
);
|
||||
const shippingDocumentToUpdate = process.shippingDocuments.find((document) => document.id === params.shippingDocumentId);
|
||||
const updatedShippingDocument = {
|
||||
...shippingDocumentToUpdate,
|
||||
products: shippingDocumentToUpdate.products.filter(
|
||||
(product) => product.id !== params.remissionProductId
|
||||
),
|
||||
products: shippingDocumentToUpdate.products.filter((product) => product.id !== params.remissionProductId),
|
||||
};
|
||||
const productToRemove = shippingDocumentToUpdate.products.find(
|
||||
(product) => product.id === params.remissionProductId
|
||||
);
|
||||
const productInProductList = products.find(
|
||||
(product) => product.id === params.remissionProductId
|
||||
);
|
||||
const productToRemove = shippingDocumentToUpdate.products.find((product) => product.id === params.remissionProductId);
|
||||
const productInProductList = products.find((product) => product.id === params.remissionProductId);
|
||||
const updatedProductInProductList = !!productInProductList
|
||||
? {
|
||||
...productInProductList,
|
||||
remissionQuantity:
|
||||
Number(productInProductList.remissionQuantity) +
|
||||
Number(productToRemove.remissionQuantity),
|
||||
remissionQuantity: Number(productInProductList.remissionQuantity) + Number(productToRemove.remissionQuantity),
|
||||
}
|
||||
: productToRemove;
|
||||
|
||||
const updatedProcess = {
|
||||
...process,
|
||||
shippingDocuments: [
|
||||
...process.shippingDocuments.filter(
|
||||
(document) => document.id !== params.shippingDocumentId
|
||||
),
|
||||
...process.shippingDocuments.filter((document) => document.id !== params.shippingDocumentId),
|
||||
updatedShippingDocument,
|
||||
],
|
||||
};
|
||||
|
||||
const updatedProducts = [
|
||||
...this.productSubject.value.filter(
|
||||
(product) => product.id !== updatedProductInProductList.id
|
||||
),
|
||||
...this.productSubject.value.filter((product) => product.id !== updatedProductInProductList.id),
|
||||
updatedProductInProductList,
|
||||
];
|
||||
|
||||
@@ -509,12 +409,8 @@ export class MockRemissionService extends RemissionService {
|
||||
this.reloadProductsSubject.next();
|
||||
}
|
||||
|
||||
createShippingDocument(params: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<ShippingDocument> {
|
||||
const processSubject = this.getRemissionFromStore(
|
||||
params.remissionProcessId
|
||||
);
|
||||
createShippingDocument(params: { remissionProcessId: number }): Observable<ShippingDocument> {
|
||||
const processSubject = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!processSubject) {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
@@ -526,10 +422,7 @@ export class MockRemissionService extends RemissionService {
|
||||
id: createRandomId(),
|
||||
shippingDocumentNumber: createRandomStringId(),
|
||||
};
|
||||
const updatedShippingDocuments = [
|
||||
...process.shippingDocuments,
|
||||
newShippingDocument,
|
||||
];
|
||||
const updatedShippingDocuments = [...process.shippingDocuments, newShippingDocument];
|
||||
const updatedProcess = {
|
||||
...process,
|
||||
shippingDocuments: updatedShippingDocuments,
|
||||
@@ -545,18 +438,14 @@ export class MockRemissionService extends RemissionService {
|
||||
shippingDocumentId: number;
|
||||
containerId: string;
|
||||
}): Observable<ActionResult<boolean>> {
|
||||
const processSubject = this.getRemissionFromStore(
|
||||
params.remissionProcessId
|
||||
);
|
||||
const processSubject = this.getRemissionFromStore(params.remissionProcessId);
|
||||
|
||||
if (!processSubject) {
|
||||
throw new Error('Prozess nicht gefunden');
|
||||
}
|
||||
|
||||
const process = processSubject.value;
|
||||
const shippingContainerToUpdate = process.shippingDocuments.find(
|
||||
(document) => document.id === params.shippingDocumentId
|
||||
);
|
||||
const shippingContainerToUpdate = process.shippingDocuments.find((document) => document.id === params.shippingDocumentId);
|
||||
const updatedShippingContainer = {
|
||||
...shippingContainerToUpdate,
|
||||
isCompleted: true,
|
||||
@@ -564,9 +453,7 @@ export class MockRemissionService extends RemissionService {
|
||||
const updatedProcess = {
|
||||
...process,
|
||||
shippingDocuments: [
|
||||
...process.shippingDocuments.filter(
|
||||
(document) => document.id !== params.shippingDocumentId
|
||||
),
|
||||
...process.shippingDocuments.filter((document) => document.id !== params.shippingDocumentId),
|
||||
updatedShippingContainer,
|
||||
],
|
||||
};
|
||||
@@ -579,9 +466,7 @@ export class MockRemissionService extends RemissionService {
|
||||
}).pipe(delay(this.delayInMs));
|
||||
}
|
||||
|
||||
completeRemissions(params: {
|
||||
remissionProcessId: number;
|
||||
}): Observable<boolean> {
|
||||
completeRemissions(params: { remissionProcessId: number }): Observable<boolean> {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -605,9 +490,7 @@ export class MockRemissionService extends RemissionService {
|
||||
return of(true);
|
||||
}
|
||||
|
||||
deleteShippingDocument(): Observable<
|
||||
ActionResult<{ deleted: boolean; completedRemissionsExist: boolean }>
|
||||
> {
|
||||
deleteShippingDocument(): Observable<ActionResult<{ deleted: boolean; completedRemissionsExist: boolean }>> {
|
||||
return of({
|
||||
result: {
|
||||
deleted: true,
|
||||
@@ -620,10 +503,7 @@ export class MockRemissionService extends RemissionService {
|
||||
return;
|
||||
}
|
||||
|
||||
getCapacities(params: {
|
||||
selectedFilters?: { [filterId: string]: string[] };
|
||||
supplierId: number;
|
||||
}): Observable<CapacityType[]> {
|
||||
getCapacities(params: { selectedFilters?: { [filterId: string]: string[] }; supplierId: number }): Observable<CapacityType[]> {
|
||||
return of([]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,11 @@
|
||||
</div>
|
||||
|
||||
<div class="branch-actions">
|
||||
<button *ngIf="(branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0" class="cta-reserve" (click)="reserve(branch)">
|
||||
<button
|
||||
*ngIf="(branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0 && branch?.isShippingEnabled"
|
||||
class="cta-reserve"
|
||||
(click)="reserve(branch)"
|
||||
>
|
||||
Reservieren
|
||||
</button>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class ModalAvailabilitiesComponent {
|
||||
(branch) =>
|
||||
branch &&
|
||||
branch?.isOnline &&
|
||||
branch?.isShippingEnabled &&
|
||||
// branch?.isShippingEnabled && ------ Rausgenommen aufgrund des Tickets #4712
|
||||
branch?.isOrderingEnabled &&
|
||||
branch?.id !== userbranch?.id &&
|
||||
branch?.branchType === 1
|
||||
|
||||
@@ -58,11 +58,11 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
this.store.isDownloadAvailabilityAvailable$,
|
||||
]).pipe(
|
||||
map((values) => values.some((v) => v)),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
showDeliveryTruck$ = combineLatest([this.store.isDeliveryAvailabilityAvailable$, this.store.isDeliveryDigAvailabilityAvailable$]).pipe(
|
||||
map(([delivery, digDelivery]) => delivery || digDelivery)
|
||||
map(([delivery, digDelivery]) => delivery || digDelivery),
|
||||
);
|
||||
|
||||
showDeliveryB2BTruck$ = combineLatest([
|
||||
@@ -71,7 +71,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
]).pipe(map(([digDelivery, b2bDelivery]) => b2bDelivery && !digDelivery));
|
||||
|
||||
customerFeatures$ = this.applicationService.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this._domainCheckoutService.getCustomerFeatures({ processId }))
|
||||
switchMap((processId) => this._domainCheckoutService.getCustomerFeatures({ processId })),
|
||||
);
|
||||
|
||||
showSubscriptionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'PFO')));
|
||||
@@ -80,13 +80,13 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
promotionPoints$ = this.store.item$.pipe(map((item) => item?.redemptionPoints));
|
||||
|
||||
showPromotionBadge$ = combineLatest([this.hasPromotionFeature$, this.promotionPoints$]).pipe(
|
||||
map(([hasPromotionFeature, promotionPoints]) => hasPromotionFeature && promotionPoints > 0)
|
||||
map(([hasPromotionFeature, promotionPoints]) => hasPromotionFeature && promotionPoints > 0),
|
||||
);
|
||||
|
||||
showArchivBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'ARC')));
|
||||
|
||||
isBadgeVisible$ = combineLatest([this.showSubscriptionBadge$, this.showPromotionBadge$, this.showArchivBadge$]).pipe(
|
||||
map(([showSubscriptionBadge, showPromotionBadge, showArchivBadge]) => showSubscriptionBadge || showPromotionBadge || showArchivBadge)
|
||||
map(([showSubscriptionBadge, showPromotionBadge, showArchivBadge]) => showSubscriptionBadge || showPromotionBadge || showArchivBadge),
|
||||
);
|
||||
|
||||
contributors$ = this.store.item$.pipe(map((item) => item?.product?.contributors?.split(';').map((m) => m.trim())));
|
||||
@@ -101,11 +101,11 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
return this._datePipe.transform(date, 'dd. MMMM y');
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
selectedBranchId$ = this.applicationService.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.applicationService.getSelectedBranch$(processId))
|
||||
switchMap((processId) => this.applicationService.getSelectedBranch$(processId)),
|
||||
);
|
||||
|
||||
get isTablet$() {
|
||||
@@ -131,7 +131,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
}
|
||||
return '';
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
priceMaintained$ = combineLatest([
|
||||
@@ -141,8 +141,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
this.store.deliveryB2BAvailability$,
|
||||
]).pipe(
|
||||
map((availabilities) => {
|
||||
return availabilities?.some((availability) => availability?.priceMaintained) ?? false;
|
||||
})
|
||||
return availabilities?.some((availability) => (availability as any)?.priceMaintained) ?? false;
|
||||
}),
|
||||
);
|
||||
|
||||
price$ = combineLatest([
|
||||
@@ -174,7 +174,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
constructor(
|
||||
@@ -193,7 +193,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
private _environment: EnvironmentService,
|
||||
private _router: Router,
|
||||
private _domainCheckoutService: DomainCheckoutService,
|
||||
private _store: Store
|
||||
private _store: Store,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -201,8 +201,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
.pipe(
|
||||
debounceTime(0),
|
||||
switchMap((params) =>
|
||||
this.applicationService.getSelectedBranch$(Number(params.processId)).pipe(map((selectedBranch) => ({ params, selectedBranch })))
|
||||
)
|
||||
this.applicationService.getSelectedBranch$(Number(params.processId)).pipe(map((selectedBranch) => ({ params, selectedBranch }))),
|
||||
),
|
||||
)
|
||||
.subscribe(({ params, selectedBranch }) => {
|
||||
const processId = Number(params.processId);
|
||||
@@ -221,13 +221,13 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
const id$ = this.activatedRoute.params.pipe(
|
||||
tap((_) => (this.showRecommendations = false)),
|
||||
map((params) => Number(params?.id) || undefined),
|
||||
filter((f) => !!f)
|
||||
filter((f) => !!f),
|
||||
);
|
||||
|
||||
const ean$ = this.activatedRoute.params.pipe(
|
||||
tap((_) => (this.showRecommendations = false)),
|
||||
map((params) => params?.ean || undefined),
|
||||
filter((f) => !!f)
|
||||
filter((f) => !!f),
|
||||
);
|
||||
|
||||
const more$ = this.activatedRoute.params.subscribe(() => (this.showMore = false));
|
||||
@@ -241,9 +241,9 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
this.store.item$
|
||||
.pipe(
|
||||
withLatestFrom(this.isTablet$),
|
||||
filter(([item, isTablet]) => !!item)
|
||||
filter(([item, isTablet]) => !!item),
|
||||
)
|
||||
.subscribe(([item, isTablet]) => (isTablet ? this.updateBreadcrumb(item) : this.updateBreadcrumbDesktop(item)))
|
||||
.subscribe(([item, isTablet]) => (isTablet ? this.updateBreadcrumb(item) : this.updateBreadcrumbDesktop(item))),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
if (result?.data) {
|
||||
this.showPurchasingModal(result.data);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -425,7 +425,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
first(),
|
||||
switchMap((customerFeatures) => {
|
||||
return this._domainCheckoutService.canSetCustomer({ processId: this.applicationService.activatedProcessId, customerFeatures });
|
||||
})
|
||||
}),
|
||||
)
|
||||
.toPromise();
|
||||
this._router.navigate(nav.path, {
|
||||
@@ -439,11 +439,10 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
async navigateToResultList() {
|
||||
const processId = this.applicationService.activatedProcessId;
|
||||
let crumbs = await this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog'])
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog', 'details'])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
crumbs = crumbs.filter((crumb) => !crumb.tags?.includes('details'));
|
||||
const crumb = crumbs[crumbs.length - 1];
|
||||
if (!!crumb) {
|
||||
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: crumb.params }).navigate();
|
||||
|
||||
@@ -24,14 +24,14 @@ import { FilterAutocompleteProvider } from '@shared/components/filter';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
private _processId$: Observable<number>;
|
||||
|
||||
constructor(
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _articleSearch: ArticleSearchService,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _navigationService: ProductCatalogNavigationService
|
||||
private _navigationService: ProductCatalogNavigationService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -58,14 +58,14 @@ export class ArticleSearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private articleSearch: ArticleSearchService,
|
||||
private _environment: EnvironmentService,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
public application: ApplicationService,
|
||||
private _navigationService: ProductCatalogNavigationService
|
||||
private _navigationService: ProductCatalogNavigationService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -155,7 +155,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
|
||||
const cleanQueryParams = this.cleanupQueryParams(queryParams);
|
||||
|
||||
if (this.route.outlet === 'primary' || processChanged) {
|
||||
if (processChanged) {
|
||||
this.scrollToItem(this._getScrollIndexFromCache());
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ export class ArticleSearchResultsComponent implements OnInit, OnDestroy, AfterVi
|
||||
}
|
||||
|
||||
private _getScrollIndexFromCache(): number {
|
||||
return this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN });
|
||||
return this.cache.get<number>({ processId: this.getProcessId(), token: this.SCROLL_INDEX_TOKEN }) ?? 0;
|
||||
}
|
||||
|
||||
scrollToItem(i?: number) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return `${this.breadcrumbRef?.nativeElement?.clientWidth}px`;
|
||||
}
|
||||
|
||||
_onDestroy$ = new Subject<boolean>();
|
||||
_onDestroy$ = new Subject<void>();
|
||||
|
||||
get isTablet$() {
|
||||
return this._environmentService.matchTablet$;
|
||||
@@ -60,7 +60,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
public auth: AuthService,
|
||||
private _environmentService: EnvironmentService,
|
||||
private _renderer: Renderer2,
|
||||
private _actions: ActionsSubject
|
||||
private _actions: ActionsSubject,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -76,7 +76,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return 'Bitte wählen Sie eine Filiale aus, um den Bestand einer anderen Filiale zu sehen';
|
||||
}
|
||||
return '';
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
|
||||
<ng-container *ngIf="payer$ | async; let payer">
|
||||
<div *ngIf="showAddresses$ | async" class="flex flex-row items-start justify-between p-5 pt-0">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Rechnungsadresse">
|
||||
<div class="mr-3">Rechnungsadresse</div>
|
||||
<div class="font-bold">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Rechnungsadresse" data-which="Rechnungsadresse">
|
||||
<div class="mr-3" data-what="title">Rechnungsadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ payer | payerAddress }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,9 +47,9 @@
|
||||
|
||||
<ng-container *ngIf="payer$ | async; let payer">
|
||||
<div *ngIf="showAddresses$ | async" class="flex flex-row items-start justify-between px-5">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Lieferadresse">
|
||||
<div class="mr-3">Lieferadresse</div>
|
||||
<div class="font-bold">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Lieferadresse" data-which="Lieferadresse">
|
||||
<div class="mr-3" data-what="title">Lieferadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ shippingAddress$ | async | shippingAddress }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,17 +19,14 @@
|
||||
placeholder="Eine Anmerkung hinzufügen"
|
||||
[(ngModel)]="value"
|
||||
[rows]="rows"
|
||||
(ngModelChange)="check()"
|
||||
(blur)="save()"
|
||||
(ngModelChange)="updateValue()"
|
||||
(blur)="updateValue()"
|
||||
></textarea>
|
||||
|
||||
<div class="comment-actions py-4">
|
||||
<button type="reset" class="clear pl-4" *ngIf="!disabled && !!value" (click)="clear(); triggerResize()">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
<button class="cta-save ml-4" type="submit" *ngIf="!disabled && isDirty" (click)="save()">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export class SpecialCommentComponent implements ControlValueAccessor {
|
||||
|
||||
clear() {
|
||||
this.value = '';
|
||||
this.save();
|
||||
this.updateValue();
|
||||
}
|
||||
|
||||
check() {
|
||||
@@ -80,11 +80,12 @@ export class SpecialCommentComponent implements ControlValueAccessor {
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
save() {
|
||||
updateValue() {
|
||||
this.initialValue = this.value;
|
||||
this.onChange(this.value);
|
||||
this.check();
|
||||
}
|
||||
|
||||
setIsDirty(isDirty: boolean) {
|
||||
this.isDirty = isDirty;
|
||||
this.isDirtyChange.emit(isDirty);
|
||||
|
||||
@@ -33,7 +33,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
|
||||
private _toaster = inject(ToasterService);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
processId = Date.now();
|
||||
selectedDate = this.dateAdapter.today();
|
||||
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
|
||||
@@ -52,17 +52,17 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
|
||||
// Ticket #4228 Für die korrekte Gruppierung der Items bei gleichem Bestellziel (Aufsplitten von Abholung und Rücklage)
|
||||
const ordersWithMultipleFeatures = filteredOrders.filter((order) =>
|
||||
order.items.find((item) => item.features.orderType !== order.features.orderType)
|
||||
order.items.find((item) => item.features.orderType !== order.features.orderType),
|
||||
);
|
||||
|
||||
if (!!ordersWithMultipleFeatures) {
|
||||
for (let orderWithMultipleFeatures of ordersWithMultipleFeatures) {
|
||||
if (orderWithMultipleFeatures?.items?.length > 1) {
|
||||
const itemsWithOrderFeature = orderWithMultipleFeatures.items.filter(
|
||||
(item) => item.features.orderType === orderWithMultipleFeatures.features.orderType
|
||||
(item) => item.features.orderType === orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
const itemsWithDifferentOrderFeature = orderWithMultipleFeatures.items.filter(
|
||||
(item) => item.features.orderType !== orderWithMultipleFeatures.features.orderType
|
||||
(item) => item.features.orderType !== orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
|
||||
filteredOrders = [...filteredOrders.filter((order) => order.id !== orderWithMultipleFeatures.id)];
|
||||
@@ -85,20 +85,20 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
});
|
||||
}),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
hasAbholung$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) => displayOrders.filter((order) => order.features?.orderType === 'Abholung')?.length > 0)
|
||||
map((displayOrders) => displayOrders.filter((order) => order.features?.orderType === 'Abholung')?.length > 0),
|
||||
);
|
||||
|
||||
totalItemCount$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) =>
|
||||
displayOrders.reduce(
|
||||
(total, displayOrder) => total + displayOrder?.items?.reduce((subTotal, order) => subTotal + order?.quantity, 0),
|
||||
0
|
||||
)
|
||||
)
|
||||
0,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
totalReadingPoints$ = this.displayOrders$.pipe(
|
||||
@@ -122,7 +122,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
return NEVER;
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
totalPrice$ = this.displayOrders$.pipe(
|
||||
@@ -130,9 +130,9 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
displayOrders.reduce(
|
||||
(total, displayOrder) =>
|
||||
total + displayOrder?.items?.reduce((subTotal, order) => subTotal + order?.price?.value?.value * order.quantity, 0),
|
||||
0
|
||||
)
|
||||
)
|
||||
0,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
isPrinting$ = new BehaviorSubject(false);
|
||||
@@ -142,20 +142,20 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
containsDeliveryOrder$ = this.displayOrders$.pipe(
|
||||
map(
|
||||
(displayOrders) =>
|
||||
displayOrders.filter((o) => ['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(o.features?.orderType) > -1)?.length > 0
|
||||
)
|
||||
displayOrders.filter((o) => ['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(o.features?.orderType) > -1)?.length > 0,
|
||||
),
|
||||
);
|
||||
|
||||
customer$ = this.displayOrders$.pipe(
|
||||
switchMap((o) => this.customerService.getCustomers(o[0].buyerNumber, { take: 5 })),
|
||||
map((customers) => customers.result[0]),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
isB2BCustomer$ = this.customer$.pipe(map((customer) => customer?.features?.find((f) => f.key === 'b2b') != null));
|
||||
|
||||
takeNowOrders$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) => displayOrders.filter((o) => o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null))
|
||||
map((displayOrders) => displayOrders.filter((o) => o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null)),
|
||||
);
|
||||
|
||||
get isDesktop$() {
|
||||
@@ -184,7 +184,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
private _productNavigationService: ProductCatalogNavigationService,
|
||||
private _shelfOutNavigationService: PickUpShelfOutNavigationService,
|
||||
private _environmentService: EnvironmentService,
|
||||
private _cdr: ChangeDetectorRef
|
||||
private _cdr: ChangeDetectorRef,
|
||||
) {
|
||||
this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['checkout'])
|
||||
@@ -319,7 +319,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
first(),
|
||||
map((printers) => {
|
||||
if (Array.isArray(printers)) return printers.find((printer) => printer.selected === true);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
|
||||
@@ -78,12 +78,12 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
switchMap((oi) =>
|
||||
this._omsService
|
||||
.getCompletedTasks({ orderId: oi.orderId, orderItemId: oi.orderItemId, orderItemSubsetId: oi.orderItemSubsetId, take: 4, skip: 0 })
|
||||
.pipe(catchError(() => NEVER))
|
||||
)
|
||||
.pipe(catchError(() => NEVER)),
|
||||
),
|
||||
);
|
||||
|
||||
canChangeQuantity$ = combineLatest([this.orderItem$, this._store.fetchPartial$]).pipe(
|
||||
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1)
|
||||
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1),
|
||||
);
|
||||
|
||||
get quantity() {
|
||||
@@ -109,7 +109,7 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
}
|
||||
|
||||
readonly selected$ = combineLatest([this.orderItem$, this._store.selectedeOrderItemSubsetIds$]).pipe(
|
||||
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId))
|
||||
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId)),
|
||||
);
|
||||
|
||||
@Output()
|
||||
@@ -120,7 +120,7 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
}
|
||||
|
||||
readonly selectable$ = combineLatest([this._store.items$, this._store.itemsSelectable$, this._store.fetchPartial$]).pipe(
|
||||
map(([orderItems, selectable, fetchPartial]) => orderItems.length > 1 && selectable && fetchPartial)
|
||||
map(([orderItems, selectable, fetchPartial]) => orderItems.length > 1 && selectable && fetchPartial),
|
||||
);
|
||||
|
||||
get receipts() {
|
||||
@@ -141,7 +141,7 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
|
||||
more$ = this.select((s) => s.more);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
get isNative() {
|
||||
return this._environment.isNative();
|
||||
@@ -152,7 +152,7 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
private _domainReceiptService: DomainReceiptService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _environment: EnvironmentService
|
||||
private _environment: EnvironmentService,
|
||||
) {
|
||||
super({
|
||||
more: false,
|
||||
@@ -194,11 +194,11 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
done?.([]);
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
() => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
async saveSpecialComment() {
|
||||
@@ -254,12 +254,15 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
|
||||
|
||||
// Macht aus einem String Array ein Array von Objekten mit den keys trackingProvider und trackingNumber
|
||||
private _trackingTransformationHelper(trackingInformationPairs: string[]): Array<{ trackingProvider: string; trackingNumber: string }> {
|
||||
return trackingInformationPairs.reduce((acc, current, index, array) => {
|
||||
if (index % 2 === 0) {
|
||||
acc.push({ trackingProvider: current, trackingNumber: array[index + 1] });
|
||||
}
|
||||
return acc;
|
||||
}, [] as { trackingProvider: string; trackingNumber: string }[]);
|
||||
return trackingInformationPairs.reduce(
|
||||
(acc, current, index, array) => {
|
||||
if (index % 2 === 0) {
|
||||
acc.push({ trackingProvider: current, trackingNumber: array[index + 1] });
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[] as { trackingProvider: string; trackingNumber: string }[],
|
||||
);
|
||||
}
|
||||
|
||||
getTrackingNumberLink(trackingNumber: string) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
|
||||
|
||||
processId = Number(this._activatedRoute?.parent?.snapshot?.data?.processId);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
@ViewChild(FilterComponent, { static: false })
|
||||
uiFilter: FilterComponent;
|
||||
@@ -79,7 +79,7 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
|
||||
private _environment: EnvironmentService,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
public application: ApplicationService,
|
||||
private _navigationService: CustomerOrdersNavigationService
|
||||
private _navigationService: CustomerOrdersNavigationService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -23,7 +23,7 @@ import { CustomerOrdersNavigationService } from '@shared/services';
|
||||
],
|
||||
})
|
||||
export class CustomerOrderSearchComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
private _processId$: Observable<number>;
|
||||
|
||||
get isTablet() {
|
||||
@@ -35,7 +35,7 @@ export class CustomerOrderSearchComponent implements OnInit, OnDestroy {
|
||||
private _navigationService: CustomerOrdersNavigationService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _environment: EnvironmentService
|
||||
private _environment: EnvironmentService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -127,7 +127,10 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
|
||||
cancelSearch$ = new Subject<void>();
|
||||
|
||||
constructor(private _domainGoodsInService: DomainCustomerOrderService, private _cache: CacheService) {
|
||||
constructor(
|
||||
private _domainGoodsInService: DomainCustomerOrderService,
|
||||
private _cache: CacheService,
|
||||
) {
|
||||
super({
|
||||
fetching: false,
|
||||
silentFetching: false,
|
||||
@@ -201,8 +204,8 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
|
||||
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
|
||||
options$.pipe(
|
||||
tap((_) => {
|
||||
this.searchStarted.next();
|
||||
tap((opt) => {
|
||||
this.searchStarted.next(opt);
|
||||
this.patchState({ message: undefined });
|
||||
}),
|
||||
withLatestFrom(this.results$, this.filter$, this.selectedBranch$),
|
||||
@@ -289,10 +292,10 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
|
||||
}
|
||||
this.patchState({ fetching: false, silentFetching: false });
|
||||
console.error('GoodsInSearchStore.search()', err);
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,8 +42,10 @@ export interface CustomerOrderSearchResultsState {
|
||||
styleUrls: ['customer-order-search-results.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerOrderSearchResultsComponent extends ComponentStore<CustomerOrderSearchResultsState>
|
||||
implements OnInit, AfterViewInit, OnDestroy {
|
||||
export class CustomerOrderSearchResultsComponent
|
||||
extends ComponentStore<CustomerOrderSearchResultsState>
|
||||
implements OnInit, AfterViewInit, OnDestroy
|
||||
{
|
||||
@ViewChildren(CustomerOrderItemComponent) listItems: QueryList<CustomerOrderItemComponent>;
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
@@ -63,7 +65,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
message$ = this._customerOrderSearchStore.message$;
|
||||
@@ -76,9 +78,9 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
actions$ = combineLatest([this.items$, this.selectedOrderItemSubsetIds$]).pipe(
|
||||
map(([items, selectedOrderItemSubsetIds]) =>
|
||||
items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
|
||||
items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId)),
|
||||
),
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action))),
|
||||
);
|
||||
|
||||
get selectedItems() {
|
||||
@@ -99,7 +101,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((data) => +data.processId));
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
trackByFn: TrackByFunction<OrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
|
||||
|
||||
@@ -108,7 +110,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
filter$ = this._customerOrderSearchStore.filter$.pipe(filter((f) => !!f));
|
||||
|
||||
hasFilter$ = combineLatest([this.filter$, this._customerOrderSearchStore.defaultSettings$]).pipe(
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), Filter.create(defaultFilter).getQueryParams()))
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), Filter.create(defaultFilter).getQueryParams())),
|
||||
);
|
||||
|
||||
get isTablet$() {
|
||||
@@ -148,7 +150,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
private _navigationService: CustomerOrdersNavigationService,
|
||||
private _application: ApplicationService,
|
||||
private _cache: CacheService,
|
||||
private _router: Router
|
||||
private _router: Router,
|
||||
) {
|
||||
super({
|
||||
selectedOrderItemSubsetIds: [],
|
||||
@@ -166,7 +168,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
const clean = { ...queryParams };
|
||||
delete clean['updateResults'];
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
@@ -174,8 +176,8 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
.pipe(
|
||||
debounceTime(150),
|
||||
switchMap(([processId, params]) =>
|
||||
this._application.getSelectedBranch$(processId).pipe(map((selectedBranch) => ({ processId, params, selectedBranch })))
|
||||
)
|
||||
this._application.getSelectedBranch$(processId).pipe(map((selectedBranch) => ({ processId, params, selectedBranch }))),
|
||||
),
|
||||
)
|
||||
.subscribe(async ({ processId, params, selectedBranch }) => {
|
||||
const branchChanged = selectedBranch?.id !== this._customerOrderSearchStore?.selectedBranch?.id;
|
||||
@@ -243,7 +245,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
if (this._activatedRoute?.outlet === 'primary') {
|
||||
await this.removeDetailsBreadcrumb(processId);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
@@ -261,12 +263,12 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
await this.navigateToDetails(
|
||||
processId,
|
||||
result?.results?.result?.find((_) => true),
|
||||
queryParams
|
||||
queryParams,
|
||||
);
|
||||
} else if ((!!result?.clear || this._activatedRoute.outlet === 'primary') && this.isDesktopLarge) {
|
||||
await this._navigationService.getCustomerOrdersResultsPath(processId, { queryParams }).navigate();
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this._customerOrderSearchStore.searchResultClearedSubject.pipe(takeUntil(this._onDestroy$)).subscribe((_) => this.clearSelectedItems());
|
||||
@@ -279,7 +281,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
|
||||
};
|
||||
this._customerOrderSearchStore?.setQueryParams(queryParams);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this._router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
|
||||
@@ -302,7 +304,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
|
||||
if (this._activatedRoute.outlet === 'primary') {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._customerOrderSearchStore.processId, token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
this.scrollContainer?.scrollPos,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class CustomerOrderComponent implements OnInit {
|
||||
return `${this.breadcrumbRef?.nativeElement?.clientWidth}px`;
|
||||
}
|
||||
|
||||
_onDestroy$ = new Subject<boolean>();
|
||||
_onDestroy$ = new Subject<void>();
|
||||
|
||||
get isTablet$() {
|
||||
return this._environmentService.matchTablet$;
|
||||
@@ -43,12 +43,12 @@ export class CustomerOrderComponent implements OnInit {
|
||||
private _renderer: Renderer2,
|
||||
private _environmentService: EnvironmentService,
|
||||
public auth: AuthService,
|
||||
private _store: CustomerOrderSearchStore
|
||||
private _store: CustomerOrderSearchStore,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.selectedBranch$ = this.application.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.application.getSelectedBranch$(Number(processId)))
|
||||
switchMap((processId) => this.application.getSelectedBranch$(Number(processId))),
|
||||
);
|
||||
|
||||
/* Ticket #4544 - Suchrequest abbrechen bei Prozesswechsel
|
||||
|
||||
@@ -41,8 +41,10 @@ export interface CustomerTypeSelectorState {
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSelectorState>
|
||||
implements OnInit, OnDestroy, ControlValueAccessor {
|
||||
export class CustomerTypeSelectorComponent
|
||||
extends ComponentStore<CustomerTypeSelectorState>
|
||||
implements OnInit, OnDestroy, ControlValueAccessor
|
||||
{
|
||||
@ViewChildren(UiCheckboxComponent)
|
||||
checkboxes: QueryList<UiCheckboxComponent>;
|
||||
|
||||
@@ -133,7 +135,7 @@ export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSe
|
||||
|
||||
return result;
|
||||
}),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,7 +146,11 @@ export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSe
|
||||
onChange = (value: string) => {};
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(private _checkoutService: DomainCheckoutService, private _cache: CacheService, private _cdr: ChangeDetectorRef) {
|
||||
constructor(
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _cache: CacheService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
) {
|
||||
super({
|
||||
processId: undefined,
|
||||
customerType: undefined,
|
||||
@@ -165,10 +171,10 @@ export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSe
|
||||
this.patchState({ options });
|
||||
this._cache.set('customerTypeOptions', options);
|
||||
},
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -197,7 +203,7 @@ export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSe
|
||||
setValue(value: { p4mUser?: boolean; customerType?: string } | string) {
|
||||
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
|
||||
|
||||
if (isString(value)) {
|
||||
if (typeof value === 'string') {
|
||||
this.value = value;
|
||||
} else {
|
||||
if (isBoolean(value.p4mUser)) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ChangeDetectorRef, Directive, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
||||
import { AbstractControl, AsyncValidatorFn, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { AbstractControl, AsyncValidatorFn, UntypedFormControl, UntypedFormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
@@ -76,7 +76,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
protected modal: UiModalService,
|
||||
protected breadcrumb: BreadcrumbService,
|
||||
protected cdr: ChangeDetectorRef,
|
||||
protected customerSearchNavigation: CustomerSearchNavigation
|
||||
protected customerSearchNavigation: CustomerSearchNavigation,
|
||||
) {
|
||||
this._initProcessId$();
|
||||
}
|
||||
@@ -88,7 +88,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
map((data) => +data.processId),
|
||||
tap((processId) => (this.latestProcessId = processId)),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1)
|
||||
shareReplay(1),
|
||||
);
|
||||
|
||||
this.processId$
|
||||
@@ -118,7 +118,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
const formData = await this.activatedRoute.queryParams
|
||||
.pipe(
|
||||
map((params) => params['formData']),
|
||||
first()
|
||||
first(),
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
@@ -190,6 +190,47 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
minBirthDateValidator = (): ValidatorFn => {
|
||||
return (control: AbstractControl): ValidationErrors | null => {
|
||||
const minAge = 16; // 16 years
|
||||
|
||||
if (!control.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const controlBirthDate = new Date(control.value);
|
||||
const minBirthDate = new Date();
|
||||
minBirthDate.setFullYear(minBirthDate.getFullYear() - minAge);
|
||||
|
||||
// Check if customer is over 16 years old
|
||||
if (this._checkIfAgeOver16(controlBirthDate, minBirthDate)) {
|
||||
return null;
|
||||
} else {
|
||||
return { minBirthDate: `Teilnahme ab ${minAge} Jahren` };
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
private _checkIfAgeOver16(inputDate: Date, minBirthDate: Date): boolean {
|
||||
// Check year
|
||||
if (inputDate.getFullYear() < minBirthDate.getFullYear()) {
|
||||
return true;
|
||||
}
|
||||
// Check Year + Month
|
||||
else if (inputDate.getFullYear() === minBirthDate.getFullYear() && inputDate.getMonth() < minBirthDate.getMonth()) {
|
||||
return true;
|
||||
}
|
||||
// Check Year + Month + Day
|
||||
else if (
|
||||
inputDate.getFullYear() === minBirthDate.getFullYear() &&
|
||||
inputDate.getMonth() === minBirthDate.getMonth() &&
|
||||
inputDate.getDate() <= minBirthDate.getDate()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
emailExistsValidator: AsyncValidatorFn = (control) => {
|
||||
return of(control.value).pipe(
|
||||
tap((_) => this.customerExists$.next(false)),
|
||||
@@ -210,7 +251,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
return of({ invalid: 'E-Mail ist ungültig' });
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}),
|
||||
tap((error) => {
|
||||
@@ -219,7 +260,7 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
}
|
||||
control.markAsTouched();
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -266,13 +307,13 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
return of({ invalid: 'Kundenkartencode ist ungültig' });
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}),
|
||||
tap(() => {
|
||||
control.markAsTouched();
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
|
||||
agbValidatorFns = [Validators.requiredTrue];
|
||||
|
||||
birthDateValidatorFns = [Validators.required];
|
||||
birthDateValidatorFns = [];
|
||||
|
||||
existingCustomer$: Observable<CustomerInfoDTO | null>;
|
||||
existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
@@ -84,7 +84,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
return this.fetchCustomerInfo();
|
||||
}
|
||||
return of(null);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this.existingCustomer$
|
||||
@@ -94,12 +94,12 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
if (info) {
|
||||
return this.customerService.getCustomer(info.id, 2).pipe(
|
||||
map((res) => res.result),
|
||||
catchError((err) => NEVER)
|
||||
catchError((err) => NEVER),
|
||||
);
|
||||
}
|
||||
return NEVER;
|
||||
}),
|
||||
withLatestFrom(this.processId$)
|
||||
withLatestFrom(this.processId$),
|
||||
)
|
||||
.subscribe(([customer, processId]) => {
|
||||
if (customer) {
|
||||
@@ -138,6 +138,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
|
||||
initMarksAndValidators() {
|
||||
this.asyncLoyaltyCardValidatorFn = [this.checkLoyalityCardValidator];
|
||||
this.birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
if (this._customerType === 'webshop') {
|
||||
this.emailRequiredMark = true;
|
||||
this.emailValidatorFn = [Validators.required, Validators.email, validateEmail];
|
||||
@@ -165,7 +166,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
|
||||
data: err,
|
||||
});
|
||||
return [null];
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer im
|
||||
|
||||
agbValidatorFns = [Validators.requiredTrue];
|
||||
|
||||
birthDateValidatorFns = [Validators.required];
|
||||
birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
|
||||
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, effect, untracked } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Subject, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, Subject, Subscription, fromEvent } from 'rxjs';
|
||||
import { CustomerSearchStore } from './store/customer-search.store';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
|
||||
@@ -8,6 +8,7 @@ import { delay, filter, first, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services';
|
||||
import { CustomerSearchMainAutocompleteProvider } from './providers/customer-search-main-autocomplete.provider';
|
||||
import { FilterAutocompleteProvider } from '@shared/components/filter';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-search',
|
||||
@@ -24,6 +25,10 @@ import { FilterAutocompleteProvider } from '@shared/components/filter';
|
||||
],
|
||||
})
|
||||
export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
private searchStore = inject(CustomerSearchStore);
|
||||
|
||||
keyEscPressed = toSignal(fromEvent(document, 'keydown').pipe(filter((e: KeyboardEvent) => e.key === 'Escape')));
|
||||
|
||||
get breadcrumb() {
|
||||
let breadcrumb: string;
|
||||
|
||||
@@ -38,7 +43,7 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
private _breadcrumbs$ = this._store.processId$.pipe(
|
||||
filter((id) => !!id),
|
||||
switchMap((id) => this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer'))
|
||||
switchMap((id) => this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer')),
|
||||
);
|
||||
|
||||
side$ = new BehaviorSubject<string | undefined>(undefined);
|
||||
@@ -73,7 +78,7 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
private _router: Router,
|
||||
private _breadcrumbService: BreadcrumbService,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
private _createNavigation: CustomerCreateNavigation
|
||||
private _createNavigation: CustomerCreateNavigation,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -542,4 +547,11 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
this._breadcrumbService.removeBreadcrumb(orderDetailsHistoryBreadcrumb.id);
|
||||
}
|
||||
}
|
||||
|
||||
cancelSearchEffect = effect(() => {
|
||||
const event = this.keyEscPressed();
|
||||
untracked(() => {
|
||||
this.searchStore.cancelSearch();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Host } from '@angular/core';
|
||||
import { CustomerSearchStore } from '../../store';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { debounceTime, map, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { Observable, Subject, combineLatest } from 'rxjs';
|
||||
import { AssignedPayerDTO, CustomerDTO, ListResponseArgsOfAssignedPayerDTO } from '@swagger/crm';
|
||||
import { AsyncPipe, NgFor, NgIf } from '@angular/common';
|
||||
@@ -27,8 +27,10 @@ interface DetailsMainViewBillingAddressesComponentState {
|
||||
standalone: true,
|
||||
imports: [NgIf, NgFor, AsyncPipe, CustomerPipesModule, RouterLink],
|
||||
})
|
||||
export class DetailsMainViewBillingAddressesComponent extends ComponentStore<DetailsMainViewBillingAddressesComponentState>
|
||||
implements OnInit, OnDestroy {
|
||||
export class DetailsMainViewBillingAddressesComponent
|
||||
extends ComponentStore<DetailsMainViewBillingAddressesComponentState>
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
assignedPayers$ = this.select((state) => state.assignedPayers);
|
||||
|
||||
selectedPayer$ = this.select((state) => state.selectedPayer);
|
||||
@@ -36,7 +38,7 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
isNotBusinessKonto$ = this._store.isBusinessKonto$.pipe(map((isBusinessKonto) => !isBusinessKonto));
|
||||
|
||||
showCustomerAddress$ = combineLatest([this._store.isBusinessKonto$, this._store.isMitarbeiter$, this._store.isKundenkarte$]).pipe(
|
||||
map(([isBusinessKonto, isMitarbeiter, isKundenkarte]) => isBusinessKonto || isMitarbeiter || isKundenkarte)
|
||||
map(([isBusinessKonto, isMitarbeiter, isKundenkarte]) => isBusinessKonto || isMitarbeiter || isKundenkarte),
|
||||
);
|
||||
|
||||
get showCustomerAddress() {
|
||||
@@ -47,12 +49,8 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
this._store.isOnlinekonto$,
|
||||
this._store.isOnlineKontoMitKundenkarte$,
|
||||
this._store.isKundenkarte$,
|
||||
this._store.isMitarbeiter$,
|
||||
]).pipe(
|
||||
map(
|
||||
([isOnlinekonto, isOnlineKontoMitKundenkarte, isKundenkarte, isMitarbeiter]) =>
|
||||
isOnlinekonto || isOnlineKontoMitKundenkarte || isKundenkarte || isMitarbeiter
|
||||
)
|
||||
map(([isOnlinekonto, isOnlineKontoMitKundenkarte, isKundenkarte]) => isOnlinekonto || isOnlineKontoMitKundenkarte || isKundenkarte),
|
||||
);
|
||||
|
||||
canEditAddress$ = combineLatest([this._store.isKundenkarte$]).pipe(map(([isKundenkarte]) => isKundenkarte));
|
||||
@@ -62,13 +60,13 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
editRoute$ = combineLatest([this._store.processId$, this._store.customerId$, this._store.isBusinessKonto$]).pipe(
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b }))
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b })),
|
||||
);
|
||||
|
||||
addBillingAddressRoute$ = combineLatest([this.canAddNewAddress$, this._store.processId$, this._store.customerId$]).pipe(
|
||||
map(([canAddNewAddress, processId, customerId]) =>
|
||||
canAddNewAddress ? this._navigation.addBillingAddressRoute({ processId, customerId }) : undefined
|
||||
)
|
||||
canAddNewAddress ? this._navigation.addBillingAddressRoute({ processId, customerId }) : undefined,
|
||||
),
|
||||
);
|
||||
|
||||
constructor(
|
||||
@@ -76,7 +74,7 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
private _store: CustomerSearchStore,
|
||||
private _customerService: CrmCustomerService,
|
||||
private _modal: UiModalService,
|
||||
private _navigation: CustomerSearchNavigation
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
) {
|
||||
super({
|
||||
assignedPayers: [],
|
||||
@@ -89,12 +87,15 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._store.customerId$.pipe(takeUntil(this._onDestroy$)).subscribe((customerId) => {
|
||||
this.resetStore();
|
||||
if (customerId) {
|
||||
this.loadAssignedPayers(customerId);
|
||||
}
|
||||
});
|
||||
combineLatest([this._store.customerId$, this._store.isMitarbeiter$])
|
||||
.pipe(takeUntil(this._onDestroy$), debounceTime(250))
|
||||
.subscribe(([customerId, isMitarbeiter]) => {
|
||||
this.resetStore();
|
||||
// #4715 Hier erfolgt ein Check auf Mitarbeiter, da Mitarbeiter keine zusätzlichen Rechnungsadressen haben sollen
|
||||
if (customerId && !isMitarbeiter) {
|
||||
this.loadAssignedPayers(customerId);
|
||||
}
|
||||
});
|
||||
|
||||
combineLatest([this.selectedPayer$, this._store.customer$])
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
@@ -151,9 +152,9 @@ export class DetailsMainViewBillingAddressesComponent extends ComponentStore<Det
|
||||
switchMap((customerId) =>
|
||||
this._customerService
|
||||
.getAssignedPayers({ customerId })
|
||||
.pipe(tapResponse(this.handleLoadAssignedPayersResponse, this.handleLoadAssignedPayersError))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.handleLoadAssignedPayersResponse, this.handleLoadAssignedPayersError)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleLoadAssignedPayersResponse = (response: ListResponseArgsOfAssignedPayerDTO) => {
|
||||
|
||||
@@ -29,12 +29,12 @@ export interface CustomerDetailsViewMainState {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDetailsViewMainState> implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
customerService = inject(CrmCustomerService);
|
||||
|
||||
fetching$ = combineLatest([this._store.fetchingCustomer$, this._store.fetchingCustomerList$]).pipe(
|
||||
map(([fetchingCustomer, fetchingList]) => fetchingCustomer || fetchingList)
|
||||
map(([fetchingCustomer, fetchingList]) => fetchingCustomer || fetchingList),
|
||||
);
|
||||
|
||||
isBusy$ = this.select((s) => s.isBusy);
|
||||
@@ -46,31 +46,31 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
customerId$ = this._store.customerId$;
|
||||
|
||||
historyRoute$ = combineLatest([this.processId$, this.customerId$]).pipe(
|
||||
map(([processId, customerId]) => this._navigation.historyRoute({ processId, customerId }))
|
||||
map(([processId, customerId]) => this._navigation.historyRoute({ processId, customerId })),
|
||||
);
|
||||
|
||||
ordersRoute$ = combineLatest([this.processId$, this.customerId$]).pipe(
|
||||
map(([processId, customerId]) => this._navigation.ordersRoute({ processId, customerId }))
|
||||
map(([processId, customerId]) => this._navigation.ordersRoute({ processId, customerId })),
|
||||
);
|
||||
|
||||
isB2b$ = this._store.isBusinessKonto$;
|
||||
|
||||
editRoute$ = combineLatest([this.processId$, this.customerId$, this.isB2b$]).pipe(
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b }))
|
||||
map(([processId, customerId, isB2b]) => this._navigation.editRoute({ processId, customerId, isB2b })),
|
||||
);
|
||||
|
||||
showEditButton$ = combineLatest([this._store.isOnlinekonto$, this._store.isBestellungOhneKonto$]).pipe(
|
||||
map(([isOnlinekonto, isBestellungOhneKonto]) => !isOnlinekonto && !isBestellungOhneKonto)
|
||||
map(([isOnlinekonto, isBestellungOhneKonto]) => !isOnlinekonto && !isBestellungOhneKonto),
|
||||
);
|
||||
|
||||
hasKundenkarte$ = combineLatest([this._store.isKundenkarte$, this._store.isOnlineKontoMitKundenkarte$]).pipe(
|
||||
map(([isKundenkarte, isOnlineKontoMitKundenkarte]) => isKundenkarte || isOnlineKontoMitKundenkarte)
|
||||
map(([isKundenkarte, isOnlineKontoMitKundenkarte]) => isKundenkarte || isOnlineKontoMitKundenkarte),
|
||||
);
|
||||
|
||||
kundenkarteRoute$ = combineLatest([this.hasKundenkarte$, this.processId$, this.customerId$]).pipe(
|
||||
map(([hasKundenkarte, processId, customerId]) =>
|
||||
hasKundenkarte ? this._navigation.kundenkarteRoute({ processId, customerId }) : undefined
|
||||
)
|
||||
hasKundenkarte ? this._navigation.kundenkarteRoute({ processId, customerId }) : undefined,
|
||||
),
|
||||
);
|
||||
|
||||
customerType$ = this._store.select((s) => s.customer?.features?.find((f) => f.enabled)?.description);
|
||||
@@ -194,7 +194,7 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
private _checkoutNavigation: CheckoutNavigationService,
|
||||
private _router: Router,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _genderSettings: GenderSettingsService
|
||||
private _genderSettings: GenderSettingsService,
|
||||
) {
|
||||
super({ isBusy: false, shoppingCart: undefined, shippingAddress: undefined, payer: undefined });
|
||||
}
|
||||
@@ -221,8 +221,8 @@ export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDet
|
||||
this._checkoutService.getShoppingCart({
|
||||
processId: pid,
|
||||
latest: true,
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.subscribe((shoppingCart) => {
|
||||
this.patchState({ shoppingCart });
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CustomerSearchStore } from '../store';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
import { CustomerSearchNavigation } from '@shared/services';
|
||||
import { Router } from '@angular/router';
|
||||
import { injectCancelSearch } from '../inject-cancel-search';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-filter-main-view',
|
||||
@@ -13,6 +14,8 @@ import { Router } from '@angular/router';
|
||||
host: { class: 'page-customer-filter-main-view' },
|
||||
})
|
||||
export class CustomerFilterMainViewComponent {
|
||||
cancelSearch = injectCancelSearch();
|
||||
|
||||
fetchingFilterSettings$ = this._store.fetchingFilter$;
|
||||
|
||||
fetching$ = this._store.fetchingCustomerList$;
|
||||
@@ -38,8 +41,10 @@ export class CustomerFilterMainViewComponent {
|
||||
private _store: CustomerSearchStore,
|
||||
private _location: Location,
|
||||
private _router: Router,
|
||||
private _customerSearchNavigation: CustomerSearchNavigation
|
||||
) {}
|
||||
private _customerSearchNavigation: CustomerSearchNavigation,
|
||||
) {
|
||||
this.cancelSearch();
|
||||
}
|
||||
|
||||
search(filter: Filter) {
|
||||
this._store.setFilter(filter);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { inject } from '@angular/core';
|
||||
import { CustomerSearchStore } from './store';
|
||||
|
||||
export function injectCancelSearch() {
|
||||
const store = inject(CustomerSearchStore);
|
||||
return () => {
|
||||
console.log('Cancel search');
|
||||
store.cancelSearch();
|
||||
};
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search(filter)"
|
||||
(queryChange)="cancelSearch()"
|
||||
[hint]="message$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
[scanner]="true"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { isEmpty } from 'lodash';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
import { CustomerResultListComponent } from '../../components/customer-result-list/customer-result-list.component';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { injectCancelSearch } from '../inject-cancel-search';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-results-main-view',
|
||||
@@ -16,6 +17,8 @@ import { EnvironmentService } from '@core/environment';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, AfterContentInit {
|
||||
cancelSearch = injectCancelSearch();
|
||||
|
||||
processId$ = this._store.processId$;
|
||||
|
||||
currentUrl$ = new BehaviorSubject<string>(this._router.url);
|
||||
@@ -38,7 +41,7 @@ export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, Afte
|
||||
}
|
||||
|
||||
return route;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
routerEventsSubscription: Subscription;
|
||||
@@ -50,7 +53,7 @@ export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, Afte
|
||||
if (!filter) return false;
|
||||
const qt = filter.getQueryToken();
|
||||
return !isEmpty(qt.filter);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
fetching$ = this._store.fetchingCustomerList$;
|
||||
@@ -73,7 +76,7 @@ export class CustomerResultsMainViewComponent implements OnInit, OnDestroy, Afte
|
||||
private _store: CustomerSearchStore,
|
||||
private _router: Router,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
private _environment: EnvironmentService
|
||||
private _environment: EnvironmentService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search(filter)"
|
||||
(queryChange)="cancelSearch()"
|
||||
[hint]="message$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
[scanner]="true"
|
||||
@@ -23,7 +24,7 @@
|
||||
</a>
|
||||
<button
|
||||
class="btn btn-light h-14 font-bold"
|
||||
(click)="closeFilter()"
|
||||
(click)="closeFilter(); cancelSearch()"
|
||||
[class.bg-active-branch]="hasFilter$ | async"
|
||||
[class.text-white]="hasFilter$ | async"
|
||||
*ngIf="isFilterRouteActive"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { CustomerResultListComponent } from '../../components/customer-result-li
|
||||
import { isEmpty } from 'lodash';
|
||||
import { CustomerSearchNavigation, NavigationRoute } from '@shared/services';
|
||||
import { Location } from '@angular/common';
|
||||
import { injectCancelSearch } from '../inject-cancel-search';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-results-side-view',
|
||||
@@ -16,6 +17,8 @@ import { Location } from '@angular/common';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
|
||||
cancelSearch = injectCancelSearch();
|
||||
|
||||
processId$ = this._store.processId$;
|
||||
|
||||
currentUrl$ = new BehaviorSubject<string>(this._router.url);
|
||||
@@ -36,7 +39,7 @@ export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
return route;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
get isFilterRouteActive() {
|
||||
@@ -57,7 +60,7 @@ export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
|
||||
if (!filter) return false;
|
||||
const qt = filter.getQueryToken();
|
||||
return !isEmpty(qt.filter);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
message$ = this._store.message$;
|
||||
@@ -76,7 +79,7 @@ export class CustomerResultsSideViewComponent implements OnInit, OnDestroy {
|
||||
private _store: CustomerSearchStore,
|
||||
private _router: Router,
|
||||
private _navigation: CustomerSearchNavigation,
|
||||
private _location: Location
|
||||
private _location: Location,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
@@ -163,7 +163,11 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
|
||||
selectedOrderItem$ = this.select(S.selectSelectedOrderItem);
|
||||
|
||||
constructor(private _customerService: CrmCustomerService, private _omsService: DomainOmsService, private _modal: UiModalService) {
|
||||
constructor(
|
||||
private _customerService: CrmCustomerService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _modal: UiModalService,
|
||||
) {
|
||||
super({ customerListCount: 0 });
|
||||
}
|
||||
|
||||
@@ -192,9 +196,9 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
switchMap(({ customerId }) =>
|
||||
this._customerService
|
||||
.getCustomer(+customerId, 2)
|
||||
.pipe(tapResponse(this.handleSelectCustomerResponse, this.handleSelectCustomerError, this.handleSelectCustomerComplete))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.handleSelectCustomerResponse, this.handleSelectCustomerError, this.handleSelectCustomerComplete)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
setFilter = this.updater((state, filter: Filter) => ({ ...state, queryParams: filter?.getQueryParams() ?? {} }));
|
||||
@@ -221,9 +225,9 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
switchMap((orderId) =>
|
||||
this._omsService
|
||||
.getOrder(orderId)
|
||||
.pipe(tapResponse(this.handleSelectOrderResponse, this.handleSelectOrderError, this.handleSelectOrderComplete))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.handleSelectOrderResponse, this.handleSelectOrderError, this.handleSelectOrderComplete)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleSelectOrderResponse = (order: OrderDTO) => {
|
||||
@@ -250,10 +254,14 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
this._omsService
|
||||
.getOrderItemsByCustomerNumber(customer.customerNumber, 0)
|
||||
.pipe(
|
||||
tapResponse(this.handleFetchCustomerOrdersResponse, this.handleFetchCustomerOrdersError, this.handleFetchCustomerOrdersComplete)
|
||||
)
|
||||
)
|
||||
)
|
||||
tapResponse(
|
||||
this.handleFetchCustomerOrdersResponse,
|
||||
this.handleFetchCustomerOrdersError,
|
||||
this.handleFetchCustomerOrdersComplete,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleFetchCustomerOrdersResponse = (orders: OrderListItemDTO[]) => {
|
||||
@@ -275,9 +283,9 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
switchMap(() =>
|
||||
this._customerService
|
||||
.filterSettings()
|
||||
.pipe(tapResponse(this.handleFetchFilterResponse, this.handleFetchFilterError, this.handleFetchFilterComplete))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.handleFetchFilterResponse, this.handleFetchFilterError, this.handleFetchFilterComplete)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleFetchFilterResponse = (result: QuerySettingsDTO) => {
|
||||
@@ -298,8 +306,8 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
delayWhen(() =>
|
||||
this.filter$.pipe(
|
||||
filter((filter) => !!filter),
|
||||
first()
|
||||
)
|
||||
first(),
|
||||
),
|
||||
),
|
||||
withLatestFrom(this.filter$, this.processId$),
|
||||
map(([a1, a2, a3]) => {
|
||||
@@ -328,24 +336,23 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
tapResponse(
|
||||
this.handleSearchResponse(filter, processId, ignoreRestore ? false : restored, !!skipNavigation),
|
||||
this.handleSearchError,
|
||||
this.handleSearchComplete
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
this.handleSearchComplete,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handleSearchResponse = (filter: Filter, processId: number, restored: boolean, skipNavigation: boolean) => (
|
||||
result: ListResponseArgsOfCustomerInfoDTO
|
||||
) => {
|
||||
this.patchState({
|
||||
customerList: result.result,
|
||||
customerListCount: result.hits,
|
||||
message: result?.hits > 0 ? '' : 'Keine Suchergebnisse',
|
||||
});
|
||||
this._customerListResponse.next([result, filter, processId, restored, skipNavigation]);
|
||||
this.cacheSearchResult();
|
||||
};
|
||||
handleSearchResponse =
|
||||
(filter: Filter, processId: number, restored: boolean, skipNavigation: boolean) => (result: ListResponseArgsOfCustomerInfoDTO) => {
|
||||
this.patchState({
|
||||
customerList: result.result,
|
||||
customerListCount: result.hits,
|
||||
message: result?.hits > 0 ? '' : 'Keine Suchergebnisse',
|
||||
});
|
||||
this._customerListResponse.next([result, filter, processId, restored, skipNavigation]);
|
||||
this.cacheSearchResult();
|
||||
};
|
||||
|
||||
handleSearchError = (err: any) => {
|
||||
this._modal.error('Fehler beim Laden der Liste', err);
|
||||
@@ -364,7 +371,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
withLatestFrom(this.filter$, this.processId$, this.customerList$, this.fetchingCustomerList$),
|
||||
filter(
|
||||
([_, __, ___, customerList, fetchingCustomerList]) =>
|
||||
!fetchingCustomerList && customerList.length && customerList.length < this.customerListCount
|
||||
!fetchingCustomerList && customerList.length && customerList.length < this.customerListCount,
|
||||
),
|
||||
delayWhen(() => this.fetchingCustomerList$.pipe(filter((fetching) => !fetching))),
|
||||
tap(() => {
|
||||
@@ -378,10 +385,10 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
})
|
||||
.pipe(
|
||||
takeUntil(this._cancelSearch),
|
||||
tapResponse(this.handlePaginateResponse, this.handlePaginateError, this.handlePaginateComplete)
|
||||
)
|
||||
)
|
||||
)
|
||||
tapResponse(this.handlePaginateResponse, this.handlePaginateError, this.handlePaginateComplete),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
handlePaginateResponse = (result: ListResponseArgsOfCustomerInfoDTO) => {
|
||||
@@ -429,7 +436,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
JSON.stringify({
|
||||
customerList,
|
||||
customerListCount,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -458,7 +465,7 @@ export class CustomerSearchStore extends ComponentStore<CustomerSearchState> imp
|
||||
`CUSTOMER_INFO_CACHE_${customerId}`,
|
||||
JSON.stringify({
|
||||
customer,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,7 @@
|
||||
<span class="ssc-text" [class.err]="sscInvalid$ | async">{{ sscText$ | async }}</span>
|
||||
</div>
|
||||
|
||||
<div class="err" *ngIf="supplierIdError$ | async">
|
||||
Kein Lieferant vorhanden
|
||||
</div>
|
||||
<div class="err" *ngIf="supplierIdError$ | async">Kein Lieferant vorhanden</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,6 +40,7 @@
|
||||
<img
|
||||
class="thumbnail"
|
||||
loading="lazy"
|
||||
[productImageNavigation]="orderItem?.product?.ean"
|
||||
*ngIf="orderItem?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
[alt]="orderItem?.product?.name"
|
||||
@@ -73,7 +72,7 @@
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
<strong>{{ orderItem.price | currency: 'EUR':'code' }}</strong>
|
||||
<strong>{{ orderItem.price | currency: 'EUR' : 'code' }}</strong>
|
||||
</div>
|
||||
<div class="status spec">
|
||||
<span>Status</span>
|
||||
@@ -98,8 +97,6 @@
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<button class="cta-reorder" (click)="showReorderModal(); $event.stopPropagation()" [disabled]="editSsc$ | async">
|
||||
Nachbestellen
|
||||
</button>
|
||||
<button class="cta-reorder" (click)="showReorderModal(); $event.stopPropagation()" [disabled]="editSsc$ | async">Nachbestellen</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -111,7 +111,7 @@ export class GoodsInListItemComponent extends ComponentStore<GoodsInListItemComp
|
||||
catchError(() => {
|
||||
this.patchState({ supplierIdError: true });
|
||||
return [undefined];
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
readonly instruction$ = this.statusCodes$.pipe(
|
||||
@@ -120,16 +120,16 @@ export class GoodsInListItemComponent extends ComponentStore<GoodsInListItemComp
|
||||
const textObj = code?.texts?.find((text) => text?.textType === 'REWORK');
|
||||
return textObj && textObj.text ? textObj.text : '';
|
||||
}),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private _omsService: DomainOmsService,
|
||||
private _command: CommandService,
|
||||
private _modal: UiModalService,
|
||||
public environmentService: EnvironmentService
|
||||
public environmentService: EnvironmentService,
|
||||
) {
|
||||
super({
|
||||
item: undefined,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ProductImageModule } from 'apps/cdn/product-image/src/public-api';
|
||||
import { NavigateOnClickDirective, ProductImageModule } from 'apps/cdn/product-image/src/public-api';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
@@ -8,7 +8,7 @@ import { GoodsInListItemComponent } from './goods-in-list-item.component';
|
||||
import { PipesModule } from 'apps/shared/components/goods-in-out/src/lib/pipes/pipes.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, PipesModule, UiIconModule, ProductImageModule, FormsModule, UiInputModule],
|
||||
imports: [CommonModule, PipesModule, UiIconModule, ProductImageModule, FormsModule, UiInputModule, NavigateOnClickDirective],
|
||||
exports: [GoodsInListItemComponent],
|
||||
declarations: [GoodsInListItemComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -50,7 +50,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
editSsc: boolean;
|
||||
@@ -59,7 +59,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
showSaveSscSpinner$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
private readonly SCROLL_POSITION_TOKEN = 'GOODS_IN_LIST_SCROLL_POSITION';
|
||||
|
||||
@@ -70,7 +70,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private _router: Router,
|
||||
private _route: ActivatedRoute,
|
||||
private readonly _config: Config,
|
||||
private _cache: CacheService
|
||||
private _cache: CacheService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -155,7 +155,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private _addScrollPositionToCache(): void {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
|
||||
this.scrollContainer?.scrollPos
|
||||
this.scrollContainer?.scrollPos,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
order$ = this.orderId$.pipe(
|
||||
filter((orderId) => !!orderId),
|
||||
switchMap((orderId) => this._omsService.getOrder(orderId)),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
get processId() {
|
||||
@@ -59,7 +59,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((params) => +params.processId));
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
@@ -67,7 +67,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
private _omsService: DomainOmsService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router,
|
||||
private _uiModal: UiModalService
|
||||
private _uiModal: UiModalService,
|
||||
) {
|
||||
super({
|
||||
fetching: false,
|
||||
@@ -166,11 +166,11 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
(err) => {},
|
||||
() => {
|
||||
this.patchState({ fetching: false });
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
openModalIfItemsHaveDifferentCustomers(items: OrderItemListItemDTO[]) {
|
||||
|
||||
@@ -37,7 +37,7 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
processId: number;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
@ViewChild(UiFilterComponent, { static: false })
|
||||
uiFilter: UiFilterComponent;
|
||||
@@ -46,7 +46,7 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy {
|
||||
private _goodsOutSearchStore: GoodsOutSearchStore,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _router: Router
|
||||
private _router: Router,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -30,10 +30,10 @@ import { GoodsOutSearchMainAutocompleteProvider } from './providers/goods-out-se
|
||||
],
|
||||
})
|
||||
export class GoodsOutSearchComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
hasFilter$ = combineLatest([this._goodsOutSearchStore.filter$, this._goodsOutSearchStore.defaultSettings$]).pipe(
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), UiFilter.create(defaultFilter).getQueryParams()))
|
||||
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), UiFilter.create(defaultFilter).getQueryParams())),
|
||||
);
|
||||
|
||||
processId$ = this._activatedRoute.data.pipe(map((data) => +data.processId));
|
||||
@@ -41,7 +41,7 @@ export class GoodsOutSearchComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private _goodsOutSearchStore: GoodsOutSearchStore,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _activatedRoute: ActivatedRoute
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
|
||||
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
selectedOrderItemSubsetIds$ = this.select((s) => s.selectedOrderItemSubsetIds);
|
||||
@@ -47,9 +47,9 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
|
||||
actions$ = combineLatest([this.items$, this.selectedOrderItemSubsetIds$]).pipe(
|
||||
map(([items, selectedOrderItemSubsetIds]) =>
|
||||
items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId))
|
||||
items?.find((item) => selectedOrderItemSubsetIds.find((orderItemSubsetId) => item.orderItemSubsetId === orderItemSubsetId)),
|
||||
),
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action)))
|
||||
map((item) => item?.actions?.filter((action) => this.selectionRules(action))),
|
||||
);
|
||||
|
||||
get selectedItems() {
|
||||
@@ -70,7 +70,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((data) => +data.processId));
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
trackByFn: TrackByFunction<OrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;
|
||||
|
||||
@@ -84,7 +84,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _commandService: CommandService,
|
||||
private _modal: UiModalService
|
||||
private _modal: UiModalService,
|
||||
) {
|
||||
super({
|
||||
selectedOrderItemSubsetIds: [],
|
||||
@@ -233,7 +233,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
this.removeScrollPosition(processId);
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
this._searchResultSubscription.add(
|
||||
@@ -244,7 +244,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
this.scrollContainer?.scrollTo(scrollPos);
|
||||
this.removeScrollPosition(processId);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
buyerNumber: orderItem.buyerNumber,
|
||||
archive,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this._router.navigate([`/kunde/${processId}/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`], {
|
||||
|
||||
@@ -5,3 +5,7 @@
|
||||
.action-wrapper {
|
||||
@apply grid grid-flow-col gap-4 justify-center my-6 fixed bottom-24 inset-x-0;
|
||||
}
|
||||
|
||||
.annotation-layout {
|
||||
@apply col-span-2;
|
||||
}
|
||||
|
||||
@@ -3,33 +3,7 @@
|
||||
</div>
|
||||
<ng-container *ngIf="packageDetails$ | async; else loader; let packageDetails" (sharedOnInit)="calculateListHeight()">
|
||||
<div class="bg-background-liste">
|
||||
<div #handlungsAnweisung [ngSwitch]="packageDetails.package.arrivalStatus | arrivalStatus">
|
||||
<div class="bg-white" *ngSwitchCase="'Falsche Filiale'">
|
||||
<p class="text-center text-xl py-10">
|
||||
Stellen Sie dieses Packstück für die andere Filiale bereit. <br />
|
||||
Der Spediteur holt es zum nächstmöglichen Zeitpunkt ab.
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white" *ngSwitchCase="'Offen'">
|
||||
<p class="text-center text-xl py-10" *ngIf="!(childActions$ | async)">
|
||||
Können Sie sich erinnern, dass Sie dieses Packstück <br />
|
||||
ausgepackt haben?
|
||||
</p>
|
||||
<p class="text-center text-xl py-10" *ngIf="!!(childActions$ | async)">
|
||||
Prüfen Sie bitte stichprobenartig den Filialbestand des <br />
|
||||
dargestellten Artikels. Ist der angezeigte Filialbestand <br />
|
||||
korrekt?
|
||||
</p>
|
||||
</div>
|
||||
<div class="bg-white" *ngSwitchCase="'Fehlt'">
|
||||
<p class="text-center text-xl py-10">
|
||||
Prüfen Sie bitte stichprobenartig den Filialbestand <br />
|
||||
des dargestellten Artikels. Ist der angezeigte Filialbestand <br />
|
||||
korrekt?
|
||||
</p>
|
||||
</div>
|
||||
<div class="pt-3" *ngSwitchDefault></div>
|
||||
</div>
|
||||
<div class="bg-white text-center text-xl py-10" [innerText]="packageDetails?.package?.features?.['description'] ?? ''"></div>
|
||||
|
||||
<div class="bg-white rounded-t shadow-card grid grid-flow-row p-4 gap-2">
|
||||
<div class="grid grid-cols-6">
|
||||
@@ -38,7 +12,7 @@
|
||||
<span *ngIf="packageDetails.package.packageNumber && packageDetails.package.deliveryNoteNumber"> | </span>
|
||||
{{ packageDetails.package.deliveryNoteNumber }}
|
||||
</div>
|
||||
<div class="col-span-3">
|
||||
<div class="col-span-3" [class.annotation-layout]="packageDetails?.package?.features?.['annotation']">
|
||||
<ng-container *ngIf="packageDetails.package.arrivalStatus !== 8; else irrlauferTmplt">
|
||||
Filialstopp
|
||||
<span class="font-bold ml-2">
|
||||
@@ -52,7 +26,7 @@
|
||||
</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div data-which="Statusmeldung" class="text-right" *ngIf="!packageDetails?.package?.features?.['annotation']; else annotationTmpl">
|
||||
<ng-container *ngIf="(packageDetails.package.arrivalStatus | arrivalStatus) === 'Fehlt'">
|
||||
<button
|
||||
class="isa-icon-button mr-2"
|
||||
@@ -78,10 +52,16 @@
|
||||
<div
|
||||
class="isa-label text-white font-bold page-package-details__arrival-status"
|
||||
[class]="packageDetails.package.arrivalStatus | arrivalStatusColorClass"
|
||||
data-what="arrival-status"
|
||||
>
|
||||
{{ packageDetails.package.arrivalStatus | arrivalStatus }}
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #annotationTmpl>
|
||||
<div class="page-package-details__annotation text-right text-[#5A728A] col-span-2" data-what="annotation">
|
||||
{{ packageDetails?.package?.features?.['annotation'] }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="grid grid-cols-6">
|
||||
<div class="col-span-2">
|
||||
|
||||
@@ -148,7 +148,7 @@ export class PackageDetailsComponent {
|
||||
|
||||
@HostListener('window:resize')
|
||||
calculateListHeight() {
|
||||
const handlungsAnweisungHeight = this.handlungsAnweisung?.nativeElement.offsetHeight;
|
||||
const handlungsAnweisungHeight = this.handlungsAnweisung?.nativeElement?.offsetHeight ?? 0;
|
||||
const windowHeight = window.innerHeight;
|
||||
this.detailsListHeight = windowHeight - handlungsAnweisungHeight - 516;
|
||||
this._cdr.markForCheck();
|
||||
|
||||
@@ -36,12 +36,22 @@
|
||||
|
||||
<page-pickup-shelf-details-tags class="mb-px-2" *ngIf="showTagsComponent$ | async"></page-pickup-shelf-details-tags>
|
||||
|
||||
<page-pickup-shelf-details-covers
|
||||
*ngIf="(coverOrderItems$ | async)?.length > 0"
|
||||
[coverItems]="coverOrderItems$ | async"
|
||||
[selectedOrderItem]="selectedItem$ | async"
|
||||
(coverClick)="coverClick($event)"
|
||||
></page-pickup-shelf-details-covers>
|
||||
<ng-container *ngIf="fetchingCoverItems$ | async; else coverItemsTmpl">
|
||||
<div class="bg-white grid grid-flow-col gap-5 justify-center items-center h-40">
|
||||
<shared-skeleton-loader class="h-16 w-12"></shared-skeleton-loader>
|
||||
<shared-skeleton-loader class="h-16 w-12"></shared-skeleton-loader>
|
||||
<shared-skeleton-loader class="h-16 w-12"></shared-skeleton-loader>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #coverItemsTmpl>
|
||||
<page-pickup-shelf-details-covers
|
||||
*ngIf="(coverOrderItems$ | async)?.length > 0"
|
||||
[coverItems]="coverOrderItems$ | async"
|
||||
[selectedOrderItem]="selectedItem$ | async"
|
||||
(coverClick)="coverClick($event)"
|
||||
></page-pickup-shelf-details-covers>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="page-pickup-shelf-in-details__action-wrapper">
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { RunCheckTrigger } from '../../trigger';
|
||||
import { PickUpShelfDetailsItemsGroupComponent } from '../../shared/pickup-shelf-details-items-group/pickup-shelf-details-items-group.component';
|
||||
import { isEqual } from 'lodash';
|
||||
import { SkeletonLoaderComponent } from '@shared/components/loader';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-in-details',
|
||||
@@ -38,6 +39,7 @@ import { isEqual } from 'lodash';
|
||||
PickupShelfAddToPreviousCompartmentCodeLabelPipe,
|
||||
UiSpinnerModule,
|
||||
OnInitDirective,
|
||||
SkeletonLoaderComponent,
|
||||
],
|
||||
})
|
||||
export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseComponent implements OnInit, AfterViewInit {
|
||||
@@ -54,12 +56,12 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
|
||||
noOrderItemsFound$ = this.store.noOrderItemsFound$;
|
||||
|
||||
fetching$ = this.store.fetchingOrder$;
|
||||
fetchingOrder$ = this.store.fetchingOrder$;
|
||||
fetchingItems$ = this.store.fetchingOrderItems$;
|
||||
fetchingCoverItems$ = this.store.fetchingCoverOrderItems$;
|
||||
|
||||
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$, this.fetchingCoverItems$]).pipe(
|
||||
map(([fetching, fetchingItems, fetchingCoverItems]) => fetching || fetchingItems || fetchingCoverItems)
|
||||
viewFetching$ = combineLatest([this.fetchingItems$, this.orderItems$]).pipe(
|
||||
map(([fetchingItems, orderItems]) => fetchingItems && orderItems.length === 0),
|
||||
);
|
||||
|
||||
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
|
||||
@@ -91,14 +93,17 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
return `${latestCompartmentInfos?.latestCompartmentCode}_${latestCompartmentInfos?.latestCompartmentInfo}`;
|
||||
}
|
||||
return latestCompartmentInfos?.latestCompartmentCode;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
addToPreviousCompartmentActionDisabled$ = combineLatest([this.displayedCompartmentInfo$, this.changeActionDisabled$]).pipe(
|
||||
map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled)
|
||||
map(([compartmentInfo, changeActionDisabled]) => !!compartmentInfo || changeActionDisabled),
|
||||
);
|
||||
|
||||
constructor(private _uiModal: UiModalService, private _activatedRoute: ActivatedRoute) {
|
||||
constructor(
|
||||
private _uiModal: UiModalService,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -108,16 +113,22 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
.subscribe(([params, items]) => {
|
||||
const orderItemSubsetId = +params?.orderItemSubsetId;
|
||||
const selectedItem: DBHOrderItemListItemDTO = items?.find(
|
||||
(item: DBHOrderItemListItemDTO) => item?.orderItemSubsetId === orderItemSubsetId
|
||||
(item: DBHOrderItemListItemDTO) => item?.orderItemSubsetId === orderItemSubsetId,
|
||||
);
|
||||
|
||||
// Trigger functions only if a new item gets selected
|
||||
if (!!selectedItem && this.store.selectPreviousSelectedOrderItemSubsetId !== orderItemSubsetId) {
|
||||
this.store.setPreviousSelectedOrderItemSubsetId(orderItemSubsetId); // Wichtig das die ID im Store vorhanden bleibt um z.B. für die Filter eine zurücknavigation zu ermöglichen
|
||||
this.store.selectOrderItem(selectedItem, true); // Wird automatisch unselected wenn die Details Seite verlassen wird
|
||||
this.store.fetchCoverOrderItems();
|
||||
}
|
||||
});
|
||||
|
||||
// Fix #4696 - Always Fetch Cover Order Items
|
||||
this._activatedRoute.params.pipe(distinctUntilChanged(isEqual), takeUntilDestroyed(this.destroyRef)).subscribe((_) => {
|
||||
if (!this.store.coverOrderItems || this.store.coverOrderItems.length === 0) {
|
||||
this.store.fetchCoverOrderItems();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@@ -168,7 +179,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
},
|
||||
side: false,
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
{ queryParamsHandling: 'preserve' },
|
||||
);
|
||||
this.listStore.patchOrderItem({
|
||||
orderItemSubsetId: item.orderItemSubsetId,
|
||||
@@ -212,20 +223,21 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
}
|
||||
|
||||
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
|
||||
this.store.updateOrderItemSubsetLoading(true);
|
||||
switch (type) {
|
||||
case 'delivery':
|
||||
this.store.selectedOrderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
case 'pickup':
|
||||
this.store.selectedOrderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
case 'preferred':
|
||||
this.store.selectedOrderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@@ -249,9 +261,9 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
{ side: this.side }
|
||||
{ side: this.side },
|
||||
).path,
|
||||
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' }
|
||||
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,9 +278,9 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
orderItemSubsetId: orderItem.orderItemSubsetId,
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
{ side: this.side }
|
||||
{ side: this.side },
|
||||
).path,
|
||||
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' }
|
||||
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -292,7 +304,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
|
||||
},
|
||||
side: this.side,
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
{ queryParamsHandling: 'preserve' },
|
||||
);
|
||||
} else {
|
||||
if (orderItems.every((item) => item.processingStatus === 128)) {
|
||||
|
||||
@@ -97,6 +97,10 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
|
||||
const order = await this.detailsStore.order$.pipe(take(1)).toPromise();
|
||||
|
||||
if (!order) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (order?.orderNumber) {
|
||||
return order?.orderNumber;
|
||||
}
|
||||
@@ -111,6 +115,12 @@ export class PickupShelfInComponent extends PickupShelfBaseComponent {
|
||||
const orderItemSubsetId = this.detailsStore.orderItemSubsetId;
|
||||
const compartmentInfo = this.detailsStore.compartmentInfo;
|
||||
|
||||
// Ticket #4692 - Wenn keine Order vorhanden ist, dann soll Breadcrumb nicht erstellt werden
|
||||
// Dies kann z.B. passieren bei Datenbankproblemen wenn das Fetchen der Order sehr lange braucht
|
||||
if (!order) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._pickupShelfInNavigationService.detailRoute({
|
||||
item: {
|
||||
orderId: order?.id,
|
||||
|
||||
@@ -46,7 +46,9 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
|
||||
order$ = this.store.order$;
|
||||
|
||||
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.store.orderItems$.pipe(
|
||||
orderItems$ = this.store.orderItems$;
|
||||
|
||||
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.orderItems$.pipe(
|
||||
map((items) => {
|
||||
const groups: Array<{ type: string; items: DBHOrderItemListItemDTO[] }> = [];
|
||||
|
||||
@@ -63,13 +65,15 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
}
|
||||
|
||||
return groups;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
fetching$ = this.store.fetchingOrder$;
|
||||
fetchingOrder$ = this.store.fetchingOrder$;
|
||||
fetchingItems$ = this.store.fetchingOrderItems$;
|
||||
|
||||
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$]).pipe(map(([fetching, fetchingItems]) => fetching || fetchingItems));
|
||||
viewFetching$ = combineLatest([this.orderItems$, this.fetchingItems$]).pipe(
|
||||
map(([orderItems, fetchingItems]) => orderItems?.length === 0 && fetchingItems),
|
||||
);
|
||||
|
||||
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
|
||||
|
||||
@@ -125,7 +129,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
}).path,
|
||||
{ queryParamsHandling: 'preserve' }
|
||||
{ queryParamsHandling: 'preserve' },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -148,20 +152,21 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
}
|
||||
|
||||
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
|
||||
this.store.updateOrderItemSubsetLoading(true);
|
||||
switch (type) {
|
||||
case 'delivery': // vsl. Lieferdatum
|
||||
this.store.orderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { estimatedShippingDate: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
case 'pickup': // Abholfrist
|
||||
this.store.orderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { compartmentStop: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
case 'preferred': // Zurücklegen bis
|
||||
this.store.orderItems.forEach((item) =>
|
||||
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } })
|
||||
this.store.patchOrderItemSubset({ item, changes: { preferredPickUpDate: date.toISOString() } }),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@@ -186,7 +191,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
}).path,
|
||||
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' }
|
||||
{ queryParams: { buyerNumber: orderItem?.buyerNumber }, queryParamsHandling: 'merge' },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +207,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
|
||||
compartmentInfo: orderItem.compartmentInfo,
|
||||
},
|
||||
}).path,
|
||||
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' }
|
||||
{ queryParams: { orderItemSubsetId: orderItem.orderItemSubsetId }, queryParamsHandling: 'merge' },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { PickupShelfIOService, PickupShelfOutService } from '@domain/pickup-shel
|
||||
import { GetNameForBreadcrumbData, GetPathForBreadcrumbData, PickupShelfBaseComponent } from '../pickup-shelf-base.component';
|
||||
import { NavigationRoute, PickUpShelfOutNavigationService } from '@shared/services';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { DBHOrderItemListItemDTO, ListResponseArgsOfDBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { provideActionHandlers } from '@core/command';
|
||||
@@ -127,7 +127,7 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
|
||||
processingStatus: item.processingStatus,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
|
||||
processingStatus: item.processingStatus,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
|
||||
processingStatus: item.processingStatus,
|
||||
compartmentInfo: item.compartmentInfo,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
|
||||
*/
|
||||
const filterQueryParams = this.listStore.filter.getQueryParams();
|
||||
|
||||
if (response.hits === 1 || this._hasSameOrderNumber(response)) {
|
||||
if (response.hits === 1) {
|
||||
const detailsPath = await this.getPathForDetail(response.result[0]).pipe(take(1)).toPromise();
|
||||
await this.router.navigate(detailsPath.path, { queryParams: { ...queryParams, ...filterQueryParams, ...detailsPath.queryParams } });
|
||||
} else if (response.hits > 1) {
|
||||
@@ -224,10 +224,13 @@ export class PickupShelfOutComponent extends PickupShelfBaseComponent {
|
||||
});
|
||||
}
|
||||
|
||||
// Fix Ticket #4684 Navigate on Details if items contain same OrderNumber
|
||||
private _hasSameOrderNumber(response: ListResponseArgsOfDBHOrderItemListItemDTO) {
|
||||
if (response.hits === 0) return false;
|
||||
const orderNumbers = new Set(response.result.map((item) => item.orderNumber));
|
||||
return orderNumbers.size === 1;
|
||||
}
|
||||
// Ticket 4720 WA // Trefferliste wird übersprungen - Bei mehreren Bestellposten pro Bestellung soll IMMER auf Trefferliste navigiert werden
|
||||
// Damit werden #4684 und #4688 überflüssig
|
||||
|
||||
// REMOVED: Fix Ticket #4684 Navigate on Details if items contain same OrderNumber
|
||||
// private _hasSameOrderNumber(response: ListResponseArgsOfDBHOrderItemListItemDTO) {
|
||||
// if (response.hits === 0) return false;
|
||||
// const orderNumbers = new Set(response.result.map((item) => item.orderNumber));
|
||||
// return orderNumbers.size === 1;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<ng-container *ngIf="orderItem$ | async; let orderItem">
|
||||
<div class="grid grid-flow-row gap-px-2">
|
||||
<div class="bg-[#F5F7FA] flex flex-row justify-between items-center p-4 rounded-t">
|
||||
<div class="grid grid-flow-col gap-[0.4375rem] items-center" *ngIf="features$ | async; let features; else: featureLoading">
|
||||
<shared-icon *ngIf="features?.length > 0" [size]="24" icon="person"></shared-icon>
|
||||
<div class="grid grid-flow-col gap-2 items-center font-bold text-p2" *ngFor="let feature of features">
|
||||
{{ feature?.description }}
|
||||
</div>
|
||||
<div class="grid grid-flow-col gap-[0.4375rem] items-center" *ngIf="fetchingCustomerDone$ | async; else featureLoading">
|
||||
<ng-container *ngIf="features$ | async; let features">
|
||||
<shared-icon *ngIf="features?.length > 0" [size]="24" icon="person"></shared-icon>
|
||||
<div class="grid grid-flow-col gap-2 items-center font-bold text-p2" *ngFor="let feature of features">
|
||||
{{ feature?.description }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -18,6 +20,10 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template #featureLoading>
|
||||
<shared-skeleton-loader class="w-64 h-6"></shared-skeleton-loader>
|
||||
</ng-template>
|
||||
|
||||
<div class="page-pickup-shelf-details-header__details bg-white px-4 pt-4 pb-5">
|
||||
<div class="flex flex-row items-center" [class.mb-8]="!orderItem?.features?.paid && !isKulturpass">
|
||||
<page-pickup-shelf-details-header-nav-menu class="mr-2" [customer]="customer$ | async"></page-pickup-shelf-details-header-nav-menu>
|
||||
@@ -96,7 +102,12 @@
|
||||
</div>
|
||||
<div class="flex flex-row page-pickup-shelf-details-header__order-source" data-detail-id="Bestellkanal">
|
||||
<div class="min-w-[9rem]">Bestellkanal</div>
|
||||
<div class="flex flex-row font-bold">{{ order?.features?.orderSource }}</div>
|
||||
<div class="flex flex-row font-bold">
|
||||
<shared-skeleton-loader class="w-32" *ngIf="fetchingOrder$ | async; else orderSourceTmpl"></shared-skeleton-loader>
|
||||
<ng-template #orderSourceTmpl>
|
||||
{{ order()?.features?.orderSource }}
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex flex-row page-pickup-shelf-details-header__change-date justify-between"
|
||||
@@ -137,24 +148,21 @@
|
||||
data-detail-id="Benachrichtigung"
|
||||
>
|
||||
<div class="min-w-[9rem]">Benachrichtigung</div>
|
||||
<div class="flex flex-row font-bold">{{ (notificationsChannel$ | async | notificationsChannel) || '-' }}</div>
|
||||
<div class="flex flex-row font-bold">
|
||||
<shared-skeleton-loader class="w-32" *ngIf="fetchingOrder$ | async; else notificationsChannelTpl"></shared-skeleton-loader>
|
||||
<ng-template #notificationsChannelTpl>
|
||||
{{ (notificationsChannel$ | async | notificationsChannel) || '-' }}
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #featureLoading>
|
||||
<div class="fetch-wrapper">
|
||||
<div class="fetching"></div>
|
||||
<div class="fetching"></div>
|
||||
<div class="fetching"></div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #abholfrist>
|
||||
<div class="min-w-[9rem]">Abholfrist</div>
|
||||
<div *ngIf="!(changeDateLoader$ | async)" class="flex flex-row font-bold">
|
||||
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
|
||||
<button
|
||||
[uiOverlayTrigger]="deadlineDatepicker"
|
||||
#deadlineDatepickerTrigger="uiOverlayTrigger"
|
||||
@@ -178,12 +186,11 @@
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"></ui-spinner>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #preferredPickUpDate>
|
||||
<div class="min-w-[9rem]">Zurücklegen bis</div>
|
||||
<div *ngIf="!(changePreferredDateLoader$ | async)" class="flex flex-row font-bold">
|
||||
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
|
||||
<button
|
||||
[uiOverlayTrigger]="preferredPickUpDatePicker"
|
||||
#preferredPickUpDatePickerTrigger="uiOverlayTrigger"
|
||||
@@ -210,12 +217,11 @@
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<ui-spinner *ngIf="changePreferredDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"> </ui-spinner>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #vslLieferdatum>
|
||||
<div class="min-w-[9rem]">vsl. Lieferdatum</div>
|
||||
<div *ngIf="!(changeDateLoader$ | async)" class="flex flex-row font-bold">
|
||||
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
|
||||
<button
|
||||
class="cta-datepicker"
|
||||
[disabled]="changeDateDisabled$ | async"
|
||||
@@ -239,6 +245,9 @@
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"></ui-spinner>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #featureLoading>
|
||||
<shared-skeleton-loader class="w-64 h-6"></shared-skeleton-loader>
|
||||
</ng-template>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { PickupShelfNotificationsChannelPipe } from '../pipes/notifications-chan
|
||||
import { PickupShelfProcessingStatusPipe } from '../pipes/processing-status.pipe';
|
||||
import { UiDatepickerModule } from '@ui/datepicker';
|
||||
import { PickUpShelfDetailsHeaderNavMenuComponent } from '../pickup-shelf-details-header-nav-menu/pickup-shelf-details-header-nav-menu.component';
|
||||
import { SkeletonLoaderComponent } from '@shared/components/loader';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-details-header',
|
||||
@@ -36,6 +38,7 @@ import { PickUpShelfDetailsHeaderNavMenuComponent } from '../pickup-shelf-detail
|
||||
UiCommonModule,
|
||||
UiDatepickerModule,
|
||||
PickUpShelfDetailsHeaderNavMenuComponent,
|
||||
SkeletonLoaderComponent,
|
||||
],
|
||||
})
|
||||
export class PickUpShelfDetailsHeaderComponent {
|
||||
@@ -50,13 +53,13 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
@Output()
|
||||
updateDate = new EventEmitter<{ date: Date; type?: 'delivery' | 'pickup' | 'preferred' }>();
|
||||
|
||||
get order$(): Observable<OrderDTO> {
|
||||
return this._store.order$;
|
||||
}
|
||||
orderItemSubsetLoading$ = this._store.orderItemSubsetLoading$;
|
||||
|
||||
get order(): OrderDTO {
|
||||
return this._store.order;
|
||||
}
|
||||
fetchingOrder$ = this._store.fetchingOrder$;
|
||||
|
||||
order$ = this._store.order$;
|
||||
|
||||
order = toSignal(this.order$);
|
||||
|
||||
findLatestPreferredPickUpDate$: Observable<Date> = this.order$.pipe(
|
||||
withLatestFrom(this._store.selectedOrderItemIds$),
|
||||
@@ -72,12 +75,12 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
latestDate = new Date(
|
||||
subsetItems?.reduce((a, b) => {
|
||||
return new Date(a.data.preferredPickUpDate) > new Date(b.data.preferredPickUpDate) ? a : b;
|
||||
})?.data?.preferredPickUpDate
|
||||
})?.data?.preferredPickUpDate,
|
||||
);
|
||||
}
|
||||
|
||||
return latestDate;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
notificationsChannel$: Observable<NotificationChannel> = this.order$.pipe(map((order) => order?.notificationChannels ?? 0));
|
||||
@@ -86,7 +89,7 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
processId?: number;
|
||||
|
||||
get isKulturpass() {
|
||||
return this.order?.features?.orderSource === 'KulturPass';
|
||||
return this.order()?.features?.orderSource === 'KulturPass';
|
||||
}
|
||||
|
||||
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
|
||||
@@ -105,12 +108,14 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
|
||||
changeDateDisabled$ = this.changeStatusDisabled$;
|
||||
|
||||
fetchingCustomerDone$ = this._store.fetchingCustomer$.pipe(map((fetchingCustomer) => !fetchingCustomer));
|
||||
|
||||
customer$ = this._store.customer$;
|
||||
|
||||
features$ = this.customer$.pipe(
|
||||
map((customer) => customer?.features || []),
|
||||
map((features) => features.filter((f) => f.enabled && !!f.description)),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
|
||||
@@ -118,14 +123,17 @@ export class PickUpShelfDetailsHeaderComponent {
|
||||
crudaUpdate$ = this.orderItem$.pipe(map((orederItem) => !!(orederItem?.cruda & 4)));
|
||||
|
||||
editButtonDisabled$ = combineLatest([this.changeStatusLoader$, this.crudaUpdate$]).pipe(
|
||||
map(([changeStatusLoader, crudaUpdate]) => changeStatusLoader || !crudaUpdate)
|
||||
map(([changeStatusLoader, crudaUpdate]) => changeStatusLoader || !crudaUpdate),
|
||||
);
|
||||
|
||||
canEditStatus$ = combineLatest([this.statusActions$, this.crudaUpdate$]).pipe(
|
||||
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate)
|
||||
map(([statusActions, crudaUpdate]) => statusActions?.length > 0 && crudaUpdate),
|
||||
);
|
||||
|
||||
constructor(private dateAdapter: DateAdapter, private cdr: ChangeDetectorRef) {}
|
||||
constructor(
|
||||
private dateAdapter: DateAdapter,
|
||||
private cdr: ChangeDetectorRef,
|
||||
) {}
|
||||
|
||||
async handleActionClick(action?: KeyValueDTOOfStringAndString) {
|
||||
this.changeStatusLoader$.next(true);
|
||||
|
||||
@@ -32,7 +32,11 @@
|
||||
</div>
|
||||
<div class="page-pickup-shelf-details-item__item-container">
|
||||
<div class="page-pickup-shelf-details-item__thumbnail">
|
||||
<img [src]="orderItem.product?.ean | productImage" [alt]="orderItem.product?.name" />
|
||||
<img
|
||||
[productImageNavigation]="orderItem?.product?.ean"
|
||||
[src]="orderItem.product?.ean | productImage"
|
||||
[alt]="orderItem.product?.name"
|
||||
/>
|
||||
</div>
|
||||
<div class="page-pickup-shelf-details-item__details">
|
||||
<div class="flex flex-row justify-between items-start mb-[1.3125rem]">
|
||||
@@ -46,9 +50,7 @@
|
||||
<div>{{ orderItem.product?.name }}</div>
|
||||
</h3>
|
||||
<div class="history-wrapper flex flex-col items-end justify-center">
|
||||
<button class="cta-history text-p1" (click)="historyClick.emit(orderItem)">
|
||||
Historie
|
||||
</button>
|
||||
<button class="cta-history text-p1" (click)="historyClick.emit(orderItem)">Historie</button>
|
||||
|
||||
<input
|
||||
*ngIf="selectable$ | async"
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
|
||||
import { ProductImageModule } from '@cdn/product-image';
|
||||
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
|
||||
import { DBHOrderItemListItemDTO, OrderDTO, ReceiptDTO } from '@swagger/oms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
@@ -57,6 +57,7 @@ export interface PickUpShelfDetailsItemComponentState {
|
||||
IconModule,
|
||||
UiQuantityDropdownModule,
|
||||
NotificationTypePipe,
|
||||
NavigateOnClickDirective,
|
||||
],
|
||||
})
|
||||
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {
|
||||
@@ -103,7 +104,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
readonly orderItem$ = this.select((s) => s.orderItem);
|
||||
|
||||
emailNotificationDates$ = this.orderItem$.pipe(
|
||||
switchMap((orderItem) => this._store.getEmailNotificationDate$(orderItem?.orderItemSubsetId))
|
||||
switchMap((orderItem) => this._store.getEmailNotificationDate$(orderItem?.orderItemSubsetId)),
|
||||
);
|
||||
|
||||
hasEmailNotification$ = this.emailNotificationDates$.pipe(map((dates) => dates?.length > 0));
|
||||
@@ -113,7 +114,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
hasSmsNotification$ = this.smsNotificationDates$.pipe(map((dates) => dates?.length > 0));
|
||||
|
||||
canChangeQuantity$ = combineLatest([this.orderItem$, this._store.fetchPartial$]).pipe(
|
||||
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1)
|
||||
map(([item, partialPickup]) => ([16, 8192].includes(item?.processingStatus) || partialPickup) && item.quantity > 1),
|
||||
);
|
||||
|
||||
get quantity() {
|
||||
@@ -140,7 +141,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
}
|
||||
|
||||
readonly selected$ = combineLatest([this.orderItem$, this._store.selectedOrderItemIds$]).pipe(
|
||||
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId))
|
||||
map(([orderItem, selectedItems]) => selectedItems.includes(orderItem?.orderItemSubsetId)),
|
||||
);
|
||||
|
||||
@Output()
|
||||
@@ -155,7 +156,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
}
|
||||
|
||||
readonly selectable$ = combineLatest([this._store.orderItems$, this._store.fetchPartial$]).pipe(
|
||||
map(([orderItems, fetchPartial]) => orderItems.length > 1 && this.isItemSelectable && fetchPartial)
|
||||
map(([orderItems, fetchPartial]) => orderItems.length > 1 && this.isItemSelectable && fetchPartial),
|
||||
);
|
||||
|
||||
get receipts() {
|
||||
@@ -174,7 +175,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
|
||||
|
||||
more$ = this.select((s) => s.more);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
expanded: boolean = false;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*ngIf="historyItem$ | async; let item"
|
||||
class="self-end"
|
||||
type="button"
|
||||
(click)="store.processId !== 7000 ? navigateToShelfOutDetailsPage(item) : navigateToShelfInDetailsPage(item)"
|
||||
(click)="listStore.processId !== 7000 ? navigateToShelfOutDetailsPage(item) : navigateToShelfInDetailsPage(item)"
|
||||
>
|
||||
<shared-icon icon="close" [size]="26"></shared-icon>
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { PickupShelfIOService } from '@domain/pickup-shelf';
|
||||
@@ -9,8 +9,8 @@ import { PickUpShelfOutNavigationService, PickupShelfInNavigationService } from
|
||||
import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||
import { PickupShelfStore } from '../../store';
|
||||
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
import { PickupShelfDetailsBaseComponent } from '../../pickup-shelf-details-base.component';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-history',
|
||||
@@ -21,9 +21,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
||||
host: { class: 'page-pickup-shelf-history' },
|
||||
imports: [AsyncPipe, NgIf, SharedHistoryListModule, IconModule],
|
||||
})
|
||||
export class PickUpShelfHistoryComponent {
|
||||
store = inject(PickupShelfStore);
|
||||
|
||||
export class PickUpShelfHistoryComponent extends PickupShelfDetailsBaseComponent {
|
||||
compartmentCode$: Observable<string> = this._activatedRoute.params.pipe(
|
||||
map((params) => decodeURIComponent(params?.compartmentCode ?? '') || undefined)
|
||||
);
|
||||
@@ -62,12 +60,14 @@ export class PickUpShelfHistoryComponent {
|
||||
private _shelfInNavigation: PickupShelfInNavigationService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _pickupShelfIOService: PickupShelfIOService
|
||||
) {}
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async navigateToShelfOutDetailsPage(item: DBHOrderItemListItemDTO) {
|
||||
await this._router.navigate(
|
||||
this._shelfOutNavigation.detailRoute({
|
||||
processId: this.store.processId,
|
||||
processId: this.listStore.processId,
|
||||
item: {
|
||||
compartmentCode: item.compartmentCode,
|
||||
orderId: item.orderId,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[routerLink]="itemDetailsLink"
|
||||
[routerLinkActive]="!isTablet && !primaryOutletActive ? 'active' : ''"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="isDesktopLarge ? scrollIntoView() : ''"
|
||||
(click)="onDetailsClick()"
|
||||
>
|
||||
<div
|
||||
class="page-pickup-shelf-list-item__item-grid-container"
|
||||
@@ -15,6 +15,7 @@
|
||||
class="page-pickup-shelf-list-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.product?.ean | productImage; let productImage"
|
||||
[productImageNavigation]="item?.product?.ean"
|
||||
[src]="productImage"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CurrencyPipe, DatePipe, NgFor, NgIf } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { ProductImageModule } from '@cdn/product-image';
|
||||
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { DBHOrderItemListItemDTO } from '@swagger/oms';
|
||||
@@ -10,6 +10,7 @@ import { PickupShelfProcessingStatusPipe } from '../pipes/processing-status.pipe
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { PickupShelfStore } from '../../store';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { CacheService } from '@core/cache';
|
||||
|
||||
@Component({
|
||||
selector: 'page-pickup-shelf-list-item',
|
||||
@@ -30,10 +31,13 @@ import { map } from 'rxjs/operators';
|
||||
ProductImageModule,
|
||||
UiCommonModule,
|
||||
PickupShelfProcessingStatusPipe,
|
||||
NavigateOnClickDirective,
|
||||
],
|
||||
providers: [PickupShelfProcessingStatusPipe],
|
||||
})
|
||||
export class PickUpShelfListItemComponent {
|
||||
private cache = inject(CacheService);
|
||||
|
||||
store = inject(PickupShelfStore);
|
||||
|
||||
@Input() item: DBHOrderItemListItemDTO;
|
||||
@@ -76,15 +80,54 @@ export class PickUpShelfListItemComponent {
|
||||
}
|
||||
|
||||
selected$ = this.store.selectedListItems$.pipe(
|
||||
map((selectedListItems) => selectedListItems?.find((item) => item?.orderItemSubsetId === this.item?.orderItemSubsetId))
|
||||
map((selectedListItems) => selectedListItems?.find((item) => item?.orderItemSubsetId === this.item?.orderItemSubsetId)),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _elRef: ElementRef,
|
||||
private _environment: EnvironmentService,
|
||||
private _processingStatusPipe: PickupShelfProcessingStatusPipe
|
||||
private _processingStatusPipe: PickupShelfProcessingStatusPipe,
|
||||
) {}
|
||||
|
||||
onDetailsClick() {
|
||||
if (this.isDesktopLarge) {
|
||||
this.scrollIntoView();
|
||||
}
|
||||
|
||||
if (!this.hasOrderItemInCache()) {
|
||||
this.addOrderItemIntoCache();
|
||||
}
|
||||
}
|
||||
|
||||
hasOrderItemInCache() {
|
||||
const items =
|
||||
this.cache.get<DBHOrderItemListItemDTO[]>({
|
||||
name: 'orderItems',
|
||||
orderId: this.item.orderId,
|
||||
compartmentCode: this.item.compartmentCode,
|
||||
compartmentInfo: this.item.compartmentInfo,
|
||||
orderItemProcessingStatus: this.item.processingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
}) ?? [];
|
||||
|
||||
return items.some((i) => i.orderItemSubsetId === this.item.orderItemSubsetId);
|
||||
}
|
||||
|
||||
addOrderItemIntoCache() {
|
||||
this.cache.set(
|
||||
{
|
||||
name: 'orderItems',
|
||||
orderId: this.item.orderId,
|
||||
compartmentCode: this.item.compartmentCode,
|
||||
compartmentInfo: this.item.compartmentInfo,
|
||||
orderItemProcessingStatus: this.item.processingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
},
|
||||
[this.item],
|
||||
{ persist: false, ttl: 1000 },
|
||||
);
|
||||
}
|
||||
|
||||
scrollIntoView() {
|
||||
this._elRef.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ export const selectSelectedOrderItemIds = (s: PickupShelfDetailsState) => s.sele
|
||||
|
||||
export const selectPreviousSelectedOrderItemSubsetId = (s: PickupShelfDetailsState) => s.previousSelectedOrderItemSubsetId;
|
||||
|
||||
export const selectOrderItemSubsetLoading = (s: PickupShelfDetailsState) => s.orderItemSubsetLoading;
|
||||
|
||||
export const selectDisableHeaderStatusDropdown = (s: PickupShelfDetailsState) => s.disableHeaderStatusDropdown;
|
||||
|
||||
export const selectedOrderItems = (s: PickupShelfDetailsState) => {
|
||||
@@ -160,10 +162,13 @@ export const selectNotifications = (orderItemSubsetId: number) => (s: PickupShel
|
||||
|
||||
return tasks
|
||||
.sort((a, b) => new Date(b.completed).getTime() - new Date(a.completed).getTime())
|
||||
.reduce((data, result) => {
|
||||
(data[result.name] = data[result.name] || []).push(new Date(result.completed));
|
||||
return data;
|
||||
}, {} as Record<string, Date[]>);
|
||||
.reduce(
|
||||
(data, result) => {
|
||||
(data[result.name] = data[result.name] || []).push(new Date(result.completed));
|
||||
return data;
|
||||
},
|
||||
{} as Record<string, Date[]>,
|
||||
);
|
||||
};
|
||||
|
||||
export const selectLatestNotificationDatesFor = (orderItemSubsetId: number, keys: string[]) => (s: PickupShelfDetailsState) => {
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface PickupShelfDetailsState {
|
||||
|
||||
fetchingOrderItemSubsetTasks: { [orderItemSubsetId: number]: boolean };
|
||||
orderItemSubsetTasks: { [orderItemSubsetId: number]: OrderItemSubsetTaskListItemDTO[] };
|
||||
orderItemSubsetLoading?: boolean;
|
||||
|
||||
coverOrderItems?: DBHOrderItemListItemDTO[];
|
||||
fetchingCoverOrderItems?: boolean;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import {
|
||||
DBHOrderItemListItemDTO,
|
||||
EntityDTOContainerOfOrderItemSubsetDTO,
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@swagger/oms';
|
||||
import { PickupShelfIOService, PickupShelfService } from '@domain/pickup-shelf';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { debounceTime, delayWhen, distinctUntilChanged, filter, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { delayWhen, filter, map, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import * as Selectors from './pickup-shelf-details.selectors';
|
||||
@@ -27,7 +27,6 @@ import { CacheService } from '@core/cache';
|
||||
import { RunCheckTrigger } from '../trigger';
|
||||
import { DomainReceiptService } from '@domain/oms';
|
||||
import { PickupShelfStore } from './pickup-shelf.store';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
@Injectable()
|
||||
export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsState> {
|
||||
@@ -57,10 +56,20 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
return this.get(Selectors.selectFetchingOrder);
|
||||
}
|
||||
|
||||
orderItems$ = this.select(Selectors.selectOrderItems);
|
||||
// orderItems$ = this.select(Selectors.selectOrderItems);
|
||||
|
||||
// get orderItems() {
|
||||
// return this.get(Selectors.selectOrderItems);
|
||||
// }
|
||||
|
||||
orderItems$ = combineLatest([
|
||||
this.select(Selectors.selectOrderItems),
|
||||
this.select((s) => s).pipe(switchMap((s) => this._listStore.itemsForPreview$(s))),
|
||||
]).pipe(map(([orderItems, itemsForPreview]) => (orderItems?.length ? orderItems : itemsForPreview)));
|
||||
|
||||
get orderItems() {
|
||||
return this.get(Selectors.selectOrderItems);
|
||||
const orderItems = this.get(Selectors.selectOrderItems);
|
||||
return orderItems?.length ? orderItems : this.get((s) => this._listStore.itemsForPreview(s));
|
||||
}
|
||||
|
||||
fetchingOrderItems$ = this.select(Selectors.selectFetchingOrderItems);
|
||||
@@ -236,6 +245,12 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
return this.get(Selectors.selectCoverOrderItems);
|
||||
}
|
||||
|
||||
orderItemSubsetLoading$ = this.select(Selectors.selectOrderItemSubsetLoading);
|
||||
|
||||
get orderItemSubsetLoading() {
|
||||
return this.get(Selectors.selectOrderItemSubsetLoading);
|
||||
}
|
||||
|
||||
// Wird benöitgt um das Status Dropdown in der Details Header Komponente zu disablen wenn eine Action gehandled wird
|
||||
disableHeaderStatusDropdown$ = this.select(Selectors.selectDisableHeaderStatusDropdown);
|
||||
|
||||
@@ -318,9 +333,9 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
ids: [item.orderItemSubsetId],
|
||||
eagerLoading: 1,
|
||||
})
|
||||
.pipe(tapResponse(this.fetchReceiptsDone, this.fetchReceiptsFailed))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.fetchReceiptsDone, this.fetchReceiptsFailed)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchReceipts = () => {
|
||||
@@ -340,14 +355,15 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
fetchOrder = this.effect((trigger$: Observable<{ orderId: number }>) =>
|
||||
trigger$.pipe(
|
||||
tap(({ orderId }) => this.beforeFetchOrder(orderId)),
|
||||
// delay(10000),
|
||||
switchMap(({ orderId }) =>
|
||||
this._pickupShelfService.getOrderByOrderId(orderId).pipe(tapResponse(this.fetchOrderSuccess, this.fetchOrderFailed))
|
||||
)
|
||||
)
|
||||
this._pickupShelfService.getOrderByOrderId(orderId).pipe(tapResponse(this.fetchOrderSuccess, this.fetchOrderFailed)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchOrder = (orderId) => {
|
||||
const order = this._cacheService.get<OrderDTO>({ name: 'order', orderId, store: 'PickupShelfDetailsStore' });
|
||||
const order = this._cacheService.get<OrderDTO>({ name: 'order', orderId, store: 'PickupShelfDetailsStore' }) ?? { id: orderId };
|
||||
const customer = this._cacheService.get<CustomerInfoDTO>({
|
||||
name: 'customer',
|
||||
orderId,
|
||||
@@ -384,7 +400,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
orderItemSubsetId?: number;
|
||||
orderDate?: string;
|
||||
done?: { resolve: (items: DBHOrderItemListItemDTO[]) => void; reject: (err?) => void };
|
||||
}>
|
||||
}>,
|
||||
) =>
|
||||
trigger$.pipe(
|
||||
tap((data) => this.beforeFetchOrderItems(data)),
|
||||
@@ -393,11 +409,11 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this._pickupShelfIOService.getOrderItemsByOrderNumberOrCompartmentCode({ compartmentCode, orderNumber, filter }).pipe(
|
||||
tapResponse(
|
||||
(r) => this.fetchOrderItemsSuccess({ compartmentCode, compartmentInfo, orderItemProcessingStatus }, done?.resolve)(r),
|
||||
(e) => this.fetchOrderItemsFailed(done?.reject)(e)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(e) => this.fetchOrderItemsFailed(done?.reject)(e),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchOrderItems = ({
|
||||
@@ -435,41 +451,43 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
});
|
||||
};
|
||||
|
||||
private fetchOrderItemsSuccess = (
|
||||
{
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
}: {
|
||||
compartmentCode?: string;
|
||||
compartmentInfo?: string;
|
||||
orderItemProcessingStatus?: OrderItemProcessingStatusValue;
|
||||
},
|
||||
resolve?: (items: DBHOrderItemListItemDTO[]) => void
|
||||
) => (res: ListResponseArgsOfDBHOrderItemListItemDTO) => {
|
||||
// // Ticket #4348
|
||||
// check if order items belong to the same customer
|
||||
// if (buyerNumbers.length > 1) {
|
||||
// this._modal.error('Fehler beim Laden der Bestellpositionen', new Error('Die Bestellpositionen gehören zu unterschiedlichen Kunden.'));
|
||||
// return;
|
||||
// }
|
||||
|
||||
this._cacheService.set<DBHOrderItemListItemDTO[]>(
|
||||
private fetchOrderItemsSuccess =
|
||||
(
|
||||
{
|
||||
name: 'orderItems',
|
||||
orderId: this.order?.id,
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
}: {
|
||||
compartmentCode?: string;
|
||||
compartmentInfo?: string;
|
||||
orderItemProcessingStatus?: OrderItemProcessingStatusValue;
|
||||
},
|
||||
res.result
|
||||
);
|
||||
resolve?: (items: DBHOrderItemListItemDTO[]) => void,
|
||||
) =>
|
||||
(res: ListResponseArgsOfDBHOrderItemListItemDTO) => {
|
||||
// // Ticket #4348
|
||||
// check if order items belong to the same customer
|
||||
// if (buyerNumbers.length > 1) {
|
||||
// this._modal.error('Fehler beim Laden der Bestellpositionen', new Error('Die Bestellpositionen gehören zu unterschiedlichen Kunden.'));
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.patchState({ fetchingOrderItems: false, orderItems: res.result });
|
||||
this._runCheckTrigger.next();
|
||||
resolve?.(res.result);
|
||||
};
|
||||
this._cacheService.set<DBHOrderItemListItemDTO[]>(
|
||||
{
|
||||
name: 'orderItems',
|
||||
orderId: this.order?.id,
|
||||
compartmentCode,
|
||||
compartmentInfo,
|
||||
orderItemProcessingStatus,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
},
|
||||
res.result,
|
||||
);
|
||||
|
||||
this.patchState({ fetchingOrderItems: false, orderItems: res.result });
|
||||
this._runCheckTrigger.next();
|
||||
resolve?.(res.result);
|
||||
};
|
||||
|
||||
private fetchOrderItemsFailed = (reject?: (err?) => void) => (err: any) => {
|
||||
this._modal.error('Fehler beim Laden der Bestellpositionen', err);
|
||||
@@ -480,11 +498,11 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
fetchCustomer = this.effect((trigger$: Observable<{ buyerNumber: string }>) =>
|
||||
trigger$.pipe(
|
||||
filter(({ buyerNumber }) => this.customer?.customerNumber !== buyerNumber),
|
||||
tap(this.beforeFetchCustomer),
|
||||
tap(() => this.beforeFetchCustomer()),
|
||||
switchMap(({ buyerNumber }) =>
|
||||
this._customerService.getCustomers(buyerNumber).pipe(tapResponse(this.fetchCustomerSuccess, this.fetchCustomerFailed))
|
||||
)
|
||||
)
|
||||
this._customerService.getCustomers(buyerNumber).pipe(tapResponse(this.fetchCustomerSuccess, this.fetchCustomerFailed)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
beforeFetchCustomer = () => {
|
||||
@@ -493,7 +511,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
orderId: this.order.id,
|
||||
store: 'PickupShelfDetailsStore',
|
||||
});
|
||||
this.patchState({ fetchingCustomer: true, customer });
|
||||
this.patchState({ fetchingCustomer: !customer, customer: customer });
|
||||
};
|
||||
|
||||
fetchCustomerSuccess = (res: ListResponseArgsOfCustomerInfoDTO) => {
|
||||
@@ -502,6 +520,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
// check if response contains exactly one customer
|
||||
if (customers.length > 1) {
|
||||
this._modal.error('Fehler beim Laden des Kunden', new Error('Es wurde mehr als ein Kunde gefunden.'));
|
||||
this.patchState({ fetchingCustomer: false });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -518,6 +537,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
// also check if the order is a KulturPass order, then there may be no customer
|
||||
if (!isKulturpass && customer?.customerNumber !== this.order.buyer.buyerNumber) {
|
||||
this._modal.error('Fehler beim Laden des Kunden', new Error('Der Kunde ist nicht der Bestellung zugeordnet.'));
|
||||
this.patchState({ fetchingCustomer: false });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -528,7 +548,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
store: 'PickupShelfDetailsStore',
|
||||
},
|
||||
customer,
|
||||
{ persist: true, ttl: 3600000 }
|
||||
{ persist: true, ttl: 3600000 },
|
||||
);
|
||||
|
||||
this.patchState({ fetchingCustomer: false, customer });
|
||||
@@ -546,11 +566,11 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
this._pickupShelfService.patchOrderItemSubset(data.item, data.changes).pipe(
|
||||
tapResponse(
|
||||
(res) => this.patchOrderItemSubsetDone(data.item)(res),
|
||||
(err) => this.patchOrderItemSubsetError(err)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => this.patchOrderItemSubsetError(err),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private patchOrderItemSubsetDone = (item: DBHOrderItemListItemDTO) => (res: ResponseArgsOfOrderItemSubsetDTO) => {
|
||||
@@ -567,6 +587,8 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
if (res.result.preferredPickUpDate) {
|
||||
this.patchPreferredPickUpDateOnOrderSubsetItemInState({ item, newPreferredPickUpDate: res.result.preferredPickUpDate });
|
||||
}
|
||||
|
||||
this.updateOrderItemSubsetLoading(false);
|
||||
};
|
||||
|
||||
private patchOrderItemSubsetError = (err: any) => {
|
||||
@@ -613,7 +635,7 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
return i;
|
||||
}) ?? [],
|
||||
};
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
fetchOrderItemSubsetTasks = this.effect((item$: Observable<DBHOrderItemListItemDTO>) =>
|
||||
@@ -622,9 +644,9 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
mergeMap((item) =>
|
||||
this._pickupShelfService
|
||||
.getOrderItemSubsetTasks(item)
|
||||
.pipe(tapResponse(this.fetchOrderItemSubsetTasksDone(item), this.fetchOrderItemSubsetTasksFailed(item)))
|
||||
)
|
||||
)
|
||||
.pipe(tapResponse(this.fetchOrderItemSubsetTasksDone(item), this.fetchOrderItemSubsetTasksFailed(item))),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchOrderItemSubsetTasks = (item: DBHOrderItemListItemDTO) => {
|
||||
@@ -683,8 +705,8 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
delayWhenCustomerNumberNotExists = delayWhen(() =>
|
||||
this.customerNumber$.pipe(
|
||||
filter((cn) => !!cn),
|
||||
take(1)
|
||||
)
|
||||
take(1),
|
||||
),
|
||||
);
|
||||
|
||||
fetchCoverOrderItems = this.effect((trigger$: Observable<void>) =>
|
||||
@@ -700,17 +722,21 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
|
||||
.pipe(
|
||||
tapResponse(
|
||||
(res) => this.fetchCoverOrderItemsDone(res),
|
||||
(err) => this.fetchCoverOrderItemsFailed(err)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => this.fetchCoverOrderItemsFailed(err),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchCoverOrderItems = () => {
|
||||
this.patchState({ fetchingCoverOrderItems: true });
|
||||
};
|
||||
|
||||
updateOrderItemSubsetLoading(orderItemSubsetLoading: boolean) {
|
||||
this.patchState({ orderItemSubsetLoading });
|
||||
}
|
||||
|
||||
private fetchCoverOrderItemsDone = (res: ListResponseArgsOfDBHOrderItemListItemDTO) => {
|
||||
this.patchState({ fetchingCoverOrderItems: false, coverOrderItems: res.result });
|
||||
};
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Filter } from '@shared/components/filter';
|
||||
import { PickupShelfState } from './pickup-shelf.state';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
|
||||
import {
|
||||
selectDisplayedCompartmentCode,
|
||||
selectDisplayedCompartmentInfo,
|
||||
selectDisplayedOrderItemProcessingStatus,
|
||||
selectOrder,
|
||||
} from './pickup-shelf-details.selectors';
|
||||
|
||||
export function selectProcessId(state: PickupShelfState) {
|
||||
return state.processId;
|
||||
@@ -64,3 +71,27 @@ export function selectListHits(state: PickupShelfState) {
|
||||
export function selectSelectedListItems(state: PickupShelfState) {
|
||||
return state.selectedListItems ?? [];
|
||||
}
|
||||
|
||||
export const selectItemsForPreview = (detailsState: PickupShelfDetailsState) => (state: PickupShelfState) => {
|
||||
const orderId = selectOrder(detailsState)?.id;
|
||||
|
||||
let items = state.list.filter((item) => item.orderId === orderId);
|
||||
|
||||
const processingStatus = selectDisplayedOrderItemProcessingStatus(detailsState);
|
||||
if (processingStatus) {
|
||||
items = items?.filter((oi) => oi.processingStatus === processingStatus);
|
||||
}
|
||||
|
||||
const compartmentCode = selectDisplayedCompartmentCode(detailsState);
|
||||
if (compartmentCode) {
|
||||
items = items?.filter((oi) => oi.compartmentCode === compartmentCode);
|
||||
|
||||
const compartmentInfo = selectDisplayedCompartmentInfo(detailsState);
|
||||
|
||||
if (compartmentInfo) {
|
||||
items = items?.filter((oi) => oi.compartmentInfo === compartmentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ import * as Selectors from './pickup-shelf.selectors';
|
||||
import { Observable, Subject, combineLatest } from 'rxjs';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { Filter } from '@shared/components/filter';
|
||||
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
|
||||
|
||||
@Injectable()
|
||||
export class PickupShelfStore extends ComponentStore<PickupShelfState> implements OnStoreInit {
|
||||
@@ -95,7 +96,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
|
||||
searchboxHint$ = combineLatest([this.fetchingList$, this.fetchListResponse$]).pipe(
|
||||
debounceTime(100),
|
||||
map(([fetching, response]) => (response?.response?.hits === 0 && !fetching ? 'Keine Suchergebnisse' : ''))
|
||||
map(([fetching, response]) => (response?.response?.hits === 0 && !fetching ? 'Keine Suchergebnisse' : '')),
|
||||
);
|
||||
|
||||
readonly selectedListItems$ = this.select(Selectors.selectSelectedListItems);
|
||||
@@ -104,6 +105,10 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
return this.get(Selectors.selectSelectedListItems);
|
||||
}
|
||||
|
||||
itemsForPreview$ = (state: PickupShelfDetailsState) => this.select(Selectors.selectItemsForPreview(state));
|
||||
|
||||
itemsForPreview = (state: PickupShelfDetailsState) => this.get(Selectors.selectItemsForPreview(state));
|
||||
|
||||
constructor() {
|
||||
// Nicht entfernen sonst wird der Store nicht initialisiert
|
||||
super({});
|
||||
@@ -169,9 +174,9 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
map(() => this.beforeFetchQuerySettings()),
|
||||
filter((shouldFetch) => shouldFetch),
|
||||
switchMap(() =>
|
||||
this._pickupShelfIOService.getQuerySettings().pipe(tapResponse(this.fetchQuerySettingsDone, this.fetchQuerySettingsError))
|
||||
)
|
||||
)
|
||||
this._pickupShelfIOService.getQuerySettings().pipe(tapResponse(this.fetchQuerySettingsDone, this.fetchQuerySettingsError)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -207,8 +212,8 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
delayWhenFilterIsNotReady = delayWhen((value: { emitFetchListResponse: boolean } | void) =>
|
||||
this.filter$.pipe(
|
||||
filter((filter) => !!filter),
|
||||
take(1)
|
||||
)
|
||||
take(1),
|
||||
),
|
||||
);
|
||||
|
||||
fetchList = this.effect((trigger$: Observable<{ emitFetchListResponse: boolean } | void>) =>
|
||||
@@ -216,7 +221,7 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
this.delayWhenFilterIsNotReady,
|
||||
withLatestFrom(this.filter$, this.processId$),
|
||||
map(([{ emitFetchListResponse } = { emitFetchListResponse: true }, filter, processId]) =>
|
||||
this.beforeFetchList(emitFetchListResponse, filter, processId)
|
||||
this.beforeFetchList(emitFetchListResponse, filter, processId),
|
||||
),
|
||||
switchMap(({ emitFetchListResponse, filter, processId, list }) =>
|
||||
this._pickupShelfIOService
|
||||
@@ -228,11 +233,11 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
takeUntil(this._cancelListRequests),
|
||||
tapResponse(
|
||||
(res) => this.fetchListDone({ processId, queryParams: filter.getQueryParams(), emitFetchListResponse })(res),
|
||||
(err) => this.fetchListError(err)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => this.fetchListError(err),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchList = (emitFetchListResponse: boolean, filter: Filter, processId: number) => {
|
||||
@@ -253,22 +258,26 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
return { emitFetchListResponse, filter, processId, list };
|
||||
};
|
||||
|
||||
private fetchListDone = ({
|
||||
processId,
|
||||
queryParams,
|
||||
emitFetchListResponse,
|
||||
}: {
|
||||
processId: number;
|
||||
queryParams: Record<string, string>;
|
||||
emitFetchListResponse: boolean;
|
||||
}) => (response: ListResponseArgsOfDBHOrderItemListItemDTO) => {
|
||||
this.patchState({ fetchingList: false, list: response.result, listHits: response.hits });
|
||||
if (emitFetchListResponse) {
|
||||
this._fetchListResponse.next({ processId, response, queryParams });
|
||||
}
|
||||
private fetchListDone =
|
||||
({
|
||||
processId,
|
||||
queryParams,
|
||||
emitFetchListResponse,
|
||||
}: {
|
||||
processId: number;
|
||||
queryParams: Record<string, string>;
|
||||
emitFetchListResponse: boolean;
|
||||
}) =>
|
||||
(response: ListResponseArgsOfDBHOrderItemListItemDTO) => {
|
||||
this.patchState({ fetchingList: false, list: response.result, listHits: response.hits });
|
||||
if (emitFetchListResponse) {
|
||||
this._fetchListResponse.next({ processId, response, queryParams });
|
||||
}
|
||||
|
||||
this._cacheService.set<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken: queryParams }, response, { persist: true });
|
||||
};
|
||||
this._cacheService.set<ListResponseArgsOfDBHOrderItemListItemDTO>({ processId, queryToken: queryParams }, response, {
|
||||
persist: true,
|
||||
});
|
||||
};
|
||||
|
||||
private fetchListError = (err: any) => {
|
||||
this._modalService.error('Fehler beim Laden der Liste', err);
|
||||
@@ -290,11 +299,11 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
takeUntil(this._cancelListRequests),
|
||||
tapResponse(
|
||||
(r) => this.fetchMoreListDone(r),
|
||||
(e) => this.fetchMoreListError(e)
|
||||
)
|
||||
(e) => this.fetchMoreListError(e),
|
||||
),
|
||||
);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
private beforeFetchMoreList = () => {
|
||||
@@ -314,6 +323,6 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
|
||||
patchOrderItem = this.updater(
|
||||
(state, { orderItemSubsetId, changes }: { orderItemSubsetId: number; changes: Partial<DBHOrderItemListItemDTO> }) => {
|
||||
return { ...state, list: this.list.map((li) => (li.orderItemSubsetId === orderItemSubsetId ? { ...li, ...changes } : li)) };
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class FinishShippingDocumentComponent implements OnInit, OnDestroy {
|
||||
private _router: Router,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _config: Config,
|
||||
private _remissionService: DomainRemissionService
|
||||
private _remissionService: DomainRemissionService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -89,15 +89,17 @@ export class FinishShippingDocumentComponent implements OnInit, OnDestroy {
|
||||
|
||||
modal.afterClosed$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
if (result?.data === 'correct') {
|
||||
await this.createReceiptAndAssignPackageNumber(query);
|
||||
try {
|
||||
await this.createReceiptAndAssignPackageNumber(query);
|
||||
|
||||
if (coerceBooleanProperty(this._activatedRoute.snapshot.queryParams?.complete)) {
|
||||
this._router.navigate(['/filiale', 'remission', this.returnId, 'shipping-document'], {
|
||||
queryParams: { complete: true },
|
||||
});
|
||||
} else {
|
||||
await this.navigateToRemissionList(query);
|
||||
}
|
||||
if (coerceBooleanProperty(this._activatedRoute.snapshot.queryParams?.complete)) {
|
||||
this._router.navigate(['/filiale', 'remission', this.returnId, 'shipping-document'], {
|
||||
queryParams: { complete: true },
|
||||
});
|
||||
} else {
|
||||
await this.navigateToRemissionList(query);
|
||||
}
|
||||
} catch (error) {}
|
||||
} else if (result?.data === 'rescan') {
|
||||
this.searchboxComponent.clear();
|
||||
}
|
||||
@@ -120,7 +122,11 @@ export class FinishShippingDocumentComponent implements OnInit, OnDestroy {
|
||||
packageNumber,
|
||||
});
|
||||
} catch (error) {
|
||||
this._modal.error('Fehler beim Speichern der Wannennummer', error);
|
||||
this._modal.error('Fehler beim Speichern der Wannennummer', error).afterClosed$.subscribe(() => {
|
||||
this._router.navigate(['/filiale', 'remission', this.returnId, 'finish-shipping-document', this.receiptId]);
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { AddProductModalData } from './add-product-modal.data';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AddProductModalComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
get processId() {
|
||||
return this._config.get('process.ids.remission');
|
||||
@@ -45,7 +45,7 @@ export class AddProductModalComponent implements OnInit, OnDestroy {
|
||||
private _remiService: DomainRemissionService,
|
||||
private _modal: UiModalService,
|
||||
private _config: Config,
|
||||
private _cache: CacheService
|
||||
private _cache: CacheService,
|
||||
) {
|
||||
this.item = this._modalRef.data.item;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { UiDropdownModule } from '@ui/dropdown';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { SharedProductGroupPipe } from '@shared/pipes/product-group';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -25,6 +26,7 @@ import { UiIconModule } from '@ui/icon';
|
||||
UiTooltipModule,
|
||||
UiDropdownModule,
|
||||
UiIconModule,
|
||||
SharedProductGroupPipe,
|
||||
],
|
||||
exports: [AddProductModalComponent],
|
||||
declarations: [AddProductModalComponent],
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export * from './product-group.pipe';
|
||||
export * from './remission-pipe.module';
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AssortmentPipe } from './assortment.pipe';
|
||||
import { ProductGroupPipe } from './product-group.pipe';
|
||||
import { ShortReceiptNumberPipe } from './short-receipt-number.pipe';
|
||||
import { SupplierPipe } from './supplier.pipe';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ProductGroupPipe, AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe],
|
||||
exports: [ProductGroupPipe, AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe],
|
||||
declarations: [AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe],
|
||||
exports: [AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe],
|
||||
})
|
||||
export class RemissionPipeModule {}
|
||||
|
||||
@@ -16,7 +16,7 @@ export class RemissionFilterComponent implements OnDestroy {
|
||||
|
||||
filter$ = this.store.filter$.pipe(map((filter) => UiFilter.create(filter)));
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
constructor(public store: RemissionListComponentStore) {}
|
||||
|
||||
@@ -26,6 +26,8 @@ export class RemissionFilterComponent implements OnDestroy {
|
||||
}
|
||||
|
||||
applyFilter(filter: UiFilter) {
|
||||
console.log('filter', filter);
|
||||
|
||||
this.store.applyFilter(filter);
|
||||
this.close.emit();
|
||||
}
|
||||
|
||||
@@ -21,22 +21,30 @@
|
||||
</div>
|
||||
<div class="product-data-grid grid gap-6 items-start">
|
||||
<div>
|
||||
<img class="max-w-full max-h-full shadow rounded" src="{{ item.ean | productImage }}" alt="item.dto.product.name" />
|
||||
<img
|
||||
[productImageNavigation]="item?.ean"
|
||||
class="max-w-full max-h-full shadow rounded"
|
||||
src="{{ item.ean | productImage }}"
|
||||
alt="item.dto.product.name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-row gap-1 w-48">
|
||||
<div
|
||||
class="overflow-hidden overflow-ellipsis whitespace-nowrap"
|
||||
*ngIf="!!item.format && !!item.formatDetail && item.format !== 'UNKNOWN'"
|
||||
>
|
||||
<img class="inline" src="/assets/images/Icon_{{ item.dto.product.format }}.svg" [alt]="item.formatDetail" />
|
||||
<span class="ml-1 font-bold format-detail">{{ item.formatDetail }}</span>
|
||||
<div class="overflow-hidden overflow-ellipsis whitespace-nowrap" *ngIf="formatAvailable">
|
||||
<img
|
||||
*ngIf="formatIconAvailable"
|
||||
class="inline mr-1"
|
||||
[src]="formatIconUrl"
|
||||
[alt]="item.formatDetail"
|
||||
(error)="formatIconAvailable = false"
|
||||
/>
|
||||
<span class="font-bold format-detail">{{ item.formatDetail }}</span>
|
||||
</div>
|
||||
<div class="font-bold ean">
|
||||
{{ item.ean }}
|
||||
</div>
|
||||
<div class="font-bold price">
|
||||
{{ item.price.value.value | currency: item.price.value.currency:'code' }}
|
||||
{{ item.price.value.value | currency: item.price.value.currency : 'code' }}
|
||||
</div>
|
||||
<div *ngIf="item.assortment | assortment; let assortment">
|
||||
<button [uiOverlayTrigger]="assortmentTooltip" data-name="assortment" class="text-active-branch font-bold">
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@swagger/remi';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from 'apps/domain/remission/src/lib/mappings';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { first, takeUntil } from 'rxjs/operators';
|
||||
import { AddProductToShippingDocumentModalComponent } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component';
|
||||
import { RemissionListComponentStore } from '../remission-list.component-store';
|
||||
@@ -23,7 +23,7 @@ import { RemissionListComponent } from '../remission-list.component';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RemissionListItemComponent implements OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
@Input()
|
||||
item: RemissionListItem;
|
||||
@@ -55,11 +55,21 @@ export class RemissionListItemComponent implements OnDestroy {
|
||||
|
||||
loading$ = this._listComponent.remittingItem$.asObservable();
|
||||
|
||||
get formatIconUrl() {
|
||||
return `/assets/images/Icon_${this.item.dto.product.format}.svg`;
|
||||
}
|
||||
|
||||
get formatAvailable() {
|
||||
return !!this.item.format && !!this.item.formatDetail && this.item.format !== 'UNKNOWN';
|
||||
}
|
||||
|
||||
formatIconAvailable: boolean = true;
|
||||
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
private _remissionService: DomainRemissionService,
|
||||
private _store: RemissionListComponentStore,
|
||||
@Host() private _listComponent: RemissionListComponent
|
||||
@Host() private _listComponent: RemissionListComponent,
|
||||
) {}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -168,7 +178,7 @@ export class RemissionListItemComponent implements OnDestroy {
|
||||
const items = await this._store.items$.pipe(first()).toPromise();
|
||||
|
||||
const itemsByEan = items?.filter(
|
||||
(i) => i.dto.product.ean === this.item.dto.product.ean && i.dto.id !== this.item.dto.id && i.placementType === 'Stapel'
|
||||
(i) => i.dto.product.ean === this.item.dto.product.ean && i.dto.id !== this.item.dto.id && i.placementType === 'Stapel',
|
||||
);
|
||||
|
||||
if (itemsByEan?.length > 0) {
|
||||
|
||||
@@ -2,12 +2,13 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { RemissionListItemComponent } from './remission-list-item.component';
|
||||
import { ProductImageModule } from '@cdn/product-image';
|
||||
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
|
||||
import { RemissionPipeModule } from '../../pipes';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { AddProductToShippingDocumentModalModule } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { SharedProductGroupPipe } from '@shared/pipes/product-group';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -18,6 +19,8 @@ import { UiSpinnerModule } from '@ui/spinner';
|
||||
RemissionPipeModule,
|
||||
UiTooltipModule,
|
||||
AddProductToShippingDocumentModalModule,
|
||||
SharedProductGroupPipe,
|
||||
NavigateOnClickDirective,
|
||||
],
|
||||
exports: [RemissionListItemComponent],
|
||||
declarations: [RemissionListItemComponent],
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ReturnItemDTO, ReturnSuggestionDTO, SupplierDTO } from '@swagger/remi';
|
||||
import { UiFilter } from '@ui/filter';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { debounceTime, distinctUntilChanged, filter, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface RemissionState {
|
||||
suppliers: SupplierDTO[];
|
||||
@@ -57,11 +57,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
}
|
||||
|
||||
get selectedSupplier() {
|
||||
return this.get((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId));
|
||||
return this.get((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId) ?? s.suppliers[0]);
|
||||
}
|
||||
|
||||
get selectedSupplier$() {
|
||||
return this.select((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId));
|
||||
return this.select((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId) ?? s.suppliers[0]);
|
||||
}
|
||||
|
||||
get sources() {
|
||||
@@ -137,7 +137,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
private readonly _config: Config,
|
||||
private readonly _cache: CacheService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _router: Router
|
||||
private readonly _router: Router,
|
||||
) {
|
||||
super({
|
||||
suppliers: [],
|
||||
@@ -186,11 +186,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
this._domainRemissionService.getSuppliers().pipe(
|
||||
tapResponse(
|
||||
(suppliers) => this.setSuppliers(suppliers),
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
loadSources = this.effect(($) =>
|
||||
@@ -200,11 +200,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
this._domainRemissionService.getSources().pipe(
|
||||
tapResponse(
|
||||
(sources) => this.setSources(sources),
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
loadRequiredCapacities = this.effect(($) =>
|
||||
@@ -220,11 +220,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
return this._domainRemissionService.getRequiredCapacities({ supplierId: supplier?.id, departments }).pipe(
|
||||
tapResponse(
|
||||
(response) => this.setRequiredCapacities(response),
|
||||
(err) => {}
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
loadFilter = this.effect((options$: Observable<{ loadDefault?: boolean }>) =>
|
||||
@@ -259,11 +259,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
this._filterChange$.next(true);
|
||||
}
|
||||
},
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
search = this.effect((options$: Observable<{ newSearch?: boolean }>) =>
|
||||
@@ -332,11 +332,11 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
this.setFetching(false);
|
||||
this._searchCompleted.next(this.get());
|
||||
},
|
||||
(err) => {}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(err) => {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
setFetching = this.updater<boolean>((state, fetching) => ({
|
||||
@@ -435,7 +435,7 @@ export class RemissionListComponentStore extends ComponentStore<RemissionState>
|
||||
{
|
||||
items: this.items,
|
||||
hits: this.hits,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('scrollContainer', { static: true })
|
||||
scrollContainer: CdkVirtualScrollViewport;
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
get processId() {
|
||||
return this._config.get('process.ids.remission');
|
||||
@@ -98,12 +98,12 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
listEmpty$ = combineLatest([this.fetching$, this.hits$]).pipe(
|
||||
map(([loading, hits]) => !loading && hits === 0),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
get queryParams$() {
|
||||
@@ -115,7 +115,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
queryParams = { ...filter.getQueryParams(), ...params, supplier, source };
|
||||
}
|
||||
return queryParams;
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
return suppliers;
|
||||
}
|
||||
return suppliers.filter((supplier) => supplier?.id === selectedSupplier?.id);
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
trackByItemId: TrackByFunction<RemissionListItem> = (_, item) => item.dto.id;
|
||||
@@ -147,7 +147,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _breadcrumb: BreadcrumbService,
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _config: Config
|
||||
private readonly _config: Config,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { ToasterService } from '@shared/shell';
|
||||
providers: [RemissionListComponentStore],
|
||||
})
|
||||
export class RemissionComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
get processId() {
|
||||
return this._config.get('process.ids.remission');
|
||||
@@ -29,7 +29,7 @@ export class RemissionComponent implements OnInit, OnDestroy {
|
||||
private _applicationService: ApplicationService,
|
||||
private _router: Router,
|
||||
private _cache: CacheService,
|
||||
private _toastService: ToasterService
|
||||
private _toastService: ToasterService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -38,7 +38,7 @@ export class RemissionComponent implements OnInit, OnDestroy {
|
||||
this._router.events
|
||||
?.pipe(
|
||||
filter((event) => event instanceof NavigationEnd),
|
||||
takeUntil(this._onDestroy$)
|
||||
takeUntil(this._onDestroy$),
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.updateProcess();
|
||||
|
||||
@@ -5,9 +5,10 @@ import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { RemissionPipeModule } from '../../pipes';
|
||||
import { SharedShippingDocumentDetailsItemComponent } from './shipping-document-details-item/shipping-document-details-item.component';
|
||||
import { SharedShippingDocumentDetailsComponent } from './shipping-document-details.component';
|
||||
import { SharedProductGroupPipe } from '@shared/pipes/product-group';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, RemissionPipeModule, ProductImageModule, UiSpinnerModule],
|
||||
imports: [CommonModule, RemissionPipeModule, ProductImageModule, UiSpinnerModule, SharedProductGroupPipe],
|
||||
exports: [SharedShippingDocumentDetailsComponent, SharedShippingDocumentDetailsItemComponent],
|
||||
declarations: [SharedShippingDocumentDetailsComponent, SharedShippingDocumentDetailsItemComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -96,13 +96,13 @@ export class TaskInfoComponent implements OnChanges {
|
||||
|
||||
teaser$ = this.info$.pipe(
|
||||
filter((info) => !isNullOrUndefined(info)),
|
||||
switchMap((info) => this.domainTaskCalendarService.getTeaserFile({ infoId: info.id }).pipe(catchError((error) => [undefined])))
|
||||
switchMap((info) => this.domainTaskCalendarService.getTeaserFile({ infoId: info.id }).pipe(catchError((error) => [undefined]))),
|
||||
);
|
||||
|
||||
attachments$ = this.info$.pipe(
|
||||
filter((info) => info.attachments > 0),
|
||||
switchMap((info) => this.domainTaskCalendarService.getFiles({ infoId: info.id })),
|
||||
map((response) => response.result)
|
||||
map((response) => response.result),
|
||||
);
|
||||
|
||||
type$ = this.info$.pipe(map((info) => this.domainTaskCalendarService.getInfoType(info)));
|
||||
@@ -111,7 +111,7 @@ export class TaskInfoComponent implements OnChanges {
|
||||
|
||||
indicatorColor$ = this.info$.pipe(map((info) => this.domainTaskCalendarService.getProcessingStatusColorCode(info)));
|
||||
|
||||
private noteAdded$ = new Subject();
|
||||
private noteAdded$ = new Subject<void>();
|
||||
|
||||
notes$ = combineLatest([this.info$, this.noteAdded$.pipe(startWith([undefined]))]).pipe(
|
||||
switchMap(([info]) => this.domainTaskCalendarService.getComments({ infoId: info.id })),
|
||||
@@ -119,19 +119,19 @@ export class TaskInfoComponent implements OnChanges {
|
||||
res.result.map((comment) => ({
|
||||
date: new Date(comment.created),
|
||||
note: comment.text,
|
||||
}))
|
||||
)
|
||||
})),
|
||||
),
|
||||
);
|
||||
|
||||
processingStatus$ = this.info$.pipe(
|
||||
map((info) => this.domainTaskCalendarService.getProcessingStatusList(info)),
|
||||
shareReplay()
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
showUpdateComment$ = combineLatest([this.info$, this.processingStatus$]).pipe(
|
||||
map(
|
||||
([info, processingStatus]) => !!info.updateComment && ((info.successor && processingStatus.includes('Removed')) || info.predecessor)
|
||||
)
|
||||
([info, processingStatus]) => !!info.updateComment && ((info.successor && processingStatus.includes('Removed')) || info.predecessor),
|
||||
),
|
||||
);
|
||||
|
||||
@HostBinding('class')
|
||||
@@ -146,7 +146,7 @@ export class TaskInfoComponent implements OnChanges {
|
||||
private uiModal: UiModalService,
|
||||
private clipboard: Clipboard,
|
||||
@Optional() private modalRef: UiModalRef,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
ngOnChanges({ info }: SimpleChanges): void {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { TaskCalendarStore } from '../../task-calendar.store';
|
||||
exportAs: 'taskSearchbar',
|
||||
})
|
||||
export class TaskSearchbarComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject();
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
control = new UntypedFormControl('', [Validators.minLength(3)]);
|
||||
|
||||
@@ -30,7 +30,7 @@ export class TaskSearchbarComponent implements OnInit, OnDestroy {
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _config: Config
|
||||
private _config: Config,
|
||||
) {}
|
||||
|
||||
getControlValueFromQuery() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user