mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
237 Commits
fix/3051-R
...
fix/hide-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9668aa8829 | ||
|
|
c49c428688 | ||
|
|
46b963c2d1 | ||
|
|
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 | ||
|
|
813f611843 | ||
|
|
1abacb75be | ||
|
|
42028a2777 | ||
|
|
0a25eeadbe | ||
|
|
70a4451f90 | ||
|
|
d859395f50 | ||
|
|
97948df14e | ||
|
|
dc84efb3ef | ||
|
|
12676a4314 | ||
|
|
3aafb9f8e7 | ||
|
|
4518db2bdd | ||
|
|
2cb161b62e | ||
|
|
9d052284be | ||
|
|
98d9029dde | ||
|
|
040c67215e | ||
|
|
d577312b24 | ||
|
|
fa10bc9c30 | ||
|
|
055339956a | ||
|
|
6cffa53ea9 | ||
|
|
fe83ef56ea | ||
|
|
abd1cacdc0 | ||
|
|
85b448ab85 | ||
|
|
5749f0018c | ||
|
|
b65d2c5ff3 | ||
|
|
348b2c4aca | ||
|
|
dfc3f32086 | ||
|
|
68d331864d | ||
|
|
24a008b20d | ||
|
|
0e5c35fae4 | ||
|
|
4d4b989dcd | ||
|
|
2983d5a068 | ||
|
|
efdc365b90 | ||
|
|
c4a8e3eb96 | ||
|
|
8cb3c98b8d | ||
|
|
9c12eda210 | ||
|
|
90afcc008d | ||
|
|
7409d096fe | ||
|
|
a4c4a3c0c8 | ||
|
|
2148af7b63 | ||
|
|
d984bc04ec | ||
|
|
1b7dfcc3ac | ||
|
|
c762871cce | ||
|
|
007ea92bd5 | ||
|
|
688b758da2 | ||
|
|
39147d7afa | ||
|
|
50cc17a44b | ||
|
|
73f592df74 | ||
|
|
821042d8b6 | ||
|
|
7123f6cc15 | ||
|
|
6c2e9906b0 | ||
|
|
37648b79c3 | ||
|
|
6b00c2c81a | ||
|
|
cd5599ff1c | ||
|
|
e870eb241b | ||
|
|
fbf8c282e8 | ||
|
|
811e363dd4 | ||
|
|
d206ba1606 | ||
|
|
c529134cd2 | ||
|
|
dafb1d335e | ||
|
|
445899e731 | ||
|
|
b21fbc974c | ||
|
|
4c98a73204 | ||
|
|
e99d669086 | ||
|
|
913ffbda97 | ||
|
|
ec2bd0bd5d | ||
|
|
08fefb1c4b | ||
|
|
e7f20bc553 | ||
|
|
f5993ca5c4 | ||
|
|
9f0c81c20f | ||
|
|
42199c8cda | ||
|
|
bc525fbffc | ||
|
|
dd6821642f | ||
|
|
b2dd96f044 | ||
|
|
0ca8a1fabf | ||
|
|
d46643cf8c | ||
|
|
fc3bea28e8 | ||
|
|
b29323864e | ||
|
|
c4cad5d56f | ||
|
|
1d83c82919 |
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
|
||||
326
angular.json
326
angular.json
@@ -3,256 +3,6 @@
|
||||
"version": 1,
|
||||
"newProjectRoot": "apps",
|
||||
"projects": {
|
||||
"ui": {
|
||||
"root": "libs/ui",
|
||||
"sourceRoot": "libs/ui",
|
||||
"projectType": "library",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"tsConfig": "libs/ui/tsconfig.lib.json",
|
||||
"project": "libs/ui/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "libs/ui/tsconfig.lib.prod.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "libs/ui/src/test.ts",
|
||||
"tsConfig": "libs/ui/tsconfig.spec.json",
|
||||
"karmaConfig": "libs/ui/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/ui/tsconfig.lib.json",
|
||||
"libs/ui/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sales": {
|
||||
"root": "apps/sales/",
|
||||
"sourceRoot": "apps/sales/src",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-builders/custom-webpack:browser",
|
||||
"options": {
|
||||
"aot": true,
|
||||
"outputPath": "dist/sales",
|
||||
"index": "apps/sales/src/index.html",
|
||||
"main": "apps/sales/src/main.ts",
|
||||
"polyfills": "apps/sales/src/polyfills.ts",
|
||||
"tsConfig": "apps/sales/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/sales/src/favicon.ico",
|
||||
"apps/sales/src/assets",
|
||||
"apps/sales/src/manifest.webmanifest",
|
||||
"apps/sales/src/browserconfig.xml",
|
||||
"apps/sales/src/silent-refresh.html"
|
||||
],
|
||||
"styles": [
|
||||
"apps/sales/src/styles.scss"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"apps/sales/src/scss"
|
||||
]
|
||||
},
|
||||
"scripts": [],
|
||||
"customWebpackConfig": {
|
||||
"path": "apps/sales/webpack.config.js"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/sales/src/environments/environment.ts",
|
||||
"with": "apps/sales/src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "25kb"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"development": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "25kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-builders/custom-webpack:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "sales:build"
|
||||
},
|
||||
"configurations": {
|
||||
"test": {
|
||||
"browserTarget": "sales:build:test"
|
||||
},
|
||||
"integration": {
|
||||
"browserTarget": "sales:build:integration"
|
||||
},
|
||||
"staging": {
|
||||
"browserTarget": "sales:build:staging"
|
||||
},
|
||||
"production": {
|
||||
"browserTarget": "sales:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "sales:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "apps/sales/src/test.ts",
|
||||
"polyfills": "apps/sales/src/polyfills.ts",
|
||||
"tsConfig": "apps/sales/tsconfig.spec.json",
|
||||
"karmaConfig": "apps/sales/karma.conf.js",
|
||||
"styles": [
|
||||
"apps/sales/src/styles.scss"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"apps/sales/src/scss"
|
||||
]
|
||||
},
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"apps/sales/src/favicon.ico",
|
||||
"apps/sales/src/assets",
|
||||
"apps/sales/src/manifest.webmanifest"
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/sales/tsconfig.app.json",
|
||||
"apps/sales/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sales-e2e": {
|
||||
"root": "apps/sales-e2e/",
|
||||
"projectType": "application",
|
||||
"prefix": "",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "apps/sales-e2e/protractor.conf.js",
|
||||
"devServerTarget": "sales:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"integration": {
|
||||
"devServerTarget": "sales:serve:integration"
|
||||
},
|
||||
"production": {
|
||||
"devServerTarget": "sales:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": "apps/sales-e2e/tsconfig.e2e.json",
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sso": {
|
||||
"root": "libs/sso",
|
||||
"sourceRoot": "libs/sso/src",
|
||||
"projectType": "library",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"tsConfig": "libs/sso/tsconfig.lib.json",
|
||||
"project": "libs/sso/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "libs/sso/tsconfig.lib.prod.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "libs/sso/src/test.ts",
|
||||
"tsConfig": "libs/sso/tsconfig.spec.json",
|
||||
"karmaConfig": "libs/sso/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/sso/tsconfig.lib.json",
|
||||
"libs/sso/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@swagger/availability": {
|
||||
"root": "apps/swagger/availability",
|
||||
"sourceRoot": "apps/swagger/availability/src",
|
||||
@@ -3310,6 +3060,11 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"allowedCommonJsDependencies": [
|
||||
"lodash",
|
||||
"pdfjs-dist/es5/build/pdf",
|
||||
"pdfjs-dist/es5/web/pdf_viewer"
|
||||
],
|
||||
"outputPath": "dist/isa-app",
|
||||
"index": "apps/isa-app/src/index.html",
|
||||
"main": "apps/isa-app/src/main.ts",
|
||||
@@ -3335,13 +3090,12 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
"maximumWarning": "25kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
@@ -3844,7 +3598,69 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@core/toast": {
|
||||
"projectType": "library",
|
||||
"root": "apps/core/toast",
|
||||
"sourceRoot": "apps/core/toast/src",
|
||||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"project": "apps/core/toast/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "apps/core/toast/tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "apps/core/toast/tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "apps/core/toast/src/test.ts",
|
||||
"tsConfig": "apps/core/toast/tsconfig.spec.json",
|
||||
"karmaConfig": "apps/core/toast/karma.conf.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@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"
|
||||
}
|
||||
@@ -181,7 +181,8 @@ describe('ApplicationService', () => {
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith({
|
||||
type: actions.patchProcess.type,
|
||||
process: {
|
||||
processId: process.id,
|
||||
changes: {
|
||||
...process,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('applicationReducer', () => {
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
const action = actions.patchProcess({ process: { ...process, name: 'Test' } });
|
||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, name: 'Test' } });
|
||||
const state = applicationReducer(
|
||||
{
|
||||
...initialState,
|
||||
@@ -81,7 +81,7 @@ describe('applicationReducer', () => {
|
||||
type: 'cart',
|
||||
};
|
||||
|
||||
const action = actions.patchProcess({ process: { ...process, id: 2 } });
|
||||
const action = actions.patchProcess({ processId: process.id, changes: { ...process, id: 2 } });
|
||||
const state = applicationReducer(
|
||||
{
|
||||
...initialState,
|
||||
|
||||
@@ -23,7 +23,7 @@ const _applicationReducer = createReducer(
|
||||
on(patchProcess, (state, { processId, changes }) => {
|
||||
const processes = state.processes.map((process) => {
|
||||
if (process.id === processId) {
|
||||
return { ...process, ...changes };
|
||||
return { ...process, ...changes, id: processId };
|
||||
}
|
||||
return process;
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
25
apps/core/toast/README.md
Normal file
25
apps/core/toast/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Toast
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project toast` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project toast`.
|
||||
|
||||
> Note: Don't forget to add `--project toast` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build toast` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build toast`, go to the dist folder `cd dist/toast` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test toast` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
@@ -9,23 +9,32 @@ module.exports = function (config) {
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true,
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true, // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, '../../../coverage/core/toast'),
|
||||
subdir: '.',
|
||||
reporters: [{ type: 'html' }, { type: 'text-summary' }],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['ChromeHeadless'],
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
7
apps/core/toast/ng-package.json
Normal file
7
apps/core/toast/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/toast",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/core/toast/package.json
Normal file
11
apps/core/toast/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@core/toast",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^12.2.0",
|
||||
"@angular/core": "^12.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
// start:ng42.barrel
|
||||
export * from './focus.directive';
|
||||
export * from './toast-animation';
|
||||
// end:ng42.barrel
|
||||
14
apps/core/toast/src/lib/animation/toast-animation.ts
Normal file
14
apps/core/toast/src/lib/animation/toast-animation.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { AnimationTriggerMetadata, trigger, state, transition, style, animate } from '@angular/animations';
|
||||
|
||||
export const slideAnimationTime = 150;
|
||||
export const toastAnimations: {
|
||||
readonly slideToast: AnimationTriggerMetadata;
|
||||
} = {
|
||||
slideToast: trigger('slideAnimation', [
|
||||
state('default', style({ transform: 'translateY(0%)' })),
|
||||
transition('void => *', [style({ transform: 'translateY(-100%)' }), animate(`${slideAnimationTime}ms ease-in`)]),
|
||||
transition('default => closing', animate(`${slideAnimationTime}ms ease-in`, style({ transform: 'translateY(-100%)' }))),
|
||||
]),
|
||||
};
|
||||
|
||||
export type ToastAnimationState = 'default' | 'closing';
|
||||
4
apps/core/toast/src/lib/defs/index.ts
Normal file
4
apps/core/toast/src/lib/defs/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './toast';
|
||||
export * from './toast-ref';
|
||||
// end:ng42.barrel
|
||||
17
apps/core/toast/src/lib/defs/toast-ref.ts
Normal file
17
apps/core/toast/src/lib/defs/toast-ref.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { OverlayRef } from '@angular/cdk/overlay';
|
||||
|
||||
export class ToastRef {
|
||||
constructor(private readonly _overlay: OverlayRef) {}
|
||||
|
||||
close() {
|
||||
this._overlay.dispose();
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
return this._overlay && this._overlay.overlayElement;
|
||||
}
|
||||
|
||||
getPosition() {
|
||||
return this._overlay.overlayElement.getBoundingClientRect();
|
||||
}
|
||||
}
|
||||
11
apps/core/toast/src/lib/defs/toast.ts
Normal file
11
apps/core/toast/src/lib/defs/toast.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TemplateRef } from '@angular/core';
|
||||
|
||||
export interface Toast {
|
||||
title?: string;
|
||||
text?: string;
|
||||
timer?: number;
|
||||
position?: 'top-left' | 'top' | 'top-right' | 'bottom-right' | 'bottom' | 'bottom-left';
|
||||
size?: 'width-full' | 'content';
|
||||
template?: TemplateRef<any>; // For rendering dynamic content
|
||||
templateContext?: {}; // For rendering dynamic content
|
||||
}
|
||||
8
apps/core/toast/src/lib/index.ts
Normal file
8
apps/core/toast/src/lib/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// start:ng42.barrel
|
||||
export * from './toast.component';
|
||||
export * from './toast.module';
|
||||
export * from './toast.service';
|
||||
export * from './defs';
|
||||
export * from './animation';
|
||||
export * from './tokens';
|
||||
// end:ng42.barrel
|
||||
15
apps/core/toast/src/lib/toast.component.html
Normal file
15
apps/core/toast/src/lib/toast.component.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<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>
|
||||
<div class="toast-content flex flex-col justify-center items-center">
|
||||
<h1 class="text-card-sub font-bold text-center py-3 whitespace-pre-wrap">{{ data.title }}</h1>
|
||||
<ng-container *ngIf="data.text; else templateRef">
|
||||
<p class="block text-base overflow-y-hidden pb-3 text-center overflow-x-hidden">{{ data.text }}</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #templateRef>
|
||||
<ng-container *ngTemplateOutlet="data.template; context: data.templateContext"> </ng-container>
|
||||
</ng-template>
|
||||
12
apps/core/toast/src/lib/toast.component.scss
Normal file
12
apps/core/toast/src/lib/toast.component.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
.toast-main {
|
||||
@apply block relative mx-auto box-border text-white p-4;
|
||||
background-color: var(--toast-background);
|
||||
min-width: 18.75rem;
|
||||
max-width: calc(100vw - 2rem);
|
||||
min-height: 5rem;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
min-height: 3rem;
|
||||
}
|
||||
24
apps/core/toast/src/lib/toast.component.spec.ts
Normal file
24
apps/core/toast/src/lib/toast.component.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ToastComponent } from './toast.component';
|
||||
|
||||
describe('ToastComponent', () => {
|
||||
let component: ToastComponent;
|
||||
let fixture: ComponentFixture<ToastComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ToastComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ToastComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
48
apps/core/toast/src/lib/toast.component.ts
Normal file
48
apps/core/toast/src/lib/toast.component.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { toastAnimations, ToastAnimationState, slideAnimationTime } from './animation';
|
||||
import { Toast, ToastRef } from './defs';
|
||||
import { TOAST_CONFIG_TOKEN } from './tokens';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-toast',
|
||||
templateUrl: 'toast.component.html',
|
||||
styleUrls: ['toast.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [toastAnimations.slideToast],
|
||||
})
|
||||
export class ToastComponent implements OnInit, OnDestroy {
|
||||
timeoutRef?: any;
|
||||
animationState: ToastAnimationState = 'default';
|
||||
width = '55.25rem';
|
||||
|
||||
constructor(
|
||||
@Inject(TOAST_CONFIG_TOKEN) public readonly data: Toast,
|
||||
private readonly _ref: ToastRef,
|
||||
private readonly _cdr: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data?.size) {
|
||||
this.width = this.data?.size === 'width-full' ? '100vw' : '55.25rem';
|
||||
}
|
||||
|
||||
this.timeoutRef = setTimeout(() => {
|
||||
this.close();
|
||||
this._cdr.markForCheck();
|
||||
}, slideAnimationTime + (this.data.timer ?? 5000));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearTimeout(this.timeoutRef);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.animationState = 'closing';
|
||||
}
|
||||
|
||||
onSlideFinished() {
|
||||
if (this.animationState === 'closing') {
|
||||
this._ref.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
apps/core/toast/src/lib/toast.module.ts
Normal file
12
apps/core/toast/src/lib/toast.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { ToastComponent } from './toast.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ToastComponent],
|
||||
imports: [CommonModule, OverlayModule, UiIconModule],
|
||||
exports: [ToastComponent],
|
||||
})
|
||||
export class ToastModule {}
|
||||
16
apps/core/toast/src/lib/toast.service.spec.ts
Normal file
16
apps/core/toast/src/lib/toast.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ToastService } from './toast.service';
|
||||
|
||||
describe('ToastService', () => {
|
||||
let service: ToastService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ToastService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
79
apps/core/toast/src/lib/toast.service.ts
Normal file
79
apps/core/toast/src/lib/toast.service.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Overlay } from '@angular/cdk/overlay';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Toast, ToastRef } from './defs';
|
||||
import { ToastComponent } from './toast.component';
|
||||
import { TOAST_CONFIG_TOKEN } from './tokens';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ToastService {
|
||||
private _lastToastRef: ToastRef;
|
||||
|
||||
get lastToastRef() {
|
||||
return this._lastToastRef;
|
||||
}
|
||||
|
||||
set lastToastRef(toastRef: ToastRef) {
|
||||
this._lastToastRef = toastRef;
|
||||
}
|
||||
|
||||
constructor(private readonly _overlay: Overlay, private readonly _injector: Injector) {}
|
||||
|
||||
create(data: Toast) {
|
||||
const positionStrategy = this.getPositionStrategy(data);
|
||||
const overlayRef = this._overlay.create({ positionStrategy });
|
||||
|
||||
this.lastToastRef = new ToastRef(overlayRef);
|
||||
const injector = this.getInjector(data, this.lastToastRef);
|
||||
|
||||
const toastPortal = new ComponentPortal(ToastComponent, null, injector);
|
||||
overlayRef.attach(toastPortal);
|
||||
|
||||
return this.lastToastRef;
|
||||
}
|
||||
|
||||
getInjector(data: Toast, ref: ToastRef) {
|
||||
return Injector.create({
|
||||
parent: this._injector,
|
||||
providers: [
|
||||
{ provide: TOAST_CONFIG_TOKEN, useValue: data },
|
||||
{ provide: ToastRef, useValue: ref },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
getPositionStrategy(data: Toast) {
|
||||
switch (data?.position) {
|
||||
case 'top':
|
||||
return this._overlay.position().global().top(this.getNextPosition()).centerHorizontally();
|
||||
case 'top-left':
|
||||
return this._overlay.position().global().top(this.getNextPosition()).left('1rem');
|
||||
case 'top-right':
|
||||
return this._overlay.position().global().top(this.getNextPosition()).right('1rem');
|
||||
case 'bottom':
|
||||
return this._overlay.position().global().bottom(this.getNextPosition(true)).centerHorizontally();
|
||||
case 'bottom-left':
|
||||
return this._overlay.position().global().bottom(this.getNextPosition(true)).left('1rem');
|
||||
case 'bottom-right':
|
||||
return this._overlay.position().global().bottom(this.getNextPosition(true)).right('1rem');
|
||||
default:
|
||||
return this._overlay.position().global().top(this.getNextPosition()).centerHorizontally();
|
||||
}
|
||||
}
|
||||
|
||||
getNextPosition(fromBottom?: boolean) {
|
||||
const lastToastIsVisible = this.lastToastRef && this.lastToastRef.isVisible();
|
||||
|
||||
let position = fromBottom ? 6 : 9;
|
||||
|
||||
if (lastToastIsVisible && fromBottom) {
|
||||
position = (window.innerHeight - this.lastToastRef.getPosition().bottom + this.lastToastRef.getPosition().height + 16) / 16;
|
||||
} else if (lastToastIsVisible) {
|
||||
position = (this.lastToastRef.getPosition().bottom + 16) / 16;
|
||||
}
|
||||
|
||||
return position + 'rem';
|
||||
}
|
||||
}
|
||||
4
apps/core/toast/src/lib/tokens.ts
Normal file
4
apps/core/toast/src/lib/tokens.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { Toast } from './defs';
|
||||
|
||||
export const TOAST_CONFIG_TOKEN = new InjectionToken<Toast>('TOAST_DATA');
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Public API Surface of sso
|
||||
* Public API Surface of toast
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,15 +1,24 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'core-js/es7/reflect';
|
||||
import 'zone.js';
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: any;
|
||||
declare const require: {
|
||||
context(
|
||||
path: string,
|
||||
deep?: boolean,
|
||||
filter?: RegExp
|
||||
): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
||||
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } });
|
||||
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
20
apps/core/toast/tsconfig.lib.json
Normal file
20
apps/core/toast/tsconfig.lib.json
Normal file
@@ -0,0 +1,20 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
}
|
||||
17
apps/core/toast/tsconfig.spec.json
Normal file
17
apps/core/toast/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -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,9 @@ 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[0], logistician: { id: logistician.id } })))
|
||||
),
|
||||
shareReplay()
|
||||
)
|
||||
)
|
||||
@@ -279,7 +302,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 +337,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 +362,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 +370,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 +378,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 +404,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 +465,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 +499,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 },
|
||||
})
|
||||
@@ -808,8 +819,8 @@ export class DomainCheckoutService {
|
||||
|
||||
@memorize()
|
||||
getBranches(): Observable<BranchDTO[]> {
|
||||
return this.storeCheckoutService
|
||||
.StoreCheckoutGetBranches({
|
||||
return this._branchService
|
||||
.StoreCheckoutBranchGetBranches({
|
||||
take: 999,
|
||||
})
|
||||
.pipe(
|
||||
@@ -828,10 +839,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 +864,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 +892,14 @@ export class DomainCheckoutService {
|
||||
return this.store.select(DomainCheckoutSelectors.selectOrders);
|
||||
}
|
||||
|
||||
updateOrderItem(item: DisplayOrderItemDTO) {
|
||||
this.store.dispatch(DomainCheckoutActions.updateOrderItem({ item }));
|
||||
}
|
||||
|
||||
removeAllOrders() {
|
||||
this.store.dispatch(DomainCheckoutActions.removeAllOrders());
|
||||
}
|
||||
|
||||
setSpecialComment({ processId, agentComment }: { processId: number; agentComment: string }) {
|
||||
this.store.dispatch(DomainCheckoutActions.setSpecialComment({ processId, agentComment }));
|
||||
}
|
||||
|
||||
@@ -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,10 @@ 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 }>());
|
||||
|
||||
export const setPayer = createAction(`${prefix} Set Payer`, props<{ processId: number; payer: PayerDTO }>());
|
||||
@@ -59,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;
|
||||
@@ -72,11 +67,39 @@ const _domainCheckoutReducer = createReducer(
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.removeProcess, (s, { processId }) => storeCheckoutAdapter.removeOne(processId, s)),
|
||||
on(DomainCheckoutActions.setOrders, (s, { orders }) => ({ ...s, orders })),
|
||||
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: [],
|
||||
})),
|
||||
on(DomainCheckoutActions.setOlaError, (s, { processId, olaErrorIds }) => {
|
||||
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);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -92,7 +115,6 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
processId,
|
||||
checkout: undefined,
|
||||
shoppingCart: undefined,
|
||||
customerFeatures: undefined,
|
||||
shippingAddress: undefined,
|
||||
orders: [],
|
||||
payer: undefined,
|
||||
@@ -100,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,14 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
AddressDTO,
|
||||
AddressService,
|
||||
AssignedPayerDTO,
|
||||
AutocompleteDTO,
|
||||
CommunicationDetailsDTO,
|
||||
CountryDTO,
|
||||
CountryService,
|
||||
CustomerDTO,
|
||||
CustomerInfoDTO,
|
||||
CustomerService,
|
||||
InputDTO,
|
||||
KeyValueDTOOfStringAndString,
|
||||
ListResponseArgsOfCustomerInfoDTO,
|
||||
NotificationChannel,
|
||||
PayerDTO,
|
||||
@@ -16,15 +19,23 @@ import {
|
||||
ResponseArgsOfHistoryDTO,
|
||||
ResponseArgsOfIEnumerableOfBonusCardInfoDTO,
|
||||
ShippingAddressDTO,
|
||||
ShippingAddressService,
|
||||
} from '@swagger/crm';
|
||||
import { isArray } from '@utils/common';
|
||||
import { PagedResult, Result } from 'apps/domain/defs/src/public-api';
|
||||
import { isNil } from 'lodash';
|
||||
import { Observable, of, ReplaySubject } from 'rxjs';
|
||||
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
|
||||
) {}
|
||||
|
||||
complete(queryString: string, filter?: { [key: string]: string }): Observable<Result<AutocompleteDTO[]>> {
|
||||
return this.customerService.CustomerCustomerAutocomplete({
|
||||
@@ -96,12 +107,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 +170,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 +425,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) => {
|
||||
@@ -263,11 +475,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 +483,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 +491,7 @@ export class CrmCustomerService {
|
||||
shippingAddressId: number,
|
||||
shippingAddress: ShippingAddressDTO,
|
||||
isDefault?: boolean
|
||||
): Observable<Result<ShippingAddressDTO>> {
|
||||
): Promise<Result<ShippingAddressDTO>> {
|
||||
const data: ShippingAddressDTO = { ...shippingAddress };
|
||||
|
||||
if (isDefault) {
|
||||
@@ -292,15 +500,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[];
|
||||
}
|
||||
@@ -36,4 +36,5 @@ export * from './shipping-note.action-handler';
|
||||
export * from './supplier-temporarily-out-of-stock.action-handler copy';
|
||||
export * from './collect-on-deliverynote.action-handler';
|
||||
export * from './create-returnitem.action-handler';
|
||||
export * from './print-pricediffqrcodelabel.action-handler';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { OrderItemsContext } from './order-items.context';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
import { ConfirmModalData, UiConfirmModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { PriceQRCodeDTO } from '@swagger/print';
|
||||
import { PrintModalComponent } from '@modal/printer';
|
||||
|
||||
@Injectable()
|
||||
export class PrintPriceDiffQrCodeLabelActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
constructor(private uiModal: UiModalService, private domainPrinterService: DomainPrinterService) {
|
||||
super('PRINT_PRICEDIFFQRCODELABEL');
|
||||
}
|
||||
|
||||
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
|
||||
await this.print(data);
|
||||
|
||||
const result = await this.uiModal
|
||||
.open({
|
||||
content: UiConfirmModalComponent,
|
||||
title: 'Bestellpreis abweichend zum Kassenpreis',
|
||||
data: {
|
||||
message:
|
||||
'Der gedruckte QR Code enthält den richtigen\nBestellpreis. Bitte überkleben Sie mit dem Ausdruck\ndas Etikett auf dem Artikel.',
|
||||
confirmLabel: 'In Ordnung',
|
||||
rejectLabel: 'QR Code erneut drucken',
|
||||
} as ConfirmModalData,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
|
||||
if (result?.data === false) {
|
||||
await this.print(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private async print(data: OrderItemsContext) {
|
||||
await this.uiModal
|
||||
.open({
|
||||
content: PrintModalComponent,
|
||||
config: { showScrollbarY: false },
|
||||
data: {
|
||||
printImmediately: true,
|
||||
printerType: 'Label',
|
||||
print: async (printer) => {
|
||||
return await this.printQrCode(data, printer);
|
||||
},
|
||||
},
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
}
|
||||
|
||||
private async printQrCode(data: OrderItemsContext, printer: string) {
|
||||
try {
|
||||
const payload = data.items.map((item) => {
|
||||
return {
|
||||
compartmentCode: data.compartmentCode,
|
||||
compartmentInfo: data.compartmentInfo,
|
||||
ean: item.product?.ean,
|
||||
price: item.retailPrice?.value,
|
||||
title: item.product?.name,
|
||||
copies: data.itemQuantity?.get(item.orderItemSubsetId) || item?.quantity,
|
||||
} as PriceQRCodeDTO;
|
||||
});
|
||||
|
||||
const response = await this.domainPrinterService.printQrCode({ printer, data: payload }).toPromise();
|
||||
if (!!response?.error) {
|
||||
this.uiModal.open({
|
||||
content: UiErrorModalComponent,
|
||||
title: 'Fehler beim Drucken des QR Code',
|
||||
data: { message: response?.message },
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (err) {
|
||||
this.uiModal.open({ content: UiErrorModalComponent, title: 'Fehler beim Drucken des QR Code', data: err });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,16 @@ import { UiModalService } from '@ui/modal';
|
||||
import { ReorderModalComponent, ReorderResult } from '@modal/reorder';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { AvailabilityDTO2, OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { ToastService } from '@core/toast';
|
||||
|
||||
@Injectable()
|
||||
export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
constructor(private _command: CommandService, private _domainCheckoutService: DomainCheckoutService, private _uiModal: UiModalService) {
|
||||
constructor(
|
||||
private _command: CommandService,
|
||||
private _domainCheckoutService: DomainCheckoutService,
|
||||
private _uiModal: UiModalService,
|
||||
private _toastService: ToastService
|
||||
) {
|
||||
super('REORDER');
|
||||
}
|
||||
|
||||
@@ -20,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,
|
||||
},
|
||||
})
|
||||
@@ -30,6 +36,8 @@ export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
const reorderResult = await this.reorder(result.data.item, result.data.availability, result.data.comment);
|
||||
const resItem = reorderResult.item1;
|
||||
|
||||
this.createToast(result?.data?.comment);
|
||||
|
||||
updatedItems.push({
|
||||
...result.data.item,
|
||||
orderItemSubsetId: resItem.id,
|
||||
@@ -57,6 +65,27 @@ export class ReOrderActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
return { ...data, items: updatedItems };
|
||||
}
|
||||
|
||||
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',
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
async reorder(orderItem: OrderItemListItemDTO, availability: AvailabilityDTO2, comment: string) {
|
||||
return await this._domainCheckoutService
|
||||
.reorder(orderItem.orderId, orderItem.orderItemId, orderItem.orderItemSubsetId, {
|
||||
|
||||
@@ -25,24 +25,42 @@ describe('DomainGoodsInServie', () => {
|
||||
expect(spectator.service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
describe('searchWareneingang', () => {
|
||||
it('should call AbholfachService.AbholfachWareneingang', () => {
|
||||
const queryTokenDto: QueryTokenDTO = {};
|
||||
abholfachServiceMock.AbholfachWareneingang.and.returnValue(hot('-a|', { a: {} }));
|
||||
expect(spectator.service.search(queryTokenDto)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(spectator.service.searchWareneingang(queryTokenDto)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(abholfachServiceMock.AbholfachWareneingang).toHaveBeenCalledWith(queryTokenDto);
|
||||
});
|
||||
});
|
||||
|
||||
describe('complete', () => {
|
||||
describe('searchWarenausgabe', () => {
|
||||
it('should call AbholfachService.AbholfachWarenausgabe', () => {
|
||||
const queryTokenDto: QueryTokenDTO = {};
|
||||
abholfachServiceMock.AbholfachWarenausgabe.and.returnValue(hot('-a|', { a: {} }));
|
||||
expect(spectator.service.searchWarenausgabe(queryTokenDto)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(abholfachServiceMock.AbholfachWarenausgabe).toHaveBeenCalledWith(queryTokenDto);
|
||||
});
|
||||
});
|
||||
|
||||
describe('wareneingangComplete', () => {
|
||||
it('should call AbholfachService.AbholfachWareneingangAutocomplete', () => {
|
||||
const autocompleteToken: AutocompleteTokenDTO = {};
|
||||
abholfachServiceMock.AbholfachWareneingangAutocomplete.and.returnValue(hot('-a|', { a: {} }));
|
||||
expect(spectator.service.complete(autocompleteToken)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(spectator.service.wareneingangComplete(autocompleteToken)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(abholfachServiceMock.AbholfachWareneingangAutocomplete).toHaveBeenCalledWith(autocompleteToken);
|
||||
});
|
||||
});
|
||||
|
||||
describe('warenausgabeComplete', () => {
|
||||
it('should call AbholfachService.AbholfachWarenausgabeAutocomplete', () => {
|
||||
const autocompleteToken: AutocompleteTokenDTO = {};
|
||||
abholfachServiceMock.AbholfachWarenausgabeAutocomplete.and.returnValue(hot('-a|', { a: {} }));
|
||||
expect(spectator.service.warenausgabeComplete(autocompleteToken)).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(abholfachServiceMock.AbholfachWarenausgabeAutocomplete).toHaveBeenCalledWith(autocompleteToken);
|
||||
});
|
||||
});
|
||||
|
||||
describe('list', () => {
|
||||
it('should call search with queryToken', () => {
|
||||
const queryToken: QueryTokenDTO = {
|
||||
@@ -58,7 +76,7 @@ describe('DomainGoodsInServie', () => {
|
||||
spyOn(dateAdapter, 'today').and.returnValue(new Date(2021, 5, 7));
|
||||
spyOn(spectator.service, 'search').and.returnValue(hot('-a|', { a: {} }));
|
||||
expect(spectator.service.list()).toBeObservable(cold('-a|', { a: {} }));
|
||||
expect(spectator.service.search).toHaveBeenCalledWith(queryToken);
|
||||
expect(spectator.service.searchWareneingang).toHaveBeenCalledWith(queryToken);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,15 +7,23 @@ import { shareReplay } from 'rxjs/operators';
|
||||
export class DomainGoodsService {
|
||||
constructor(private abholfachService: AbholfachService, private dateAdapter: DateAdapter) {}
|
||||
|
||||
search(queryToken: QueryTokenDTO) {
|
||||
searchWareneingang(queryToken: QueryTokenDTO) {
|
||||
return this.abholfachService.AbholfachWareneingang(queryToken);
|
||||
}
|
||||
|
||||
complete(autocompleteToken: AutocompleteTokenDTO) {
|
||||
searchWarenausgabe(queryToken: QueryTokenDTO) {
|
||||
return this.abholfachService.AbholfachWarenausgabe(queryToken);
|
||||
}
|
||||
|
||||
wareneingangComplete(autocompleteToken: AutocompleteTokenDTO) {
|
||||
return this.abholfachService.AbholfachWareneingangAutocomplete(autocompleteToken);
|
||||
}
|
||||
|
||||
getItemByOrderNumber(orderNumber: string) {
|
||||
warenausgabeComplete(autocompleteToken: AutocompleteTokenDTO) {
|
||||
return this.abholfachService.AbholfachWarenausgabeAutocomplete(autocompleteToken);
|
||||
}
|
||||
|
||||
getWareneingangItemByOrderNumber(orderNumber: string) {
|
||||
return this.abholfachService.AbholfachWareneingang({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
input: {
|
||||
@@ -24,8 +32,17 @@ export class DomainGoodsService {
|
||||
});
|
||||
}
|
||||
|
||||
getItemByCompartment(compartmentCode: string) {
|
||||
return this.abholfachService.AbholfachWareneingang({
|
||||
getWarenausgabeItemByOrderNumber(orderNumber: string) {
|
||||
return this.abholfachService.AbholfachWarenausgabe({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
input: {
|
||||
qs: orderNumber,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getWarenausgabeItemByCompartment(compartmentCode: string) {
|
||||
return this.abholfachService.AbholfachWarenausgabe({
|
||||
filter: { all_branches: 'true', archive: 'true' },
|
||||
input: {
|
||||
qs: compartmentCode,
|
||||
@@ -33,12 +50,12 @@ export class DomainGoodsService {
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -56,7 +73,7 @@ export class DomainGoodsService {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
};
|
||||
return this.search(queryToken);
|
||||
return this.searchWareneingang(queryToken);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ import {
|
||||
CheckoutPrintService,
|
||||
ItemDTO,
|
||||
OMSPrintService,
|
||||
PriceQRCodeDTO,
|
||||
PrintRequestOfIEnumerableOfItemDTO,
|
||||
PrintRequestOfIEnumerableOfLong,
|
||||
PrintRequestOfIEnumerableOfPriceQRCodeDTO,
|
||||
PrintService,
|
||||
ResponseArgs,
|
||||
LoyaltyCardPrintService,
|
||||
} from '@swagger/print';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError, filter, map, switchMap, timeout } from 'rxjs/operators';
|
||||
@@ -23,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 }> {
|
||||
@@ -141,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,
|
||||
@@ -228,4 +239,8 @@ export class DomainPrinterService {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
printQrCode(data: PrintRequestOfIEnumerableOfPriceQRCodeDTO) {
|
||||
return this.oMSPrintService.OMSPrintPriceQRCode(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
ReceiptDTO,
|
||||
ReturnDTO,
|
||||
ReturnQueryTokenDTO,
|
||||
BatchResponseArgsOfReturnItemDTOAndReturnItemDTO,
|
||||
} from '@swagger/remi';
|
||||
import { memorize } from '@utils/common';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
@@ -140,17 +141,21 @@ export class DomainRemissionService {
|
||||
)
|
||||
);
|
||||
} else if (arg.source === 'Abteilungsremission') {
|
||||
result$ = this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this.getItemsForAbteilungsremission({
|
||||
queryToken: {
|
||||
stockId: stock.id,
|
||||
supplierId: arg.supplierId,
|
||||
...arg.queryToken,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
if (!arg.queryToken.filter.abteilungen) {
|
||||
result$ = of({ hits: 0, result: [] });
|
||||
} else {
|
||||
result$ = this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this.getItemsForAbteilungsremission({
|
||||
queryToken: {
|
||||
stockId: stock.id,
|
||||
supplierId: arg.supplierId,
|
||||
...arg.queryToken,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this._logger.error('Unbekannte Quelle', arg.source);
|
||||
return throwError(new Error(`Unknown source: ${arg.source}`));
|
||||
@@ -196,7 +201,7 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getStockInformation(items: RemissionListItem[]) {
|
||||
getStockInformation(items: RemissionListItem[], recalculate: boolean = false) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._stockService
|
||||
@@ -226,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;
|
||||
@@ -295,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.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core';
|
||||
import { SignalrHub, SignalRHubOptions } from '@core/signalr';
|
||||
import { merge, of } from 'rxjs';
|
||||
import { filter, shareReplay, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, merge, of } from 'rxjs';
|
||||
import { filter, map, shareReplay, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { EnvelopeDTO, MessageBoardItemDTO } from './defs';
|
||||
|
||||
export const NOTIFICATIONS_HUB_OPTIONS = new InjectionToken<SignalRHubOptions>('hub.notifications.options');
|
||||
|
||||
@Injectable()
|
||||
export class NotificationsHub extends SignalrHub {
|
||||
updateNotification$ = new BehaviorSubject<MessageBoardItemDTO>(undefined);
|
||||
constructor(@Inject(NOTIFICATIONS_HUB_OPTIONS) options: SignalRHubOptions) {
|
||||
super(options);
|
||||
}
|
||||
@@ -16,6 +17,14 @@ export class NotificationsHub extends SignalrHub {
|
||||
of(this._getNotifications()).pipe(filter((f) => !!f)),
|
||||
this.listen<EnvelopeDTO<MessageBoardItemDTO[]>>('messageBoard')
|
||||
).pipe(
|
||||
withLatestFrom(this.updateNotification$),
|
||||
map(([d, update]) => {
|
||||
const data = d;
|
||||
if (update && !!data && !data?.data?.find((message) => message?.category === 'ISA-Update')) {
|
||||
data.data.push(update);
|
||||
}
|
||||
return data;
|
||||
}),
|
||||
tap((data) => this._storeNotifactions(data)),
|
||||
shareReplay(1)
|
||||
);
|
||||
@@ -33,4 +42,13 @@ export class NotificationsHub extends SignalrHub {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
updateNotification() {
|
||||
this.updateNotification$.next({
|
||||
category: 'ISA-Update',
|
||||
type: 'update',
|
||||
headline: 'Update Benachrichtigung',
|
||||
text: 'Es steht eine aktuellere Version der ISA bereit. Bitte aktualisieren Sie die Anwendung.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ import {
|
||||
CanActivateCustomerWithProcessIdGuard,
|
||||
CanActivateGoodsInGuard,
|
||||
CanActivateGoodsOutGuard,
|
||||
CanActivateGoodsOutWithProcessIdGuard,
|
||||
CanActivateProductGuard,
|
||||
CanActivateProductWithProcessIdGuard,
|
||||
CanActivateRemissionGuard,
|
||||
CanActivateTaskCalendarGuard,
|
||||
InitStoreGuard,
|
||||
IsAuthenticatedGuard,
|
||||
} from './guards';
|
||||
import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers';
|
||||
@@ -21,7 +21,10 @@ import { TokenLoginComponent, TokenLoginModule } from './token-login';
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
children: [{ path: ':token', component: TokenLoginComponent }],
|
||||
children: [
|
||||
{ path: ':token', component: TokenLoginComponent },
|
||||
{ path: '**', redirectTo: 'kunde', pathMatch: 'full' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
@@ -29,12 +32,11 @@ const routes: Routes = [
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
canActivate: [InitStoreGuard],
|
||||
canActivate: [],
|
||||
children: [
|
||||
{
|
||||
path: 'kunde',
|
||||
component: ShellComponent,
|
||||
canActivate: [InitStoreGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
@@ -77,6 +79,12 @@ const routes: Routes = [
|
||||
loadChildren: () => import('@page/goods-out').then((m) => m.GoodsOutModule),
|
||||
canActivate: [CanActivateGoodsOutGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/goods/out',
|
||||
loadChildren: () => import('@page/goods-out').then((m) => m.GoodsOutModule),
|
||||
canActivate: [CanActivateGoodsOutWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{ path: '**', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
],
|
||||
resolve: { section: CustomerSectionResolver },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { ActionReducer, INIT, MetaReducer, StoreModule } from '@ngrx/store';
|
||||
import { ActionReducer, MetaReducer, StoreModule } from '@ngrx/store';
|
||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||
import { storeFreeze } from 'ngrx-store-freeze';
|
||||
import packageInfo from 'package';
|
||||
@@ -11,7 +11,7 @@ import { RootState } from './store/root.state';
|
||||
|
||||
export function storeInLocalStorage(reducer: ActionReducer<any>): ActionReducer<any> {
|
||||
return function (state, action) {
|
||||
if (action.type === INIT) {
|
||||
if (action.type === 'HYDRATE') {
|
||||
const initialState = RootStateService.LoadFromLocalStorage();
|
||||
|
||||
if (initialState?.version === packageInfo.version) {
|
||||
|
||||
@@ -6,17 +6,22 @@ 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 { NotificationsHub } from '@hub/notifications';
|
||||
import { discardPeriodicTasks, fakeAsync, flush, tick } from '@angular/core/testing';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let spectator: Spectator<AppComponent>;
|
||||
let config: SpyObject<Config>;
|
||||
let renderer: SpyObject<Renderer2>;
|
||||
let applicationServiceMock: SpyObject<ApplicationService>;
|
||||
let notificationsHubMock: SpyObject<NotificationsHub>;
|
||||
let swUpdateMock: SpyObject<SwUpdate>;
|
||||
const createComponent = createComponentFactory({
|
||||
component: AppComponent,
|
||||
imports: [CommonModule, RouterTestingModule],
|
||||
providers: [],
|
||||
mocks: [Config],
|
||||
mocks: [Config, SwUpdate],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -25,6 +30,10 @@ describe('AppComponent', () => {
|
||||
applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined));
|
||||
renderer = jasmine.createSpyObj('Renderer2', ['addClass', 'removeClass']);
|
||||
|
||||
notificationsHubMock = createSpyObject(NotificationsHub);
|
||||
notificationsHubMock.notifications$ = of({});
|
||||
swUpdateMock = createSpyObject(SwUpdate);
|
||||
|
||||
spectator = createComponent({
|
||||
providers: [
|
||||
{ provide: ApplicationService, useValue: applicationServiceMock },
|
||||
@@ -32,6 +41,8 @@ describe('AppComponent', () => {
|
||||
provide: Renderer2,
|
||||
useValue: renderer,
|
||||
},
|
||||
{ provide: NotificationsHub, useValue: notificationsHubMock },
|
||||
{ provide: SwUpdate, useValue: swUpdateMock },
|
||||
],
|
||||
});
|
||||
config = spectator.inject(Config);
|
||||
@@ -77,13 +88,48 @@ describe('AppComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// describe('sectionChangeHandler', () => {
|
||||
// fit('should add class customer and remove class branch from body when section is customer', () => {
|
||||
// spectator.component.sectionChangeHandler('customer');
|
||||
// console.log(renderer);
|
||||
// console.log('expect');
|
||||
// expect(renderer.removeClass).toHaveBeenCalled();
|
||||
// expect(renderer.addClass).toHaveBeenCalled();
|
||||
// --------------------------------------------------------
|
||||
// Unit Tests Implementation for Angular Version 13.x.x
|
||||
|
||||
// describe('updateClient()', () => {
|
||||
// it('should call checkForUpdate() if SwUpdate.isEnabled is True', () => {
|
||||
// spyOn(spectator.component, 'checkForUpdate');
|
||||
// spyOn(spectator.component, 'initialCheckForUpdate');
|
||||
// (swUpdateMock as any).isEnabled = true;
|
||||
// spectator.component.updateClient();
|
||||
// expect(spectator.component.initialCheckForUpdate).toHaveBeenCalled();
|
||||
// expect(spectator.component.checkForUpdate).toHaveBeenCalled();
|
||||
// });
|
||||
|
||||
// it('should not call checkForUpdate() if SwUpdate.isEnabled is False', () => {
|
||||
// spyOn(spectator.component, 'checkForUpdate');
|
||||
// spyOn(spectator.component, 'initialCheckForUpdate');
|
||||
// (swUpdateMock as any).isEnabled = false;
|
||||
// spectator.component.updateClient();
|
||||
// expect(spectator.component.initialCheckForUpdate).not.toHaveBeenCalled();
|
||||
// expect(spectator.component.checkForUpdate).not.toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('checkForUpdate', () => {
|
||||
// it('should call swUpdate.checkForUpdate() and notifications.updateNotification() every second', fakeAsync(() => {
|
||||
// swUpdateMock.checkForUpdate.and.returnValue(Promise.resolve());
|
||||
// spectator.component.checkForUpdates = 1000;
|
||||
// spectator.component.checkForUpdate();
|
||||
|
||||
// spectator.detectChanges();
|
||||
// tick(1100);
|
||||
|
||||
// expect(notificationsHubMock.updateNotification).toHaveBeenCalled();
|
||||
// discardPeriodicTasks();
|
||||
// }));
|
||||
// });
|
||||
|
||||
// describe('initialCheckForUpdate', () => {
|
||||
// it('should call swUpdate.checkForUpdate()', () => {
|
||||
// swUpdateMock.checkForUpdate.and.returnValue(new Promise(undefined));
|
||||
// spectator.component.initialCheckForUpdate();
|
||||
// expect(swUpdateMock.checkForUpdate).toHaveBeenCalled();
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Component, Inject, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { Config } from '@core/config';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -11,17 +16,37 @@ import packageInfo from 'package';
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
private _checkForUpdates: number = this._config.get('checkForUpdates');
|
||||
updateAvailableObs: Observable<UpdateAvailableEvent>;
|
||||
|
||||
get checkForUpdates(): number {
|
||||
return this._checkForUpdates;
|
||||
}
|
||||
|
||||
// For Unit Testing
|
||||
set checkForUpdates(time: number) {
|
||||
this._checkForUpdates = time;
|
||||
}
|
||||
|
||||
subscriptions = new Subscription();
|
||||
|
||||
constructor(
|
||||
private readonly _config: Config,
|
||||
private readonly _title: Title,
|
||||
private readonly _appService: ApplicationService,
|
||||
@Inject(DOCUMENT) private readonly _document: Document,
|
||||
private readonly _renderer: Renderer2
|
||||
) {}
|
||||
private readonly _renderer: Renderer2,
|
||||
private readonly _swUpdate: SwUpdate,
|
||||
private readonly _notifications: NotificationsHub,
|
||||
private readonly _platform: Platform
|
||||
) {
|
||||
this.updateClient();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setTitle();
|
||||
this.logVersion();
|
||||
this.determinePlatform();
|
||||
|
||||
this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this));
|
||||
}
|
||||
@@ -34,6 +59,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');
|
||||
@@ -43,4 +80,74 @@ export class AppComponent implements OnInit {
|
||||
this._renderer.addClass(this._document.body, 'branch');
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Implementation before Angular Version 13.x.x
|
||||
|
||||
async updateClient() {
|
||||
if (!this._swUpdate.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.initialCheckForUpdate();
|
||||
this.checkForUpdateInterval();
|
||||
}
|
||||
|
||||
checkForUpdateInterval() {
|
||||
this.updateAvailableObs = this._swUpdate.available.pipe(
|
||||
tap((availableEvent) => {
|
||||
if (availableEvent?.current?.hash !== availableEvent?.available?.hash) {
|
||||
this._notifications.updateNotification();
|
||||
this.subscriptions.unsubscribe();
|
||||
}
|
||||
})
|
||||
);
|
||||
this.subscriptions.add(
|
||||
interval(this._checkForUpdates).subscribe(async () => {
|
||||
await this._swUpdate.checkForUpdate();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async initialCheckForUpdate() {
|
||||
this.updateAvailableObs = this._swUpdate.available.pipe(
|
||||
tap((availableEvent) => {
|
||||
if (availableEvent?.current?.hash !== availableEvent?.available?.hash) {
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
);
|
||||
this.subscriptions.add(this.updateAvailableObs.subscribe());
|
||||
await this._swUpdate.checkForUpdate();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Implementation for Angular Version 13.x.x
|
||||
|
||||
// updateClient() {
|
||||
// if (!this._swUpdate.isEnabled) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// this.initialCheckForUpdate();
|
||||
// this.checkForUpdate();
|
||||
// }
|
||||
|
||||
// checkForUpdate() {
|
||||
// interval(this._checkForUpdates).subscribe(() => {
|
||||
// this._swUpdate.checkForUpdate().then((value) => {
|
||||
// if (value) {
|
||||
// this._notifications.updateNotification();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// initialCheckForUpdate() {
|
||||
// this._swUpdate.checkForUpdate().then((value) => {
|
||||
// if (value) {
|
||||
// location.reload();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, ErrorHandler, LOCALE_ID, NgModule } from '@angular/core';
|
||||
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';
|
||||
@@ -28,14 +30,25 @@ import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger';
|
||||
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';
|
||||
|
||||
registerLocaleData(localeDe, localeDeExtra);
|
||||
registerLocaleData(localeDe, 'de', localeDeExtra);
|
||||
|
||||
export function _appInitializerFactory(config: Config, auth: AuthService) {
|
||||
export function _appInitializerFactory(config: Config, auth: AuthService, injector: Injector) {
|
||||
return async () => {
|
||||
const statusElement = document.querySelector('#init-status');
|
||||
statusElement.innerHTML = 'Konfigurationen werden geladen...';
|
||||
await config.init();
|
||||
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
|
||||
await auth.init();
|
||||
|
||||
if (auth.isAuthenticated()) {
|
||||
statusElement.innerHTML = 'App wird initialisiert...';
|
||||
const state = injector.get(RootStateService);
|
||||
await state.init();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -59,6 +72,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
useConfigLoader: JsonConfigLoader,
|
||||
jsonConfigLoaderUrl: '/config/config.json',
|
||||
}),
|
||||
CoreCommandModule.forRoot(Object.values(Commands)),
|
||||
CoreLoggerModule.forRoot(),
|
||||
AppStoreModule,
|
||||
AuthModule.forRoot(),
|
||||
@@ -71,13 +85,14 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
|
||||
registrationStrategy: 'registerWhenStable:30000',
|
||||
}),
|
||||
ScanAdapterModule.forRoot(!environment.production),
|
||||
PlatformModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: _appInitializerFactory,
|
||||
multi: true,
|
||||
deps: [Config, AuthService],
|
||||
deps: [Config, AuthService, Injector],
|
||||
},
|
||||
{
|
||||
provide: NOTIFICATIONS_HUB_OPTIONS,
|
||||
|
||||
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,44 @@
|
||||
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: false,
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
4
apps/isa-app/src/app/commands/index.ts
Normal file
4
apps/isa-app/src/app/commands/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './close.command';
|
||||
export * from './create-customer.command';
|
||||
export * from './create-kubi-customer.command';
|
||||
export * from './print-kubi-agb.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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@@ -25,11 +25,16 @@ export class CanActivateCartWithProcessIdGuard implements CanActivate {
|
||||
id: +route.params.processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${processes.length + 1}`,
|
||||
name: `Vorgang ${this.processNumber(processes)}`,
|
||||
});
|
||||
}
|
||||
|
||||
this._applicationService.activateProcess(+route.params.processId);
|
||||
return true;
|
||||
}
|
||||
|
||||
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,11 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
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,11 +26,42 @@ export class CanActivateCustomerWithProcessIdGuard implements CanActivate {
|
||||
id: +route.params.processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${processes.length + 1}`,
|
||||
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 ? 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
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, private readonly _breadcrumbService: BreadcrumbService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const process = await this._applicationService
|
||||
.getProcessById$(+route.params.processId)
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
if (!process) {
|
||||
// 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`,
|
||||
});
|
||||
}
|
||||
|
||||
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,24 +1,183 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { Config } from '@core/config';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
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 _config: Config) {}
|
||||
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 process = await this._applicationService.getProcessById$(this._config.get('process.ids.goodsOut')).pipe(first()).toPromise();
|
||||
if (!process) {
|
||||
await this._applicationService.createProcess({
|
||||
id: this._config.get('process.ids.goodsOut'),
|
||||
type: 'goods-out',
|
||||
section: 'customer',
|
||||
name: 'Warenausgabe',
|
||||
});
|
||||
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
|
||||
|
||||
let lastActivatedProcessId = (
|
||||
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 (!!lastActivatedCartCheckoutProcessId && lastActivatedCartCheckoutProcessId === activatedProcessId) {
|
||||
await this.fromCartCheckoutProcess(processes, route, lastActivatedCartCheckoutProcessId);
|
||||
return false;
|
||||
}
|
||||
this._applicationService.activateProcess(this._config.get('process.ids.goodsOut'));
|
||||
return true;
|
||||
|
||||
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: '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) {
|
||||
return this.getUrlFromSnapshot(route.firstChild, url);
|
||||
}
|
||||
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 { ApplicationService } from '@core/application';
|
||||
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,11 +26,45 @@ export class CanActivateProductWithProcessIdGuard implements CanActivate {
|
||||
id: +route.params.processId,
|
||||
type: 'cart',
|
||||
section: 'customer',
|
||||
name: `Vorgang ${processes.length + 1}`,
|
||||
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 ? 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,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { Config } from '@core/config';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
@@ -27,7 +26,8 @@ export class CanActivateRemissionGuard implements CanActivate {
|
||||
this._applicationService.activateProcess(this._config.get('process.ids.remission'));
|
||||
|
||||
if (!!process?.data?.active && !state.url.includes(`/filiale/remission/${process?.data?.active}`)) {
|
||||
await this._router.navigate(['/filiale', 'remission', process?.data?.active, 'list']);
|
||||
const queryParams = process?.data?.queryParams ?? {};
|
||||
await this._router.navigate(['/filiale', 'remission', process?.data?.active, 'list'], { queryParams });
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -3,10 +3,10 @@ export * from './can-activate-cart.guard';
|
||||
export * from './can-activate-customer-with-process-id.guard';
|
||||
export * from './can-activate-customer.guard';
|
||||
export * from './can-activate-goods-in.guard';
|
||||
export * from './can-activate-goods-out-with-process-id.guard';
|
||||
export * from './can-activate-goods-out.guard';
|
||||
export * from './can-activate-product-with-process-id.guard';
|
||||
export * from './can-activate-product.guard';
|
||||
export * from './can-activate-remission.guard';
|
||||
export * from './can-activate-task-calendar.guard';
|
||||
export * from './init-store.guard';
|
||||
export * from './is-authenticated.guard';
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { RootStateService } from '../store/root-state.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class InitStoreGuard implements CanActivate {
|
||||
constructor(private readonly _rootStateService: RootStateService) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (await this._rootStateService.load()) {
|
||||
window.location.reload();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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: () => {
|
||||
@@ -52,8 +58,8 @@ describe('HttpErrorInterceptor', () => {
|
||||
httpErrorInterceptor.handleError(error as any);
|
||||
expect(modalMock.open).toHaveBeenCalledWith({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Die Netzwerkverbindung wurde unterbrochen',
|
||||
data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' },
|
||||
title: 'Sie sind offline, keine Verbindung zum Netzwerk',
|
||||
data: { message: 'Bereits geladene Inhalte werden angezeigt. Interaktionen sind aktuell nicht möglich.' },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,12 @@ 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';
|
||||
|
||||
@Injectable()
|
||||
export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
constructor(private _modal: UiModalService) {}
|
||||
constructor(private _modal: UiModalService, private _auth: AuthService) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req).pipe(catchError((error: HttpErrorResponse, caught: any) => this.handleError(error)));
|
||||
@@ -14,11 +15,26 @@ export class HttpErrorInterceptor implements HttpInterceptor {
|
||||
|
||||
handleError(error: HttpErrorResponse): Observable<any> {
|
||||
if (error.status === 0) {
|
||||
this._modal.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Die Netzwerkverbindung wurde unterbrochen',
|
||||
data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' },
|
||||
});
|
||||
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))
|
||||
);
|
||||
}
|
||||
|
||||
return throwError(error);
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
|
||||
@Injectable()
|
||||
export class IsaErrorHandler implements ErrorHandler {
|
||||
constructor(private _modal: UiModalService) {}
|
||||
constructor(private _modal: UiModalService, private _authService: AuthService) {}
|
||||
|
||||
handleError(error: any): void {
|
||||
async handleError(error: any): Promise<void> {
|
||||
console.error(error);
|
||||
this._modal.open({ content: UiErrorModalComponent, title: 'Unbekannter Fehler', data: error });
|
||||
|
||||
// Bei Klick auf Abbrechen auf der Login Seite erneut zur Login Seite weiterleiten
|
||||
if (error?.type === 'token_error') {
|
||||
this._authService.login();
|
||||
return;
|
||||
}
|
||||
|
||||
if (error instanceof HttpErrorResponse && error?.status === 401) {
|
||||
await this._modal
|
||||
.open({
|
||||
content: UiDialogModalComponent,
|
||||
title: 'Sitzung abgelaufen',
|
||||
data: {
|
||||
content: 'Sie waren zu lange nicht in der ISA aktiv. Bitte melden Sie sich erneut an',
|
||||
actions: [{ command: 'CLOSE', selected: true, label: 'Erneut anmelden' }],
|
||||
},
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
|
||||
this._authService.logout();
|
||||
return;
|
||||
}
|
||||
|
||||
this._modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
title:
|
||||
!navigator.onLine || (error instanceof HttpErrorResponse && error?.status === 0)
|
||||
? 'Sie sind offline, keine Verbindung zum Netzwerk'
|
||||
: 'Unbekannter Fehler',
|
||||
data: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<ui-icon icon="box_return" size="24px"></ui-icon>
|
||||
Abholfach
|
||||
</a>
|
||||
<a [routerLink]="['/filiale/remission']" routerLinkActive="active">
|
||||
<a [routerLink]="[remissionUrl$ | async]" [queryParams]="remissionQueryParams$ | async" routerLinkActive="active">
|
||||
<ui-icon icon="documents_refresh" size="24px"></ui-icon>
|
||||
Remission
|
||||
</a>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Router } from '@angular/router';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { Config } from '@core/config';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainDashboardService } from '@domain/isa';
|
||||
@@ -56,13 +57,14 @@ describe('ShellComponent', () => {
|
||||
MockComponent(ShellProcessTabComponent),
|
||||
MockComponent(UiIconComponent),
|
||||
],
|
||||
mocks: [BreadcrumbService, DomainAvailabilityService, AuthService, DomainDashboardService],
|
||||
mocks: [BreadcrumbService, DomainAvailabilityService, AuthService, DomainDashboardService, Config],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
applicationServiceMock = createSpyObject(ApplicationService);
|
||||
applicationServiceMock.getSection$.and.returnValue(of('customer'));
|
||||
applicationServiceMock.getProcesses$.and.returnValue(of([]));
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of({ id: 4000 }));
|
||||
applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined));
|
||||
applicationServiceMock.getLastActivatedProcessWithSectionAndType$.and.returnValue(of({}));
|
||||
applicationServiceMock.getLastActivatedProcessWithSection$.and.returnValue(of({}));
|
||||
@@ -225,6 +227,63 @@ describe('ShellComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('remissionProcess$', () => {
|
||||
it('should call _appService.getProcessById$() with Remission Id and return its value', async () => {
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of({ id: 4000 }));
|
||||
await spectator.component.remissionProcess$.pipe(first()).toPromise();
|
||||
expect(applicationServiceMock.getProcessById$).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('remissionUrl$', () => {
|
||||
it('should return the correct url if process.data.active is available', async () => {
|
||||
const process = {
|
||||
id: 4000,
|
||||
data: {
|
||||
active: 9999,
|
||||
},
|
||||
};
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of(process));
|
||||
const url = await spectator.component.remissionUrl$.pipe(first()).toPromise();
|
||||
expect(url).toBe('/filiale/remission/9999/list');
|
||||
});
|
||||
|
||||
it('should return the correct url if process.data.active is not available', async () => {
|
||||
const process = {
|
||||
id: 4000,
|
||||
data: {},
|
||||
};
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of(process));
|
||||
const url = await spectator.component.remissionUrl$.pipe(first()).toPromise();
|
||||
expect(url).toBe('/filiale/remission');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remissionQueryParams$', () => {
|
||||
it('should return the correct queryParams if process.data.active and process.data.queryParams are available', async () => {
|
||||
const process = {
|
||||
id: 4000,
|
||||
data: {
|
||||
active: 9999,
|
||||
queryParams: { filter: 'test' },
|
||||
},
|
||||
};
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of(process));
|
||||
const queryParams = await spectator.component.remissionQueryParams$.pipe(first()).toPromise();
|
||||
expect(queryParams).toEqual(process.data.queryParams);
|
||||
});
|
||||
|
||||
it('should return the correct queryParams if process.data.active and process.data.queryParams are not available', async () => {
|
||||
const process = {
|
||||
id: 4000,
|
||||
data: {},
|
||||
};
|
||||
applicationServiceMock.getProcessById$.and.returnValue(of(process));
|
||||
const queryParams = await spectator.component.remissionQueryParams$.pipe(first()).toPromise();
|
||||
expect(queryParams).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSection()', () => {
|
||||
it('should call _appService.setSection() with the argument section', async () => {
|
||||
await spectator.component.setSection('customer');
|
||||
@@ -284,7 +343,7 @@ describe('ShellComponent', () => {
|
||||
applicationServiceMock.getSection$.and.returnValue(of('customer'));
|
||||
applicationServiceMock.getProcesses$.and.returnValue(of(processes));
|
||||
await spectator.component.closeProcess(1);
|
||||
expect(router.navigate).not.toHaveBeenCalled();
|
||||
expect(router.navigate).not.toHaveBeenCalledWith(['/kunde', 'dashboard']);
|
||||
});
|
||||
|
||||
it('should activate the next process when it was not the last process', async () => {
|
||||
@@ -326,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']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChildren, QueryList } from '@angular/core';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { first, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { first, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { NotificationsHub } from '@hub/notifications';
|
||||
import { ModalNotificationsComponent } from '@modal/notifications';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { ConfirmModalData, UiConfirmModalComponent, UiModalService } from '@ui/modal';
|
||||
import { Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { ShellProcessTabComponent } from '@shell/process';
|
||||
import { Config } from '@core/config';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shell',
|
||||
@@ -26,7 +27,11 @@ export class ShellComponent {
|
||||
notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length));
|
||||
|
||||
get activatedProcessId$() {
|
||||
return this._appService.getActivatedProcessId$().pipe(shareReplay());
|
||||
return this._appService.getActivatedProcessId$().pipe(
|
||||
tap((activatedProcessId) => {
|
||||
this.processTabs?.find((process) => process?.process?.id === activatedProcessId && !process?.isActive)?.slideIntoView();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get section$() {
|
||||
@@ -37,6 +42,22 @@ export class ShellComponent {
|
||||
return this.section$.pipe(switchMap((section) => this._appService.getProcesses$(section)));
|
||||
}
|
||||
|
||||
get remissionProcess$() {
|
||||
return this._appService.getProcessById$(this._config.get('process.ids.remission'));
|
||||
}
|
||||
|
||||
get remissionUrl$() {
|
||||
return this.remissionProcess$.pipe(
|
||||
map((process) => (process?.data?.active ? `/filiale/remission/${process.data.active}/list` : '/filiale/remission'))
|
||||
);
|
||||
}
|
||||
|
||||
get remissionQueryParams$() {
|
||||
return this.remissionProcess$.pipe(
|
||||
map((process) => (process?.data?.active && process?.data?.queryParams ? process.data.queryParams : {}))
|
||||
);
|
||||
}
|
||||
|
||||
get addProcessLabel$() {
|
||||
return combineLatest([this.section$, this.processes$]).pipe(
|
||||
map(([section, processes]) => (section === 'customer' && processes.length === 0 ? 'VORGANG STARTEN' : ''))
|
||||
@@ -53,6 +74,7 @@ export class ShellComponent {
|
||||
|
||||
constructor(
|
||||
private readonly _appService: ApplicationService,
|
||||
private readonly _config: Config,
|
||||
private readonly _notificationsHub: NotificationsHub,
|
||||
private readonly _modal: UiModalService,
|
||||
private readonly _router: Router,
|
||||
@@ -79,12 +101,11 @@ export class ShellComponent {
|
||||
}
|
||||
|
||||
async activateProcess(activatedProcessId: number) {
|
||||
const latestCrumb = await this._breadcrumbService.getLastActivatedBreadcrumbByKey$(activatedProcessId).pipe(first()).toPromise();
|
||||
|
||||
const latestCrumb = await this._breadcrumbService?.getLastActivatedBreadcrumbByKey$(activatedProcessId)?.pipe(first()).toPromise();
|
||||
if (latestCrumb) {
|
||||
await this._router.navigate([latestCrumb.path], { queryParams: latestCrumb.params });
|
||||
} else {
|
||||
await this._router.navigate(['/kunde']);
|
||||
await this._router.navigate(['/kunde', activatedProcessId, 'product']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,13 @@ export class RootStateService {
|
||||
|
||||
window['clearUserState'] = () => {
|
||||
this.clear();
|
||||
console.log('UserState wurde geleert. Bitte Seite neu laden.');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.load();
|
||||
this._store.dispatch({ type: 'HYDRATE', payload: RootStateService.LoadFromLocalStorage() });
|
||||
this.initSave();
|
||||
}
|
||||
|
||||
@@ -67,6 +70,7 @@ export class RootStateService {
|
||||
.toPromise()
|
||||
.catch((error) => this._logger.log(LogLevel.ERROR, error));
|
||||
RootStateService.RemoveFromLocalStorage();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
static SaveToLocalStorage(state: RootState) {
|
||||
|
||||
@@ -15,7 +15,9 @@ export class TokenLoginComponent implements OnInit {
|
||||
if (this._route.snapshot.params.token && !this._authService.isAuthenticated()) {
|
||||
this._authService.setKeyCardToken(this._route.snapshot.params.token);
|
||||
this._authService.login();
|
||||
} else {
|
||||
} else if (!this._authService.isAuthenticated()) {
|
||||
this._authService.login();
|
||||
} else if (this._authService.isAuthenticated()) {
|
||||
this._router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
||||
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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user