mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
255 Commits
fix/3213-C
...
3754-liefe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea6a0d131 | ||
|
|
dc42107668 | ||
|
|
a9479db5be | ||
|
|
262e0c5e11 | ||
|
|
8718dd8231 | ||
|
|
0274b2af3b | ||
|
|
2e286b2de3 | ||
|
|
168847b24a | ||
|
|
69c23a55f7 | ||
|
|
f344c2b0e8 | ||
|
|
11819e59c6 | ||
|
|
caf7331d28 | ||
|
|
68e5926568 | ||
|
|
9d46f49634 | ||
|
|
daae9323e8 | ||
|
|
80c425aa6f | ||
|
|
1cbe309778 | ||
|
|
189dc64a0f | ||
|
|
2f0ede7170 | ||
|
|
c7e444d446 | ||
|
|
f5ac916663 | ||
|
|
b3b9f0223e | ||
|
|
70455df6d3 | ||
|
|
b89889a3e9 | ||
|
|
7ec00925ed | ||
|
|
d3e3d127b3 | ||
|
|
11c4d8fb72 | ||
|
|
9b02a19b9c | ||
|
|
57262919b8 | ||
|
|
f787a15347 | ||
|
|
bc74eac86d | ||
|
|
0a1df250cb | ||
|
|
8a7b9de29d | ||
|
|
8efb87a1f7 | ||
|
|
27ee667bfb | ||
|
|
c64ff772e7 | ||
|
|
313efebb8b | ||
|
|
9eee4fff6c | ||
|
|
b5a7c96181 | ||
|
|
5bea71e19e | ||
|
|
751e533dce | ||
|
|
9e58e8aad9 | ||
|
|
d62a7d704a | ||
|
|
f8d7d12d61 | ||
|
|
2ad71cad78 | ||
|
|
689d8ead24 | ||
|
|
b6a7df76f6 | ||
|
|
41e8145858 | ||
|
|
3a5b80657c | ||
|
|
41c6897224 | ||
|
|
8d644cdd65 | ||
|
|
7a68229432 | ||
|
|
4dab0ef6f6 | ||
|
|
75cad811bc | ||
|
|
0709f1dd6a | ||
|
|
478950c446 | ||
|
|
b5e5601671 | ||
|
|
0ee86faa00 | ||
|
|
fa7b204f89 | ||
|
|
22f9ba80fe | ||
|
|
065878a6e9 | ||
|
|
5df433d603 | ||
|
|
21375855dd | ||
|
|
b847ca6b3e | ||
|
|
50caf9811d | ||
|
|
f0df9e1157 | ||
|
|
3e1347f17e | ||
|
|
86082bbfda | ||
|
|
6e6551ceae | ||
|
|
08ef5f0853 | ||
|
|
92131453e8 | ||
|
|
888a95d2a0 | ||
|
|
ab745cba18 | ||
|
|
dfe6b3977f | ||
|
|
e7e8b71a70 | ||
|
|
c158e16bd3 | ||
|
|
464fac660b | ||
|
|
79b1920b15 | ||
|
|
f071e7b2d5 | ||
|
|
3afb8d6ed1 | ||
|
|
87a2e94dd6 | ||
|
|
d711d4a816 | ||
|
|
a4b4aeed64 | ||
|
|
3707572bd8 | ||
|
|
aef5c06f0a | ||
|
|
a021ac0da3 | ||
|
|
1e4e6da44e | ||
|
|
6b0d9774c5 | ||
|
|
e1720e6023 | ||
|
|
3078724ced | ||
|
|
f3eb0a67f6 | ||
|
|
65d7a6f5a4 | ||
|
|
b2b5456400 | ||
|
|
a598be069d | ||
|
|
bf61f2c982 | ||
|
|
63225491f1 | ||
|
|
0dbc773775 | ||
|
|
47b7d42dd3 | ||
|
|
d713c787e6 | ||
|
|
68a2eab425 | ||
|
|
886f063d1b | ||
|
|
ed6ee36509 | ||
|
|
ab345dae0d | ||
|
|
fbb1e6c4a2 | ||
|
|
4ede9226b4 | ||
|
|
fbfecbd8ae | ||
|
|
3c4612d15c | ||
|
|
7fa2e7862d | ||
|
|
114267362c | ||
|
|
4050e9605d | ||
|
|
fdd5373aaf | ||
|
|
2dfe7ec05b | ||
|
|
82513b5dde | ||
|
|
14d1bb6ac8 | ||
|
|
d589c94681 | ||
|
|
eca19bb507 | ||
|
|
282ff30b3e | ||
|
|
5d0b810674 | ||
|
|
e32482c634 | ||
|
|
650026b0c0 | ||
|
|
cd25d6da38 | ||
|
|
9dd0954967 | ||
|
|
fdaceb9bf8 | ||
|
|
4ab3a3b3cf | ||
|
|
eb8b54dc63 | ||
|
|
4703aee60c | ||
|
|
93b0d43bd7 | ||
|
|
1029310e0d | ||
|
|
c083684db2 | ||
|
|
d6775aad69 | ||
|
|
3eff10bbb4 | ||
|
|
e4cbab8365 | ||
|
|
18212e7a4c | ||
|
|
0cd0b1abfd | ||
|
|
a66137873c | ||
|
|
469110eabf | ||
|
|
55474fa4e3 | ||
|
|
3bdcdee031 | ||
|
|
246c5a61dd | ||
|
|
0c8bfba515 | ||
|
|
3c8d9bb1e5 | ||
|
|
da2c1c8316 | ||
|
|
0334b2dd33 | ||
|
|
96356042af | ||
|
|
21adff8d0c | ||
|
|
35def2a7c7 | ||
|
|
e3d82794a3 | ||
|
|
20cbac8f17 | ||
|
|
6067e02729 | ||
|
|
eb77664ea1 | ||
|
|
8b1baf9ebd | ||
|
|
12fb774b73 | ||
|
|
d79dbb11fe | ||
|
|
19ccb29248 | ||
|
|
199c4f30e7 | ||
|
|
732c0d4e35 | ||
|
|
fa1769da9f | ||
|
|
029997d624 | ||
|
|
d2546409cb | ||
|
|
cb2bc8d65b | ||
|
|
57bd8d4dd4 | ||
|
|
cc1e210799 | ||
|
|
4bee08d483 | ||
|
|
8cfb160989 | ||
|
|
6325167eda | ||
|
|
8925eae4c5 | ||
|
|
9282bcd779 | ||
|
|
5aa6499598 | ||
|
|
f766781928 | ||
|
|
02834b7102 | ||
|
|
a9e3430505 | ||
|
|
4b9a23001a | ||
|
|
8de7ec9124 | ||
|
|
19a0a3c7c3 | ||
|
|
42a7d6e4b7 | ||
|
|
66818b1647 | ||
|
|
bc8ba9adc8 | ||
|
|
4ae5759361 | ||
|
|
b5cfcf8036 | ||
|
|
061982cf2c | ||
|
|
0e1422c2c4 | ||
|
|
3e534029a0 | ||
|
|
8d9ee9fe5c | ||
|
|
675aa04564 | ||
|
|
88c8885a81 | ||
|
|
151760aef9 | ||
|
|
6c89969b60 | ||
|
|
0fd5e66c33 | ||
|
|
c8aa526e4d | ||
|
|
f2c492c6ea | ||
|
|
11cf845235 | ||
|
|
ae6fbc7c64 | ||
|
|
71eda539f6 | ||
|
|
f43b948ac9 | ||
|
|
1b77020b6a | ||
|
|
1f62040560 | ||
|
|
cc5c3167b1 | ||
|
|
b9b79b949f | ||
|
|
a0d729fe6d | ||
|
|
f618dd3865 | ||
|
|
3fd3f972db | ||
|
|
2311655e5e | ||
|
|
c589836097 | ||
|
|
dbc641cfce | ||
|
|
f13bc58925 | ||
|
|
94d5892cf1 | ||
|
|
8e32b15f26 | ||
|
|
fe5f0ef2eb | ||
|
|
daa27d5f2d | ||
|
|
bb7626609e | ||
|
|
9ed58b685b | ||
|
|
4eb81ad30a | ||
|
|
a1f2cb57b3 | ||
|
|
62b8e387ca | ||
|
|
07498db711 | ||
|
|
2adc8c6f5d | ||
|
|
f15a43f303 | ||
|
|
e35aea5a7e | ||
|
|
0e1ed9d8cc | ||
|
|
f62ef06e51 | ||
|
|
30f4d4588f | ||
|
|
e102396dab | ||
|
|
f60815ef63 | ||
|
|
7b11b53774 | ||
|
|
abff7715ee | ||
|
|
83c0a20d61 | ||
|
|
a4de2391e9 | ||
|
|
732566eacd | ||
|
|
2c98128531 | ||
|
|
9a45823bdb | ||
|
|
fa43a08831 | ||
|
|
6fb8bdaff1 | ||
|
|
35e6d60ac0 | ||
|
|
ecfc241fb5 | ||
|
|
866a5100b9 | ||
|
|
3d972fd740 | ||
|
|
59c9275cc4 | ||
|
|
3ada83efff | ||
|
|
e263048a35 | ||
|
|
63980298b6 | ||
|
|
b904e94156 | ||
|
|
78f91b937f | ||
|
|
1ecd08d053 | ||
|
|
8c03accae7 | ||
|
|
456d3f2f3a | ||
|
|
fa2838ea5c | ||
|
|
e228490812 | ||
|
|
13d0ac0cbd | ||
|
|
09bed1456e | ||
|
|
c6827e499e | ||
|
|
d54cc7a2fd | ||
|
|
67dae94524 | ||
|
|
3273a21246 | ||
|
|
b40b61a46c | ||
|
|
4518db2bdd |
4
.npmrc
4
.npmrc
@@ -1,3 +1 @@
|
||||
@isa:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
||||
@cmf:registry=https://pkgs.dev.azure.com/hugendubel/_packaging/hugendubel%40Local/npm/registry/
|
||||
always-auth=true
|
||||
@paragondata:registry=https://npm.pkg.github.com
|
||||
31
angular.json
31
angular.json
@@ -3629,6 +3629,37 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@ui/form-field": {
|
||||
"projectType": "library",
|
||||
"root": "apps/ui/form-field",
|
||||
"sourceRoot": "apps/ui/form-field/src",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"project": "apps/ui/form-field/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "apps/ui/form-field/src/test.ts",
|
||||
"tsConfig": "apps/ui/form-field/tsconfig.spec.json",
|
||||
"karmaConfig": "apps/ui/form-field/karma.conf.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "isa-app"
|
||||
|
||||
@@ -31,7 +31,11 @@ export class AuthService {
|
||||
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
|
||||
|
||||
this._oAuthService.setupAutomaticSilentRefresh();
|
||||
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
|
||||
try {
|
||||
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
|
||||
} catch (error) {
|
||||
this.login();
|
||||
}
|
||||
|
||||
this._initialized.next(true);
|
||||
}
|
||||
|
||||
11
apps/core/cache/src/lib/cache.service.ts
vendored
11
apps/core/cache/src/lib/cache.service.ts
vendored
@@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CacheOptions } from './cache-options';
|
||||
import { Cached } from './cached';
|
||||
import { sha1 } from 'object-hash';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -60,7 +59,15 @@ export class CacheService {
|
||||
}
|
||||
|
||||
private getKey(token: Object) {
|
||||
return sha1(token);
|
||||
return this.hash(JSON.stringify(token));
|
||||
}
|
||||
|
||||
private hash(data: string): string {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
hash = data.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
return hash.toString(16);
|
||||
}
|
||||
|
||||
private serialize(data: Cached): string {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Injectable, Injector, Optional, SkipSelf } from '@angular/core';
|
||||
import { ActionHandler } from './action-handler.interface';
|
||||
import { FEATURE_ACTION_HANDLERS, ROOT_ACTION_HANDLERS } from './tokens';
|
||||
|
||||
@Injectable()
|
||||
export class CommandService {
|
||||
constructor(private injector: Injector) {}
|
||||
constructor(private injector: Injector, @Optional() @SkipSelf() private _parent: CommandService) {}
|
||||
|
||||
async handleCommand<T>(command: string, data?: T): Promise<T> {
|
||||
const actions = this.getActions(command);
|
||||
@@ -15,7 +15,7 @@ export class CommandService {
|
||||
console.error('CommandService.handleCommand', 'Action Handler does not exist', { action });
|
||||
throw new Error('Action Handler does not exist');
|
||||
}
|
||||
console.log('handle command', handler, data);
|
||||
|
||||
data = await handler.handler(data);
|
||||
}
|
||||
return data;
|
||||
@@ -25,10 +25,16 @@ export class CommandService {
|
||||
return command?.split('|') || [];
|
||||
}
|
||||
|
||||
getActionHandler(action: string): ActionHandler {
|
||||
getActionHandler(action: string): ActionHandler | undefined {
|
||||
const featureActionHandlers: ActionHandler[] = this.injector.get(FEATURE_ACTION_HANDLERS, []);
|
||||
const rootActionHandlers: ActionHandler[] = this.injector.get(ROOT_ACTION_HANDLERS, []);
|
||||
|
||||
return [...featureActionHandlers, ...rootActionHandlers].find((handler) => handler.action === action);
|
||||
let handler = [...featureActionHandlers, ...rootActionHandlers].find((handler) => handler.action === action);
|
||||
|
||||
if (this._parent && !handler) {
|
||||
handler = this._parent.getActionHandler(action);
|
||||
}
|
||||
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
<div
|
||||
class="toast-main"
|
||||
[style.width]="width"
|
||||
[@slideAnimation]="{ value: animationState }"
|
||||
(@slideAnimation.done)="onSlideFinished($event)"
|
||||
>
|
||||
<div class="toast-main" [style.width]="width" [@slideAnimation]="{ value: animationState }" (@slideAnimation.done)="onSlideFinished()">
|
||||
<button class="absolute top-2 right-2 p-6 border-none bg-transparent" (click)="close()">
|
||||
<ui-icon icon="close" size="20px"></ui-icon>
|
||||
</button>
|
||||
|
||||
@@ -11,9 +11,9 @@ import { TOAST_CONFIG_TOKEN } from './tokens';
|
||||
animations: [toastAnimations.slideToast],
|
||||
})
|
||||
export class ToastComponent implements OnInit, OnDestroy {
|
||||
timeoutRef: NodeJS.Timeout;
|
||||
timeoutRef?: any;
|
||||
animationState: ToastAnimationState = 'default';
|
||||
width: string = '55.25rem';
|
||||
width = '55.25rem';
|
||||
|
||||
constructor(
|
||||
@Inject(TOAST_CONFIG_TOKEN) public readonly data: Toast,
|
||||
@@ -40,7 +40,7 @@ export class ToastComponent implements OnInit, OnDestroy {
|
||||
this.animationState = 'closing';
|
||||
}
|
||||
|
||||
onSlideFinished(event: AnimationEvent) {
|
||||
onSlideFinished() {
|
||||
if (this.animationState === 'closing') {
|
||||
this._ref.close();
|
||||
}
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ItemDTO, SearchService } from '@swagger/cat';
|
||||
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, StoreCheckoutService, SupplierDTO } from '@swagger/checkout';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import {
|
||||
AvailabilityDTO,
|
||||
BranchDTO,
|
||||
OLAAvailabilityDTO,
|
||||
StoreCheckoutBranchService,
|
||||
StoreCheckoutSupplierService,
|
||||
SupplierDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import {
|
||||
AvailabilityRequestDTO,
|
||||
AvailabilityService as SwaggerAvailabilityService,
|
||||
AvailabilityService,
|
||||
AvailabilityDTO as SwaggerAvailabilityDTO,
|
||||
AvailabilityType,
|
||||
} from '@swagger/availability';
|
||||
import { AvailabilityDTO as CatAvailabilityDTO } from '@swagger/cat';
|
||||
import { map, shareReplay, switchMap, withLatestFrom, mergeMap, timeout } from 'rxjs/operators';
|
||||
import { isArray, memorize } from '@utils/common';
|
||||
import { OrderService } from '@swagger/oms';
|
||||
import { LogisticianDTO, LogisticianService } from '@swagger/oms';
|
||||
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
|
||||
import { ItemData } from './defs/item-data.model';
|
||||
import { PriceDTO } from '@swagger/availability';
|
||||
import { AvailabilityByBranchDTO } from './defs/availability-by-branch-dto.model';
|
||||
import { AvailabilityByBranchDTO, ItemData } from './defs';
|
||||
import { Availability } from './defs/availability';
|
||||
|
||||
@Injectable()
|
||||
export class DomainAvailabilityService {
|
||||
constructor(
|
||||
private swaggerAvailabilityService: SwaggerAvailabilityService,
|
||||
private storeCheckoutService: StoreCheckoutService,
|
||||
private orderService: OrderService,
|
||||
private _stock: StockService
|
||||
private _availabilityService: AvailabilityService,
|
||||
private _logisticanService: LogisticianService,
|
||||
private _stockService: StockService,
|
||||
private _supplierService: StoreCheckoutSupplierService,
|
||||
private _branchService: StoreCheckoutBranchService
|
||||
) {}
|
||||
|
||||
@memorize()
|
||||
getSuppliers(): Observable<SupplierDTO[]> {
|
||||
return this.storeCheckoutService.StoreCheckoutGetSuppliers({}).pipe(
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay()
|
||||
);
|
||||
@@ -36,15 +44,15 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize()
|
||||
getTakeAwaySupplier(): Observable<SupplierDTO> {
|
||||
return this.storeCheckoutService.StoreCheckoutGetSuppliers({}).pipe(
|
||||
map(({ result }) => result.find((supplier) => supplier?.supplierNumber === 'F')),
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map(({ result }) => result?.find((supplier) => supplier?.supplierNumber === 'F')),
|
||||
shareReplay()
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getBranches(): Observable<BranchDTO[]> {
|
||||
return this.storeCheckoutService.StoreCheckoutGetBranches({}).pipe(
|
||||
return this._branchService.StoreCheckoutBranchGetBranches({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay()
|
||||
);
|
||||
@@ -52,7 +60,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize()
|
||||
getCurrentStock(): Observable<StockDTO> {
|
||||
return this._stock.StockCurrentStock().pipe(
|
||||
return this._stockService.StockCurrentStock().pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay()
|
||||
);
|
||||
@@ -60,7 +68,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize()
|
||||
getCurrentBranch(): Observable<BranchDTO> {
|
||||
return this._stock.StockCurrentBranch().pipe(
|
||||
return this._stockService.StockCurrentBranch().pipe(
|
||||
map((response) => ({
|
||||
id: response.result.id,
|
||||
name: response.result.name,
|
||||
@@ -83,8 +91,8 @@ export class DomainAvailabilityService {
|
||||
}
|
||||
|
||||
@memorize({})
|
||||
getLogisticians() {
|
||||
return this.orderService.OrderGetLogisticians({}).pipe(
|
||||
getLogisticians(): Observable<LogisticianDTO> {
|
||||
return this._logisticanService.LogisticianGetLogisticians({}).pipe(
|
||||
map((response) => response.result?.find((l) => l.logisticianNumber === '2470')),
|
||||
shareReplay()
|
||||
);
|
||||
@@ -101,7 +109,7 @@ export class DomainAvailabilityService {
|
||||
price: PriceDTO;
|
||||
quantity: number;
|
||||
}): Observable<AvailabilityByBranchDTO[]> {
|
||||
return this._stock.StockStockRequest({ stockRequest: { branchIds, itemId } }).pipe(
|
||||
return this._stockService.StockStockRequest({ stockRequest: { branchIds, itemId } }).pipe(
|
||||
map((response) => response.result),
|
||||
withLatestFrom(this.getTakeAwaySupplier()),
|
||||
map(([result, supplier]) => {
|
||||
@@ -125,8 +133,13 @@ export class DomainAvailabilityService {
|
||||
|
||||
getTakeAwayAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((s) => this._stock.StockInStock({ articleIds: [item.itemId], stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getCurrentBranch()),
|
||||
switchMap((s) =>
|
||||
combineLatest([
|
||||
this._stockService.StockInStock({ articleIds: [item.itemId], stockId: s.id }),
|
||||
this.getTakeAwaySupplier(),
|
||||
this.getCurrentBranch(),
|
||||
])
|
||||
),
|
||||
map(([response, supplier, branch]) => {
|
||||
const price = item?.price;
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branch, quantity, price });
|
||||
@@ -148,7 +161,7 @@ export class DomainAvailabilityService {
|
||||
quantity: number;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
return combineLatest([
|
||||
this._stock.StockStockRequest({ stockRequest: { branchIds: [branch.id], itemId } }),
|
||||
this._stockService.StockStockRequest({ stockRequest: { branchIds: [branch.id], itemId } }),
|
||||
this.getTakeAwaySupplier(),
|
||||
]).pipe(
|
||||
map(([response, supplier]) => {
|
||||
@@ -168,7 +181,7 @@ export class DomainAvailabilityService {
|
||||
quantity: number;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((s) => this._stock.StockInStockByEAN({ eans, stockId: s.id })),
|
||||
switchMap((s) => this._stockService.StockInStockByEAN({ eans, stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getCurrentBranch()),
|
||||
map(([response, supplier, branch]) => {
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branch, quantity, price });
|
||||
@@ -180,7 +193,7 @@ export class DomainAvailabilityService {
|
||||
getTakeAwayAvailabilitiesByEans({ eans }: { eans: string[] }): Observable<StockInfoDTO[]> {
|
||||
const eansFiltered = Array.from(new Set(eans));
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((s) => this._stock.StockInStockByEAN({ eans: eansFiltered, stockId: s.id })),
|
||||
switchMap((s) => this._stockService.StockInStockByEAN({ eans: eansFiltered, stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getCurrentBranch()),
|
||||
map((response) => response[0].result),
|
||||
shareReplay()
|
||||
@@ -188,8 +201,16 @@ export class DomainAvailabilityService {
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getPickUpAvailability({ item, branch, quantity }: { item: ItemData; quantity: number; branch: BranchDTO }): Observable<AvailabilityDTO> {
|
||||
return this.swaggerAvailabilityService
|
||||
getPickUpAvailability({
|
||||
item,
|
||||
branch,
|
||||
quantity,
|
||||
}: {
|
||||
item: ItemData;
|
||||
quantity: number;
|
||||
branch: BranchDTO;
|
||||
}): Observable<Availability<AvailabilityDTO, SwaggerAvailabilityDTO>> {
|
||||
return this._availabilityService
|
||||
.AvailabilityStoreAvailability([
|
||||
{
|
||||
qty: quantity,
|
||||
@@ -207,7 +228,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
|
||||
return this.swaggerAvailabilityService
|
||||
return this._availabilityService
|
||||
.AvailabilityShippingAvailability([
|
||||
{
|
||||
ean: item?.ean,
|
||||
@@ -225,7 +246,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDigDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
|
||||
return this.swaggerAvailabilityService
|
||||
return this._availabilityService
|
||||
.AvailabilityShippingAvailability([
|
||||
{
|
||||
qty: quantity,
|
||||
@@ -270,7 +291,11 @@ export class DomainAvailabilityService {
|
||||
timeout(5000),
|
||||
mergeMap((branch) =>
|
||||
this.getPickUpAvailability({ item, quantity, branch }).pipe(
|
||||
mergeMap((availability) => logistician$.pipe(map((logistician) => ({ ...availability, logistician: { id: logistician.id } })))),
|
||||
mergeMap((availability) =>
|
||||
logistician$.pipe(
|
||||
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } }))
|
||||
)
|
||||
),
|
||||
shareReplay()
|
||||
)
|
||||
)
|
||||
@@ -279,7 +304,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDownloadAvailability({ item }: { item: ItemData }): Observable<AvailabilityDTO> {
|
||||
return this.swaggerAvailabilityService
|
||||
return this._availabilityService
|
||||
.AvailabilityShippingAvailability([
|
||||
{
|
||||
ean: item?.ean,
|
||||
@@ -314,11 +339,11 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getTakeAwayAvailabilities(items: { id: number; price: PriceDTO }[], branchId: number) {
|
||||
return this._stock.StockGetStocksByBranch({ branchId }).pipe(
|
||||
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
|
||||
map((req) => req.result?.find((_) => true)?.id),
|
||||
switchMap((stockId) =>
|
||||
stockId
|
||||
? this._stock.StockInStock({ articleIds: items.map((i) => i.id), stockId })
|
||||
? this._stockService.StockInStock({ articleIds: items.map((i) => i.id), stockId })
|
||||
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO)
|
||||
),
|
||||
timeout(20000),
|
||||
@@ -339,7 +364,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getPickUpAvailabilities(payload: AvailabilityRequestDTO[], preferred?: boolean) {
|
||||
return this.swaggerAvailabilityService.AvailabilityStoreAvailability(payload).pipe(
|
||||
return this._availabilityService.AvailabilityStoreAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result))
|
||||
);
|
||||
@@ -347,7 +372,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.swaggerAvailabilityService.AvailabilityShippingAvailability(payload).pipe(
|
||||
return this._availabilityService.AvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
);
|
||||
@@ -355,7 +380,7 @@ export class DomainAvailabilityService {
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDigDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.swaggerAvailabilityService.AvailabilityShippingAvailability(payload).pipe(
|
||||
return this._availabilityService.AvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
);
|
||||
@@ -381,7 +406,7 @@ export class DomainAvailabilityService {
|
||||
): PriceDTO {
|
||||
switch (purchasingOption) {
|
||||
case 'take-away':
|
||||
return availability?.price || availability?.retailPrice;
|
||||
return availability?.price || catalogAvailability?.price;
|
||||
case 'delivery':
|
||||
case 'dig-delivery':
|
||||
if (catalogAvailability?.price?.value?.value < availability?.price?.value?.value) {
|
||||
@@ -442,9 +467,11 @@ export class DomainAvailabilityService {
|
||||
inStock: inStock,
|
||||
supplierSSC: quantity <= inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
|
||||
price,
|
||||
price: price ?? stockInfo?.retailPrice,
|
||||
supplier: { id: supplier?.id },
|
||||
retailPrice: (stockInfo as any)?.retailPrice, // TODO: Change after API Update
|
||||
// TODO: Change after API Update
|
||||
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice
|
||||
// retailPrice: (stockInfo as any)?.retailPrice,
|
||||
};
|
||||
return availability;
|
||||
}
|
||||
@@ -474,26 +501,29 @@ export class DomainAvailabilityService {
|
||||
return availability;
|
||||
}
|
||||
|
||||
private _mapToPickUpAvailability(availabilities: SwaggerAvailabilityDTO[]) {
|
||||
private _mapToPickUpAvailability(availabilities: SwaggerAvailabilityDTO[]): Availability<AvailabilityDTO, SwaggerAvailabilityDTO>[] {
|
||||
if (isArray(availabilities)) {
|
||||
const preferred = availabilities.filter((f) => f.preferred === 1);
|
||||
const totalAvailable = availabilities.reduce((sum, av) => sum + (av?.qty || 0), 0);
|
||||
|
||||
return preferred.map((p) => {
|
||||
return {
|
||||
availabilityType: p?.status,
|
||||
ssc: p?.ssc,
|
||||
sscText: p?.sscText,
|
||||
supplier: { id: p?.supplierId },
|
||||
isPrebooked: p?.isPrebooked,
|
||||
estimatedShippingDate: p?.requestStatusCode === '32' ? p?.altAt : p?.at,
|
||||
price: p?.price,
|
||||
inStock: totalAvailable,
|
||||
supplierProductNumber: p?.supplierProductNumber,
|
||||
supplierInfo: p?.requestStatusCode,
|
||||
lastRequest: p?.requested,
|
||||
itemId: p.itemId,
|
||||
};
|
||||
return [
|
||||
{
|
||||
availabilityType: p?.status,
|
||||
ssc: p?.ssc,
|
||||
sscText: p?.sscText,
|
||||
supplier: { id: p?.supplierId },
|
||||
isPrebooked: p?.isPrebooked,
|
||||
estimatedShippingDate: p?.requestStatusCode === '32' ? p?.altAt : p?.at,
|
||||
price: p?.price,
|
||||
inStock: totalAvailable,
|
||||
supplierProductNumber: p?.supplierProductNumber,
|
||||
supplierInfo: p?.requestStatusCode,
|
||||
lastRequest: p?.requested,
|
||||
itemId: p.itemId,
|
||||
},
|
||||
p,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
1
apps/domain/availability/src/lib/defs/availability.ts
Normal file
1
apps/domain/availability/src/lib/defs/availability.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type Availability<T, S> = [T, S];
|
||||
@@ -1,3 +1,3 @@
|
||||
// start:ng42.barrel
|
||||
export * from './item-data.model';
|
||||
// end:ng42.barrel
|
||||
export * from './availability-by-branch-dto';
|
||||
export * from './availability';
|
||||
export * from './item-data';
|
||||
|
||||
@@ -21,8 +21,13 @@ import {
|
||||
UpdateShoppingCartItemDTO,
|
||||
InputDTO,
|
||||
ItemPayload,
|
||||
StoreCheckoutShoppingCartService,
|
||||
StoreCheckoutPaymentService,
|
||||
StoreCheckoutBuyerService,
|
||||
StoreCheckoutPayerService,
|
||||
StoreCheckoutBranchService,
|
||||
} from '@swagger/checkout';
|
||||
import { DisplayOrderDTO, OrderCheckoutService, ReorderValues } from '@swagger/oms';
|
||||
import { DisplayOrderDTO, DisplayOrderItemDTO, OrderCheckoutService, ReorderValues } from '@swagger/oms';
|
||||
import { isNullOrUndefined, memorize } from '@utils/common';
|
||||
import { combineLatest, Observable, of, concat, isObservable, throwError } from 'rxjs';
|
||||
import { bufferCount, catchError, filter, first, map, mergeMap, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
@@ -32,6 +37,7 @@ import * as DomainCheckoutActions from './store/domain-checkout.actions';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CustomerDTO, EntityDTOContainerOfAttributeDTO } from '@swagger/crm';
|
||||
|
||||
@Injectable()
|
||||
export class DomainCheckoutService {
|
||||
@@ -40,7 +46,12 @@ export class DomainCheckoutService {
|
||||
private applicationService: ApplicationService,
|
||||
private storeCheckoutService: StoreCheckoutService,
|
||||
private orderCheckoutService: OrderCheckoutService,
|
||||
private availabilityService: DomainAvailabilityService
|
||||
private availabilityService: DomainAvailabilityService,
|
||||
private _shoppingCartService: StoreCheckoutShoppingCartService,
|
||||
private _paymentService: StoreCheckoutPaymentService,
|
||||
private _buyerService: StoreCheckoutBuyerService,
|
||||
private _payerService: StoreCheckoutPayerService,
|
||||
private _branchService: StoreCheckoutBranchService
|
||||
) {}
|
||||
|
||||
//#region shoppingcart
|
||||
@@ -53,8 +64,8 @@ export class DomainCheckoutService {
|
||||
return false;
|
||||
} else if (cart && _latest) {
|
||||
_latest = false;
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutGetShoppingCart({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartGetShoppingCart({
|
||||
shoppingCartId: cart.id,
|
||||
})
|
||||
.pipe(
|
||||
@@ -77,7 +88,7 @@ export class DomainCheckoutService {
|
||||
}
|
||||
|
||||
createShoppingCart({ processId }: { processId: number }): Observable<ShoppingCartDTO> {
|
||||
return this.storeCheckoutService.StoreCheckoutCreateShoppingCart().pipe(
|
||||
return this._shoppingCartService.StoreCheckoutShoppingCartCreateShoppingCart().pipe(
|
||||
map((response) => response.result),
|
||||
tap((shoppingCart) =>
|
||||
this.store.dispatch(
|
||||
@@ -94,8 +105,8 @@ export class DomainCheckoutService {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((cart) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutAddItemToShoppingCart({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartAddItemToShoppingCart({
|
||||
items,
|
||||
shoppingCartId: cart.id,
|
||||
})
|
||||
@@ -125,8 +136,8 @@ export class DomainCheckoutService {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((shoppingCart) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutSetLogisticianOnDestinationsByBuyer({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartSetLogisticianOnDestinationsByBuyer({
|
||||
shoppingCartId: shoppingCart?.id,
|
||||
payload: { customerFeatures },
|
||||
})
|
||||
@@ -139,7 +150,7 @@ export class DomainCheckoutService {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((cart) =>
|
||||
this.storeCheckoutService.StoreCheckoutCanAddDestination({
|
||||
this._shoppingCartService.StoreCheckoutShoppingCartCanAddDestination({
|
||||
shoppingCartId: cart.id,
|
||||
payload: destinationDTO,
|
||||
})
|
||||
@@ -166,8 +177,8 @@ export class DomainCheckoutService {
|
||||
first(),
|
||||
withLatestFrom(this.store.select(DomainCheckoutSelectors.selectCustomerFeaturesByProcessId, { processId })),
|
||||
mergeMap(([shoppingCart, customerFeatures]) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutCanAddItem({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartCanAddItem({
|
||||
shoppingCartId: shoppingCart?.id,
|
||||
payload: {
|
||||
customerFeatures,
|
||||
@@ -199,8 +210,8 @@ export class DomainCheckoutService {
|
||||
orderType,
|
||||
};
|
||||
});
|
||||
return this.storeCheckoutService
|
||||
.StoreCheckoutCanAddItems({
|
||||
return this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartCanAddItems({
|
||||
shoppingCartId: shoppingCart.id,
|
||||
payload,
|
||||
})
|
||||
@@ -222,7 +233,7 @@ export class DomainCheckoutService {
|
||||
shoppingCartItemId: number;
|
||||
availability: AvailabilityDTO;
|
||||
}) {
|
||||
return this.storeCheckoutService.StoreCheckoutUpdateShoppingCartItemAvailability({
|
||||
return this._shoppingCartService.StoreCheckoutShoppingCartUpdateShoppingCartItemAvailability({
|
||||
shoppingCartId,
|
||||
shoppingCartItemId,
|
||||
availability,
|
||||
@@ -241,8 +252,8 @@ export class DomainCheckoutService {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((shoppingCart) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutUpdateShoppingCartItem({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartUpdateShoppingCartItem({
|
||||
shoppingCartId: shoppingCart.id,
|
||||
shoppingCartItemId,
|
||||
values: update,
|
||||
@@ -318,8 +329,8 @@ export class DomainCheckoutService {
|
||||
return this.getCheckout({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((checkout) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutGetCheckoutPayment({
|
||||
this._paymentService
|
||||
.StoreCheckoutPaymentGetCheckoutPayment({
|
||||
checkoutId: checkout.id,
|
||||
})
|
||||
.pipe(map((response) => response.result))
|
||||
@@ -335,8 +346,8 @@ export class DomainCheckoutService {
|
||||
return this.getCheckout({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((checkout) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutSetPaymentType({
|
||||
this._paymentService
|
||||
.StoreCheckoutPaymentSetPaymentType({
|
||||
checkoutId: checkout?.id,
|
||||
paymentType,
|
||||
})
|
||||
@@ -352,8 +363,8 @@ export class DomainCheckoutService {
|
||||
return this.getCheckout({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((checkout) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutSetBuyer({
|
||||
this._buyerService
|
||||
.StoreCheckoutBuyerSetBuyerPOST({
|
||||
checkoutId: checkout?.id,
|
||||
buyerDTO: buyer,
|
||||
})
|
||||
@@ -369,8 +380,8 @@ export class DomainCheckoutService {
|
||||
return this.getCheckout({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((checkout) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutSetPayer({
|
||||
this._payerService
|
||||
.StoreCheckoutPayerSetPayerPOST({
|
||||
checkoutId: checkout?.id,
|
||||
payerDTO: payer,
|
||||
})
|
||||
@@ -389,7 +400,7 @@ export class DomainCheckoutService {
|
||||
mergeMap((cart) =>
|
||||
concat(
|
||||
...cart.items.map((item) =>
|
||||
this.storeCheckoutService.StoreCheckoutUpdateShoppingCartItem({
|
||||
this._shoppingCartService.StoreCheckoutShoppingCartUpdateShoppingCartItem({
|
||||
shoppingCartId: cart.id,
|
||||
shoppingCartItemId: item.id,
|
||||
values: { specialComment },
|
||||
@@ -650,7 +661,7 @@ export class DomainCheckoutService {
|
||||
first(),
|
||||
mergeMap((checkout) =>
|
||||
this.orderCheckoutService
|
||||
.OrderCheckoutCreateOrder({
|
||||
.OrderCheckoutCreateOrderPOST({
|
||||
checkoutId: checkout.id,
|
||||
})
|
||||
.pipe(
|
||||
@@ -734,8 +745,8 @@ export class DomainCheckoutService {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
first(),
|
||||
mergeMap((shoppingCart) =>
|
||||
this.storeCheckoutService
|
||||
.StoreCheckoutCanAddBuyer({
|
||||
this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartCanAddBuyer({
|
||||
shoppingCartId: shoppingCart.id,
|
||||
payload: { customerFeatures },
|
||||
})
|
||||
@@ -771,35 +782,39 @@ export class DomainCheckoutService {
|
||||
return this.canSetCustomer({ processId, customerFeatures: undefined }).pipe(
|
||||
map((res) => {
|
||||
let setableTypes: { [key: string]: boolean } = {
|
||||
store: true,
|
||||
guest: true,
|
||||
webshop: true,
|
||||
b2b: true,
|
||||
store: false,
|
||||
guest: false,
|
||||
webshop: false,
|
||||
b2b: false,
|
||||
};
|
||||
|
||||
if (Object.keys(res.filter).length === 0) {
|
||||
return setableTypes;
|
||||
}
|
||||
res.create?.options?.values?.forEach((option) => {
|
||||
setableTypes[option.value] = option.enabled !== false;
|
||||
});
|
||||
|
||||
const customerTypes = res.filter?.customertype?.split(';') || [];
|
||||
const customerAttributes = res.filter?.customerattributes?.split(';') || [];
|
||||
// if (Object.keys(res.filter).length === 0) {
|
||||
// return setableTypes;
|
||||
// }
|
||||
|
||||
const typesAndAttributes = [...customerTypes, ...customerAttributes];
|
||||
if (typesAndAttributes.includes('webshop') && !typesAndAttributes.includes('!guest')) {
|
||||
typesAndAttributes.push('guest');
|
||||
}
|
||||
// const customerTypes = res.filter?.customertype?.split(';') || [];
|
||||
// const customerAttributes = res.filter?.customerattributes?.split(';') || [];
|
||||
|
||||
for (const key in setableTypes) {
|
||||
if (Object.prototype.hasOwnProperty.call(setableTypes, key)) {
|
||||
if (typesAndAttributes.includes(key)) {
|
||||
setableTypes[key] = true;
|
||||
} else if (typesAndAttributes.includes(`!${key}`)) {
|
||||
setableTypes[key] = false;
|
||||
} else {
|
||||
setableTypes[key] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// const typesAndAttributes = [...customerTypes, ...customerAttributes];
|
||||
// if (typesAndAttributes.includes('webshop') && !typesAndAttributes.includes('!guest')) {
|
||||
// typesAndAttributes.push('guest');
|
||||
// }
|
||||
|
||||
// for (const key in setableTypes) {
|
||||
// if (Object.prototype.hasOwnProperty.call(setableTypes, key)) {
|
||||
// if (typesAndAttributes.includes(key)) {
|
||||
// setableTypes[key] = true;
|
||||
// } else if (typesAndAttributes.includes(`!${key}`)) {
|
||||
// setableTypes[key] = false;
|
||||
// } else {
|
||||
// setableTypes[key] = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return setableTypes;
|
||||
})
|
||||
@@ -808,8 +823,8 @@ export class DomainCheckoutService {
|
||||
|
||||
@memorize()
|
||||
getBranches(): Observable<BranchDTO[]> {
|
||||
return this.storeCheckoutService
|
||||
.StoreCheckoutGetBranches({
|
||||
return this._branchService
|
||||
.StoreCheckoutBranchGetBranches({
|
||||
take: 999,
|
||||
})
|
||||
.pipe(
|
||||
@@ -828,10 +843,6 @@ export class DomainCheckoutService {
|
||||
.pipe(map((response) => response.result));
|
||||
}
|
||||
|
||||
setCustomerFeatures({ processId, customerFeatures }: { processId: number; customerFeatures: { [key: string]: string } }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setCustomerFeatures({ processId, customerFeatures }));
|
||||
}
|
||||
|
||||
setOlaErrors({ processId, errorIds }: { processId: number; errorIds: number[] }) {
|
||||
this.store.dispatch(
|
||||
DomainCheckoutActions.setOlaError({
|
||||
@@ -857,6 +868,14 @@ export class DomainCheckoutService {
|
||||
this.store.dispatch(DomainCheckoutActions.removeProcess({ processId }));
|
||||
}
|
||||
|
||||
setCustomer({ processId, customerDto }: { processId: number; customerDto: CustomerDTO }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setCustomer({ processId, customer: customerDto }));
|
||||
}
|
||||
|
||||
getCustomer({ processId }: { processId: number }): Observable<CustomerDTO> {
|
||||
return this.store.select(DomainCheckoutSelectors.selectCustomerByProcessId, { processId });
|
||||
}
|
||||
|
||||
setPayer({ processId, payer }: { processId: number; payer: PayerDTO }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setPayer({ processId, payer }));
|
||||
}
|
||||
@@ -877,6 +896,10 @@ export class DomainCheckoutService {
|
||||
return this.store.select(DomainCheckoutSelectors.selectOrders);
|
||||
}
|
||||
|
||||
updateOrderItem(item: DisplayOrderItemDTO) {
|
||||
this.store.dispatch(DomainCheckoutActions.updateOrderItem({ item }));
|
||||
}
|
||||
|
||||
removeAllOrders() {
|
||||
this.store.dispatch(DomainCheckoutActions.removeAllOrders());
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { BuyerDTO, CheckoutDTO, NotificationChannel, PayerDTO, ShippingAddressDTO, ShoppingCartDTO } from '@swagger/checkout';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO } from '@swagger/oms';
|
||||
|
||||
export interface CheckoutEntity {
|
||||
processId: number;
|
||||
checkout: CheckoutDTO;
|
||||
shoppingCart: ShoppingCartDTO;
|
||||
customerFeatures: { [key: string]: string };
|
||||
customer: CustomerDTO;
|
||||
payer: PayerDTO;
|
||||
buyer: BuyerDTO;
|
||||
shippingAddress: ShippingAddressDTO;
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
BuyerDTO,
|
||||
PayerDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { DisplayOrderDTO } from '@swagger/oms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO, DisplayOrderItemDTO } from '@swagger/oms';
|
||||
|
||||
const prefix = '[DOMAIN-CHECKOUT]';
|
||||
|
||||
@@ -38,11 +39,6 @@ export const setCheckoutDestination = createAction(
|
||||
props<{ processId: number; destination: DestinationDTO }>()
|
||||
);
|
||||
|
||||
export const setCustomerFeatures = createAction(
|
||||
`${prefix} Set Customer Features`,
|
||||
props<{ processId: number; customerFeatures: { [key: string]: string } }>()
|
||||
);
|
||||
|
||||
export const setShippingAddress = createAction(
|
||||
`${prefix} Set Shipping Address`,
|
||||
props<{ processId: number; shippingAddress: ShippingAddressDTO }>()
|
||||
@@ -52,6 +48,8 @@ export const removeProcess = createAction(`${prefix} Remove Process`, props<{ pr
|
||||
|
||||
export const setOrders = createAction(`${prefix} Add Orders`, props<{ orders: DisplayOrderDTO[] }>());
|
||||
|
||||
export const updateOrderItem = createAction(`${prefix} Update Orders`, props<{ item: DisplayOrderItemDTO }>());
|
||||
|
||||
export const removeAllOrders = createAction(`${prefix} Remove All Orders`);
|
||||
|
||||
export const setBuyer = createAction(`${prefix} Set Buyer`, props<{ processId: number; buyer: BuyerDTO }>());
|
||||
@@ -61,3 +59,5 @@ export const setPayer = createAction(`${prefix} Set Payer`, props<{ processId: n
|
||||
export const setSpecialComment = createAction(`${prefix} Set Agent Comment`, props<{ processId: number; agentComment: string }>());
|
||||
|
||||
export const setOlaError = createAction(`${prefix} Set Ola Error`, props<{ processId: number; olaErrorIds: number[] }>());
|
||||
|
||||
export const setCustomer = createAction(`${prefix} Set Customer`, props<{ processId: number; customer: CustomerDTO }>());
|
||||
|
||||
@@ -46,11 +46,6 @@ const _domainCheckoutReducer = createReducer(
|
||||
};
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setCustomerFeatures, (s, { processId, customerFeatures }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.customerFeatures = customerFeatures;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setShippingAddress, (s, { processId, shippingAddress }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.shippingAddress = shippingAddress;
|
||||
@@ -73,6 +68,25 @@ const _domainCheckoutReducer = createReducer(
|
||||
}),
|
||||
on(DomainCheckoutActions.removeProcess, (s, { processId }) => storeCheckoutAdapter.removeOne(processId, s)),
|
||||
on(DomainCheckoutActions.setOrders, (s, { orders }) => ({ ...s, orders: [...s.orders, ...orders] })),
|
||||
on(DomainCheckoutActions.updateOrderItem, (s, { item }) => {
|
||||
const orders = [...s.orders];
|
||||
|
||||
const orderToUpdate = orders?.find((order) => order.items?.find((i) => i.id === item?.id));
|
||||
const orderToUpdateIndex = orders?.indexOf(orderToUpdate);
|
||||
|
||||
const orderItemToUpdate = orderToUpdate?.items?.find((i) => i.id === item?.id);
|
||||
const orderItemToUpdateIndex = orderToUpdate?.items?.indexOf(orderItemToUpdate);
|
||||
|
||||
const items = [...orderToUpdate?.items];
|
||||
items[orderItemToUpdateIndex] = item;
|
||||
|
||||
orders[orderToUpdateIndex] = {
|
||||
...orderToUpdate,
|
||||
items: [...items],
|
||||
};
|
||||
|
||||
return { ...s, orders: [...orders] };
|
||||
}),
|
||||
on(DomainCheckoutActions.removeAllOrders, (s) => ({
|
||||
...s,
|
||||
orders: [],
|
||||
@@ -81,6 +95,11 @@ const _domainCheckoutReducer = createReducer(
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.olaErrorIds = olaErrorIds;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setCustomer, (s, { processId, customer }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.customer = customer;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -96,7 +115,6 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
processId,
|
||||
checkout: undefined,
|
||||
shoppingCart: undefined,
|
||||
customerFeatures: undefined,
|
||||
shippingAddress: undefined,
|
||||
orders: [],
|
||||
payer: undefined,
|
||||
@@ -104,6 +122,7 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
specialComment: '',
|
||||
notificationChannels: 0,
|
||||
olaErrorIds: [],
|
||||
customer: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { CheckoutEntity } from './defs/checkout.entity';
|
||||
import { storeCheckoutAdapter, storeFeatureSelector } from './domain-checkout.state';
|
||||
|
||||
@@ -22,7 +23,7 @@ export const selectCheckoutByProcessId = createSelector(
|
||||
|
||||
export const selectCustomerFeaturesByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.customerFeatures
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => getCusomterFeatures(entities[processId]?.customer)
|
||||
);
|
||||
|
||||
export const selectShippingAddressByProcessId = createSelector(
|
||||
@@ -61,3 +62,19 @@ export const selectOlaErrorsByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.olaErrorIds
|
||||
);
|
||||
|
||||
export const selectCustomerByProcessId = createSelector(
|
||||
selectEntities,
|
||||
(entities: Dictionary<CheckoutEntity>, { processId }: { processId: number }) => entities[processId]?.customer
|
||||
);
|
||||
|
||||
function getCusomterFeatures(custoemr: CustomerDTO): { [key: string]: string } {
|
||||
const customerFeatures = custoemr?.features ?? [];
|
||||
const features: { [key: string]: string } = {};
|
||||
|
||||
for (const feature of customerFeatures) {
|
||||
features[feature.key] = feature.key;
|
||||
}
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
AddressDTO,
|
||||
AddressService,
|
||||
AssignedPayerDTO,
|
||||
AutocompleteDTO,
|
||||
CommunicationDetailsDTO,
|
||||
CountryDTO,
|
||||
CountryService,
|
||||
CustomerDTO,
|
||||
CustomerInfoDTO,
|
||||
CustomerService,
|
||||
InputDTO,
|
||||
KeyValueDTOOfStringAndString,
|
||||
ListResponseArgsOfCustomerInfoDTO,
|
||||
LoyaltyCardService,
|
||||
NotificationChannel,
|
||||
PayerDTO,
|
||||
PayerService,
|
||||
ResponseArgsOfHistoryDTO,
|
||||
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
||||
ShippingAddressDTO,
|
||||
ShippingAddressService,
|
||||
} from '@swagger/crm';
|
||||
import { isArray } from '@utils/common';
|
||||
import { PagedResult, Result } from 'apps/domain/defs/src/public-api';
|
||||
@@ -24,7 +29,14 @@ import { catchError, map, mergeMap, retry } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CrmCustomerService {
|
||||
constructor(private customerService: CustomerService, private payerService: PayerService) {}
|
||||
constructor(
|
||||
private customerService: CustomerService,
|
||||
private payerService: PayerService,
|
||||
private addressService: AddressService,
|
||||
private countryService: CountryService,
|
||||
private shippingAddressService: ShippingAddressService,
|
||||
private loyaltyCardService: LoyaltyCardService
|
||||
) {}
|
||||
|
||||
complete(queryString: string, filter?: { [key: string]: string }): Observable<Result<AutocompleteDTO[]>> {
|
||||
return this.customerService.CustomerCustomerAutocomplete({
|
||||
@@ -66,10 +78,6 @@ export class CrmCustomerService {
|
||||
return this.customerService.CustomerGetAssignedPayersByCustomerId(params);
|
||||
}
|
||||
|
||||
getFilters(): Observable<Result<InputDTO[]>> {
|
||||
return this.customerService.CustomerQueryCustomerFilter();
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails,
|
||||
@@ -96,12 +104,23 @@ export class CrmCustomerService {
|
||||
return this.customerService.CustomerPatchCustomer({ customerId, customer: { ...customer, notificationChannels } });
|
||||
}
|
||||
|
||||
createB2BCustomer(customer: CustomerDTO) {
|
||||
createB2BCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
|
||||
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails: customer?.communicationDetails,
|
||||
});
|
||||
|
||||
return this.customerService.CustomerCreateCustomer({ ...customer, customerType: 16, notificationChannels });
|
||||
const payload: CustomerDTO = { ...customer, customerType: 16, notificationChannels };
|
||||
|
||||
payload.shippingAddresses = payload.shippingAddresses ?? [];
|
||||
|
||||
payload.payers = payload.payers ?? [];
|
||||
|
||||
return this.customerService
|
||||
.CustomerCreateCustomer({
|
||||
customer: payload,
|
||||
modifiers: [{ key: 'b2b', group: 'customertype' }],
|
||||
})
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
createOnlineCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
@@ -148,63 +167,253 @@ export class CrmCustomerService {
|
||||
];
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateOnlineCustomer({ ...payload, notificationChannels });
|
||||
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
|
||||
|
||||
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
|
||||
|
||||
if (p4mUser) {
|
||||
modifiers.push({ key: 'add-loyalty-card', value: p4mUser });
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: { ...payload, notificationChannels },
|
||||
modifiers,
|
||||
});
|
||||
}
|
||||
|
||||
createGuestCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
mapCustomerToPayer(customer: CustomerDTO): PayerDTO {
|
||||
return {
|
||||
address: customer.address,
|
||||
communicationDetails: customer.communicationDetails,
|
||||
firstName: customer.firstName,
|
||||
lastName: customer.lastName,
|
||||
organisation: customer.organisation,
|
||||
title: customer.title,
|
||||
payerType: 1,
|
||||
gender: customer.gender,
|
||||
};
|
||||
}
|
||||
|
||||
mapCustomerToShippingAddress(customer: CustomerDTO): ShippingAddressDTO {
|
||||
return {
|
||||
address: customer.address,
|
||||
communicationDetails: customer.communicationDetails,
|
||||
firstName: customer.firstName,
|
||||
gender: customer.gender,
|
||||
lastName: customer.lastName,
|
||||
organisation: customer.organisation,
|
||||
title: customer.title,
|
||||
type: 1,
|
||||
};
|
||||
}
|
||||
|
||||
async updateToOnlineCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
|
||||
const payload: CustomerDTO = { shippingAddresses: [], payers: [], ...customer, customerType: 8, hasOnlineAccount: true };
|
||||
|
||||
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails: payload?.communicationDetails,
|
||||
});
|
||||
|
||||
const shippingAddressesToAdd = payload.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
|
||||
payload.shippingAddresses = payload.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
|
||||
|
||||
if (payload.shippingAddresses.length === 0) {
|
||||
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(payload));
|
||||
}
|
||||
|
||||
const payersToAdd = payload.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
|
||||
payload.payers = payload.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
|
||||
|
||||
if (payload.payers.length === 0) {
|
||||
payersToAdd.unshift({
|
||||
...this.mapCustomerToPayer(payload),
|
||||
payerType: payload.customerType,
|
||||
});
|
||||
}
|
||||
|
||||
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
|
||||
|
||||
const res = await this.customerService
|
||||
.CustomerUpdateCustomer({
|
||||
customerId: customer.id,
|
||||
payload: {
|
||||
modifiers,
|
||||
customer: { ...payload, notificationChannels },
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
for (let shippingAddress of shippingAddressesToAdd) {
|
||||
await this.createShippingAddress(res.result.id, shippingAddress, true);
|
||||
}
|
||||
|
||||
for (let payer of payersToAdd) {
|
||||
await this.createPayer(res.result.id, payer, true);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async updateToP4MOnlineCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
|
||||
const shippingAddressesToAdd = customer.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
|
||||
customer.shippingAddresses = customer.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
|
||||
|
||||
if (customer.shippingAddresses.length === 0) {
|
||||
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(customer));
|
||||
}
|
||||
|
||||
const payersToAdd = customer.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
|
||||
customer.payers = customer.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
|
||||
|
||||
if (customer.payers.length === 0) {
|
||||
payersToAdd.unshift({
|
||||
...this.mapCustomerToPayer(customer),
|
||||
payerType: customer.customerType,
|
||||
});
|
||||
}
|
||||
|
||||
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
|
||||
|
||||
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
|
||||
|
||||
if (p4mUser) {
|
||||
modifiers.push({ key: 'add-loyalty-card', value: p4mUser });
|
||||
}
|
||||
|
||||
const res = await this.customerService
|
||||
.CustomerUpdateCustomer({
|
||||
customerId: customer.id,
|
||||
payload: {
|
||||
customer,
|
||||
modifiers,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
for (let shippingAddress of shippingAddressesToAdd) {
|
||||
await this.createShippingAddress(res.result.id, shippingAddress, true);
|
||||
}
|
||||
|
||||
for (let payer of payersToAdd) {
|
||||
await this.createPayer(res.result.id, payer, true);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async updateStoreP4MToWebshopP4M(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
|
||||
const shippingAddressesToAdd = customer.shippingAddresses?.filter((sa) => !sa.id)?.map((m) => m.data) ?? [];
|
||||
customer.shippingAddresses = customer.shippingAddresses?.filter((sa) => !!sa.id) ?? [];
|
||||
|
||||
if (customer.shippingAddresses.length === 0) {
|
||||
shippingAddressesToAdd.unshift(this.mapCustomerToShippingAddress(customer));
|
||||
}
|
||||
|
||||
const payersToAdd = customer.payers?.filter((p) => !p.assignedToCustomer)?.map((p) => p.payer?.data) ?? [];
|
||||
customer.payers = customer.payers?.filter((p) => !!p.assignedToCustomer) ?? [];
|
||||
|
||||
if (customer.payers.length === 0) {
|
||||
payersToAdd.unshift({
|
||||
...this.mapCustomerToPayer(customer),
|
||||
payerType: customer.customerType,
|
||||
});
|
||||
}
|
||||
|
||||
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'webshop', group: 'customertype' }];
|
||||
|
||||
const res = await this.customerService
|
||||
.CustomerUpdateCustomer({
|
||||
customerId: customer.id,
|
||||
payload: {
|
||||
customer,
|
||||
modifiers,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
for (let shippingAddress of shippingAddressesToAdd) {
|
||||
await this.createShippingAddress(res.result.id, shippingAddress, true);
|
||||
}
|
||||
|
||||
for (let payer of payersToAdd) {
|
||||
await this.createPayer(res.result.id, payer, true);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async createGuestCustomer(customer: CustomerDTO): Promise<Result<CustomerDTO>> {
|
||||
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails: customer?.communicationDetails,
|
||||
});
|
||||
const payload: CustomerDTO = { ...customer, customerType: 8, isGuestAccount: true, notificationChannels };
|
||||
|
||||
if (!(isArray(payload.shippingAddresses) && payload.shippingAddresses.length > 0)) {
|
||||
payload.shippingAddresses = [
|
||||
{
|
||||
data: {
|
||||
address: payload.address,
|
||||
communicationDetails: payload.communicationDetails,
|
||||
firstName: payload.firstName,
|
||||
gender: payload.gender,
|
||||
lastName: payload.lastName,
|
||||
organisation: payload.organisation,
|
||||
title: payload.title,
|
||||
type: 1,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
payload.shippingAddresses = customer.shippingAddresses ?? [];
|
||||
|
||||
if (!(isArray(payload.payers) && payload.payers.length > 0)) {
|
||||
payload.payers = [
|
||||
{
|
||||
payer: {
|
||||
data: {
|
||||
address: payload.address,
|
||||
communicationDetails: payload.communicationDetails,
|
||||
firstName: payload.firstName,
|
||||
gender: payload.gender,
|
||||
lastName: payload.lastName,
|
||||
organisation: payload.organisation,
|
||||
title: payload.title,
|
||||
payerType: payload.customerType,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
payload.shippingAddresses.push({
|
||||
data: this.mapCustomerToShippingAddress(customer),
|
||||
});
|
||||
|
||||
return this.customerService.CustomerCreateOnlineCustomer(payload);
|
||||
payload.payers = customer.payers ?? [];
|
||||
|
||||
payload.payers.push({
|
||||
payer: {
|
||||
data: {
|
||||
...this.mapCustomerToPayer(customer),
|
||||
payerType: payload.customerType,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const res = await this.customerService
|
||||
.CustomerCreateCustomer({
|
||||
customer: payload,
|
||||
modifiers: [{ key: 'webshop', group: 'customertype' }],
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
createBranchCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
createStoreCustomer(customer: CustomerDTO): Observable<Result<CustomerDTO>> {
|
||||
const notificationChannels = this.getNotificationChannelForCommunicationDetails({
|
||||
communicationDetails: customer?.communicationDetails,
|
||||
});
|
||||
return this.customerService.CustomerCreateCustomer({ ...customer, customerType: 8, notificationChannels });
|
||||
|
||||
const p4mUser = customer.features.find((f) => f.key === 'p4mUser')?.value;
|
||||
|
||||
const modifiers: KeyValueDTOOfStringAndString[] = [{ key: 'store', group: 'customertype' }];
|
||||
|
||||
if (p4mUser) {
|
||||
modifiers.push({ key: 'add-loyalty-card', value: p4mUser });
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateCustomer({
|
||||
customer: { ...customer, customerType: 8, notificationChannels },
|
||||
modifiers,
|
||||
});
|
||||
}
|
||||
|
||||
validateAddress(address: AddressDTO): Observable<Result<AddressDTO[]>> {
|
||||
return this.customerService.CustomerValidateAddress(address);
|
||||
return this.addressService.AddressValidateAddress(address);
|
||||
}
|
||||
|
||||
getOnlineCustomerByEmail(email: string): Observable<CustomerInfoDTO | null> {
|
||||
return this.getCustomers(email, {
|
||||
take: 1,
|
||||
filter: {
|
||||
customertype: 'webshop',
|
||||
},
|
||||
}).pipe(
|
||||
map((r) => {
|
||||
if (r.hits === 1) {
|
||||
return r.result[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
catchError((err) => [null])
|
||||
);
|
||||
}
|
||||
|
||||
private cachedCountriesFailed = false;
|
||||
@@ -213,8 +422,8 @@ export class CrmCustomerService {
|
||||
if (!this.cachedCountries || this.cachedCountriesFailed) {
|
||||
this.cachedCountriesFailed = false;
|
||||
this.cachedCountries = new ReplaySubject();
|
||||
this.customerService
|
||||
.CustomerGetCountries({})
|
||||
this.countryService
|
||||
.CountryGetCountries({})
|
||||
.pipe(
|
||||
retry(3),
|
||||
catchError((err) => {
|
||||
@@ -235,22 +444,28 @@ export class CrmCustomerService {
|
||||
return this.customerService.CustomerEmailExists(email);
|
||||
}
|
||||
|
||||
createPayer(customerId: number, payer: PayerDTO, isDefault?: boolean): Observable<[Result<PayerDTO>, Result<AssignedPayerDTO>]> {
|
||||
return this.getCustomer(customerId).pipe(
|
||||
mergeMap((customerResponse) =>
|
||||
this.payerService
|
||||
.PayerCreatePayer({ ...payer, payerType: customerResponse.result.customerType })
|
||||
.pipe(
|
||||
mergeMap((payerResponse) =>
|
||||
this.customerService
|
||||
.CustomerAddPayerReference({ customerId: customerId, payerId: payerResponse.result.id, isDefault: isDefault })
|
||||
.pipe(
|
||||
map((assigendPayerResponse) => [payerResponse, assigendPayerResponse] as [Result<PayerDTO>, Result<AssignedPayerDTO>])
|
||||
)
|
||||
checkLoyaltyCard({ loyaltyCardNumber, customerId }: { loyaltyCardNumber: string; customerId?: number }) {
|
||||
return this.loyaltyCardService.LoyaltyCardCheckLoyaltyCard({ loyaltyCardNumber, customerId });
|
||||
}
|
||||
|
||||
createPayer(customerId: number, payer: PayerDTO, isDefault?: boolean): Promise<[Result<PayerDTO>, Result<AssignedPayerDTO>]> {
|
||||
return this.getCustomer(customerId)
|
||||
.pipe(
|
||||
mergeMap((customerResponse) =>
|
||||
this.payerService
|
||||
.PayerCreatePayer({ ...payer, payerType: customerResponse.result.customerType })
|
||||
.pipe(
|
||||
mergeMap((payerResponse) =>
|
||||
this.customerService
|
||||
.CustomerAddPayerReference({ customerId: customerId, payerId: payerResponse.result.id, isDefault: isDefault })
|
||||
.pipe(
|
||||
map((assigendPayerResponse) => [payerResponse, assigendPayerResponse] as [Result<PayerDTO>, Result<AssignedPayerDTO>])
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
updatePayer(customerId: number, payerId: number, payer: PayerDTO, isDefault?: boolean): Observable<Result<PayerDTO>> {
|
||||
@@ -263,11 +478,7 @@ export class CrmCustomerService {
|
||||
return this.customerService.CustomerModifyPayerReference({ payerId, customerId, isDefault });
|
||||
}
|
||||
|
||||
createShippingAddress(
|
||||
customerId: number,
|
||||
shippingAddress: ShippingAddressDTO,
|
||||
isDefault?: boolean
|
||||
): Observable<Result<ShippingAddressDTO>> {
|
||||
createShippingAddress(customerId: number, shippingAddress: ShippingAddressDTO, isDefault?: boolean): Promise<Result<ShippingAddressDTO>> {
|
||||
const data: ShippingAddressDTO = { ...shippingAddress };
|
||||
if (isDefault) {
|
||||
data.isDefault = new Date().toJSON();
|
||||
@@ -275,7 +486,7 @@ export class CrmCustomerService {
|
||||
delete data.isDefault;
|
||||
}
|
||||
|
||||
return this.customerService.CustomerCreateShippingAddress({ customerId, shippingAddress: data });
|
||||
return this.shippingAddressService.ShippingAddressCreateShippingAddress({ customerId, shippingAddress: data }).toPromise();
|
||||
}
|
||||
|
||||
updateShippingAddress(
|
||||
@@ -283,7 +494,7 @@ export class CrmCustomerService {
|
||||
shippingAddressId: number,
|
||||
shippingAddress: ShippingAddressDTO,
|
||||
isDefault?: boolean
|
||||
): Observable<Result<ShippingAddressDTO>> {
|
||||
): Promise<Result<ShippingAddressDTO>> {
|
||||
const data: ShippingAddressDTO = { ...shippingAddress };
|
||||
|
||||
if (isDefault) {
|
||||
@@ -292,15 +503,17 @@ export class CrmCustomerService {
|
||||
delete data.isDefault;
|
||||
}
|
||||
|
||||
return this.customerService.CustomerUpdateShippingAddress({ shippingAddressId, shippingAddress: data, customerId });
|
||||
return this.shippingAddressService
|
||||
.ShippingAddressUpdateShippingAddress({ shippingAddressId, shippingAddress: data, customerId })
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
getShippingAddress(shippingAddressId: number): Observable<Result<ShippingAddressDTO>> {
|
||||
return this.customerService.CustomerGetShippingaddress(shippingAddressId);
|
||||
return this.shippingAddressService.ShippingAddressGetShippingaddress(shippingAddressId);
|
||||
}
|
||||
|
||||
getShippingAddresses(params: CustomerService.CustomerGetShippingAddressesParams): Observable<Result<ShippingAddressDTO[]>> {
|
||||
return this.customerService.CustomerGetShippingAddresses(params);
|
||||
getShippingAddresses(params: ShippingAddressService.ShippingAddressGetShippingAddressesParams): Observable<Result<ShippingAddressDTO[]>> {
|
||||
return this.shippingAddressService.ShippingAddressGetShippingAddresses(params);
|
||||
}
|
||||
|
||||
getPayer(payerId: number): Observable<Result<PayerDTO>> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { DialogOfString } from '@swagger/crm';
|
||||
|
||||
export interface Result<T> {
|
||||
/** Ergebnis */
|
||||
result?: T;
|
||||
@@ -13,4 +15,6 @@ export interface Result<T> {
|
||||
|
||||
/** Fehlerhafte Daten */
|
||||
invalidProperties?: { [key: string]: string };
|
||||
|
||||
dialog?: DialogOfString;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// start:ng42.barrel
|
||||
export * from './info-feed-item';
|
||||
export * from './info-feed';
|
||||
export * from './kpi-feed-item';
|
||||
export * from './kpi-feed';
|
||||
export * from './products-feed';
|
||||
|
||||
4
apps/domain/isa/src/lib/defs/info-feed-item.ts
Normal file
4
apps/domain/isa/src/lib/defs/info-feed-item.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface InfoFeedItem {
|
||||
heading: string;
|
||||
text: string;
|
||||
}
|
||||
7
apps/domain/isa/src/lib/defs/info-feed.ts
Normal file
7
apps/domain/isa/src/lib/defs/info-feed.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { FeedDTO } from '@swagger/isa';
|
||||
import { InfoFeedItem } from './info-feed-item';
|
||||
|
||||
export interface InfoFeed extends FeedDTO {
|
||||
type: 'info';
|
||||
items: InfoFeedItem[];
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
content: ReorderModalComponent,
|
||||
title: 'Artikel nachbestellen',
|
||||
data: {
|
||||
item: orderItem,
|
||||
item: { ...orderItem, quantity: data.itemQuantity?.get(orderItem.orderItemSubsetId) ?? orderItem.quantity },
|
||||
showReasons: true,
|
||||
},
|
||||
})
|
||||
@@ -66,24 +66,15 @@ export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
}
|
||||
|
||||
createToast(comment?: string) {
|
||||
let text: string = '';
|
||||
|
||||
switch (comment) {
|
||||
case 'Artikel unverkäuflich, (und physisch in der Filiale vorhanden)':
|
||||
text = 'Der Beschädigte Artikel wurde auf die Remi-Liste gesetzt.';
|
||||
break;
|
||||
case 'Falscher Titel geliefert (richtiges Etikett)':
|
||||
text = 'Die Falschlieferung wurde auf die Remi-Liste gesetzt.';
|
||||
break;
|
||||
default:
|
||||
text = '';
|
||||
break;
|
||||
this._toastService.create({
|
||||
title: 'Artikel wurde nachbestellt',
|
||||
});
|
||||
}
|
||||
|
||||
this._toastService.create({
|
||||
title: 'Artikel wurde nachbestellt',
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
async reorder(orderItem: OrderItemListItemDTO, availability: AvailabilityDTO2, comment: string) {
|
||||
|
||||
@@ -23,7 +23,7 @@ export class DomainGoodsService {
|
||||
return this.abholfachService.AbholfachWarenausgabeAutocomplete(autocompleteToken);
|
||||
}
|
||||
|
||||
getItemByOrderNumber(orderNumber: string) {
|
||||
getWareneingangItemByOrderNumber(orderNumber: string) {
|
||||
return this.abholfachService.AbholfachWareneingang({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
input: {
|
||||
@@ -32,21 +32,30 @@ export class DomainGoodsService {
|
||||
});
|
||||
}
|
||||
|
||||
getItemByCompartment(compartmentCode: string) {
|
||||
return this.abholfachService.AbholfachWareneingang({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
getWarenausgabeItemByOrderNumber(orderNumber: string, archive: boolean) {
|
||||
return this.abholfachService.AbholfachWarenausgabe({
|
||||
filter: { all_branches: 'true', archive: `${archive}` },
|
||||
input: {
|
||||
qs: orderNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getWarenausgabeItemByCompartment(compartmentCode: string, archive: boolean) {
|
||||
return this.abholfachService.AbholfachWarenausgabe({
|
||||
filter: { all_branches: 'true', archive: `${archive}` },
|
||||
input: {
|
||||
qs: compartmentCode,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getItemByCustomerNumber(customerNumber: string) {
|
||||
getWareneingangItemByCustomerNumber(customerNumber: string) {
|
||||
// Suche anhand der Kundennummer mit Status Bestellt, nachbestellt, eingetroffen, weitergeleitet intern
|
||||
return this.abholfachService.AbholfachWareneingang({
|
||||
filter: { orderitemprocessingstatus: '16;128;8192;1048576' },
|
||||
input: {
|
||||
qs: customerNumber,
|
||||
customer_name: customerNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
BranchService,
|
||||
ChangeStockStatusCodeValues,
|
||||
HistoryDTO,
|
||||
NotificationChannel,
|
||||
@@ -11,8 +12,10 @@ import {
|
||||
OrderService,
|
||||
ReceiptService,
|
||||
StatusValues,
|
||||
StockStatusCodeService,
|
||||
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
|
||||
ValueTupleOfOrderItemSubsetDTOAndOrderItemSubsetDTO,
|
||||
VATService,
|
||||
} from '@swagger/oms';
|
||||
import { memorize } from '@utils/common';
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -23,6 +26,9 @@ export class DomainOmsService {
|
||||
constructor(
|
||||
private orderService: OrderService,
|
||||
private receiptService: ReceiptService,
|
||||
private branchService: BranchService,
|
||||
private vatService: VATService,
|
||||
private stockStatusCodeService: StockStatusCodeService,
|
||||
private _orderCheckoutService: OrderCheckoutService
|
||||
) {}
|
||||
|
||||
@@ -37,7 +43,7 @@ export class DomainOmsService {
|
||||
}
|
||||
|
||||
getBranches() {
|
||||
return this.orderService.OrderGetBranches({});
|
||||
return this.branchService.BranchGetBranches({});
|
||||
}
|
||||
|
||||
getHistory(orderItemSubsetId: number): Observable<HistoryDTO> {
|
||||
@@ -62,13 +68,13 @@ export class DomainOmsService {
|
||||
|
||||
@memorize()
|
||||
getVATs() {
|
||||
return this.orderService.OrderGetVATs({}).pipe(map((response) => response.result));
|
||||
return this.vatService.VATGetVATs({}).pipe(map((response) => response.result));
|
||||
}
|
||||
|
||||
// ttl 4 Stunden
|
||||
@memorize({ ttl: 14400000 })
|
||||
getStockStatusCodes({ supplierId, eagerLoading = 0 }: { supplierId: number; eagerLoading?: number }) {
|
||||
return this.orderService.OrderGetStockStatusCodes({ supplierId, eagerLoading }).pipe(
|
||||
return this.stockStatusCodeService.StockStatusCodeGetStockStatusCodes({ supplierId, eagerLoading }).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay()
|
||||
);
|
||||
@@ -152,6 +158,10 @@ export class DomainOmsService {
|
||||
.pipe(map((response) => response.result));
|
||||
}
|
||||
|
||||
setPreferredPickUpDate({ data }: { data: { [key: string]: string } }) {
|
||||
return this.orderService.OrderSetPreferredPickUpDate({ data });
|
||||
}
|
||||
|
||||
changeOrderItemStatus(data: OrderService.OrderChangeStatusParams) {
|
||||
return this.orderService.OrderChangeStatus(data);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ReceiptOrderItemSubsetReferenceValues, ReceiptService } from '@swagger/oms';
|
||||
import { memorize } from '@utils/common';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class DomainReceiptService {
|
||||
@@ -9,9 +11,12 @@ export class DomainReceiptService {
|
||||
return this.receiptService.ReceiptCreateShippingNote2(params);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 1000 })
|
||||
getReceipts(payload: ReceiptOrderItemSubsetReferenceValues) {
|
||||
return this.receiptService.ReceiptGetReceiptsByOrderItemSubset({
|
||||
payload: payload,
|
||||
});
|
||||
return this.receiptService
|
||||
.ReceiptGetReceiptsByOrderItemSubset({
|
||||
payload: payload,
|
||||
})
|
||||
.pipe(shareReplay(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
PrintRequestOfIEnumerableOfPriceQRCodeDTO,
|
||||
PrintService,
|
||||
ResponseArgs,
|
||||
LoyaltyCardPrintService,
|
||||
} from '@swagger/print';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, filter, map, switchMap, timeout } from 'rxjs/operators';
|
||||
@@ -25,7 +26,8 @@ export class DomainPrinterService {
|
||||
private oMSPrintService: OMSPrintService,
|
||||
private catalogPrintService: CatalogPrintService,
|
||||
private checkoutPrintService: CheckoutPrintService,
|
||||
private eisPublicDocumentService: EISPublicDocumentService
|
||||
private eisPublicDocumentService: EISPublicDocumentService,
|
||||
private _loyaltyCardPrintService: LoyaltyCardPrintService
|
||||
) {}
|
||||
|
||||
getAvailablePrinters(): Observable<Printer[] | { error: string }> {
|
||||
@@ -143,6 +145,13 @@ export class DomainPrinterService {
|
||||
});
|
||||
}
|
||||
|
||||
printKubiAgb({ p4mCode, printer }: { p4mCode: string; printer: string }) {
|
||||
return this._loyaltyCardPrintService.LoyaltyCardPrintPrintLoyaltyCardAGB({
|
||||
printer,
|
||||
data: p4mCode,
|
||||
});
|
||||
}
|
||||
|
||||
printProduct({ item, printer }: { item: ItemDTO; printer: string }): Observable<ResponseArgs> {
|
||||
const params = <PrintRequestOfIEnumerableOfItemDTO>{
|
||||
printer: printer,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ReceiptDTO,
|
||||
ReturnDTO,
|
||||
ReturnQueryTokenDTO,
|
||||
BatchResponseArgsOfReturnItemDTOAndReturnItemDTO,
|
||||
} from '@swagger/remi';
|
||||
import { memorize } from '@utils/common';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
@@ -200,7 +201,7 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getStockInformation(items: RemissionListItem[]) {
|
||||
getStockInformation(items: RemissionListItem[], recalculate: boolean = false) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._stockService
|
||||
@@ -230,14 +231,13 @@ export class DomainRemissionService {
|
||||
|
||||
let { remainingQuantity, remissionQuantity } = item;
|
||||
|
||||
if (!remissionQuantity) {
|
||||
if (!remissionQuantity || recalculate) {
|
||||
remissionQuantity = inStock - (remainingQuantity || 0);
|
||||
if (remissionQuantity < 0) {
|
||||
remissionQuantity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!remainingQuantity) {
|
||||
if (!remainingQuantity || recalculate) {
|
||||
remainingQuantity = inStock - (remissionQuantity || 0);
|
||||
if (remainingQuantity < 0) {
|
||||
remainingQuantity = 0;
|
||||
@@ -299,6 +299,12 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
canAddReturnItem(item: ReturnItemDTO): Observable<BatchResponseArgsOfReturnItemDTOAndReturnItemDTO> {
|
||||
return this._remiService.RemiCanAddReturnItem({
|
||||
data: [item],
|
||||
});
|
||||
}
|
||||
|
||||
async createReturn(supplierId: number, returnGroup?: string): Promise<ReturnDTO> {
|
||||
const response = await this._returnService
|
||||
.ReturnCreateReturn({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis';
|
||||
import { DisplayInfoDTO, EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
import { memorize } from '@utils/common';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
@@ -279,4 +279,80 @@ export class DomainTaskCalendarService {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
moveRemovedToEnd(a: DisplayInfoDTO, b: DisplayInfoDTO) {
|
||||
const statusA = this.getProcessingStatusList(a)?.includes('Removed');
|
||||
const statusB = this.getProcessingStatusList(b)?.includes('Removed');
|
||||
|
||||
if (statusA && statusB) {
|
||||
return 0;
|
||||
} else if (statusA && !statusB) {
|
||||
return 1;
|
||||
} else if (!statusA && statusB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Array of DisplayInfoDTO, sorted by ProcessingStatus
|
||||
* Ignores Overdue if Task is already Completed
|
||||
* Compared DisploayInfoDTO is of Type Task and Info Or PreInfo then sort by Type
|
||||
* @param items DisplayInfoDTO Array to sort
|
||||
* @param order Processing Status Order
|
||||
* @returns DisplayInfoDTO Array ordered by Processing Status anf Type
|
||||
*/
|
||||
sort(items: DisplayInfoDTO[], order: ProcessingStatusList) {
|
||||
let result = [...items];
|
||||
const reversedOrder = [...order].reverse();
|
||||
|
||||
for (const status of reversedOrder) {
|
||||
result = result?.sort((a, b) => {
|
||||
const statusA = this.getProcessingStatusList(a);
|
||||
const statusB = this.getProcessingStatusList(b);
|
||||
|
||||
// Ignore Overdue when it is already Completed
|
||||
if (status === 'Overdue' && statusA.includes('Completed')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const aHasStatus = statusA.includes(status);
|
||||
const bHasStatus = statusB.includes(status);
|
||||
|
||||
if (aHasStatus && bHasStatus) {
|
||||
// If it has the same ProcessingStatus then Sort by Type
|
||||
const aType = this.getInfoType(a);
|
||||
const bType = this.getInfoType(b);
|
||||
if (aType !== bType) {
|
||||
if (aType === 'Info' || aType === 'PreInfo') {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (statusB.includes('Completed')) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if (aHasStatus && !bHasStatus) {
|
||||
return -1;
|
||||
} else if (!aHasStatus && bHasStatus) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getDateGroupKey(d: string) {
|
||||
// Get Date as string key to ignore time for grouping
|
||||
const date = new Date(d);
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import { ApplicationService } from '@core/application';
|
||||
import { of } from 'rxjs';
|
||||
import { Renderer2 } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ServiceWorkerModule, SwUpdate } from '@angular/service-worker';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
import { NotificationsHub } from '@hub/notifications';
|
||||
import { discardPeriodicTasks, fakeAsync, flush, tick } from '@angular/core/testing';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let spectator: Spectator<AppComponent>;
|
||||
@@ -21,7 +21,7 @@ describe('AppComponent', () => {
|
||||
component: AppComponent,
|
||||
imports: [CommonModule, RouterTestingModule],
|
||||
providers: [],
|
||||
mocks: [Config, SwUpdate],
|
||||
mocks: [Config, SwUpdate, UserStateService],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -8,6 +8,9 @@ import { NotificationsHub } from '@hub/notifications';
|
||||
import packageInfo from 'package';
|
||||
import { interval, Observable, Subscription } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { Platform } from '@angular/cdk/platform';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
import { IsaLogProvider } from './providers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -36,15 +39,18 @@ export class AppComponent implements OnInit {
|
||||
@Inject(DOCUMENT) private readonly _document: Document,
|
||||
private readonly _renderer: Renderer2,
|
||||
private readonly _swUpdate: SwUpdate,
|
||||
private readonly _notifications: NotificationsHub
|
||||
private readonly _notifications: NotificationsHub,
|
||||
private readonly _platform: Platform,
|
||||
private infoService: UserStateService
|
||||
) {
|
||||
this.updateClient();
|
||||
IsaLogProvider.InfoService = infoService;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setTitle();
|
||||
this.logVersion();
|
||||
|
||||
this.determinePlatform();
|
||||
this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this));
|
||||
}
|
||||
|
||||
@@ -56,6 +62,18 @@ export class AppComponent implements OnInit {
|
||||
console.log(`%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`, 'font-weight: bold; font-size: 20px;');
|
||||
}
|
||||
|
||||
determinePlatform() {
|
||||
if (this._platform.IOS && !this._platform.SAFARI) {
|
||||
this._renderer.addClass(this._document.body, 'tablet');
|
||||
this._renderer.addClass(this._document.body, 'tablet-native');
|
||||
} else if (this._platform.IOS && this._platform.SAFARI) {
|
||||
this._renderer.addClass(this._document.body, 'tablet');
|
||||
this._renderer.addClass(this._document.body, 'tablet-browser');
|
||||
} else if (this._platform.isBrowser) {
|
||||
this._renderer.addClass(this._document.body, 'desktop');
|
||||
}
|
||||
}
|
||||
|
||||
sectionChangeHandler(section: string) {
|
||||
if (section === 'customer') {
|
||||
this._renderer.removeClass(this._document.body, 'branch');
|
||||
|
||||
@@ -2,9 +2,11 @@ import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, ErrorHandler, Injector, LOCALE_ID, NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { PlatformModule } from '@angular/cdk/platform';
|
||||
|
||||
import { Config, ConfigModule, JsonConfigLoader } from '@core/config';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { CoreCommandModule } from '@core/command';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -29,6 +31,9 @@ import { IsaLogProvider } from './providers';
|
||||
import { IsaErrorHandler } from './providers/isa.error-handler';
|
||||
import { ScanAdapterModule } from '@adapter/scan';
|
||||
import { RootStateService } from './store/root-state.service';
|
||||
import * as Commands from './commands';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
@@ -69,6 +74,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
useConfigLoader: JsonConfigLoader,
|
||||
jsonConfigLoaderUrl: '/config/config.json',
|
||||
}),
|
||||
CoreCommandModule.forRoot(Object.values(Commands)),
|
||||
CoreLoggerModule.forRoot(),
|
||||
AppStoreModule,
|
||||
AuthModule.forRoot(),
|
||||
@@ -81,6 +87,13 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
registrationStrategy: 'registerWhenStable:30000',
|
||||
}),
|
||||
ScanAdapterModule.forRoot(!environment.production),
|
||||
PlatformModule,
|
||||
UiIconModule.forRoot({
|
||||
aliases: [
|
||||
{ alias: 'd-account', name: 'account' },
|
||||
{ alias: 'd-no-account', name: 'package-variant-closed' },
|
||||
],
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
||||
14
apps/isa-app/src/app/commands/close.command.ts
Normal file
14
apps/isa-app/src/app/commands/close.command.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
|
||||
/** Dummy Command um Fehlermeldungen aus dem Diloag zu verhinden */
|
||||
@Injectable()
|
||||
export class CloseCommand extends ActionHandler<any> {
|
||||
constructor() {
|
||||
super('CLOSE');
|
||||
}
|
||||
|
||||
handler(ctx: any): any {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
35
apps/isa-app/src/app/commands/create-customer.command.ts
Normal file
35
apps/isa-app/src/app/commands/create-customer.command.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
|
||||
@Injectable()
|
||||
export class CreateCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _router: Router, private _application: ApplicationService) {
|
||||
super('CREATE_CUSTOMER');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
let customerType: string;
|
||||
if (data.result.length > 0) {
|
||||
const customerInfo = data.result[0];
|
||||
if (customerInfo.features) {
|
||||
if (customerInfo.features.some((f) => f.key === 'store')) {
|
||||
customerType = 'store';
|
||||
}
|
||||
if (customerInfo.features.some((f) => f.key === 'webshop')) {
|
||||
customerType = 'webshop';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!customerType) {
|
||||
customerType = 'store';
|
||||
}
|
||||
|
||||
await this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'create', customerType]);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from 'apps/page/customer/src/lib/create-customer';
|
||||
|
||||
@Injectable()
|
||||
export class CreateKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _router: Router, private _application: ApplicationService) {
|
||||
super('CREATE_KUBI_CUSTOMER');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
let customerType: string;
|
||||
let formData: string;
|
||||
if (data.result.length > 0) {
|
||||
const customerInfo = data.result[0];
|
||||
const fd = mapCustomerInfoDtoToCustomerCreateFormData(customerInfo);
|
||||
formData = encodeFormData({
|
||||
...fd,
|
||||
agb: true,
|
||||
});
|
||||
|
||||
if (customerInfo.features) {
|
||||
if (customerInfo.features.some((f) => f.key === 'store')) {
|
||||
customerType = 'store';
|
||||
}
|
||||
if (customerInfo.features.some((f) => f.key === 'webshop')) {
|
||||
customerType = 'webshop';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!customerType) {
|
||||
customerType = 'store';
|
||||
}
|
||||
|
||||
await this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'create', `${customerType}-p4m`], {
|
||||
queryParams: { formData },
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
5
apps/isa-app/src/app/commands/index.ts
Normal file
5
apps/isa-app/src/app/commands/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './close.command';
|
||||
export * from './create-customer.command';
|
||||
export * from './create-kubi-customer.command';
|
||||
export * from './print-kubi-agb.command';
|
||||
export * from './remit.command';
|
||||
34
apps/isa-app/src/app/commands/print-kubi-agb.command.ts
Normal file
34
apps/isa-app/src/app/commands/print-kubi-agb.command.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { Result } from '@domain/defs';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { CustomerInfoDTO } from '@swagger/crm';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
|
||||
@Injectable()
|
||||
export class PrintKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
|
||||
constructor(private _uiModal: UiModalService, private _printerService: DomainPrinterService) {
|
||||
super('PRINT_KUBI_AGB');
|
||||
}
|
||||
|
||||
async handler(data: Result<CustomerInfoDTO[]>): Promise<Result<CustomerInfoDTO[]>> {
|
||||
const customerInfo = data.result ? data.result[0] : undefined;
|
||||
let p4mCode: string;
|
||||
if (customerInfo) {
|
||||
p4mCode = customerInfo.features.find((f) => f.key === 'p4mUser').value;
|
||||
}
|
||||
|
||||
await this._uiModal
|
||||
.open({
|
||||
content: PrintModalComponent,
|
||||
config: { showScrollbarY: false },
|
||||
data: {
|
||||
printerType: 'Label',
|
||||
print: (printer) => this._printerService.printKubiAgb({ printer, p4mCode }).toPromise(),
|
||||
} as PrintModalData,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
14
apps/isa-app/src/app/commands/remit.command.ts
Normal file
14
apps/isa-app/src/app/commands/remit.command.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
|
||||
/** Dummy Command um Fehlermeldungen aus dem Diloag zu verhinden */
|
||||
@Injectable()
|
||||
export class RemitCommand extends ActionHandler<any> {
|
||||
constructor() {
|
||||
super('remit');
|
||||
}
|
||||
|
||||
handler(ctx: any): any {
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateCustomerWithProcessIdGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService) {}
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const process = await this._applicationService
|
||||
@@ -25,16 +26,42 @@ export class CanActivateCustomerWithProcessIdGuard implements CanActivate {
|
||||
id: +route.params.processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes)}`,
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
}
|
||||
|
||||
await this.removeBreadcrumbWithSameProcessId(route);
|
||||
this._applicationService.activateProcess(+route.params.processId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
|
||||
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
|
||||
const crumbs = await this._breadcrumbService
|
||||
.getBreadcrumbByKey$(+route.params.processId)
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
// Entferne alle Crumbs die nichts mit der Kundensuche zu tun haben
|
||||
if (crumbs.length > 1) {
|
||||
const crumbsToRemove = crumbs.filter((crumb) => crumb.tags.find((tag) => tag === 'customer') === undefined);
|
||||
for (const crumb of crumbsToRemove) {
|
||||
await this._breadcrumbService.removeBreadcrumb(crumb.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers?.length > 0 ? Math.max(...processNumbers) + 1 : 1;
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
return missingNumber;
|
||||
}
|
||||
}
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,117 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateCustomerGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {}
|
||||
constructor(
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _checkoutService: DomainCheckoutService,
|
||||
private readonly _router: Router
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
let lastActivatedProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
if (!lastActivatedProcessId) {
|
||||
lastActivatedProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: lastActivatedProcessId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${processes.length + 1}`,
|
||||
});
|
||||
|
||||
const lastActivatedCartCheckoutProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
const lastActivatedGoodsOutProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();
|
||||
|
||||
// Darf nur reinkommen wenn der aktuell aktive Tab ein Bestellabschluss Tab ist
|
||||
if (!!lastActivatedCartCheckoutProcessId && lastActivatedCartCheckoutProcessId === activatedProcessId) {
|
||||
await this.fromCartCheckoutProcess(processes, lastActivatedCartCheckoutProcessId);
|
||||
return false;
|
||||
} else if (!!lastActivatedGoodsOutProcessId && lastActivatedGoodsOutProcessId === activatedProcessId) {
|
||||
await this.fromGoodsOutProcess(processes, lastActivatedGoodsOutProcessId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lastActivatedProcessId) {
|
||||
await this.fromCartProcess(processes);
|
||||
return false;
|
||||
} else {
|
||||
await this._router.navigate(['/kunde', String(lastActivatedProcessId), 'customer']);
|
||||
}
|
||||
await this._router.navigate(['/kunde', lastActivatedProcessId, 'customer']);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bei offener Artikelsuche/Kundensuche und Klick auf Footer Kundensuche
|
||||
async fromCartProcess(processes: ApplicationProcess[]) {
|
||||
const newProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: newProcessId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
|
||||
await this._router.navigate(['/kunde', String(newProcessId), 'customer']);
|
||||
}
|
||||
|
||||
// Bei offener Bestellbestätigung und Klick auf Footer Kundensuche
|
||||
async fromCartCheckoutProcess(processes: ApplicationProcess[], processId: number) {
|
||||
// Um alle Checkout Daten zu resetten die mit dem Prozess assoziiert sind
|
||||
this._checkoutService.removeProcess({ processId });
|
||||
|
||||
// Ändere type cart-checkout zu cart
|
||||
this._applicationService.patchProcess(processId, {
|
||||
id: processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
data: {},
|
||||
});
|
||||
|
||||
// Navigation
|
||||
await this._router.navigate(['/kunde', String(processId), 'customer']);
|
||||
}
|
||||
|
||||
// Bei offener Warenausgabe und Klick auf Footer Kundensuche
|
||||
async fromGoodsOutProcess(processes: ApplicationProcess[], processId: number) {
|
||||
const buyer = await this._checkoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
const customerFeatures = await this._checkoutService.getCustomerFeatures({ processId }).pipe(first()).toPromise();
|
||||
const name = buyer
|
||||
? customerFeatures?.b2b
|
||||
? buyer.organisation?.name
|
||||
? buyer.organisation?.name
|
||||
: buyer.lastName
|
||||
: buyer.lastName
|
||||
: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`;
|
||||
|
||||
// Ändere type goods-out zu cart
|
||||
this._applicationService.patchProcess(processId, {
|
||||
id: processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name,
|
||||
});
|
||||
|
||||
// Navigation
|
||||
await this._router.navigate(['/kunde', String(processId), 'customer']);
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
return missingNumber;
|
||||
}
|
||||
}
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateGoodsOutWithProcessIdGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService) {}
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const process = await this._applicationService
|
||||
@@ -14,19 +15,36 @@ export class CanActivateGoodsOutWithProcessIdGuard implements CanActivate {
|
||||
.toPromise();
|
||||
|
||||
if (!process) {
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
// const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
await this._applicationService.createProcess({
|
||||
id: +route.params.processId,
|
||||
type: 'goods-out',
|
||||
section: 'customer',
|
||||
name: `Warenausgabe ${this.processNumber(processes)}`,
|
||||
name: `Warenausgabe`,
|
||||
});
|
||||
}
|
||||
|
||||
await this.removeBreadcrumbWithSameProcessId(route);
|
||||
this._applicationService.activateProcess(+route.params.processId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
|
||||
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
|
||||
const crumbs = await this._breadcrumbService
|
||||
.getBreadcrumbByKey$(+route.params.processId)
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
// Entferne alle Crumbs die nichts mit der Warenausgabe zu tun haben
|
||||
if (crumbs.length > 1) {
|
||||
const crumbsToRemove = crumbs.filter((crumb) => crumb.tags.find((tag) => tag === 'goods-out') === undefined);
|
||||
for (const crumb of crumbsToRemove) {
|
||||
await this._breadcrumbService.removeBreadcrumb(crumb.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers?.length > 0 ? Math.max(...processNumbers) + 1 : 1;
|
||||
|
||||
@@ -1,44 +1,164 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { Config } from '@core/config';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateGoodsOutGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {}
|
||||
constructor(
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _checkoutService: DomainCheckoutService,
|
||||
private readonly _router: Router
|
||||
) {}
|
||||
|
||||
// !!! Ticket #3272 Code soll vorerst bestehen bleiben. Prozess Warenausgabe soll wieder Vorgang heißen (wie aktuell im Produktiv), bis zum neuen Navigationskonzept
|
||||
// -----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
// const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
|
||||
// let lastActivatedProcessId = (
|
||||
// await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
|
||||
// )?.id;
|
||||
|
||||
// const lastActivatedCartProcessId = (
|
||||
// await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
|
||||
// )?.id;
|
||||
|
||||
// const lastActivatedCartCheckoutProcessId = (
|
||||
// await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
|
||||
// )?.id;
|
||||
|
||||
// const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();
|
||||
|
||||
// // Darf nur reinkommen wenn der aktuell aktive Tab ein Bestellabschluss Tab ist
|
||||
// if (!!lastActivatedCartProcessId && lastActivatedCartProcessId === activatedProcessId) {
|
||||
// await this.fromCartProcess(processes, route, lastActivatedCartProcessId);
|
||||
// return false;
|
||||
// } else if (!!lastActivatedCartCheckoutProcessId && lastActivatedCartCheckoutProcessId === activatedProcessId) {
|
||||
// await this.fromCartCheckoutProcess(processes, route, lastActivatedCartCheckoutProcessId);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if (!lastActivatedProcessId) {
|
||||
// await this.fromGoodsOutProcess(processes, route);
|
||||
// return false;
|
||||
// } else {
|
||||
// await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(lastActivatedProcessId)]));
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // Bei offener Warenausgabe und Klick auf Footer Warenausgabe
|
||||
// async fromGoodsOutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot) {
|
||||
// const newProcessId = Date.now();
|
||||
// await this._applicationService.createProcess({
|
||||
// id: newProcessId,
|
||||
// type: 'goods-out',
|
||||
// section: 'customer',
|
||||
// name: `Warenausgabe ${this.processNumber(processes.filter((process) => process.type === 'goods-out'))}`,
|
||||
// });
|
||||
|
||||
// await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(newProcessId)]));
|
||||
// }
|
||||
|
||||
// // Bei offener Artikelsuche/Kundensuche und Klick auf Footer Warenausgabe
|
||||
// async fromCartProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot, processId: number) {
|
||||
// // Ändere type cart zu goods-out
|
||||
// this._applicationService.patchProcess(processId, {
|
||||
// id: processId,
|
||||
// type: 'goods-out',
|
||||
// section: 'customer',
|
||||
// name: `Warenausgabe ${this.processNumber(processes.filter((process) => process.type === 'goods-out'))}`,
|
||||
// });
|
||||
|
||||
// // Navigation
|
||||
// await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(processId)]));
|
||||
// }
|
||||
|
||||
// // Bei offener Bestellbestätigung, Artikelsuche/Kundensuche und Klick auf Footer Warenausgabe
|
||||
// async fromCartCheckoutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot, processId: number) {
|
||||
// // Um alle Checkout Daten zu resetten die mit dem Prozess assoziiert sind
|
||||
// this._checkoutService.removeProcess({ processId });
|
||||
|
||||
// // Ändere type cart-checkout zu goods-out
|
||||
// this._applicationService.patchProcess(processId, {
|
||||
// id: processId,
|
||||
// type: 'goods-out',
|
||||
// section: 'customer',
|
||||
// name: `Warenausgabe ${this.processNumber(processes.filter((process) => process.type === 'goods-out'))}`,
|
||||
// data: {},
|
||||
// });
|
||||
|
||||
// // Navigation
|
||||
// await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(processId)]));
|
||||
// }
|
||||
|
||||
// !!! Ticket #3272 Code soll vorerst bestehen bleiben. Prozess Warenausgabe soll wieder Vorgang heißen (wie aktuell im Produktiv), bis zum neuen Navigationskonzept
|
||||
// -----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const processesIds = await this._applicationService
|
||||
.getProcesses$('customer')
|
||||
.pipe(
|
||||
first(),
|
||||
map((p) => {
|
||||
return p.filter((process) => process.type === 'goods-out').map((process) => +process.name.replace('Warenausgabe', '').trim());
|
||||
})
|
||||
)
|
||||
.toPromise();
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
|
||||
let lastActivatedProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
console.log(processesIds);
|
||||
const lastActivatedCartCheckoutProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
// if (!lastActivatedProcessId) {
|
||||
lastActivatedProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: lastActivatedProcessId,
|
||||
type: 'goods-out',
|
||||
section: 'customer',
|
||||
name: `Warenausgabe ${Math.max(...processesIds, 0) + 1}`,
|
||||
});
|
||||
// }
|
||||
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();
|
||||
|
||||
// Darf nur reinkommen wenn der aktuell aktive Tab ein Bestellabschluss Tab ist
|
||||
if (!!lastActivatedCartCheckoutProcessId && lastActivatedCartCheckoutProcessId === activatedProcessId) {
|
||||
await this.fromCartCheckoutProcess(processes, route, lastActivatedCartCheckoutProcessId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lastActivatedProcessId) {
|
||||
await this.fromGoodsOutProcess(processes, route);
|
||||
return false;
|
||||
} else {
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(lastActivatedProcessId)]));
|
||||
}
|
||||
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(lastActivatedProcessId)]));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bei offener Warenausgabe und Klick auf Footer Warenausgabe
|
||||
async fromGoodsOutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot) {
|
||||
const newProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: newProcessId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(newProcessId)]));
|
||||
}
|
||||
|
||||
// Bei offener Bestellbestätigung und Klick auf Footer Warenausgabe
|
||||
async fromCartCheckoutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot, processId: number) {
|
||||
// Um alle Checkout Daten zu resetten die mit dem Prozess assoziiert sind
|
||||
this._checkoutService.removeProcess({ processId });
|
||||
|
||||
// Ändere type cart-checkout zu goods-out
|
||||
this._applicationService.patchProcess(processId, {
|
||||
id: processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
data: {},
|
||||
});
|
||||
|
||||
// Navigation
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(processId)]));
|
||||
}
|
||||
|
||||
getUrlFromSnapshot(route: ActivatedRouteSnapshot, url: string[] = []): string[] {
|
||||
url.push(...route.url.map((segment) => segment.path));
|
||||
if (route.firstChild) {
|
||||
@@ -46,4 +166,18 @@ export class CanActivateGoodsOutGuard implements CanActivate {
|
||||
}
|
||||
return url.filter((segment) => !!segment);
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
return missingNumber;
|
||||
}
|
||||
}
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateProductWithProcessIdGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService) {}
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const process = await this._applicationService
|
||||
@@ -25,16 +26,45 @@ export class CanActivateProductWithProcessIdGuard implements CanActivate {
|
||||
id: +route.params.processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes)}`,
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
}
|
||||
|
||||
await this.removeBreadcrumbWithSameProcessId(route);
|
||||
this._applicationService.activateProcess(+route.params.processId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
|
||||
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
|
||||
const crumbs = await this._breadcrumbService
|
||||
.getBreadcrumbByKey$(+route.params.processId)
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
// Entferne alle Crumbs die nichts mit der Artikelsuche zu tun haben
|
||||
if (crumbs.length > 1) {
|
||||
const crumbsToRemove = crumbs.filter((crumb) => crumb.tags.find((tag) => tag === 'catalog') === undefined);
|
||||
for (const crumb of crumbsToRemove) {
|
||||
await this._breadcrumbService.removeBreadcrumb(crumb.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers?.length > 0 ? Math.max(...processNumbers) + 1 : 1;
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
// Ticket #3272 Bei Klick auf "+" bzw. neuen Prozess hinzufügen soll der neue Tab immer die höchste Nummer haben (wie aktuell im Produktiv)
|
||||
// ----------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
// if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
// return missingNumber;
|
||||
// }
|
||||
// }
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CanActivateProductGuard implements CanActivate {
|
||||
constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {}
|
||||
constructor(
|
||||
private readonly _applicationService: ApplicationService,
|
||||
private readonly _checkoutService: DomainCheckoutService,
|
||||
private readonly _router: Router
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
@@ -13,20 +18,90 @@ export class CanActivateProductGuard implements CanActivate {
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
if (!lastActivatedProcessId) {
|
||||
lastActivatedProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: lastActivatedProcessId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${processes.length + 1}`,
|
||||
});
|
||||
const lastActivatedCartCheckoutProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
const lastActivatedGoodsOutProcessId = (
|
||||
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
|
||||
)?.id;
|
||||
|
||||
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();
|
||||
|
||||
// Darf nur reinkommen wenn der aktuell aktive Tab ein Bestellabschluss Tab ist
|
||||
if (!!lastActivatedCartCheckoutProcessId && lastActivatedCartCheckoutProcessId === activatedProcessId) {
|
||||
await this.fromCartCheckoutProcess(processes, route, lastActivatedCartCheckoutProcessId);
|
||||
return false;
|
||||
} else if (!!lastActivatedGoodsOutProcessId && lastActivatedGoodsOutProcessId === activatedProcessId) {
|
||||
await this.fromGoodsOutProcess(processes, route, lastActivatedGoodsOutProcessId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!lastActivatedProcessId) {
|
||||
await this.fromCartProcess(processes, route);
|
||||
return false;
|
||||
} else {
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(lastActivatedProcessId)]));
|
||||
}
|
||||
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(lastActivatedProcessId)]));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bei offener Artikelsuche/Kundensuche und Klick auf Footer Artikelsuche
|
||||
async fromCartProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot) {
|
||||
const newProcessId = Date.now();
|
||||
await this._applicationService.createProcess({
|
||||
id: newProcessId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
});
|
||||
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(newProcessId)]));
|
||||
}
|
||||
|
||||
// Bei offener Warenausgabe und Klick auf Footer Artikelsuche
|
||||
async fromGoodsOutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot, processId: number) {
|
||||
const buyer = await this._checkoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
const customerFeatures = await this._checkoutService.getCustomerFeatures({ processId }).pipe(first()).toPromise();
|
||||
const name = buyer
|
||||
? customerFeatures?.b2b
|
||||
? buyer.organisation?.name
|
||||
? buyer.organisation?.name
|
||||
: buyer.lastName
|
||||
: buyer.lastName
|
||||
: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`;
|
||||
|
||||
// Ändere type goods-out zu cart
|
||||
this._applicationService.patchProcess(processId, {
|
||||
id: processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name,
|
||||
});
|
||||
|
||||
// Navigation
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(processId)]));
|
||||
}
|
||||
|
||||
// Bei offener Bestellbestätigung und Klick auf Footer Artikelsuche
|
||||
async fromCartCheckoutProcess(processes: ApplicationProcess[], route: ActivatedRouteSnapshot, processId: number) {
|
||||
// Um alle Checkout Daten zu resetten die mit dem Prozess assoziiert sind
|
||||
this._checkoutService.removeProcess({ processId });
|
||||
|
||||
// Ändere type cart-checkout zu cart
|
||||
this._applicationService.patchProcess(processId, {
|
||||
id: processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
|
||||
data: {},
|
||||
});
|
||||
|
||||
// Navigation
|
||||
await this._router.navigate(this.getUrlFromSnapshot(route, ['/kunde', String(processId)]));
|
||||
}
|
||||
|
||||
getUrlFromSnapshot(route: ActivatedRouteSnapshot, url: string[] = []): string[] {
|
||||
url.push(...route.url.map((segment) => segment.path));
|
||||
if (route.firstChild) {
|
||||
@@ -34,4 +109,18 @@ export class CanActivateProductGuard implements CanActivate {
|
||||
}
|
||||
return url.filter((segment) => !!segment);
|
||||
}
|
||||
|
||||
processNumber(processes: ApplicationProcess[]) {
|
||||
const processNumbers = processes?.map((process) => Number(process?.name?.replace(/\D/g, '')));
|
||||
return !!processNumbers && processNumbers.length > 0 ? this.findMissingNumber(processNumbers) : 1;
|
||||
}
|
||||
|
||||
findMissingNumber(processNumbers: number[]) {
|
||||
for (let missingNumber = 1; missingNumber < Math.max(...processNumbers); missingNumber++) {
|
||||
if (!processNumbers.find((number) => number === missingNumber)) {
|
||||
return missingNumber;
|
||||
}
|
||||
}
|
||||
return Math.max(...processNumbers) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { HttpErrorInterceptor } from './http-error.interceptor';
|
||||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
|
||||
import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { throwError } from 'rxjs';
|
||||
import { UiMessageModalComponent, UiModalResult, UiModalService } from '@ui/modal';
|
||||
import { of, Subject, throwError } from 'rxjs';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { AuthService } from '@core/auth';
|
||||
|
||||
describe('HttpErrorInterceptor', () => {
|
||||
let spectator: SpectatorService<HttpErrorInterceptor>;
|
||||
@@ -11,13 +12,17 @@ describe('HttpErrorInterceptor', () => {
|
||||
|
||||
const createService = createServiceFactory({
|
||||
service: HttpErrorInterceptor,
|
||||
mocks: [UiModalService],
|
||||
mocks: [UiModalService, AuthService],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createService();
|
||||
httpErrorInterceptor = spectator.service;
|
||||
modalMock = spectator.inject(UiModalService);
|
||||
|
||||
modalMock.open.and.returnValue({
|
||||
afterClosed$: of({} as UiModalResult<any>),
|
||||
} as any);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
@@ -31,6 +36,7 @@ describe('HttpErrorInterceptor', () => {
|
||||
statusText: '',
|
||||
url: '',
|
||||
});
|
||||
|
||||
const handleErrorSpy = spyOn(httpErrorInterceptor, 'handleError').and.callThrough();
|
||||
httpErrorInterceptor.intercept(null, { handle: () => throwError(error) }).subscribe({
|
||||
error: () => {
|
||||
|
||||
@@ -2,11 +2,14 @@ import { Injectable } from '@angular/core';
|
||||
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { catchError, mergeMap, tap } from 'rxjs/operators';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { IsaLogProvider } from '../providers';
|
||||
import { LogLevel } from '@core/logger';
|
||||
|
||||
@Injectable()
|
||||
export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
constructor(private _modal: UiModalService) {}
|
||||
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)));
|
||||
@@ -14,13 +17,30 @@ export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
|
||||
handleError(error: HttpErrorResponse): Observable<any> {
|
||||
if (error.status === 0) {
|
||||
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.' },
|
||||
});
|
||||
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) {
|
||||
return this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Sie sind nicht mehr angemeldet',
|
||||
data: { message: 'Sie werden neu angemeldet' },
|
||||
})
|
||||
.afterClosed$.pipe(
|
||||
tap(() => {
|
||||
this._auth.login();
|
||||
}),
|
||||
mergeMap(() => throwError(error))
|
||||
);
|
||||
}
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Http Error', error);
|
||||
|
||||
return throwError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { DialogModel, UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { IsaLogProvider } from './isa.log-provider';
|
||||
import { LogLevel } from '@core/logger';
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IsaErrorHandler implements ErrorHandler {
|
||||
constructor(private _modal: UiModalService, private _authService: AuthService) {}
|
||||
constructor(private _modal: UiModalService, private _authService: AuthService, private _isaLogProvider: IsaLogProvider) {}
|
||||
|
||||
handleError(error: any): void {
|
||||
async handleError(error: any): Promise<void> {
|
||||
console.error(error);
|
||||
|
||||
// Bei Klick auf Abbrechen auf der Login Seite erneut zur Login Seite weiterleiten
|
||||
@@ -16,6 +18,25 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error instanceof HttpErrorResponse && error?.status === 401) {
|
||||
await this._modal
|
||||
.open({
|
||||
content: UiDialogModalComponent,
|
||||
title: 'Sitzung abgelaufen',
|
||||
data: {
|
||||
handleCommand: false,
|
||||
content: 'Sie waren zu lange nicht in der ISA aktiv. Bitte melden Sie sich erneut an',
|
||||
actions: [{ command: 'CLOSE', selected: true, label: 'Erneut anmelden' }],
|
||||
} as DialogModel,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
|
||||
this._authService.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Client Error', error);
|
||||
|
||||
this._modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
title:
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { LogLevel, LogProvider } from '@core/logger';
|
||||
import { UserStateService } from '@swagger/isa';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable()
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IsaLogProvider implements LogProvider {
|
||||
constructor(private readonly _infoService: UserStateService) {}
|
||||
static InfoService: UserStateService | undefined;
|
||||
|
||||
log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void {
|
||||
constructor() {}
|
||||
|
||||
log(logLevel: LogLevel, message: string, error: Error, ...optionalParams: any[]): void {
|
||||
if (!environment.production && (logLevel === LogLevel.WARN || logLevel === LogLevel.ERROR)) {
|
||||
this._infoService
|
||||
.UserStateSaveLog({
|
||||
logType: logLevel,
|
||||
message: message,
|
||||
content: JSON.stringify(optionalParams),
|
||||
})
|
||||
IsaLogProvider.InfoService?.UserStateSaveLog({
|
||||
logType: logLevel,
|
||||
message: message,
|
||||
content: JSON.stringify({
|
||||
error: error?.name,
|
||||
message: error?.message,
|
||||
stack: error?.stack,
|
||||
data: optionalParams,
|
||||
}),
|
||||
})
|
||||
.toPromise()
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ describe('ShellComponent', () => {
|
||||
spyOn(router, 'navigate');
|
||||
|
||||
await spectator.component.activateProcess(1);
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/kunde']);
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/kunde', 1, 'product']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { first, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { NotificationsHub } from '@hub/notifications';
|
||||
import { ModalNotificationsComponent } from '@modal/notifications';
|
||||
import { ConfirmModalData, UiConfirmModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { combineLatest } from 'rxjs';
|
||||
@@ -105,7 +105,7 @@ export class ShellComponent {
|
||||
if (latestCrumb) {
|
||||
await this._router.navigate([latestCrumb.path], { queryParams: latestCrumb.params });
|
||||
} else {
|
||||
await this._router.navigate(['/kunde']);
|
||||
await this._router.navigate(['/kunde', activatedProcessId, 'product']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-cyrillic-ext1.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-cyrillic-ext1.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-cyrillic2.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-cyrillic2.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-greek-ext3.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-greek-ext3.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-greek4.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-greek4.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-hebrew5.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-hebrew5.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-latin-ext7.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-latin-ext7.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-latin8.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-latin8.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-vietnamese6.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-400-vietnamese6.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-cyrillic-ext9.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-cyrillic-ext9.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-cyrillic10.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-cyrillic10.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-greek-ext11.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-greek-ext11.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-greek12.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-greek12.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-hebrew13.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-hebrew13.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-latin-ext15.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-latin-ext15.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-latin16.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-latin16.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-vietnamese14.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-600-vietnamese14.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-cyrillic-ext17.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-cyrillic-ext17.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-cyrillic18.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-cyrillic18.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-greek-ext19.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-greek-ext19.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-greek20.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-greek20.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-hebrew21.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-hebrew21.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-latin-ext23.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-latin-ext23.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-latin24.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-latin24.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-vietnamese22.woff2
Normal file
BIN
apps/isa-app/src/assets/fonts/Open_Sans-700-vietnamese22.woff2
Normal file
Binary file not shown.
219
apps/isa-app/src/assets/fonts/fonts.css
Normal file
219
apps/isa-app/src/assets/fonts/fonts.css
Normal file
@@ -0,0 +1,219 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-cyrillic-ext1.woff2') format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-cyrillic2.woff2') format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-greek-ext3.woff2') format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-greek4.woff2') format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-hebrew5.woff2') format('woff2');
|
||||
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-vietnamese6.woff2') format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-latin-ext7.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-400-latin8.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-cyrillic-ext9.woff2') format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-cyrillic10.woff2') format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-greek-ext11.woff2') format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-greek12.woff2') format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-hebrew13.woff2') format('woff2');
|
||||
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-vietnamese14.woff2') format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-latin-ext15.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-600-latin16.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-cyrillic-ext17.woff2') format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-cyrillic18.woff2') format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-greek-ext19.woff2') format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-greek20.woff2') format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* hebrew */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-hebrew21.woff2') format('woff2');
|
||||
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-vietnamese22.woff2') format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-latin-ext23.woff2') format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-stretch: normal;
|
||||
src: url('./Open_Sans-700-latin24.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
|
||||
U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@@ -212,4 +212,7 @@
|
||||
<g id="shiiping_document" transform="matrix(1.11057,0,0,1.11057,2.2921,-1.02614)">
|
||||
<path d="M20.713,0.924L2.151,0.924C1.824,0.924 1.517,1.048 1.288,1.283C1.06,1.512 0.936,1.819 0.936,2.139L0.936,25.634C0.936,26.307 1.478,26.849 2.151,26.849C2.151,26.849 2.758,26.849 2.758,26.849C2.758,26.849 2.758,28.523 2.758,28.523C2.758,29.196 3.3,29.738 3.973,29.738L22.535,29.738C23.208,29.738 23.75,29.196 23.75,28.523L23.75,5.028C23.75,4.361 23.202,3.819 22.535,3.819C22.535,3.819 21.928,3.819 21.928,3.819C21.928,3.819 21.928,2.139 21.928,2.139C21.928,1.466 21.386,0.924 20.713,0.924ZM22.471,5.098L22.471,28.459L4.037,28.459L4.037,26.849C4.037,26.849 20.713,26.849 20.713,26.849C21.341,26.849 21.855,26.371 21.921,25.766L21.928,25.64C21.928,25.64 21.828,25.734 21.828,25.734L21.928,25.634L21.928,5.098L22.471,5.098ZM2.215,2.197L2.215,25.57L20.649,25.57L20.649,2.197L2.215,2.197ZM9.403,22.1L3.397,22.1C3.042,22.1 2.758,22.384 2.758,22.739C2.758,23.094 3.042,23.379 3.397,23.379C3.397,23.379 9.403,23.379 9.403,23.379C9.758,23.379 10.043,23.094 10.043,22.739C10.043,22.384 9.758,22.1 9.403,22.1ZM14.522,19.469L3.277,19.469C2.996,19.469 2.758,19.747 2.758,20.108C2.758,20.469 2.996,20.747 3.277,20.747L14.522,20.747C14.803,20.747 15.041,20.469 15.041,20.108C15.041,19.747 14.803,19.469 14.522,19.469ZM12.352,16.843L3.397,16.843C3.042,16.843 2.758,17.127 2.758,17.483C2.758,17.838 3.042,18.122 3.397,18.122C3.397,18.122 12.352,18.122 12.352,18.122C12.707,18.122 12.992,17.838 12.992,17.483C12.992,17.127 12.707,16.843 12.352,16.843ZM14.478,14.212L3.322,14.212C3.013,14.212 2.758,14.492 2.758,14.851C2.758,15.211 3.013,15.491 3.322,15.491C3.322,15.491 14.478,15.491 14.478,15.491C14.786,15.491 15.041,15.211 15.041,14.851C15.041,14.492 14.786,14.212 14.478,14.212ZM9.593,11.587L3.298,11.587C3.004,11.587 2.758,11.866 2.758,12.226C2.758,12.587 3.004,12.866 3.298,12.866C3.298,12.866 9.593,12.866 9.593,12.866C9.887,12.866 10.133,12.587 10.133,12.226C10.133,11.866 9.887,11.587 9.593,11.587ZM15.163,3.657C12.86,3.657 10.987,5.53 10.987,7.833C10.987,10.136 12.86,12.009 15.163,12.009C17.466,12.009 19.345,10.136 19.345,7.833C19.345,5.53 17.472,3.657 15.163,3.657ZM15.163,4.936C16.762,4.936 18.066,6.234 18.066,7.833C18.066,9.432 16.768,10.73 15.163,10.73C13.564,10.73 12.266,9.432 12.266,7.833C12.266,6.234 13.564,4.936 15.163,4.936ZM9.525,8.956L3.366,8.956C3.03,8.956 2.758,9.238 2.758,9.595C2.758,9.952 3.03,10.234 3.366,10.234L9.525,10.234C9.861,10.234 10.133,9.952 10.133,9.595C10.133,9.238 9.861,8.956 9.525,8.956ZM14.821,7.954L14.61,7.634C14.459,7.403 14.127,7.319 13.867,7.453C13.596,7.593 13.507,7.902 13.664,8.142C13.664,8.142 14.228,9.011 14.228,9.011C14.313,9.141 14.457,9.233 14.626,9.251C14.651,9.256 14.676,9.256 14.701,9.256C14.846,9.256 14.984,9.207 15.087,9.116C15.087,9.116 17.248,7.196 17.248,7.196C17.469,6.999 17.469,6.68 17.248,6.483C17.037,6.291 16.693,6.291 16.481,6.479L14.821,7.954ZM2.164,2.197C2.16,2.197 2.157,2.197 2.154,2.197C2.152,2.197 2.151,2.197 2.151,2.197L2.164,2.197ZM2.151,2.097L2.151,2.097L2.151,2.197L2.151,2.097Z" style="fill:rgb(0,4,0);"/>
|
||||
</g>
|
||||
<g id="filter_new" transform="matrix(1.77778,0,0,2,0.000431998,3.99944)">
|
||||
<path d="M7,12L11,12L11,10L7,10L7,12ZM0,0L0,2L18,2L18,0L0,0ZM3,7L15,7L15,5L3,5L3,7Z" style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
20
apps/isa-app/src/assets/icons/icons.css
Normal file
20
apps/isa-app/src/assets/icons/icons.css
Normal file
@@ -0,0 +1,20 @@
|
||||
@font-face {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
src: url(./materials-icons-outlined.woff2) format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Material Symbols Rounded';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
src: url(./materials-icons-rounded.woff2) format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Material Symbols Sharp';
|
||||
font-style: normal;
|
||||
font-weight: 100 700;
|
||||
src: url(./materials-icons-sharp.woff2) format('woff2');
|
||||
}
|
||||
BIN
apps/isa-app/src/assets/icons/materials-icons-outlined.woff2
Normal file
BIN
apps/isa-app/src/assets/icons/materials-icons-outlined.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/icons/materials-icons-rounded.woff2
Normal file
BIN
apps/isa-app/src/assets/icons/materials-icons-rounded.woff2
Normal file
Binary file not shown.
BIN
apps/isa-app/src/assets/icons/materials-icons-sharp.woff2
Normal file
BIN
apps/isa-app/src/assets/icons/materials-icons-sharp.woff2
Normal file
Binary file not shown.
@@ -17,19 +17,19 @@
|
||||
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/print/v1"
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
|
||||
@@ -60,4 +60,4 @@
|
||||
}
|
||||
},
|
||||
"checkForUpdates": 3600000
|
||||
}
|
||||
}
|
||||
@@ -14,31 +14,31 @@
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa.paragon-data.net/isa/v1"
|
||||
"rootUrl": "https://isa.paragon-systems.de/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa.paragon-systems.de/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa.paragon-systems.de/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa.paragon-systems.de/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa.paragon-systems.de/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa.paragon-systems.de/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa.paragon-data.net/print/v1"
|
||||
"rootUrl": "https://isa.paragon-systems.de/print/v1"
|
||||
},
|
||||
"@swagger/eis": {
|
||||
"rootUrl": "https://filialinformationsystem.paragon-systems.de/eiswebapi/v1"
|
||||
},
|
||||
"@swagger/remi": {
|
||||
"rootUrl": "https://isa.paragon-data.net/inv/v1"
|
||||
"rootUrl": "https://isa.paragon-systems.de/inv/v1"
|
||||
},
|
||||
"hubs": {
|
||||
"notifications": {
|
||||
|
||||
@@ -14,31 +14,31 @@
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/isa/v1"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/print/v1"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/print/v1"
|
||||
},
|
||||
"@swagger/eis": {
|
||||
"rootUrl": "https://filialinformationsystem-staging.paragon-systems.de/eiswebapi/v1"
|
||||
},
|
||||
"@swagger/remi": {
|
||||
"rootUrl": "https://isa-staging.paragon-data.net/inv/v1"
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/inv/v1"
|
||||
},
|
||||
"hubs": {
|
||||
"notifications": {
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
|
||||
},
|
||||
"@swagger/cat": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v5"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
|
||||
},
|
||||
"@swagger/av": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
|
||||
},
|
||||
"@swagger/checkout": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
|
||||
},
|
||||
"@swagger/crm": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v3"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
|
||||
},
|
||||
"@swagger/oms": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v4"
|
||||
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
|
||||
},
|
||||
"@swagger/print": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" />
|
||||
<link href="/assets/fonts/fonts.css" rel="stylesheet" />
|
||||
<link href="/assets/icons/icons.css" rel="stylesheet" />
|
||||
<link rel="manifest" href="manifest.webmanifest" />
|
||||
<meta name="theme-color" content="#1976d2" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<app-root>
|
||||
<div class="grid place-items-center h-screen">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import * as moment from 'moment';
|
||||
|
||||
moment.locale('de');
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
body.branch {
|
||||
--bg-color: #edeff0;
|
||||
|
||||
// @isa-app/scrollbar
|
||||
--scrollbar-color: #596470;
|
||||
|
||||
// @shell/header
|
||||
--shell-header-button-color: #89949e;
|
||||
--shell-header-button-color-active: #586470;
|
||||
|
||||
1
apps/isa-app/src/scss/_components.scss
Normal file
1
apps/isa-app/src/scss/_components.scss
Normal file
@@ -0,0 +1 @@
|
||||
@import './components/icon';
|
||||
@@ -1,6 +1,9 @@
|
||||
body.customer {
|
||||
--bg-color: #e6eff9;
|
||||
|
||||
// @isa-app/scrollbar
|
||||
--scrollbar-color: #1f466c;
|
||||
|
||||
// @shell/header
|
||||
--shell-header-button-color: #9db2c6;
|
||||
--shell-header-button-color-active: #557596;
|
||||
|
||||
121
apps/isa-app/src/scss/components/_icon.scss
Normal file
121
apps/isa-app/src/scss/components/_icon.scss
Normal file
@@ -0,0 +1,121 @@
|
||||
@layer components {
|
||||
.icon {
|
||||
--icon-fill: 0;
|
||||
--icon-weight: 400;
|
||||
--icon-grade: 0;
|
||||
--icon-optical-size: 48;
|
||||
}
|
||||
|
||||
.icon,
|
||||
.icon-outlined {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.icon-rounded {
|
||||
font-family: 'Material Symbols Rounded';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.icon-sharp {
|
||||
font-family: 'Material Symbols Sharp';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.icon-fill {
|
||||
--icon-fill: 1;
|
||||
}
|
||||
|
||||
.icon-weight-100 {
|
||||
--icon-weight: 100;
|
||||
}
|
||||
|
||||
.icon-weight-200 {
|
||||
--icon-weight: 200;
|
||||
}
|
||||
|
||||
.icon-weight-300 {
|
||||
--icon-weight: 300;
|
||||
}
|
||||
|
||||
.icon-weight-400 {
|
||||
--icon-weight: 400;
|
||||
}
|
||||
|
||||
.icon-weight-500 {
|
||||
--icon-weight: 500;
|
||||
}
|
||||
|
||||
.icon-weight-600 {
|
||||
--icon-weight: 600;
|
||||
}
|
||||
|
||||
.icon-weight-700 {
|
||||
--icon-weight: 700;
|
||||
}
|
||||
|
||||
.icon-grade {
|
||||
--icon-grade: 0;
|
||||
}
|
||||
|
||||
.icon-grade-200 {
|
||||
--icon-grade: 200;
|
||||
}
|
||||
|
||||
.icon-grade--25 {
|
||||
--icon-grade: -25;
|
||||
}
|
||||
|
||||
.icon-optical-size-20 {
|
||||
--icon-optical-size: 20;
|
||||
}
|
||||
|
||||
.icon-optical-size-24 {
|
||||
--icon-optical-size: 24;
|
||||
}
|
||||
|
||||
.icon-optical-size-40 {
|
||||
--icon-optical-size: 40;
|
||||
}
|
||||
|
||||
.icon-optical-size-48 {
|
||||
--icon-optical-size: 48;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-variation-settings: 'FILL' var(--icon-fill), 'wght' var(--icon-weight), 'GRAD' var(--icon-grade), 'opsz' var(--icon-optical-size);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import '~@angular/cdk/overlay-prebuilt.css';
|
||||
|
||||
@import 'tailwindcss/base';
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import './scss/components';
|
||||
|
||||
@import './scss/root';
|
||||
@import './scss/customer';
|
||||
@@ -21,3 +23,23 @@ body {
|
||||
width: 0; // remove scrollbar space
|
||||
background: transparent; // optional: just make scrollbar invisible */
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar,
|
||||
.desktop pdf-viewer ::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-thumb,
|
||||
.desktop pdf-viewer ::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 6px rgb(0 0 0 / 10%);
|
||||
background-color: var(--scrollbar-color);
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-track,
|
||||
.desktop pdf-viewer ::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 4px rgb(0 0 0 / 10%);
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { AvailabilityByBranchDTO } from 'apps/domain/availability/src/lib/defs/availability-by-branch-dto.model';
|
||||
import { AvailabilityByBranchDTO } from '@domain/availability';
|
||||
|
||||
@Pipe({
|
||||
name: 'inStock',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { AvailabilityByBranchDTO } from 'apps/domain/availability/src/lib/defs/availability-by-branch-dto.model';
|
||||
import { AvailabilityByBranchDTO } from '@domain/availability';
|
||||
|
||||
@Pipe({
|
||||
name: 'stockInfo',
|
||||
|
||||
@@ -53,7 +53,7 @@ hr {
|
||||
}
|
||||
|
||||
span.number {
|
||||
@apply text-right;
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
span {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<div class="product-details">
|
||||
<div class="product-image">
|
||||
<button class="image-button" (click)="showImages()">
|
||||
<img [src]="item.imageId | productImage: 195:315:true" alt="product image" />
|
||||
<ui-icon icon="search_add" size="22px"></ui-icon>
|
||||
<img (load)="loadImage()" [src]="item.imageId | productImage: 195:315:true" alt="product image" />
|
||||
<ui-icon *ngIf="imageLoaded$ | async" icon="search_add" size="22px"></ui-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="showReviews()" class="recessions" *ngIf="item.reviews?.length > 0">
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="format">
|
||||
<div class="format" *ngIf="item?.product?.format && item?.product?.formatDetail">
|
||||
<img
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
class="format-icon"
|
||||
@@ -84,18 +84,33 @@
|
||||
<div data-name="product-ean">{{ item.product?.ean }}</div>
|
||||
<div class="right">
|
||||
<div class="availability-icons">
|
||||
<div class="fetching medium" *ngIf="fetchingAvailabilities$ | async"></div>
|
||||
<ng-container *ngIf="!(fetchingAvailabilities$ | async)">
|
||||
<ui-icon *ngIf="store.isTakeAwayAvailabilityAvailable$ | async" icon="shopping_bag" size="18px"></ui-icon>
|
||||
<ui-icon *ngIf="store.isPickUpAvailabilityAvailable$ | async" icon="box_out" size="18px"></ui-icon>
|
||||
<ui-icon class="truck" *ngIf="showDeliveryTruck$ | async" icon="truck" size="30px"></ui-icon>
|
||||
<ui-icon class="truck_b2b" *ngIf="showDeliveryB2BTruck$ | async" icon="truck_b2b" size="40px"></ui-icon>
|
||||
<div class="fetching xsmall" *ngIf="store.fetchingTakeAwayAvailability$ | async; else showAvailabilityTakeAwayIcon"></div>
|
||||
<ng-template #showAvailabilityTakeAwayIcon>
|
||||
<ui-icon *ngIf="store.isTakeAwayAvailabilityAvailable$ | async" icon="shopping_bag" size="18px"> </ui-icon>
|
||||
</ng-template>
|
||||
|
||||
<span *ngIf="store.isDownload$ | async" class="download-icon">
|
||||
<ui-icon icon="download" size="18px"></ui-icon>
|
||||
<span class="label">Download</span>
|
||||
</span>
|
||||
</ng-container>
|
||||
<div class="fetching xsmall" *ngIf="store.fetchingPickUpAvailability$ | async; else showAvailabilityPickUpIcon"></div>
|
||||
<ng-template #showAvailabilityPickUpIcon>
|
||||
<ui-icon *ngIf="store.isPickUpAvailabilityAvailable$ | async" icon="box_out" size="18px"></ui-icon>
|
||||
</ng-template>
|
||||
|
||||
<div class="fetching xsmall" *ngIf="store.fetchingDeliveryAvailability$ | async; else showAvailabilityDeliveryIcon"></div>
|
||||
<ng-template #showAvailabilityDeliveryIcon>
|
||||
<ui-icon *ngIf="showDeliveryTruck$ | async" class="truck" icon="truck" size="30px"></ui-icon>
|
||||
</ng-template>
|
||||
|
||||
<div
|
||||
class="fetching xsmall"
|
||||
*ngIf="store.fetchingDeliveryB2BAvailability$ | async; else showAvailabilityDeliveryB2BIcon"
|
||||
></div>
|
||||
<ng-template #showAvailabilityDeliveryB2BIcon>
|
||||
<ui-icon *ngIf="showDeliveryB2BTruck$ | async" class="truck_b2b" icon="truck_b2b" size="40px"> </ui-icon>
|
||||
</ng-template>
|
||||
|
||||
<span *ngIf="store.isDownload$ | async" class="download-icon">
|
||||
<ui-icon icon="download" size="18px"></ui-icon>
|
||||
<span class="label">Download</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -108,6 +108,10 @@
|
||||
animation: load 0.75s linear infinite;
|
||||
}
|
||||
|
||||
.xsmall {
|
||||
@apply w-6;
|
||||
}
|
||||
|
||||
.small {
|
||||
@apply w-16;
|
||||
}
|
||||
@@ -117,7 +121,7 @@
|
||||
}
|
||||
|
||||
.availability-icons {
|
||||
@apply flex flex-row justify-end text-dark-cerulean mt-4;
|
||||
@apply flex flex-row items-center justify-end text-dark-cerulean mt-4;
|
||||
|
||||
ui-icon {
|
||||
@apply mx-1;
|
||||
@@ -168,7 +172,7 @@
|
||||
}
|
||||
|
||||
.product-text {
|
||||
@apply flex flex-col whitespace-pre-line mb-px-100;
|
||||
@apply flex flex-col whitespace-pre-line mb-px-100 break-words;
|
||||
|
||||
h3 {
|
||||
@apply my-4;
|
||||
@@ -255,7 +259,7 @@
|
||||
max-width: 916px;
|
||||
|
||||
.product-button {
|
||||
@apply flex flex-row justify-center items-center w-full text-xl bg-white text-ucla-blue font-bold border-none outline-none bg-transparent rounded-t-card;
|
||||
@apply flex flex-row justify-center items-center w-full text-xl bg-white text-ucla-blue font-bold border-none outline-none rounded-t-card;
|
||||
box-shadow: 0 -2px 24px 0 #dce2e9;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ElementRef } fro
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
import { ItemDTO as PrinterItemDTO } from '@swagger/print';
|
||||
import { PrintModalComponent, PrintModalData } from '@modal/printer';
|
||||
import { AvailabilityDTO, BranchDTO } from '@swagger/checkout';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { ModalReviewsComponent } from '@modal/reviews';
|
||||
import { PurchasingOptionsModalComponent, PurchasingOptionsModalData } from 'apps/page/checkout/src/lib/modals/purchasing-options-modal';
|
||||
import { PurchasingOptions } from 'apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.store';
|
||||
import { combineLatest, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, combineLatest, Subscription } from 'rxjs';
|
||||
import { filter, first, map, shareReplay } from 'rxjs/operators';
|
||||
import { ArticleDetailsStore } from './article-details.store';
|
||||
import { ModalImagesComponent } from 'apps/modal/images/src/public-api';
|
||||
@@ -32,6 +33,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
private readonly subscriptions = new Subscription();
|
||||
showRecommendations: boolean;
|
||||
|
||||
imageLoaded$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
fetchingAvailabilities$ = combineLatest([
|
||||
this.store.fetchingDeliveryAvailability$,
|
||||
this.store.fetchingDeliveryB2BAvailability$,
|
||||
@@ -146,7 +149,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
content: PrintModalComponent,
|
||||
data: {
|
||||
printerType: 'Label',
|
||||
print: (printer) => this.domainPrinterService.printProduct({ item, printer }).toPromise(),
|
||||
// TODO: remove item: item as PrinterItemDTO when the backend is fixed
|
||||
print: (printer) => this.domainPrinterService.printProduct({ item: item as PrinterItemDTO, printer }).toPromise(),
|
||||
} as PrintModalData,
|
||||
config: {
|
||||
panelClass: [],
|
||||
@@ -268,4 +272,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
const element = this.elementRef.nativeElement.closest('.main-wrapper');
|
||||
element?.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
loadImage() {
|
||||
this.imageLoaded$.next(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import { DomainAvailabilityService, ItemData } from '@domain/availability';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ItemDTO, ResponseArgsOfItemDTO } from '@swagger/cat';
|
||||
import { AvailabilityDTO } from '@swagger/checkout';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { catchError, filter, first, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
|
||||
@@ -113,11 +115,24 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
|
||||
|
||||
//#region Abholung
|
||||
readonly fetchingPickUpAvailability$ = this.select((s) => s.fetchingPickUpAvailability);
|
||||
readonly pickUpAvailability$ = combineLatest([this.itemData$, this.branch$, this.isDownload$]).pipe(
|
||||
readonly pickUpAvailability$: Observable<AvailabilityDTO> = combineLatest([this.itemData$, this.branch$, this.isDownload$]).pipe(
|
||||
tap(() => this.patchState({ fetchingPickUpAvailability: true, fetchingPickUpAvailabilityError: undefined })),
|
||||
switchMap(([item, branch, isDownload]) =>
|
||||
!!item && !!branch && !isDownload
|
||||
? this.domainAvailabilityService.getPickUpAvailability({ item, branch, quantity: 1 }).pipe(
|
||||
map((av) => {
|
||||
if (av?.length > 0) {
|
||||
if (av[1].availableFor) {
|
||||
if ((av[1].availableFor & 2) === 2) {
|
||||
return av[0];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return av[0];
|
||||
}
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('getPickUpAvailability failed.', err);
|
||||
this.patchState({ fetchingPickUpAvailabilityError: 'Fehler beim laden der Abholung.' });
|
||||
@@ -224,22 +239,42 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
|
||||
readonly isDeliveryB2BAvailabilityAvailable$ = this.select(this.deliveryB2BAvailability$, (availability) =>
|
||||
this.domainAvailabilityService.isAvailable({ availability })
|
||||
);
|
||||
//#endregion
|
||||
//#endregion#
|
||||
|
||||
readonly sscText$ = combineLatest([
|
||||
this.item$,
|
||||
this.isDownload$,
|
||||
this.pickUpAvailability$,
|
||||
this.deliveryDigAvailability$,
|
||||
this.deliveryB2BAvailability$,
|
||||
this.downloadAvailability$,
|
||||
]).pipe(
|
||||
map(([item, isDownload, pickupAvailability, deliveryDigAvailability, downloadAvailability]) => {
|
||||
const availability = isDownload ? downloadAvailability : pickupAvailability || deliveryDigAvailability;
|
||||
return item?.catalogAvailability?.supplier === 'S' && !isDownload
|
||||
? `${item?.catalogAvailability?.ssc} - ${item?.catalogAvailability?.sscText}`
|
||||
: availability?.ssc || availability?.sscText
|
||||
? `${availability?.ssc} - ${availability?.sscText}`
|
||||
: 'Keine Lieferanten vorhanden';
|
||||
map(([item, isDownload, pickupAvailability, deliveryDigAvailability, deliveryB2BAvailability, downloadAvailability]) => {
|
||||
// const availability = isDownload ? downloadAvailability : pickupAvailability || deliveryDigAvailability || deliveryB2BAvailability;
|
||||
|
||||
let availability: AvailabilityDTO;
|
||||
|
||||
if (isDownload) {
|
||||
availability = downloadAvailability;
|
||||
} else {
|
||||
if (pickupAvailability?.sscText) {
|
||||
availability = pickupAvailability;
|
||||
} else if (deliveryDigAvailability?.sscText) {
|
||||
availability = deliveryDigAvailability;
|
||||
} else if (deliveryB2BAvailability?.sscText) {
|
||||
availability = deliveryB2BAvailability;
|
||||
}
|
||||
}
|
||||
|
||||
if (item?.catalogAvailability?.supplier === 'S' && !isDownload) {
|
||||
return [item?.catalogAvailability?.ssc, item?.catalogAvailability?.sscText].filter((f) => !!f).join(' - ');
|
||||
}
|
||||
|
||||
if (availability?.ssc || availability?.sscText) {
|
||||
return [availability?.ssc, availability?.sscText].filter((f) => !!f).join(' - ');
|
||||
}
|
||||
|
||||
return 'Keine Lieferanten vorhanden';
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user