Compare commits

...

274 Commits

Author SHA1 Message Date
Michael Auer
180e93a7da Merge branch 'release/2.3' 2023-08-24 11:50:39 +02:00
Lorenz Hilpert
9671683a93 #4246 UI Searchbox Hint Erneut anzeigen 2023-08-04 15:56:51 +02:00
Lorenz Hilpert
d909d6e804 #4236 Kulturpass - Artikel ohne Preisbindung erhalten günstigeren Preis
(cherry picked from commit 1d865c47d7)
2023-08-03 17:06:46 +02:00
Lorenz Hilpert
15c50779b4 #4245 Wannernummer-Prüfung - Leerzeichen entfernen 2023-08-03 17:05:45 +02:00
Lorenz Hilpert
5bdfec7c3f #4222 Packstückprüfung aktiviert 2023-08-02 10:55:54 +02:00
Lorenz Hilpert
810653c4d1 #4194 Icons DR und PP hinzugefügt 2023-07-28 14:50:44 +02:00
Lorenz Hilpert
eddff0d93f #4232 Preisanzeige bei Versanbestellung 2023-07-27 17:50:09 +02:00
Lorenz Hilpert
44b406fad4 #4221 Scanner - Adapter sind nicht bereit
(cherry picked from commit 78e76818b5)
2023-07-25 14:57:09 +02:00
Lorenz Hilpert
8416028113 (cherry picked from commit edb21308d4) 2023-07-21 10:52:21 +02:00
Lorenz Hilpert
44b33c1e4c Revert "#4209 - FIX - KulturPass-Einlösecode lässt Abholfrist ändern"
This reverts commit 02d60e9bd5.
2023-07-20 13:56:08 +02:00
Lorenz Hilpert
6fb72e4b2f #4121 Vormerker kann nicht manuell bearbeitet werden 2023-07-20 11:42:41 +02:00
Lorenz Hilpert
fce50daff6 #4209 - FIX - KulturPass-Einlösecode lässt Abholfrist ändern 2023-07-19 18:41:06 +02:00
Lorenz Hilpert
b954947bb7 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-07-19 17:45:36 +02:00
Lorenz Hilpert
ddd5d50c5d #4211 Kaufoptionen popup - Prüfung ob Artikel mit Kunden kombinierbar ist 2023-07-19 17:45:17 +02:00
Lorenz Hilpert
215d7ca341 Benachrichtigung für Packstückprüfung einblenden 2023-07-17 01:31:17 +02:00
Lorenz Hilpert
1255df10e0 Benachrichtigungen für Packstückprüfung ausblenden 2023-07-17 01:30:40 +02:00
Lorenz Hilpert
ac35cc237e Leere Notifications nicht anzeigen 2023-07-14 14:50:08 +02:00
Lorenz Hilpert
4fe5034e1c #3666 Design Anpassung 2023-07-14 13:12:48 +02:00
Nino Righi
eec1cb5666 Merged PR 1589: #4180 Hotfix Mehrfachauswahl Für Download Artikel direkt Logistician setzen
#4180 Hotfix Mehrfachauswahl Für Download Artikel direkt Logistician setzen
2023-07-13 08:05:16 +00:00
Lorenz Hilpert
b62e6e8e35 #3666 Benachrichtigung über die Glocke 2023-07-12 17:57:31 +02:00
Nino Righi
b845147050 Merged PR 1584: #4180 Hotfix Add Logistician to Download Destination
#4180 Hotfix Add Logistician to Download Destination
2023-07-11 11:45:10 +00:00
Michael Auer
6bc265a358 Merge branch 'release/2.3' 2023-07-11 12:20:14 +02:00
Nino Righi
98e963d782 Merged PR 1574: #4160 Hotfix Preselect InStore PurchasingOption Tile
#4160 Hotfix Preselect InStore PurchasingOption Tile
2023-07-06 14:34:42 +00:00
Lorenz Hilpert
90268b4ec1 #4165 console log entfernt 2023-07-06 16:30:32 +02:00
Lorenz Hilpert
034693df98 #4165 Performance Fix 2023-07-06 16:30:02 +02:00
Lorenz Hilpert
5b31661f8d Fix Kulturpass in der Warenaugabe und Eingang - Datum nicht bearbeitbar 2023-07-06 13:47:30 +02:00
Lorenz Hilpert
d48b59b374 Scandit Lizenz 2023-07-05 13:12:04 +02:00
Lorenz Hilpert
7d72fc56db Remission Routing fix 2023-07-04 19:04:24 +02:00
Lorenz Hilpert
90edb7dd7e Scanner Fix 2023-07-04 18:28:57 +02:00
Lorenz Hilpert
7ee0c4d145 #4164 Wannennummer scannen beim Öffnen eines WBS ohne Wannenummer 2023-07-04 17:15:38 +02:00
Lorenz Hilpert
67f2ff54be #4163 WBS abschließen ohne Wannennummer möglich 2023-07-04 15:51:26 +02:00
Lorenz Hilpert
f0470d3faa #4161 Artikel ohne Bestand sollen nicht mit dem Wertcode einlösbar sein 2023-07-03 16:53:22 +02:00
Lorenz Hilpert
fb97db99b0 #4151 Aktivierung Packstückprüfung 2023-07-02 11:18:17 +02:00
Lorenz Hilpert
92aad95603 #4157 Warnungstext auf Artikelebene überlappt Menge 2023-06-29 15:52:39 +02:00
Lorenz Hilpert
513cef2a66 #4153 EAN ISBN soll bleiben wenn Artikel nicht gefunden wurde 2023-06-29 15:49:21 +02:00
Lorenz Hilpert
bdf3bbc530 #4153 Artikel können hinzugefügt werden auch ohne Bestand 2023-06-29 14:14:20 +02:00
Lorenz Hilpert
47baf2fa89 Kulturpass - Mehrfache Artikelauswahl über EAN überschreitet verfügbare Exemplare 2023-06-28 17:06:55 +02:00
Lorenz Hilpert
57bfc523ab Kulturpass - Early Exit wenn Dialog geschlossen wird 2023-06-28 14:07:28 +02:00
Lorenz Hilpert
23edea5fa4 Merge branch 'feature/kulturpass' into release/2.3 2023-06-28 13:34:38 +02:00
Lorenz Hilpert
330c59f7e9 #4134 Einlösecode- Warenkorb Pop Up 2023-06-27 15:03:07 +02:00
Lorenz Hilpert
0c65e9dace #4133 vsl. Lieferdatum und Zurücklegen bis deaktiviert 2023-06-26 13:58:58 +02:00
Lorenz Hilpert
9b8ea11866 Kulturpass - loader anzeige 2023-06-23 17:32:49 +02:00
Lorenz Hilpert
17aed38316 Kulturpass Scanner Fix 2023-06-23 16:57:35 +02:00
Lorenz Hilpert
48a7cc4dd4 Kulturpass - Shoppingcart erstellung vor dem start und checkout beim bestellen 2023-06-23 16:51:50 +02:00
Lorenz Hilpert
4417fd9f0d Kulturpass Mengenänderung Verfügbarkeit Check 2023-06-23 15:30:41 +02:00
Lorenz Hilpert
3e429cb261 Kulturpass Hinweistexte 2023-06-23 12:12:47 +02:00
Lorenz Hilpert
48e30e795f Kulturpass - Hinweis leerer Warenkorb 2023-06-23 11:47:27 +02:00
Lorenz Hilpert
953e528298 Kulturpass Warenkorb Mengenänderung und Entfernen 2023-06-23 11:36:38 +02:00
Lorenz Hilpert
4276f427b9 Kulturpass Warenkorb #4133 #4134 2023-06-23 10:39:19 +02:00
Lorenz Hilpert
264d914044 #4124 Kulturpass- Abholfrist auf Bearbeitenseite 2023-06-15 16:28:44 +02:00
Lorenz Hilpert
12f1e7c3af #4124 Kulturpass - Zurücklegen bis und Abholfrist nicht ausgrauen 2023-06-15 14:33:41 +02:00
Lorenz Hilpert
9eb4fbab65 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-06-15 13:52:19 +02:00
Lorenz Hilpert
8d1a63e47f #4120 Marker Bezahlt über KulturPass in der Ergebnisliste 2023-06-15 13:51:57 +02:00
Nino Righi
e449e612c1 Merged PR 1566: #4112 Hotfix Manueller Preis Archivartikel
#4112 Hotfix Manueller Preis Archivartikel
2023-06-15 11:43:09 +00:00
Lorenz Hilpert
6ba65f031b #4119 Kaufoptionen - Nicht verfügbare Kaufoptionen werden ausgegraut 2023-06-15 11:56:05 +02:00
Lorenz Hilpert
f31ac4c2e3 #4117 Bug Fix Collect On Delivery 2023-06-15 11:01:39 +02:00
Lorenz Hilpert
f980f5aaf9 #4113 Wareneingang Navigation entfernt 2023-06-14 17:53:29 +02:00
Lorenz Hilpert
87a1e8a2c4 Kulturpass Handler Fix COLLECT_WITH_SMALLAMOUNTINVOICE 2023-06-14 16:30:19 +02:00
Lorenz Hilpert
5bb9ebd6ec #3961 Bestelldeadline anzeigen 2023-06-14 10:27:20 +02:00
Lorenz Hilpert
45ab1e9cc5 Kulturpass Drucken Fix 2023-06-13 17:51:33 +02:00
Lorenz Hilpert
8d2685a8c3 Fix Kulturpass Anzeige 2023-06-13 17:34:27 +02:00
Lorenz Hilpert
d38c41a99d Kulturpass - Fix - erneut drucken 2023-06-13 16:08:42 +02:00
Lorenz Hilpert
ff3dacde39 #4101 Kulturpass 2023-06-13 15:25:08 +02:00
Nino Righi
90b752e185 Merged PR 1558: #4092 Fix updateNotificationsGroup get orderId from first Item not from initi...
#4092 Fix updateNotificationsGroup get orderId from first Item not from initial form
2023-06-09 12:52:28 +00:00
Nino Righi
3ed9227508 Merged PR 1553: #4074 Bugfixes
#4074 Bugfixes
2023-06-06 16:12:56 +00:00
Nino Righi
9cfe8176d4 Merged PR 1548: #4083 Goods In Out Order Edit Bestellkanal orderSource from Order not from fi...
#4083 Goods In Out Order Edit Bestellkanal orderSource from Order not from first item
2023-06-06 14:53:47 +00:00
Nino Righi
3999b8f623 Merged PR 1544: #4074 Kaufoptionen Manuelle Preiseingabe
#4074 Kaufoptionen Manuelle Preiseingabe
2023-06-06 13:28:57 +00:00
Andreas Schickinger
c8f54b8be5 Merged PR 1540: #4073 Hotfix PP Filter wird zurückgesetzt, wenn nicht vollständig geladen
#4073 Hotfix PP Filter wird zurückgesetzt, wenn nicht vollständig geladen

Related work items: #4073
2023-06-02 14:34:52 +00:00
Nino Righi
413d2f5178 Merged PR 1539: #4048 Changed clientChannel to orderSource
#4048 Changed clientChannel to orderSource
2023-06-02 11:48:23 +00:00
Andreas Schickinger
35c2a6a046 Merged PR 1537: #4050 Hotfix Kaufoptionen Popup - Weiterleiten zur Kundensuche
#4050 Hotfix Kaufoptionen Popup - Weiterleiten zur Kundensuche

Related work items: #4050
2023-06-02 11:23:21 +00:00
Andreas Schickinger
1df201525c Merged PR 1536: #4068 Hotfix Warenkorb Rücklage Menge ändern
#4068 Warenkorb Rücklage Menge ändern hotfix
2023-06-01 15:42:19 +00:00
Lorenz Hilpert
ef72dcf554 #3984 - Filtereinstellungen wenn Artikel für Dig-Versand in Warenkorb sind falsch
(cherry picked from commit 6717f0ee3d)
2023-05-25 10:54:08 +02:00
Michael Auer
c228147c00 Merged PR 1529: Floating Docker Branch-Tags
Es wird zusätzlich zu den bisherigen Docker-Tags, ein Docker-Tag vergeben, das den aktuellen Branch repräsentiert. Da dieses (identische) Tag für jeden neuen Build aus einem Branch wieder vergeben wird, wandert das Tag auf das neueste Docker-Image aus diesem Branch (vgl. Tag _latest_)

Beispiele:
develop => refs_head_develop
release/2.3 => refs_head_release_2.3

(cherry picked from commit 8880ed0df6)
2023-05-24 18:37:47 +02:00
Lorenz Hilpert
4b56b973c8 #3979 SSC Nummer Anzeigen 2023-05-24 17:53:54 +02:00
Lorenz Hilpert
28a00b9f22 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-05-24 17:38:57 +02:00
Nino Righi
b1fe692de5 Merged PR 1527: #4009 Filter reset and Branch Selector Dropdown Bugfixes
#4009 Filter reset and Branch Selector Dropdown Bugfixes
2023-05-23 11:55:14 +00:00
Lorenz Hilpert
08b2f6cbc9 removed console log 2023-05-23 13:32:28 +02:00
Lorenz Hilpert
10e1db388f #3979 Meldenummer in UI anzeigen 2023-05-23 13:32:05 +02:00
Lorenz Hilpert
cd7f71b968 #4044 Handlungsanweisung im Status fehlt 2023-05-22 16:13:57 +02:00
Lorenz Hilpert
9361a631dd Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-05-22 10:46:34 +02:00
Lorenz Hilpert
59c5abfd93 Deadline Pipe Fix 2023-05-22 10:46:10 +02:00
Nino Righi
633c23a6f0 Merged PR 1526: #4009 Clear Filter Release
#4009 Clear Filter Release
2023-05-18 22:27:32 +00:00
Lorenz Hilpert
779e7323e2 #3965 HSC ISA - Kundenkarten anlegen raus 2023-05-17 15:51:35 +02:00
Lorenz Hilpert
6e7fbd940b #3961 Order Deadline Popup 2023-05-17 14:56:25 +02:00
Lorenz Hilpert
6878b608fc #4035 branch slector fix 2023-05-17 13:23:43 +02:00
Lorenz Hilpert
a4fab8b64e #4035 Filiale wird bei Reservierung setzen 2023-05-16 15:32:38 +02:00
Lorenz Hilpert
21c0cc8794 #4039 - Irrläufer verschwinden nicht von der Liste 2023-05-16 13:30:51 +02:00
Lorenz Hilpert
7ea11ea0c4 #4039 Neu laden der Liste wenn man im Popup auf erledigt klickt 2023-05-12 16:19:21 +02:00
Lorenz Hilpert
778343f636 #4036 Bugfix scaling von texten 2023-05-09 13:03:02 +02:00
Lorenz Hilpert
d2fb3b6c85 #4036 - Kaufoptionen 2023-05-09 10:33:21 +02:00
Lorenz Hilpert
4c7b5eec38 #4016 Kaufoptionen - Preis bei Versandbestellungen 2023-05-03 16:22:51 +02:00
Lorenz Hilpert
35068e23cd #4021 PP - Suche nach Packstuecken oeffnet details bei einem treffer 2023-05-02 11:33:56 +02:00
Lorenz Hilpert
bc30b86cce #4020 PP - Label wird auf IPad nicht merh zwei zeilig angezeigt 2023-05-02 11:24:36 +02:00
Lorenz Hilpert
461f166e33 #4017 PP - scanId wird beim aender des status mit uebermittelt 2023-05-02 11:14:22 +02:00
Lorenz Hilpert
ad95faffa7 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-04-24 23:09:21 +02:00
Michael Auer
a0eb3c0459 Revert "Update Remi Api Url"
This reverts commit ea9920c4d5.
2023-04-24 16:53:01 +02:00
Lorenz Hilpert
fea435a6d2 Bugfix - Hor-Scroll removed 2023-04-24 16:41:52 +02:00
Lorenz Hilpert
ea9920c4d5 Update Remi Api Url 2023-04-24 16:19:50 +02:00
Lorenz Hilpert
259d0f1648 #3999 Kaufoptionen - Anzeigefehler in Titel/Format 2023-04-24 15:19:52 +02:00
Lorenz Hilpert
872d3ff383 #3391 - Filiale mit Klick auswählen 2023-04-24 14:57:16 +02:00
Michael Auer
d68055f8a9 ~ Version Bump: 2.3 2023-04-24 11:52:12 +02:00
Michael Auer
8d98dcf7e7 Merge tag '2.2' into develop 2023-04-24 11:16:13 +02:00
Michael Auer
c828a69f66 Merge branch 'release/2.2' 2023-04-24 11:16:04 +02:00
Lorenz Hilpert
6311ebe467 Show Navigation for Package-inspection 2023-04-21 10:33:34 +02:00
Lorenz Hilpert
f8a5ceed97 #3391 Fix - Tablet Kaufoptionen PopUp 2023-04-20 13:55:21 +02:00
Lorenz Hilpert
0420bda5da Bugfix - Default Branch in Kaufoptionen 2023-04-20 11:07:45 +02:00
Lorenz Hilpert
6dc532f40e Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2023-04-20 08:31:50 +02:00
Lorenz Hilpert
0560d01f30 Merge branch 'release/2.2' into develop 2023-04-20 08:31:42 +02:00
Lorenz Hilpert
bc67ec3287 URL Change paragon-data -> paragon-systems.de 2023-04-20 08:28:54 +02:00
Lorenz Hilpert
bed35a2377 URL Change wws 2023-04-20 08:10:45 +02:00
Nino Righi
8d6bd80902 Merged PR 1522: #3989 Hotfix Remission List Item Loading Spinner
#3989 Hotfix Remission List Item Loading Spinner
2023-04-18 11:24:24 +00:00
Nino Righi
8aa870dddd Merged PR 1521: #3506 Fix Removed PackageCode from Payload of ReturnFinalizeReceipt
#3506 Fix Removed PackageCode from Payload of ReturnFinalizeReceipt
2023-04-18 08:44:40 +00:00
Nino Righi
26a3c76d5f Merged PR 1520: #3906 Fix Instock Bug Article Search Results
#3906 Fix Instock Bug Article Search Results
2023-04-17 12:25:29 +00:00
Lorenz Hilpert
18f738f2c3 Bereich Wareneingang aus Navigation entfernt 2023-04-17 11:07:50 +02:00
Lorenz Hilpert
bba50ccbcc Merge branch 'release/2.2' into develop 2023-04-13 14:11:53 +02:00
Lorenz Hilpert
3eea3b913d #3688 PopUp mit Irrläufern - Navigation disabled 2023-04-13 14:10:48 +02:00
Lorenz Hilpert
2dbeec831e #3976 nur ein Titel auf einmal remittieren 2023-04-13 14:00:09 +02:00
Lorenz Hilpert
88a06628e3 #3973 added missing tabindex 2023-04-12 18:24:07 +02:00
Lorenz Hilpert
37ceb30ffb #3973 Bedienung per Tastatur funktioniert fehlerhaft 2023-04-12 13:23:48 +02:00
Lorenz Hilpert
ebd0515e96 #3978 instock-Anfrage wird nicht immer ausgeführt 2023-04-11 16:41:00 +02:00
Nino Righi
ece5d0fa0d Merged PR 1519: #3957 Fix Develop Branch Selector Implementation with new purchasing options...
#3957 Fix Develop Branch Selector Implementation with new purchasing options modal
2023-04-11 14:26:23 +00:00
Lorenz Hilpert
6be214c6cd #3977 Fehlermeldung erscheint an falscher Stelle 2023-04-11 16:25:23 +02:00
Lorenz Hilpert
c470453ea4 Merge branch 'release/2.2' into develop 2023-04-05 11:58:29 +02:00
Nino Righi
bbe9326954 Merged PR 1518: #3957 Filter Current Branch if Role is Store
#3957 Filter Current Branch if Role is Store
2023-04-05 09:55:41 +00:00
Lorenz Hilpert
27961bb4e5 Checkbox Styling Kaufoptionen 2023-04-05 11:09:41 +02:00
Lorenz Hilpert
4952c090ef #3975 Anzahl der Irrläufer in der Liste auf 10 gesetzt 2023-04-05 11:06:17 +02:00
Lorenz Hilpert
74e4016625 #3928 Kaufoptionen - Filialen die IS_ORDERING_ENABLED true sind können ausgewählt werden 2023-04-03 13:14:58 +02:00
Lorenz Hilpert
c389008811 #3967 Fix - Nicht bestellbaren Artikel löschen führt zu Fehlermeldung 2023-04-03 10:21:21 +02:00
Nino Righi
e6dcf22012 Merged PR 1516: #3957 Filialdropdown Sortierung mit Store Rolle
#3957 Filialdropdown Sortierung mit Store Rolle
2023-03-31 15:14:30 +00:00
Nino Righi
d067f925b9 Merged PR 1517: #3927 #3929 #3964 Remission Wannennummer Changes
#3927 #3929 #3964 Remission Wannennummer Changes
2023-03-31 15:05:21 +00:00
Lorenz Hilpert
8b5609f765 #3672 Detailseite 'Falsche Filiale' - Erstes Item wird bei einem Irrläufer auch verblasst angezeigt 2023-03-31 14:08:52 +02:00
Lorenz Hilpert
dd04a1f2af #3963 Fix Falsche Verfügbarkeiten 2023-03-31 14:05:15 +02:00
Lorenz Hilpert
9caabb6cc0 #3960 Buttons Alle auswählen and Alle abwählen will now work correctly. 2023-03-31 13:34:00 +02:00
Nino Righi
33b28d5f41 Merged PR 1515: #3244 Remission Removed "Are you sure you want to remit" - Popup
#3244 Remission Removed "Are you sure you want to remit" - Popup
2023-03-31 11:21:28 +00:00
Lorenz Hilpert
475f9b5e34 Mark PurchaseOptionsModalComponent Fetching if Availabilities are being fetched 2023-03-31 11:29:42 +02:00
Lorenz Hilpert
60f1348ea5 Stock Request Verhindern 2023-03-31 11:10:46 +02:00
Lorenz Hilpert
533b6e1fcf Unit Test Fix 2023-03-31 10:21:38 +02:00
Lorenz Hilpert
8961730b74 #3672 Irrlauefer Details Anpassung 2023-03-30 18:26:51 +02:00
Lorenz Hilpert
5d580714c8 #3953 Minusbestände als Nullbestand anzeigt 2023-03-30 18:08:05 +02:00
Lorenz Hilpert
daf1ead75b #3962 Anrede ist nicht mehr Pflichtfeld 2023-03-30 14:24:12 +02:00
Lorenz Hilpert
aef2654a39 #3962 Anrede ist nicht mehr Pflichtfeld 2023-03-30 13:41:22 +02:00
Lorenz Hilpert
8243cd3528 #3947 PopUp wird nach unten nicht vollständig angezeigt 2023-03-30 11:41:37 +02:00
Lorenz Hilpert
447456d7a6 #3946 Anzeige lieferbare Menge nur in Popup 2023-03-30 11:22:07 +02:00
Lorenz Hilpert
241a34d7a8 Merge branch 'release/2.2' into develop 2023-03-30 11:04:04 +02:00
Lorenz Hilpert
4e67b2e8b9 #3936 - Kundenbestellung Leere Edit Seite
(cherry picked from commit 83406277ad)
2023-03-30 10:54:41 +02:00
Lorenz Hilpert
8bc2ea8373 #3385 Preiseingabe Geschenkkarte 2023-03-30 10:39:40 +02:00
Nino Righi
00a6a113c8 Merged PR 1514: #3956 Hotfix PDP Wording Change Branch Availability
#3956 Hotfix PDP Wording Change Branch Availability
2023-03-30 08:35:25 +00:00
Nino Righi
dc04619128 #3948 Hotfix HSC Kundenbestellungen Details Header Anzeige 2023-03-29 15:12:35 +02:00
Lorenz Hilpert
e066da3762 #3386 Canadd Wurde fuer Download Artikel falsch hinterlegt 2023-03-29 11:54:19 +02:00
Lorenz Hilpert
ad96278956 #3688 List Styling angepasst 2023-03-29 10:29:32 +02:00
Lorenz Hilpert
83406277ad #3936 - Kundenbestellung Leere Edit Seite 2023-03-28 14:40:49 +02:00
Lorenz Hilpert
9e89348381 #3958 Wahl Rücklage Kaufoption wirft Fehler 2023-03-28 14:10:25 +02:00
Lorenz Hilpert
0fb7419598 #3385 HFI Gutschein 2023-03-27 11:19:09 +02:00
Lorenz Hilpert
1790298cb4 #3931 Löschen von Positionen in der Kaufoption 2023-03-24 11:28:53 +01:00
Lorenz Hilpert
ffe8e39c85 #3934 - Anzeige Message Wenn Keine Verfügbarkeiten Existieren 2023-03-24 11:20:14 +01:00
Lorenz Hilpert
67e0f4bd46 #3945 Reinfolge von verfügbar als-Daten ist nicht fest 2023-03-23 19:24:22 +01:00
Lorenz Hilpert
e7b3a58da3 #3946 Fehler Lieferbare Exemplare 2023-03-23 19:20:24 +01:00
Lorenz Hilpert
598f9f3777 #3385 HFI Gutschein Anzeige Kaufoptionen 2023-03-23 18:25:51 +01:00
Lorenz Hilpert
0a7dca2e12 Packstück Kontoller aktivieren 2023-03-23 10:09:48 +01:00
Lorenz Hilpert
4b342778df #3365 Navigation angepasst 2023-03-22 15:32:20 +01:00
Lorenz Hilpert
10e8fd904a #3933 2023-03-22 14:29:57 +01:00
Lorenz Hilpert
1de342fd3b Update Add To Shopping Cart from PDP
(cherry picked from commit 4a5dd23561fdec458447c19c93faa1654ab80c7c)
2023-03-21 11:21:35 +01:00
Lorenz Hilpert
f4c1c3dd7f Merged PR 1513: Kaufoptionen
Related work items: #3365, #3366, #3385, #3386, #3391
2023-03-20 17:11:53 +00:00
Nino Righi
80bfc59356 Merged PR 1500: #3506 Remission Wanne Scanner nach Start der Remission
#3506 Remission Wanne Scanner nach Start der Remission
2023-03-17 09:25:43 +00:00
Lorenz Hilpert
3796f3ed5f Authentication Config Update 2023-03-16 15:30:01 +01:00
Lorenz Hilpert
f2e03d22d8 Merge branch 'develop' into release/2.2 2023-03-16 15:28:36 +01:00
Nino Righi
ef967b66e8 Merged PR 1512: #3887 Removed Scrollbar until Responsive Design
#3887 Removed Scrollbar until Responsive Design
2023-03-16 13:59:47 +00:00
Lorenz Hilpert
58ea70cc6c Merge branch 'develop' into release/2.2 2023-03-15 17:42:51 +01:00
Nino Righi
e4823950df Merged PR 1511: #3887 HSC Details Kundenbestellungen 360 Grad
#3887 HSC Details Kundenbestellungen 360 Grad
2023-03-15 16:41:57 +00:00
Lorenz Hilpert
8f9923ba5d #3673 ArrivalStatus Prüfung 2023-03-14 18:22:17 +01:00
Lorenz Hilpert
72bbd2c36e Checkbox Styling with Sub Options 2023-03-14 14:15:07 +01:00
Lorenz Hilpert
9bdb902a56 #3673 #3905 2023-03-14 13:55:38 +01:00
Nino Righi
bbc2e55ae3 Merged PR 1510: #3878 Updated Section Toggle if Role CallCenter is Active
#3878 Updated Section Toggle if Role CallCenter is Active
2023-03-14 09:16:44 +00:00
Nino Righi
6995bdb527 Merged PR 1508: Filter Doku Readme angelegt mit 2 konkreten Anwendungsbeispielen der Filter I...
Filter Doku Readme angelegt mit 2 konkreten Anwendungsbeispielen der Filter IST Implementierung

Co-authored-by: lorenzh <lorenzh@users.noreply.github.com>
2023-03-13 15:43:59 +00:00
Nino Righi
a7abc35316 Merged PR 1509: #3899 Fix Customer Orders loadItems request with compartmentCode
#3899 Fix Customer Orders loadItems request with compartmentCode
2023-03-10 14:28:29 +00:00
Lorenz Hilpert
772aed597b Wareneingang ausblenden 2023-03-10 08:05:37 +01:00
Lorenz Hilpert
9e18825c27 Merge branch 'develop' into release/2.2 2023-03-10 08:04:44 +01:00
Lorenz Hilpert
27b7ffcf99 #3898 Trefferliste wird in anderen Vorgang übernommen 2023-03-09 14:44:17 +01:00
Nino Righi
d804a744b6 Merged PR 1506: #3895 Purchaseing Options Modal Selected branch check if correct branchType i...
#3895 Purchaseing Options Modal Selected branch check if correct branchType is selected
2023-03-08 19:29:49 +00:00
Nino Righi
fc5cf27bd1 Merged PR 1507: #3882 Fix Hide Select Bullets if items have no actions
#3882 Fix Hide Select Bullets if items have no actions
2023-03-08 19:24:42 +00:00
Lorenz Hilpert
355bba8966 Merge branch 'ipad-bugfix' into develop 2023-03-08 20:22:02 +01:00
Lorenz Hilpert
e31c5f5a19 IPad Bugfix Scanner 2023-03-08 20:03:43 +01:00
Lorenz Hilpert
22837bbf8d IPAd Test Logging 2023-03-08 19:45:25 +01:00
Lorenz Hilpert
6324486dca ipad test 4 2023-03-08 19:21:33 +01:00
Lorenz Hilpert
653100f539 IPad test 4 2023-03-08 19:21:14 +01:00
Lorenz Hilpert
d671ba583d IPad Test 3 2023-03-08 19:20:00 +01:00
Lorenz Hilpert
fba324c6cb IPad fIx test 2 2023-03-08 18:58:02 +01:00
Lorenz Hilpert
ccbec5bcff Scanner test 2023-03-08 18:39:27 +01:00
Lorenz Hilpert
da1978bf21 Wareneingang in der Navigation Aktiviert 2023-03-08 16:01:41 +01:00
Lorenz Hilpert
1672b89775 Message Event Logs 2023-03-08 15:35:31 +01:00
Lorenz Hilpert
d9a9db6ec8 Added Debug Page for IPad 2023-03-08 15:01:17 +01:00
Lorenz Hilpert
0882bc2ca7 Merge branch 'develop' into release/2.2 2023-03-08 13:15:49 +01:00
Nino Righi
89b8d07bb4 Merged PR 1503: #3876 Fix Reorder Modal with Selected Branch now returns correct availabiliti...
#3876 Fix Reorder Modal with Selected Branch now returns correct availabilities based by selected branch
2023-03-08 12:15:07 +00:00
Nino Righi
c65c8edd2d Merged PR 1504: #3891 Fix Tooltip PDP and Articlesearch Availability by Branch
#3891 Fix Tooltip PDP and Articlesearch Availability by Branch
2023-03-08 12:09:08 +00:00
Nino Righi
5f7ce96919 Merged PR 1505: #3896 Assortment Price Update Item Added "x" to stock value
#3896 Assortment Price Update Item Added "x" to stock value
2023-03-08 12:06:13 +00:00
Lorenz Hilpert
d9a2601f75 Merge branch 'develop' into release/2.2 2023-03-08 11:37:49 +01:00
Lorenz Hilpert
37a04cadf8 Added scandit Lizenz 2023-03-08 11:03:59 +01:00
Lorenz Hilpert
ca10c01398 #3360 Bestandsanzeige einer anderen Filiale 2023-03-06 17:37:36 +01:00
Nino Righi
24f6ba117d Merged PR 1502: #3358 Disable Pick Up Option in purchasing Modal if no valid targetBranch is...
#3358 Disable Pick Up Option in purchasing Modal if no valid targetBranch is selected
2023-03-06 14:36:31 +00:00
Lorenz Hilpert
7508144e27 #3886 Informationen nur für Filiale hinterlegen sessionstorage 2023-03-06 13:36:23 +01:00
Lorenz Hilpert
9f0fec8046 Auth Service Delay Window reload 2023-03-06 13:00:27 +01:00
Nino Righi
67d70fac8e Merged PR 1501: #3771 #3876 Customer Orders Select Bullets and Reorder Modal Bugfix
#3771 #3876 Customer Orders Select Bullets and Reorder Modal Bugfix
2023-03-06 09:35:48 +00:00
Lorenz Hilpert
1775f6fd89 #3773 Filter Zielfiliale 2023-03-03 14:56:42 +01:00
Lorenz Hilpert
438c367101 Fix Erneut anmelden 2023-03-03 14:34:17 +01:00
Lorenz Hilpert
cb9d8ffa91 Silent Refresh with code flow 2023-03-03 11:51:13 +01:00
Lorenz Hilpert
2ecb0c5cf6 Update Auth Token Refresh and Configs 2023-03-02 17:43:38 +01:00
Lorenz Hilpert
b01ce5b3b6 Merge branch 'develop' into release/2.2 2023-03-02 15:52:30 +01:00
Lorenz Hilpert
8a55d52b2b AuthService SilentRefresh Catch Error 2023-03-02 11:24:30 +01:00
Lorenz Hilpert
c7e6f00ddb Merge branch 'release/2.2' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.2 2023-03-02 10:58:39 +01:00
Lorenz Hilpert
85831ffe5d Config Fix Statging - wws API 2023-03-02 10:58:26 +01:00
Lorenz Hilpert
762a5a2072 Config Fix wws Endpoint 2023-03-02 10:57:30 +01:00
Nino Righi
405e1ed023 Merged PR 1498: #3774 Meldenummer und Branch Select Filter, Unit Test Fixes
#3774 Meldenummer und Branch Select Filter, Unit Test Fixes
2023-03-01 18:11:36 +00:00
Lorenz Hilpert
41177436d4 Silent Refresh Error - Catch Error 2023-03-01 19:04:25 +01:00
Nino Righi
d1fca976a2 Merged PR 1499: #3782 Fix Resetting Filter Settings Properly After Click On Product Search on...
#3782 Fix Resetting Filter Settings Properly After Click On Product Search on Navigation
2023-03-01 17:54:01 +00:00
Lorenz Hilpert
59cf407c26 #3759 Bearbeiten deaktivieren 2023-03-01 17:56:31 +01:00
Lorenz Hilpert
e95828a514 Improvements for Authentication and Silent Refresh 2023-03-01 15:46:01 +01:00
Lorenz Hilpert
8b915c7c83 Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2023-02-28 11:40:15 +01:00
Lorenz Hilpert
ebc6a01b7a #3880 Branch Id in Autocomplete Request - Kundenbestellung 2023-02-28 11:40:05 +01:00
Lorenz Hilpert
8cad3c4c14 Merge branch 'release/2.2' into develop 2023-02-27 18:48:14 +01:00
Lorenz Hilpert
a5537c21a1 Merge branch 'develop' into release/2.2 2023-02-27 18:47:47 +01:00
Lorenz Hilpert
edf96434b7 Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2023-02-27 18:33:08 +01:00
Lorenz Hilpert
257df95c72 #3876 Artikel nachbestellen - In ausgewählte Filiale 2023-02-27 18:32:55 +01:00
Nino Righi
a268df503a Merged PR 1497: #3871 Gelbe Seiten Fachbodenbeschriftung
#3871 Gelbe Seiten Fachbodenbeschriftung
2023-02-27 17:27:23 +00:00
Nino Righi
a57ccbe4c2 Merged PR 1495: #3875 Display Estimated Shipping Date if orderType is not 1
#3875 Display Estimated Shipping Date if orderType is not 1
2023-02-27 17:21:16 +00:00
Lorenz Hilpert
7f37771dc7 Merge branch 'develop' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into develop 2023-02-27 17:56:00 +01:00
Lorenz Hilpert
3a753dde83 Fix unit Test For AuthService 2023-02-27 17:55:52 +01:00
Nino Righi
8e91b1363b Merged PR 1496: #3357 Fixed Branches OrderBy
#3357 Fixed Branches OrderBy
2023-02-27 16:53:05 +00:00
Lorenz Hilpert
e77929ab89 #3357 Ausgewählte Filiae wird berücksichtigt 2023-02-27 17:38:09 +01:00
Lorenz Hilpert
02031e97e3 Minor Bugfixes 2023-02-27 17:20:28 +01:00
Lorenz Hilpert
ede1c505d4 #3872 Wording Anpassung 2023-02-27 14:03:13 +01:00
Lorenz Hilpert
5cd5b685d2 #3673 Farbanpassung - Wareneingang 2023-02-27 10:46:42 +01:00
Lorenz Hilpert
d856f1d1cc Fix Unit Test 2023-02-27 10:17:11 +01:00
Lorenz Hilpert
10bb912bda Kundenbestellung Anpassung API 2023-02-27 10:08:16 +01:00
Nino Righi
3b2135a570 Merged PR 1494: #3357 Implementierung Umkreissuche + Unit Test Fix
#3357 Implementierung Umkreissuche + Unit Test Fix
2023-02-24 16:46:31 +00:00
Lorenz Hilpert
9c5d209887 #3870 Load Spinner PP 2023-02-24 17:27:54 +01:00
Lorenz Hilpert
763a770bcf Skip Unit Test 2023-02-24 14:58:29 +01:00
Lorenz Hilpert
f8f3456ba3 #3790 Filialsuche hinzugefügt 2023-02-24 14:51:05 +01:00
Lorenz Hilpert
4da7f02cf7 #3790 Kundenbestellungen 2023-02-24 13:42:58 +01:00
Nino Righi
4772e24c78 Merged PR 1493: #3865 Assortment OrderBy Implementation
#3865 Assortment OrderBy Implementation
2023-02-24 10:45:32 +00:00
Lorenz Hilpert
c3a9b82abb #3867 Preisänderungen - Scrollbalken fehlt 2023-02-23 10:03:54 +01:00
Lorenz Hilpert
4716940708 #3868 Button überdeckt letzte Kachel 2023-02-23 10:02:25 +01:00
Lorenz Hilpert
0eb09e2dbb Merge branch 'develop' into release/2.2 2023-02-22 16:38:42 +01:00
Lorenz Hilpert
111a33b12f #3805 Liste Weitere Verfügbarkeiten 2023-02-22 14:08:26 +01:00
Lorenz Hilpert
bcff2272ab #3853 Gelbe Seiten - Auf Erledigt gesetzte Posten bleiben auf die Liste 2023-02-22 13:30:58 +01:00
Lorenz Hilpert
f30ae91854 Merge branch 'release/2.2' into develop 2023-02-21 10:26:27 +01:00
Lorenz Hilpert
7eaad843a9 Merge branch 'develop' into release/2.2 2023-02-21 10:26:01 +01:00
Lorenz Hilpert
cb367d32c3 #3854 Filter in Sortiment und Wareineingang reagieren nicht einheitlich mit anderen Filtern 2023-02-20 18:15:27 +01:00
Lorenz Hilpert
b4cf88bd54 Bugfix Filter 2023-02-20 18:02:16 +01:00
Lorenz Hilpert
b8097fcd3a Filter Verhalten angepasst 2023-02-20 18:00:15 +01:00
Lorenz Hilpert
6db2238096 Item Update and Error Message for Packstück Kontrolle 2023-02-20 16:51:52 +01:00
Lorenz Hilpert
cd426d5534 #3859 Navigation Tätigkeitskalender 2023-02-20 14:41:26 +01:00
Lorenz Hilpert
c214d47aad Null Check 2023-02-20 13:58:10 +01:00
Lorenz Hilpert
c09c44ec5f Merge branch 'develop' into release/2.2 2023-02-20 13:09:44 +01:00
Lorenz Hilpert
f9f6d0d836 Merged PR 1492: Packstück Kontrolle
Related work items: #3688, #3690, #3816
2023-02-20 12:09:01 +00:00
Nino Righi
74a6c75c21 Merged PR 1490: #3848 IPAD2 Removed Grid and added Flex on Scroll Arrow inside Filter Input O...
#3848 IPAD2 Removed Grid and added Flex on Scroll Arrow inside Filter Input Options
2023-02-17 13:49:55 +00:00
Nino Righi
526ebc77bc Merged PR 1491: #3849 Improvement Sortiment Filter Display Hint
#3849 Improvement Sortiment Filter Display Hint
2023-02-17 13:49:47 +00:00
Lorenz Hilpert
addac44c0f Merge branch 'develop' into release/2.2 2023-02-17 14:05:46 +01:00
Lorenz Hilpert
303d575fde config.json - scope hinzugefuegt - isa-wws-webapi 2023-02-17 14:02:24 +01:00
Lorenz Hilpert
bf8438b229 Version Bump 2.2 2023-02-17 13:37:16 +01:00
Nino Righi
ea8bbafbfa Merged PR 1489: Small fixes
Small fixes
2023-02-17 09:28:50 +00:00
Nino Righi
14815e79d5 Merged PR 1488: #3354 #3814 Ipad styling bugfixes, check environment and native container update
#3354 #3814 Ipad styling bugfixes, check environment and native container update
2023-02-16 16:45:58 +00:00
Nino Righi
4a3de35224 Merged PR 1486: #3524 History Added Word Break normal on caption
#3524 History Added Word Break normal on caption
2023-02-16 15:55:29 +00:00
Nino Righi
974f549c31 Merged PR 1487: #3836 #3837 Gelbe Seiten IPAD Styling fixes and Filter Scrolling
#3836 #3837 Gelbe Seiten IPAD Styling fixes and Filter Scrolling
2023-02-16 15:55:20 +00:00
Lorenz Hilpert
76596939c5 #3794 Submit mit Enter und Lupen-Symbol führt zu unterschiedliche Ergebnisse 2023-02-16 11:31:24 +01:00
Nino Righi
138974bca7 Merged PR 1485: #3359 Removed resetSelectedBranch logic on navigation click
#3359 Removed resetSelectedBranch logic on navigation click
2023-02-14 14:58:47 +00:00
Nino Righi
02aee02694 Merged PR 1482: #3813 Fix IPAD2 Added web-animations-js polyfill
#3813 Fix IPAD2 Added web-animations-js polyfill
2023-02-14 11:01:29 +00:00
Nino Righi
e2ada75611 Merged PR 1483: #3806 IPAD2 Styling Branch Dropdown
#3806 IPAD2 Styling Branch Dropdown
2023-02-14 11:01:24 +00:00
Nino Righi
78b757c55b Merged PR 1484: #3828 Bugfixes Disable Print
#3828 Bugfixes Disable Print
2023-02-14 11:01:15 +00:00
Lorenz Hilpert
dfd273e7bf Build Fehler Fix 2023-02-13 17:52:32 +01:00
Nino Righi
1a72c23412 Merged PR 1481: Gelbe Seiten - Sortiment - Preisänderung Implementation + Init Unit Tests
Gelbe Seiten - Sortiment - Preisänderung Implementation + Init Unit Tests
2023-02-13 16:42:08 +00:00
Michael Auer
55a92ad029 Merge tag '3754-lieferschein-erneut-drucken' into develop 2023-02-13 17:28:42 +01:00
Michael Auer
aea6a0d131 Merge branch 'hotfix/3754-lieferschein-erneut-drucken' 2023-02-13 17:11:39 +01:00
Lorenz Hilpert
e76e031675 Merged PR 1480: #3806 Suchfeld wird nun als geschlossen erkannt und stylings greifen wieder
#3806 Suchfeld wird nun als geschlossen erkannt und stylings greifen wieder
2023-02-08 09:54:55 +00:00
Lorenz Hilpert
dc42107668 #3754 Lieferschein erneut drucken - CTA Fix 2023-01-17 16:11:53 +01:00
678 changed files with 16997 additions and 8201 deletions

View File

@@ -1600,6 +1600,43 @@
}
}
}
},
"@domain/product-list": {
"projectType": "library",
"root": "apps/domain/product-list",
"sourceRoot": "apps/domain/product-list/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/domain/product-list/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/domain/product-list/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/domain/product-list/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"tsConfig": "apps/domain/product-list/tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"polyfills": [
"zone.js",
"zone.js/testing"
]
}
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@@ -11,13 +11,9 @@ export class DevScanAdapter implements ScanAdapter {
constructor(private _modal: UiModalService, private _environmentService: EnvironmentService) {}
async init(): Promise<boolean> {
if (this._environmentService.isTablet()) {
return new Promise((resolve, reject) => {
resolve(isDevMode());
});
}
return false;
return new Promise((resolve, reject) => {
resolve(isDevMode());
});
}
scan(): Observable<string> {

View File

@@ -12,7 +12,7 @@ export class NativeScanAdapter implements ScanAdapter {
init(): Promise<boolean> {
return new Promise((resolve, reject) => {
resolve(this.nativeContainerService.isUiWebview().isNative);
resolve(this.nativeContainerService.isNative);
});
}

View File

@@ -14,7 +14,6 @@ export class ScanAdapterService {
async init(): Promise<void> {
for (const adapter of this.scanAdapters) {
const isReady = await adapter.init();
console.log('ScanAdapterService.init', adapter.name, isReady);
this._readyAdapters[adapter.name] = isReady;
}
}
@@ -24,42 +23,30 @@ export class ScanAdapterService {
}
getAdapter(name: string): ScanAdapter | undefined {
return this.scanAdapters.find((adapter) => adapter.name === name);
return this._readyAdapters[name] && this.scanAdapters.find((adapter) => adapter.name === name);
}
// return true if at least one adapter is ready
isReady(): boolean {
return Object.values(this._readyAdapters).some((ready) => ready);
}
scan(ops: { use?: string; include?: string[]; exclude?: string[] } = { exclude: ['Dev'] }): Observable<string> {
scan(): Observable<string> {
const adapterOrder = ['Native', 'Scandit', 'Dev'];
let adapter: ScanAdapter;
if (ops.use == undefined) {
// get the first adapter that is ready to use
adapter = this.scanAdapters
.filter((adapter) => {
if (ops.include?.length) {
return ops.include.includes(adapter.name);
} else if (ops.exclude?.length) {
return !ops.exclude.includes(adapter.name);
} else {
return true;
}
})
.find((adapter) => this._readyAdapters[adapter.name]);
} else {
adapter = this.getAdapter(ops.use);
for (const name of adapterOrder) {
adapter = this.getAdapter(name);
if (adapter) {
break;
}
}
if (!adapter) {
return throwError('No adapter found');
}
if (this._readyAdapters[adapter.name] == false) {
return throwError('Adapter is not ready');
}
return adapter.scan();
}
}

View File

@@ -4,7 +4,7 @@ import { Store } from '@ngrx/store';
import { BranchDTO } from '@swagger/checkout';
import { isBoolean, isNumber } from '@utils/common';
import { BehaviorSubject, Observable } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { first, map, switchMap } from 'rxjs/operators';
import { ApplicationProcess } from './defs';
import {
removeProcess,
@@ -70,7 +70,13 @@ export class ApplicationService {
this.store.dispatch(patchProcessData({ processId, data }));
}
getSelectedBranch$(processId: number): Observable<BranchDTO> {
getSelectedBranch$(processId?: number): Observable<BranchDTO> {
if (!processId) {
return this.activatedProcessId$.pipe(
switchMap((processId) => this.getProcessById$(processId).pipe(map((process) => process?.data?.selectedBranch)))
);
}
return this.getProcessById$(processId).pipe(map((process) => process?.data?.selectedBranch));
}

View File

@@ -1,7 +1,11 @@
import { ModuleWithProviders, NgModule } from '@angular/core';
import { AuthService } from './auth.service';
import { OAuthModule } from 'angular-oauth2-oidc';
@NgModule({})
import { IfRoleDirective } from './if-role.directive';
@NgModule({
declarations: [IfRoleDirective],
exports: [IfRoleDirective],
})
export class AuthModule {
static forRoot(): ModuleWithProviders<AuthModule> {
return {

View File

@@ -1,9 +1,10 @@
import { coerceArray, coerceStringArray } from '@angular/cdk/coercion';
import { Injectable } from '@angular/core';
import { Config } from '@core/config';
import { isNullOrUndefined } from '@utils/common';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { BehaviorSubject } from 'rxjs';
import { asapScheduler, BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
@@ -14,20 +15,29 @@ export class AuthService {
return this._initialized.asObservable();
}
constructor(private _config: Config, private readonly _oAuthService: OAuthService) {}
private _authConfig: AuthConfig;
constructor(private _config: Config, private readonly _oAuthService: OAuthService) {
this._oAuthService.events?.subscribe((event) => {
if (event.type === 'token_received') {
console.log('SSO Token Expiration:', new Date(this._oAuthService.getAccessTokenExpiration()));
}
});
}
async init() {
if (this._initialized.getValue()) {
throw new Error('AuthService is already initialized');
}
const authConfig: AuthConfig = this._config.get('@core/auth');
this._authConfig = this._config.get('@core/auth');
authConfig.redirectUri = window.location.origin;
authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html';
authConfig.useSilentRefresh = true;
this._authConfig.redirectUri = window.location.origin;
this._oAuthService.configure(authConfig);
this._authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html';
this._authConfig.useSilentRefresh = true;
this._oAuthService.configure(this._authConfig);
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
this._oAuthService.setupAutomaticSilentRefresh();
@@ -84,5 +94,32 @@ export class AuthService {
async logout() {
await this._oAuthService.revokeTokenAndLogout();
// asapScheduler.schedule(() => {
// window.location.reload();
// }, 250);
}
hasRole(role: string | string[]) {
const roles = coerceArray(role);
const userRoles = this.getClaimByKey('role');
if (isNullOrUndefined(userRoles)) {
return false;
}
return roles.every((r) => userRoles.includes(r));
}
async refresh() {
try {
if (this._authConfig.responseType.includes('code') && this._authConfig.scope.includes('offline_access')) {
await this._oAuthService.refreshToken();
} else {
await this._oAuthService.silentRefresh();
}
} catch (error) {
console.error(error);
}
}
}

View File

@@ -0,0 +1,65 @@
// import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator';
// import { IfRoleDirective } from './if-role.directive';
// import { AuthService } from './auth.service';
// import { TemplateRef, ViewContainerRef } from '@angular/core';
// describe('IfRoleDirective', () => {
// let spectator: SpectatorDirective<IfRoleDirective>;
// const createDirective = createDirectiveFactory({
// directive: IfRoleDirective,
// mocks: [AuthService],
// });
// it('should create an instance', () => {
// spectator = createDirective(`<div *ifRole="'admin'"></div>`);
// expect(spectator.directive).toBeTruthy();
// });
// it('should render template when user has the role', () => {
// spectator = createDirective(`<div *ifRole="'admin'"></div>`);
// const authService = spectator.inject(AuthService);
// const viewContainerRef = spectator.inject(ViewContainerRef);
// const templateRef = spectator.inject(TemplateRef);
// authService.hasRole.and.returnValue(true);
// spectator.directive.ngOnChanges();
// expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(templateRef, spectator.directive.getContext());
// });
// it('should render else template when user does not have the role', () => {
// authService.hasRole.and.returnValue(false);
// const elseTemplateRef = {} as TemplateRef<any>;
// spectator = createDirective(`<ng-template #elseTemplateRef></ng-template><div *ifRole="'admin'; else elseTemplateRef"></div>`, {
// hostProps: {
// elseTemplateRef,
// },
// });
// spectator.directive.ngOnChanges();
// expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(elseTemplateRef, spectator.directive.getContext());
// });
// it('should render else template when user does not have the role using ifNotRole input', () => {
// authService.hasRole.and.returnValue(false);
// const elseTemplateRef = {} as TemplateRef<any>;
// spectator = createDirective(`<ng-template #elseTemplateRef></ng-template><div *ifNotRole="'admin'; else elseTemplateRef"></div>`, {
// hostProps: {
// elseTemplateRef,
// },
// });
// spectator.directive.ngOnChanges();
// expect(viewContainerRef.createEmbeddedView).toHaveBeenCalledWith(elseTemplateRef, spectator.directive.getContext());
// });
// it('should clear view when user does not have the role and elseTemplateRef is not defined', () => {
// authService.hasRole.and.returnValue(false);
// spectator = createDirective(`<div *ifRole="'admin'"></div>`);
// spectator.directive.ngOnChanges();
// expect(viewContainerRef.clear).toHaveBeenCalled();
// });
// it('should set $implicit to ifRole or ifNotRole input', () => {
// spectator = createDirective(`<div *ifRole="'admin'"></div>`);
// expect(spectator.directive.getContext().$implicit).toEqual('admin');
// spectator.setInput('ifNotRole', 'user');
// expect(spectator.directive.getContext().$implicit).toEqual('user');
// });
// });

View File

@@ -0,0 +1,59 @@
import { Directive, Input, OnChanges, TemplateRef, ViewContainerRef } from '@angular/core';
import { AuthService } from './auth.service';
@Directive({
selector: '[ifRole],[ifRoleElse],[ifNotRole],[ifNotRoleElse]',
})
export class IfRoleDirective implements OnChanges {
@Input()
ifRole: string | string[];
@Input()
ifRoleElse: TemplateRef<any>;
@Input()
ifNotRole: string | string[];
@Input()
ifNotRoleElse: TemplateRef<any>;
get renderTemplateRef() {
if (this.ifRole) {
return this._authService.hasRole(this.ifRole);
}
if (this.ifNotRole) {
return !this._authService.hasRole(this.ifNotRole);
}
return false;
}
get elseTemplateRef() {
return this.ifRoleElse || this.ifNotRoleElse;
}
constructor(private _templateRef: TemplateRef<any>, private _viewContainer: ViewContainerRef, private _authService: AuthService) {}
ngOnChanges() {
this.render();
}
render() {
if (this.renderTemplateRef) {
this._viewContainer.createEmbeddedView(this._templateRef, this.getContext());
return;
}
if (this.elseTemplateRef) {
this._viewContainer.createEmbeddedView(this.elseTemplateRef, this.getContext());
return;
}
this._viewContainer.clear();
}
getContext(): { $implicit: string | string[] } {
return {
$implicit: this.ifRole || this.ifNotRole,
};
}
}

View File

@@ -1,4 +1,6 @@
import { CommandService } from './command.service';
export abstract class ActionHandler<T = any> {
constructor(readonly action: string) {}
abstract handler(data: T): Promise<T>;
abstract handler(data: T, service?: CommandService): Promise<T>;
}

View File

@@ -1,8 +1,12 @@
import { ModuleWithProviders, NgModule, Type } from '@angular/core';
import { ModuleWithProviders, NgModule, Provider, Type } from '@angular/core';
import { ActionHandler } from './action-handler.interface';
import { CommandService } from './command.service';
import { FEATURE_ACTION_HANDLERS, ROOT_ACTION_HANDLERS } from './tokens';
export function provideActionHandlers(actionHandlers: Type<ActionHandler>[]): Provider[] {
return [CommandService, actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true }))];
}
@NgModule({})
export class CoreCommandModule {
static forRoot(actionHandlers: Type<ActionHandler>[]): ModuleWithProviders<CoreCommandModule> {

View File

@@ -16,7 +16,7 @@ export class CommandService {
throw new Error('Action Handler does not exist');
}
data = await handler.handler(data);
data = await handler.handler(data, this);
}
return data;
}

View File

@@ -1,21 +1,26 @@
import { Injectable } from '@angular/core';
import { Platform } from '@angular/cdk/platform';
import { NativeContainerService } from 'native-container';
@Injectable({
providedIn: 'root',
})
export class EnvironmentService {
constructor(private _platform: Platform) {}
constructor(private _platform: Platform, private _nativeContainer: NativeContainerService) {}
isDesktop(): boolean {
return !this.isTablet();
}
isTablet(): boolean {
return this._platform.ANDROID || this._platform.IOS;
return this.isNative() || this.isSafari();
}
isNative(): boolean {
return this._nativeContainer.isNative;
}
isSafari(): boolean {
return this._platform.SAFARI;
return (this._platform.ANDROID || this._platform.IOS) && this._platform.SAFARI;
}
}

View File

@@ -34,6 +34,11 @@ export class DomainAvailabilityService {
private _branchService: StoreCheckoutBranchService
) {}
@memorize({ ttl: 10000 })
memorizedAvailabilityShippingAvailability(request: Array<AvailabilityRequestDTO>) {
return this._availabilityService.AvailabilityShippingAvailability(request).pipe(shareReplay(1));
}
@memorize()
getSuppliers(): Observable<SupplierDTO[]> {
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
@@ -59,8 +64,8 @@ export class DomainAvailabilityService {
}
@memorize()
getStockByBranch(branch: BranchDTO): Observable<StockDTO> {
return this._stockService.StockGetStocksByBranch({ branchId: branch.id }).pipe(
getStockByBranch(branchId: number): Observable<StockDTO> {
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
map((response) => response.result),
map((result) => result?.find((_) => true)),
shareReplay(1)
@@ -140,6 +145,7 @@ export class DomainAvailabilityService {
);
}
@memorize({ ttl: 10000 })
getTakeAwayAvailability({
item,
quantity,
@@ -149,7 +155,8 @@ export class DomainAvailabilityService {
quantity: number;
branch?: BranchDTO;
}): Observable<AvailabilityDTO> {
const request = !!branch ? this.getStockByBranch(branch) : this.getDefaultStock();
console.log('getTakeAwayAvailability', item, quantity, branch);
const request = !!branch ? this.getStockByBranch(branch.id) : this.getDefaultStock();
return request.pipe(
switchMap((s) =>
combineLatest([
@@ -160,7 +167,7 @@ export class DomainAvailabilityService {
),
map(([response, supplier, defaultBranch]) => {
const price = item?.price;
return this._mapToTakeAwayAvailability({ response, supplier, branch: branch ?? defaultBranch, quantity, price });
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
}),
shareReplay(1)
);
@@ -183,7 +190,7 @@ export class DomainAvailabilityService {
this.getTakeAwaySupplier(),
]).pipe(
map(([response, supplier]) => {
return this._mapToTakeAwayAvailability({ response, supplier, branch, quantity, price });
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch.id, quantity, price });
}),
shareReplay(1)
);
@@ -193,16 +200,19 @@ export class DomainAvailabilityService {
eans,
price,
quantity,
branchId,
}: {
eans: string[];
price: PriceDTO;
quantity: number;
branchId?: number;
}): Observable<AvailabilityDTO> {
return this.getDefaultStock().pipe(
const request = !!branchId ? this.getStockByBranch(branchId) : this.getDefaultStock();
return request.pipe(
switchMap((s) => this._stockService.StockInStockByEAN({ eans, stockId: s.id })),
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
map(([response, supplier, branch]) => {
return this._mapToTakeAwayAvailability({ response, supplier, branch, quantity, price });
map(([response, supplier, defaultBranch]) => {
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branchId ?? defaultBranch.id, quantity, price });
}),
shareReplay(1)
);
@@ -246,57 +256,53 @@ export class DomainAvailabilityService {
@memorize({ ttl: 10000 })
getDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
return this._availabilityService
.AvailabilityShippingAvailability([
{
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
qty: quantity,
},
])
.pipe(
timeout(5000),
map((r) => this._mapToShippingAvailability(r.result)?.find((_) => true)),
shareReplay(1)
);
return this.memorizedAvailabilityShippingAvailability([
{
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
qty: quantity,
},
]).pipe(
timeout(5000),
map((r) => this._mapToShippingAvailability(r.result)?.find((_) => true)),
shareReplay(1)
);
}
@memorize({ ttl: 10000 })
getDigDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
return this._availabilityService
.AvailabilityShippingAvailability([
{
qty: quantity,
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
},
])
.pipe(
timeout(5000),
map((r) => {
const availabilities = r.result;
const preferred = availabilities?.find((f) => f.preferred === 1);
return this.memorizedAvailabilityShippingAvailability([
{
qty: quantity,
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
},
]).pipe(
timeout(5000),
map((r) => {
const availabilities = r.result;
const preferred = availabilities?.find((f) => f.preferred === 1);
const availability: AvailabilityDTO = {
availabilityType: preferred?.status,
ssc: preferred?.ssc,
sscText: preferred?.sscText,
supplier: { id: preferred?.supplierId },
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
estimatedDelivery: preferred?.estimatedDelivery,
price: preferred?.price,
logistician: { id: preferred?.logisticianId },
supplierProductNumber: preferred?.supplierProductNumber,
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
shareReplay(1)
);
const availability: AvailabilityDTO = {
availabilityType: preferred?.status,
ssc: preferred?.ssc,
sscText: preferred?.sscText,
supplier: { id: preferred?.supplierId },
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
estimatedDelivery: preferred?.estimatedDelivery,
price: preferred?.price,
logistician: { id: preferred?.logisticianId },
supplierProductNumber: preferred?.supplierProductNumber,
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
shareReplay(1)
);
}
@memorize({ ttl: 10000 })
@@ -330,37 +336,35 @@ export class DomainAvailabilityService {
@memorize({ ttl: 10000 })
getDownloadAvailability({ item }: { item: ItemData }): Observable<AvailabilityDTO> {
return this._availabilityService
.AvailabilityShippingAvailability([
{
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
qty: 1,
},
])
.pipe(
map((r) => {
const availabilities = r.result;
const preferred = availabilities?.find((f) => f.preferred === 1);
return this.memorizedAvailabilityShippingAvailability([
{
ean: item?.ean,
itemId: item?.itemId ? String(item?.itemId) : null,
price: item?.price,
qty: 1,
},
]).pipe(
map((r) => {
const availabilities = r.result;
const preferred = availabilities?.find((f) => f.preferred === 1);
const availability: AvailabilityDTO = {
availabilityType: preferred?.status,
ssc: preferred?.ssc,
sscText: preferred?.sscText,
supplier: { id: preferred?.supplierId },
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
price: preferred?.price,
supplierProductNumber: preferred?.supplierProductNumber,
logistician: { id: preferred?.logisticianId },
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
shareReplay(1)
);
const availability: AvailabilityDTO = {
availabilityType: preferred?.status,
ssc: preferred?.ssc,
sscText: preferred?.sscText,
supplier: { id: preferred?.supplierId },
isPrebooked: preferred?.isPrebooked,
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
price: preferred?.price,
supplierProductNumber: preferred?.supplierProductNumber,
logistician: { id: preferred?.logisticianId },
supplierInfo: preferred?.requestStatusCode,
lastRequest: preferred?.requested,
};
return availability;
}),
shareReplay(1)
);
}
@memorize({ ttl: 10000 })
@@ -398,7 +402,7 @@ export class DomainAvailabilityService {
@memorize({ ttl: 10000 })
getDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
return this._availabilityService.AvailabilityShippingAvailability(payload).pipe(
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
timeout(20000),
map((response) => this._mapToShippingAvailability(response.result))
);
@@ -406,7 +410,7 @@ export class DomainAvailabilityService {
@memorize({ ttl: 10000 })
getDigDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
return this._availabilityService.AvailabilityShippingAvailability(payload).pipe(
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
timeout(20000),
map((response) => this._mapToShippingAvailability(response.result))
);
@@ -444,6 +448,9 @@ export class DomainAvailabilityService {
}
isAvailable({ availability }: { availability: AvailabilityDTO }) {
if (availability?.supplier?.id === 16 && availability?.inStock == 0) {
return false;
}
return [2, 32, 256, 1024, 2048, 4096].some((code) => availability?.availabilityType === code);
}
@@ -476,17 +483,17 @@ export class DomainAvailabilityService {
private _mapToTakeAwayAvailability({
response,
supplier,
branch,
branchId,
quantity,
price,
}: {
response: ResponseArgsOfIEnumerableOfStockInfoDTO;
supplier: SupplierDTO;
branch: BranchDTO;
branchId: number;
quantity: number;
price: PriceDTO;
}): AvailabilityDTO {
const stockInfo = response.result?.find((si) => si.branchId === branch.id);
const stockInfo = response.result?.find((si) => si.branchId === branchId);
const inStock = stockInfo?.inStock ?? 0;
const availability: AvailabilityDTO = {
availabilityType: quantity <= inStock ? 1024 : 1, // 1024 (=Available)
@@ -535,6 +542,7 @@ export class DomainAvailabilityService {
return preferred.map((p) => {
return [
{
orderDeadline: p?.orderDeadline,
availabilityType: p?.status,
ssc: p?.ssc,
sscText: p?.sscText,
@@ -556,7 +564,6 @@ export class DomainAvailabilityService {
private _mapToShippingAvailability(availabilities: SwaggerAvailabilityDTO[]) {
const preferred = availabilities.filter((f) => f.preferred === 1);
return preferred.map((p) => {
return {
availabilityType: p?.status,
@@ -570,6 +577,7 @@ export class DomainAvailabilityService {
supplierInfo: p?.requestStatusCode,
lastRequest: p?.requested,
itemId: p.itemId,
priceMaintained: p.priceMaintained,
};
});
}
@@ -609,7 +617,7 @@ export class DomainAvailabilityService {
}
getInStock({ itemIds, branchId }: { itemIds: number[]; branchId: number }): Observable<StockInfoDTO[]> {
return this.getStockByBranch({ id: branchId }).pipe(
return this.getStockByBranch(branchId).pipe(
mergeMap((stock) =>
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result))
)

View File

@@ -1,8 +1,8 @@
import { Injectable } from '@angular/core';
import { StockInfoDTO } from '@swagger/remi';
import { groupBy } from 'lodash';
import { BehaviorSubject, combineLatest, Observable, OperatorFunction, Subject, timer } from 'rxjs';
import { buffer, bufferTime, bufferWhen, debounceTime } from 'rxjs/operators';
import { groupBy, isEqual, uniqWith } from 'lodash';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { buffer, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { DomainAvailabilityService } from './availability.service';
export type ItemBranch = { itemId: number; branchId: number };
@@ -25,8 +25,9 @@ export class DomainInStockService {
}
private _handleStockDataToFetch = (itemBranchData: ItemBranch[]) => {
if (itemBranchData?.length > 0) {
this._fetchStockData(itemBranchData);
const unique = uniqWith(itemBranchData, isEqual);
if (unique?.length > 0) {
this._fetchStockData(unique);
}
};
@@ -44,16 +45,24 @@ export class DomainInStockService {
const key = this.getKey({ itemId, branchId });
this._addToInStockQueue({ itemId, branchId });
const sub = combineLatest([this._inStockMap, this._inStockFetchingMap]).subscribe(([inStockMap, inStockFetchingMap]) => {
const inStock: InStock = {
itemId,
branchId,
inStock: inStockMap[key],
fetching: inStockFetchingMap[key] ?? false,
};
let _previousValue: InStock;
obs.next(inStock);
});
const sub = combineLatest([this._inStockMap, this._inStockFetchingMap])
.pipe(distinctUntilChanged(isEqual))
.subscribe(([inStockMap, inStockFetchingMap]) => {
const inStock: InStock = {
itemId,
branchId,
inStock: inStockMap[key],
fetching: inStockFetchingMap[key] ?? false,
};
if (!isEqual(inStock, _previousValue)) {
obs.next(inStock);
}
_previousValue = inStock;
});
return () => {
sub.unsubscribe();
};
@@ -71,10 +80,10 @@ export class DomainInStockService {
this._inStockFetchingMap.next({ ...current, [key]: value });
}
private _setInStock({ itemId, branchId }: ItemBranch, value: number) {
private _setInStock({ itemId, branchId }: ItemBranch, inStock: number) {
const key = this.getKey({ itemId, branchId });
const current = this._inStockMap.getValue();
this._inStockMap.next({ ...current, [key]: value });
this._inStockMap.next({ ...current, [key]: inStock });
}
private _fetchStockData(itemBranchData: ItemBranch[]) {
@@ -92,9 +101,11 @@ export class DomainInStockService {
itemIds.forEach((itemId) => {
const stockInfo = stockInfos.find((stockInfo) => stockInfo.itemId === itemId && stockInfo.branchId === branchId);
let inStock = 0;
if (stockInfo?.inStock) {
inStock = stockInfo.inStock;
}
this._setInStockFetching({ itemId, branchId }, false);
this._setInStock({ itemId, branchId }, inStock);
});

View File

@@ -26,8 +26,15 @@ import {
StoreCheckoutBuyerService,
StoreCheckoutPayerService,
StoreCheckoutBranchService,
ItemsResult,
} from '@swagger/checkout';
import { DisplayOrderDTO, DisplayOrderItemDTO, OrderCheckoutService, ReorderValues } from '@swagger/oms';
import {
DisplayOrderDTO,
DisplayOrderItemDTO,
OrderCheckoutService,
ReorderValues,
ResponseArgsOfValueTupleOfIEnumerableOfDisplayOrderDTOAndIEnumerableOfKeyValueDTOOfStringAndString,
} 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';
@@ -198,7 +205,15 @@ export class DomainCheckoutService {
);
}
canAddItems({ processId, payload, orderType }: { processId: number; payload: ItemPayload[]; orderType: string }) {
canAddItems({
processId,
payload,
orderType,
}: {
processId: number;
payload: ItemPayload[];
orderType: string;
}): Observable<ItemsResult[]> {
return this.getShoppingCart({ processId }).pipe(
first(),
withLatestFrom(this.store.select(DomainCheckoutSelectors.selectCustomerFeaturesByProcessId, { processId })),
@@ -217,7 +232,8 @@ export class DomainCheckoutService {
})
.pipe(
map((response) => {
return response.result;
// TODO: remove this when the API is fixed
return (response.result as unknown) as ItemsResult[];
})
);
})
@@ -362,8 +378,9 @@ export class DomainCheckoutService {
_setBuyer({ processId, buyer }: { processId: number; buyer: BuyerDTO }): Observable<CheckoutDTO> {
return this.getCheckout({ processId }).pipe(
first(),
mergeMap((checkout) =>
this._buyerService
mergeMap((checkout) => {
console.log('checkout', checkout, processId);
return this._buyerService
.StoreCheckoutBuyerSetBuyerPOST({
checkoutId: checkout?.id,
buyerDTO: buyer,
@@ -371,8 +388,8 @@ export class DomainCheckoutService {
.pipe(
map((response) => response.result),
tap((checkout) => this.store.dispatch(DomainCheckoutActions.setCheckout({ processId, checkout })))
)
)
);
})
);
}
@@ -700,6 +717,47 @@ export class DomainCheckoutService {
.pipe(mergeMap((_) => completeOrder$.pipe(tap(console.log.bind(window, 'completeOrder$')))));
}
completeKulturpassOrder({
processId,
orderItemSubsetId,
}: {
processId: number;
orderItemSubsetId: number;
}): Observable<ResponseArgsOfValueTupleOfIEnumerableOfDisplayOrderDTOAndIEnumerableOfKeyValueDTOOfStringAndString> {
const refreshShoppingCart$ = this.getShoppingCart({ processId, latest: true }).pipe(first());
const refreshCheckout$ = this.getCheckout({ processId, refresh: true }).pipe(first());
const setBuyer$ = this.getBuyer({ processId }).pipe(
first(),
mergeMap((buyer) => this._setBuyer({ processId, buyer }))
);
const setPayer$ = this.getPayer({ processId }).pipe(
first(),
mergeMap((payer) => this._setPayer({ processId, payer }))
);
const checkAvailabilities$ = this.checkAvailabilities({ processId });
const updateAvailabilities$ = this.updateAvailabilities({ processId });
return refreshShoppingCart$.pipe(
mergeMap((_) => refreshCheckout$),
mergeMap((_) => checkAvailabilities$),
mergeMap((_) => updateAvailabilities$),
mergeMap((_) => setBuyer$),
mergeMap((_) => setPayer$),
mergeMap((checkout) =>
this.orderCheckoutService.OrderCheckoutCreateKulturPassOrder({
payload: {
checkoutId: checkout.id,
orderItemSubsetId: String(orderItemSubsetId),
},
})
)
);
}
updateDestination({
processId,
destinationId,
@@ -918,7 +976,7 @@ export class DomainCheckoutService {
//#region Common
private updateProcessCount(processId: number, count: number) {
this.applicationService.patchProcess(processId, { data: { count } });
this.applicationService.patchProcessData(processId, { count });
}
//#endregion

View File

@@ -21,6 +21,7 @@ export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItems
const response = await this.orderService
.OrderCollectOnDeliveryNote({
data,
eagerLoading: 1,
})
.toPromise();
@@ -29,7 +30,7 @@ export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItems
return {
...context,
receipts: response.result,
receipts: response.result.map((r) => r.data),
};
}
}

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderService } from '@swagger/oms';
import { OrderItemsContext } from './order-items.context';
@Injectable()
export class CollectWithSmallAmountinvoiceActionHandler extends ActionHandler<OrderItemsContext> {
constructor(private orderService: OrderService) {
super('COLLECT_WITH_SMALLAMOUNTINVOICE');
}
async handler(context: OrderItemsContext): Promise<OrderItemsContext> {
const data: Record<number, number> = {};
context.items.forEach((orderItemSubsetId) => {
data[orderItemSubsetId.orderItemSubsetId] =
context.itemQuantity?.get(orderItemSubsetId.orderItemSubsetId) ?? orderItemSubsetId.quantity;
});
const response = await this.orderService
.OrderCollectWithSmallAmountInvoice({
data,
eagerLoading: 1,
})
.toPromise();
// Für korrekte Navigation nach Aufruf, da ProcessingStatus Serverseitig auf abgeholt gesetzt wird
context.items?.forEach((i) => (i.processingStatus = 256));
return {
...context,
receipts: response.result.map((r) => r.data),
};
}
}

View File

@@ -1,4 +1,3 @@
// start:ng42.barrel
export * from './accepted.action-handler';
export * from './arrived.action-handler';
export * from './assembled.action-handler';
@@ -7,6 +6,10 @@ export * from './back-to-stock.action-handler';
export * from './canceled-by-buyer.action-handler';
export * from './canceled-by-retailer.action-handler';
export * from './canceled-by-supplier.action-handler';
export * from './change-order-item-status-base.action-handler';
export * from './collect-on-deliverynote.action-handler';
export * from './collect-with-smallamountinvoice.action-handler';
export * from './create-returnitem.action-handler';
export * from './create-shipping-note.action-handler';
export * from './delivered.action-handler';
export * from './determine-supplier.action-handler';
@@ -25,16 +28,15 @@ export * from './parked.action-handler';
export * from './placed.action-handler';
export * from './preperation-for-shipping.action-handler';
export * from './print-compartment-label.action-handler';
export * from './print-pricediffqrcodelabel.action-handler';
export * from './print-shipping-note.action-handler';
export * from './re-ordered.action-handler';
export * from './print-smallamountinvoice.action-handler';
export * from './re-order.action-handler';
export * from './re-ordered.action-handler';
export * from './redirected-internally.action-handler';
export * from './requested.action-handler';
export * from './reserved.action-handler';
export * from './returned-by-buyer.action-handler';
export * from './shipping-note.action-handler';
export * from './shop-with-kulturpass.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

View File

@@ -1,4 +1,4 @@
import { OrderItemListItemDTO, ReceiptDTO } from '@swagger/oms';
import { OrderItemListItemDTO, ReceiptDTO, OrderDTO } from '@swagger/oms';
export interface OrderItemsContext {
items: OrderItemListItemDTO[];
@@ -12,4 +12,6 @@ export interface OrderItemsContext {
receipts?: ReceiptDTO[];
shippingDelayComment?: string;
order?: OrderDTO;
}

View File

@@ -22,7 +22,7 @@ export class PrintCompartmentLabelActionHandler extends ActionHandler<OrderItems
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isUiWebview().isNative,
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: (printer) =>
this.domainPrinterService

View File

@@ -23,11 +23,12 @@ export class PrintShippingNoteActionHandler extends ActionHandler<OrderItemsCont
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isUiWebview().isNative,
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => {
try {
for (const group of groupBy(data?.receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 1);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.domainPrinterService.printShippingNote({ printer, receipts: group?.items?.map((r) => r?.id) }).toPromise();
}
return {

View File

@@ -0,0 +1,56 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { OMSPrintService } from '@swagger/print';
import { UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { NativeContainerService } from 'native-container';
import { groupBy } from '@ui/common';
@Injectable()
export class PrintSmallamountinvoiceActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private omsPrintService: OMSPrintService,
private nativeContainerService: NativeContainerService
) {
super('PRINT_SMALLAMOUNTINVOICE');
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => {
try {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 128);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.omsPrintService
.OMSPrintKleinbetragsrechnung({
data: group?.items?.map((r) => r?.id),
printer,
})
.toPromise();
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
},
} as PrintModalData,
})
.afterClosed$.toPromise();
return data;
}
}

View File

@@ -0,0 +1,82 @@
import { Injectable } from '@angular/core';
import { OrderItemsContext } from './order-items.context';
import { ActionHandler, CommandService } from '@core/command';
import { KulturpassOrderModalService } from '@shared/modals/kulturpass-order-modal';
import { DisplayOrderItemSubsetDTO, OrderItemListItemDTO, ReceiptDTO } from '@swagger/oms';
import { DomainReceiptService } from '../receipt.service';
import { DomainGoodsService } from '../goods.service';
import { map } from 'rxjs/operators';
@Injectable()
export class ShopWithKulturpassActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private _modal: KulturpassOrderModalService,
private _receiptService: DomainReceiptService,
private _goodsService: DomainGoodsService
) {
super('SHOP_WITH_KULTURPASS');
}
async handler(data: OrderItemsContext, service: CommandService): Promise<OrderItemsContext> {
const items: OrderItemListItemDTO[] = [];
const receipts: ReceiptDTO[] = [];
let command: string;
for (const item of data.items) {
const result = await this._modal.open({ orderItemListItem: item, order: data.order }).afterClosed$.toPromise();
if (result.data == null) {
return data;
}
const displayOrder = result.data[0];
command = result.data[1];
if (displayOrder) {
const subsetItems = displayOrder.items.reduce((acc, item) => [...acc, ...item.subsetItems], [] as DisplayOrderItemSubsetDTO[]);
const orderItems = await this.getItems(displayOrder.orderNumber);
items.push(...orderItems);
const subsetItemIds = subsetItems.map((item) => item.id);
const r = await this.getReceipts(subsetItemIds);
receipts.push(...r);
}
}
if (!command) {
return {
...data,
items,
receipts,
};
} else {
return service.handleCommand(command, {
...data,
items,
receipts,
});
}
}
getReceipts(ids: number[]) {
return this._receiptService
.getReceipts({
receiptType: 128,
eagerLoading: 1,
ids,
})
.pipe(map((res) => res.result.map((data) => data.item3.data).filter((data) => !!data)))
.toPromise();
}
getItems(orderNumber: string) {
return this._goodsService
.getWarenausgabeItemByOrderNumber(orderNumber, false)
.pipe(map((res) => res.result))
.toPromise();
}
}

View File

@@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { AutocompleteTokenDTO, OrderService, QueryTokenDTO } from '@swagger/oms';
@Injectable({ providedIn: 'root' })
export class DomainCustomerOrderService {
constructor(private _orderService: OrderService) {}
complete(payload: AutocompleteTokenDTO) {
return this._orderService.OrderKundenbestellungenAutocomplete(payload);
}
search(payload: QueryTokenDTO) {
return this._orderService.OrderKundenbestellungen({ ...payload });
// branch_id'
}
getOrderItemsByOrderNumber(orderNumber: string) {
return this._orderService.OrderKundenbestellungen({
filter: { all_branches: 'true', archive: 'true' },
input: {
qs: orderNumber,
},
});
}
settings() {
return this._orderService.OrderKundenbestellungenSettings();
}
}

View File

@@ -11,7 +11,6 @@ import {
OrderListItemDTO,
OrderService,
ReceiptService,
ResponseArgsOfIEnumerableOfHistoryDTO,
StatusValues,
StockStatusCodeService,
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
@@ -20,7 +19,7 @@ import {
} from '@swagger/oms';
import { memorize } from '@utils/common';
import { Observable } from 'rxjs';
import { map, mergeMap, shareReplay } from 'rxjs/operators';
import { map, shareReplay } from 'rxjs/operators';
@Injectable()
export class DomainOmsService {
@@ -189,6 +188,10 @@ export class DomainOmsService {
);
}
getOrderSource(orderId: number): Observable<string> {
return this.getOrder(orderId).pipe(map((order) => order?.features?.orderSource));
}
updateNotifications(orderId: number, changes: { selected: NotificationChannel; email: string; mobile: string }) {
const communicationDetails = {
email: changes.email,

View File

@@ -1,5 +1,7 @@
import { Injectable } from '@angular/core';
import { ReceiptOrderItemSubsetReferenceValues, ReceiptService } from '@swagger/oms';
import { memorize } from '@utils/common';
import { shareReplay } from 'rxjs/operators';
@Injectable()
export class DomainReceiptService {
@@ -9,9 +11,12 @@ export class DomainReceiptService {
return this.receiptService.ReceiptCreateShippingNote2(params);
}
@memorize({ ttl: 1000 })
getReceipts(payload: ReceiptOrderItemSubsetReferenceValues) {
return this.receiptService.ReceiptGetReceiptsByOrderItemSubset({
payload: payload,
});
return this.receiptService
.ReceiptGetReceiptsByOrderItemSubset({
payload: payload,
})
.pipe(shareReplay(1));
}
}

View File

@@ -7,3 +7,4 @@ export * from './lib/receipt.service';
export * from './lib/oms.service';
export * from './lib/oms.module';
export * from './lib/action-handlers';
export * from './lib/customer-order.service';

View File

@@ -0,0 +1,11 @@
import { PackageArrivalStatusDTO } from '@swagger/wws';
export abstract class PackageInspectionEvent {
constructor(public readonly type: string) {}
}
export class PackageStatusChangedEvent extends PackageInspectionEvent {
constructor(public readonly packageId: string, public readonly status: PackageArrivalStatusDTO) {
super('PackageStatusChangedEvent');
}
}

View File

@@ -1,10 +1,8 @@
import { Injectable } from '@angular/core';
import {
ListResponseArgsOfPackageDTO,
ListResponseArgsOfPackageDTO2,
PackageArrivalStatusDTO,
PackageDetailResponseDTO,
PackageDTO,
PackageDTO2,
QuerySettingsDTO,
QueryTokenDTO,
@@ -13,13 +11,18 @@ import {
ResponseArgsOfQuerySettingsDTO,
WareneingangService,
} from '@swagger/wws';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { PackageInspectionEvent, PackageStatusChangedEvent } from './events';
@Injectable({
providedIn: 'root',
})
export class DomainPackageInspectionService {
private _events = new Subject<PackageInspectionEvent>();
events = this._events.asObservable();
constructor(private _wareneingang: WareneingangService) {}
getQuerySettingsResponse(): Observable<ResponseArgsOfQuerySettingsDTO> {
@@ -47,23 +50,26 @@ export class DomainPackageInspectionService {
}
changePackageStatusResponse(pkg: PackageDTO2, modifier: string): Observable<ResponseArgsOfPackageArrivalStatusDTO> {
return this._wareneingang.WareneingangChangePackageStatus({
packageId: pkg.id,
payload: {
id: pkg.id,
annotation: pkg.annotation,
area: pkg.area,
arrivalChecked: pkg.arrivalChecked,
arrivalStatus: pkg.arrivalStatus,
deliveryNoteNumber: pkg.deliveryNoteNumber,
deliveryTarget: pkg.deliveryTarget,
estimatedDeliveryDate: pkg.estimatedDeliveryDate,
packageNumber: pkg.packageNumber,
supplier: pkg.supplier,
trackingNumber: pkg.trackingNumber,
},
modifier,
});
return this._wareneingang
.WareneingangChangePackageStatus({
packageId: pkg.id,
payload: {
id: pkg.id,
annotation: pkg.annotation,
area: pkg.area,
arrivalChecked: pkg.arrivalChecked,
arrivalStatus: pkg.arrivalStatus,
deliveryNoteNumber: pkg.deliveryNoteNumber,
deliveryTarget: pkg.deliveryTarget,
estimatedDeliveryDate: pkg.estimatedDeliveryDate,
packageNumber: pkg.packageNumber,
supplier: pkg.supplier,
trackingNumber: pkg.trackingNumber,
scanId: pkg.scanId,
},
modifier,
})
.pipe(tap((res) => this._events.next(new PackageStatusChangedEvent(pkg.id, res.result))));
}
changePackageStatus(pkg: PackageDTO2, modifier: string): Observable<PackageArrivalStatusDTO> {

View File

@@ -1,6 +1,6 @@
/*
* Public API Surface of package-inspection
*/
export * from './lib/events';
export * from './lib/package-inspection.service';
export * from './lib/package-inspection.module';

View File

@@ -5,7 +5,6 @@ import {
CheckoutPrintService,
ItemDTO,
OMSPrintService,
PriceQRCodeDTO,
PrintRequestOfIEnumerableOfItemDTO,
PrintRequestOfIEnumerableOfLong,
PrintRequestOfIEnumerableOfPriceQRCodeDTO,
@@ -13,6 +12,12 @@ import {
ResponseArgs,
LoyaltyCardPrintService,
} from '@swagger/print';
import {
DocumentPayloadOfIEnumerableOfProductListItemDTO,
ProductListItemDTO,
ProductListService,
ResponseArgsOfString,
} from '@swagger/wws';
import { Observable, of } from 'rxjs';
import { catchError, filter, map, switchMap, timeout } from 'rxjs/operators';
import { Printer } from './defs/printer.model';
@@ -27,7 +32,8 @@ export class DomainPrinterService {
private catalogPrintService: CatalogPrintService,
private checkoutPrintService: CheckoutPrintService,
private eisPublicDocumentService: EISPublicDocumentService,
private _loyaltyCardPrintService: LoyaltyCardPrintService
private _loyaltyCardPrintService: LoyaltyCardPrintService,
private _productListService: ProductListService
) {}
getAvailablePrinters(): Observable<Printer[] | { error: string }> {
@@ -202,6 +208,29 @@ export class DomainPrinterService {
});
}
printProductListItemsResponse(payload: DocumentPayloadOfIEnumerableOfProductListItemDTO): Observable<ResponseArgsOfString> {
return this._productListService.ProductListProductListItemPdfAsBase64(payload);
}
printProductListItems({
data,
printer,
title,
}: {
data: ProductListItemDTO[];
printer: string;
title?: string;
}): Observable<ResponseArgs> {
return this.printProductListItemsResponse({ data, title }).pipe(
switchMap((res) => {
if (!res.error) {
return this.printPdf({ printer, data: res.result });
}
return of(res);
})
);
}
printDisplayInfoDTOArticles({
articles,
printer,

View File

@@ -0,0 +1,25 @@
# ProductList
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.0.
## Code scaffolding
Run `ng generate component component-name --project product-list` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project product-list`.
> Note: Don't forget to add `--project product-list` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build product-list` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build product-list`, go to the dist folder `cd dist/product-list` and run `npm publish`.
## Running unit tests
Run `ng test product-list` 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.

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/domain/product-list",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "@domain/product-list",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^15.0.0",
"@angular/core": "^15.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,4 @@
import { NgModule } from '@angular/core';
@NgModule({})
export class ProductListModule {}

View File

@@ -0,0 +1,52 @@
import { Injectable } from '@angular/core';
import {
BatchResponseArgsOfProductListItemDTOAndString,
ListResponseArgsOfProductListItemDTO,
ProductListItemDTO,
ProductListService,
QuerySettingsDTO,
QueryTokenDTO,
ResponseArgsOfProductListItemDTO,
ResponseArgsOfQuerySettingsDTO,
} from '@swagger/wws';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class DomainProductListService {
constructor(private _productList: ProductListService) {}
getQuerySettingsResponse(): Observable<ResponseArgsOfQuerySettingsDTO> {
return this._productList.ProductListQueryProductListItemsSettings();
}
getQuerySettings(): Observable<QuerySettingsDTO> {
return this.getQuerySettingsResponse().pipe(map((response) => response.result));
}
queryProductListResponse(queryToken: QueryTokenDTO): Observable<ListResponseArgsOfProductListItemDTO> {
return this._productList.ProductListQueryProductListItem(queryToken);
}
queryProductList(queryToken: QueryTokenDTO): Observable<ProductListItemDTO[]> {
return this.queryProductListResponse(queryToken).pipe(map((response) => response.result));
}
completeProductListItemResponse(productListItemUId: string): Observable<ResponseArgsOfProductListItemDTO> {
return this._productList.ProductListProductListItemCompleted(productListItemUId);
}
completeProductListItem(productListItemUId: string): Observable<ProductListItemDTO> {
return this.completeProductListItemResponse(productListItemUId).pipe(map((response) => response.result));
}
completeProductListItemsResponse(uids: string[]): Observable<BatchResponseArgsOfProductListItemDTOAndString> {
return this._productList.ProductListProductListItemsCompleted(uids);
}
completeProductListItems(uids: string[]): Observable<BatchResponseArgsOfProductListItemDTOAndString> {
return this.completeProductListItemsResponse(uids);
}
}

View File

@@ -0,0 +1,6 @@
/*
* Public API Surface of product-list
*/
export * from './lib/product-list.service';
export * from './lib/product-list.module';

View File

@@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/lib",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"outDir": "../../../out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -447,7 +447,7 @@ export class DomainRemissionService {
* Create a new receipt for the given return/remission
* @param returnId Return ID
* @param receiptNumber Receipt number
* @returns ReturnDTO - ShippingDocument
* @returns ReceiptDTO
*/
async createReceipt(returnDTO: ReturnDTO, receiptNumber?: string): Promise<ReceiptDTO> {
const stock = await this._getStock();
@@ -471,14 +471,41 @@ export class DomainRemissionService {
return receipt;
}
async completeReceipt(returnId: number, receiptId: number, packageCode: string): Promise<ReceiptDTO> {
/**
* Create a new Package and assign it to a receipt
* @param returnId Return ID
* @param receiptId Receipt ID
* @param packageNumber Packagenumber
* @returns ReceiptDTO
*/
async createReceiptAndAssignPackage({
returnId,
receiptId,
packageNumber,
}: {
returnId: number;
receiptId: number;
packageNumber: string;
}): Promise<ReceiptDTO> {
const response = await this._returnService
.ReturnCreateAndAssignPackage({
returnId,
receiptId,
data: {
packageNumber,
},
})
.toPromise();
const receipt: ReceiptDTO = response.result;
return receipt;
}
async completeReceipt(returnId: number, receiptId: number): Promise<ReceiptDTO> {
const res = await this._returnService
.ReturnFinalizeReceipt({
returnId,
receiptId,
data: {
packageCode,
},
data: {},
})
.toPromise();

View File

@@ -1,54 +1,121 @@
import { Inject, Injectable, InjectionToken } from '@angular/core';
import { AuthService } from '@core/auth';
import { SignalrHub, SignalRHubOptions } from '@core/signalr';
import { BehaviorSubject, merge, of } from 'rxjs';
import { filter, map, shareReplay, tap, withLatestFrom } from 'rxjs/operators';
import { EnvelopeDTO, MessageBoardItemDTO } from './defs';
import { cloneDeep } from 'lodash';
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);
get branchNo() {
return String(this._auth.getClaimByKey('branch_no') || this._auth.getClaimByKey('sub'));
}
notifications$ = merge(
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);
// get sessionStoragesessionStorageKey() {
// return `NOTIFICATIONS_BOARD_${this.branchNo}`;
// }
get sessionStoragesessionStorageKey() {
return `NOTIFICATIONS_BOARD_AREA_${this.branchNo}`;
}
messageBoardItems$ = new BehaviorSubject<Record<string, MessageBoardItemDTO[]>>({});
constructor(@Inject(NOTIFICATIONS_HUB_OPTIONS) options: SignalRHubOptions, private _auth: AuthService) {
super(options);
this.messageBoardItems$.next(this._getNotifications());
this.messageBoardItems$.subscribe((data) => {
this._storeNotifactions(data);
});
this.listen<EnvelopeDTO<MessageBoardItemDTO[]>>('messageBoard').subscribe((envelope) => {
if (envelope.action === 'refresh') {
this.refreshMessageBoardItems(envelope.target.area, envelope.data);
}
return data;
}),
tap((data) => this._storeNotifactions(data)),
shareReplay(1)
});
}
refreshMessageBoardItems(targetArea: string, messages: MessageBoardItemDTO[]) {
const current = cloneDeep(this.messageBoardItems$.value);
current[targetArea] = messages ?? [];
this.messageBoardItems$.next(current);
}
notifications$ = this.messageBoardItems$.asObservable().pipe(
map((data) => {
const messages = { ...data };
const keys = Object.keys(data);
for (let key of keys) {
if (data[key].length === 0 || data[key] === undefined) {
delete messages[key];
}
}
return messages;
})
);
private _storeNotifactions(data: EnvelopeDTO<MessageBoardItemDTO[]>) {
if (data) {
localStorage.setItem('NOTIFICATIONS_BOARD', JSON.stringify(data));
}
}
// notifications$ = merge(
// of(this._getNotifications()).pipe(filter((f) => !!f)),
// this.listen<EnvelopeDTO<MessageBoardItemDTO[]>>('messageBoard')
// ).pipe(
// withLatestFrom(this.updateNotification$),
// map(([d, update]) => {
// console.log('notifications$', 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)
// );
private _getNotifications(): EnvelopeDTO<MessageBoardItemDTO[]> {
const stringData = localStorage.getItem('NOTIFICATIONS_BOARD');
// private _storeNotifactions(data: EnvelopeDTO<MessageBoardItemDTO[]>) {
// if (data) {
// sessionStorage.setItem(this.sessionStoragesessionStorageKey, JSON.stringify(data));
// }
// }
// private _getNotifications(): EnvelopeDTO<MessageBoardItemDTO[]> {
// const stringData = sessionStorage.getItem(this.sessionStoragesessionStorageKey);
// if (stringData) {
// return JSON.parse(stringData);
// }
// return undefined;
// }
private _getNotifications(): Record<string, MessageBoardItemDTO[]> {
const stringData = sessionStorage.getItem(this.sessionStoragesessionStorageKey);
if (stringData) {
return JSON.parse(stringData);
}
return undefined;
return {};
}
private _storeNotifactions(data: Record<string, MessageBoardItemDTO[]>) {
if (data) {
delete data['messageBoard/isa-update'];
sessionStorage.setItem(this.sessionStoragesessionStorageKey, JSON.stringify(data));
}
}
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.',
});
this.refreshMessageBoardItems('messageBoard/isa-update', [
{
category: 'ISA-Update',
type: 'update',
headline: 'Update Benachrichtigung',
text: 'Es steht eine aktuellere Version der ISA bereit. Bitte aktualisieren Sie die Anwendung.',
},
]);
}
}

View File

@@ -1,5 +1,6 @@
import { isDevMode, NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DebugComponent } from './debug/debug.component';
import {
CanActivateCartGuard,
CanActivateCartWithProcessIdGuard,
@@ -14,6 +15,7 @@ import {
CanActivateTaskCalendarGuard,
IsAuthenticatedGuard,
} from './guards';
import { CanActivateAssortmentGuard } from './guards/can-activate-assortment.guard';
import { CanActivatePackageInspectionGuard } from './guards/can-activate-package-inspection.guard';
import { PreviewComponent } from './preview';
import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers';
@@ -55,6 +57,17 @@ const routes: Routes = [
canActivate: [CanActivateProductWithProcessIdGuard],
resolve: { processId: ProcessIdResolver },
},
{
path: 'order',
loadChildren: () => import('@page/customer-order').then((m) => m.CustomerOrderModule),
canActivate: [CanActivateGoodsOutGuard],
},
{
path: ':processId/order',
loadChildren: () => import('@page/customer-order').then((m) => m.CustomerOrderModule),
canActivate: [CanActivateGoodsOutWithProcessIdGuard],
resolve: { processId: ProcessIdResolver },
},
{
path: 'customer',
loadChildren: () => import('@page/customer').then((m) => m.PageCustomerModule),
@@ -115,6 +128,11 @@ const routes: Routes = [
loadChildren: () => import('@page/package-inspection').then((m) => m.PackageInspectionModule),
canActivate: [CanActivatePackageInspectionGuard],
},
{
path: 'assortment',
loadChildren: () => import('@page/assortment').then((m) => m.AssortmentModule),
canActivate: [CanActivateAssortmentGuard],
},
{ path: '**', redirectTo: 'task-calendar', pathMatch: 'full' },
],
resolve: { section: BranchSectionResolver },

View File

@@ -9,6 +9,8 @@ import { CommonModule } from '@angular/common';
import { SwUpdate } from '@angular/service-worker';
import { NotificationsHub } from '@hub/notifications';
import { UserStateService } from '@swagger/isa';
import { UiModalService } from '@ui/modal';
import { AuthService } from '@core/auth';
describe('AppComponent', () => {
let spectator: Spectator<AppComponent>;
@@ -21,7 +23,7 @@ describe('AppComponent', () => {
component: AppComponent,
imports: [CommonModule, RouterTestingModule],
providers: [],
mocks: [Config, SwUpdate, UserStateService],
mocks: [Config, SwUpdate, UserStateService, UiModalService, AuthService],
});
beforeEach(() => {

View File

@@ -1,17 +1,18 @@
import { DOCUMENT } from '@angular/common';
import { Component, Inject, OnInit, Renderer2 } from '@angular/core';
import { Component, HostListener, 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';
import { Router } from '@angular/router';
import { asapScheduler, interval, Observable, Subscription } from 'rxjs';
import { UserStateService } from '@swagger/isa';
import { IsaLogProvider } from './providers';
import { EnvironmentService } from '@core/environment';
import { AuthService } from '@core/auth';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { tap } from 'rxjs/operators';
@Component({
selector: 'app-root',
@@ -41,19 +42,34 @@ export class AppComponent implements OnInit {
private readonly _renderer: Renderer2,
private readonly _swUpdate: SwUpdate,
private readonly _notifications: NotificationsHub,
private readonly _platform: Platform,
private router: Router,
private infoService: UserStateService
private infoService: UserStateService,
private readonly _environment: EnvironmentService,
private readonly _authService: AuthService,
private readonly _modal: UiModalService
) {
this.updateClient();
IsaLogProvider.InfoService = infoService;
IsaLogProvider.InfoService = this.infoService;
}
ngOnInit() {
this.setTitle();
this.logVersion();
this.determinePlatform();
asapScheduler.schedule(() => this.determinePlatform(), 250);
this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this));
this.setupSilentRefresh();
}
// Setup interval for silent refresh
setupSilentRefresh() {
const silentRefreshInterval = this._config.get('silentRefresh.interval');
if (silentRefreshInterval > 0) {
interval(silentRefreshInterval).subscribe(() => {
if (this._authService.isAuthenticated()) {
this._authService.refresh();
}
});
}
}
setTitle() {
@@ -65,13 +81,15 @@ export class AppComponent implements OnInit {
}
determinePlatform() {
if (this._platform.IOS && !this._platform.SAFARI) {
this._renderer.addClass(this._document.body, 'tablet');
if (this._environment.isNative()) {
this._renderer.addClass(this._document.body, 'tablet-native');
} else if (this._platform.IOS && this._platform.SAFARI) {
this._renderer.addClass(this._document.body, 'tablet');
} else if (this._environment.isTablet()) {
this._renderer.addClass(this._document.body, 'tablet-browser');
} else if (this._platform.isBrowser) {
}
if (this._environment.isTablet()) {
this._renderer.addClass(this._document.body, 'tablet');
}
if (this._environment.isDesktop()) {
this._renderer.addClass(this._document.body, 'desktop');
}
}
@@ -86,49 +104,6 @@ export class AppComponent implements OnInit {
}
}
// --------------------------------------------------------
// 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;
@@ -155,4 +130,22 @@ export class AppComponent implements OnInit {
}
});
}
@HostListener('window:visibilitychange', ['$event'])
onVisibilityChange(event: Event) {
// refresh token when app is in background
if (this._document.hidden && this._authService.isAuthenticated()) {
this._authService.refresh();
} else if (!this._authService.isAuthenticated()) {
return this._modal
.open({
content: UiMessageModalComponent,
title: 'Sie sind nicht mehr angemeldet',
data: { message: 'Sie werden neu angemeldet' },
})
.afterClosed$.subscribe(() => {
this._authService.login();
});
}
}
}

View File

@@ -34,11 +34,18 @@ import { RootStateService } from './store/root-state.service';
import * as Commands from './commands';
import { UiIconModule } from '@ui/icon';
import { PreviewComponent } from './preview';
import { NativeContainerService } from 'native-container';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
export function _appInitializerFactory(config: Config, auth: AuthService, injector: Injector, scanAdapter: ScanAdapterService) {
export function _appInitializerFactory(
config: Config,
auth: AuthService,
injector: Injector,
scanAdapter: ScanAdapterService,
nativeContainer: NativeContainerService
) {
return async () => {
const statusElement = document.querySelector('#init-status');
statusElement.innerHTML = 'Konfigurationen werden geladen...';
@@ -52,6 +59,10 @@ export function _appInitializerFactory(config: Config, auth: AuthService, inject
await state.init();
}
statusElement.innerHTML = 'Native Container wird initialisiert...';
await nativeContainer.init();
statusElement.innerHTML = 'Scanner wird initialisiert...';
await scanAdapter.init();
};
}
@@ -96,6 +107,25 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
aliases: [
{ alias: 'd-account', name: 'account' },
{ alias: 'd-no-account', name: 'package-variant-closed' },
{ name: 'isa-audio', alias: 'AU' },
{ name: 'isa-audio', alias: 'CAS' },
{ name: 'isa-audio', alias: 'DL' },
{ name: 'isa-audio', alias: 'KAS' },
{ name: 'isa-hard-cover', alias: 'BUCH' },
{ name: 'isa-hard-cover', alias: 'GEB' },
{ name: 'isa-hard-cover', alias: 'HC' },
{ name: 'isa-hard-cover', alias: 'KT' },
{ name: 'isa-ebook', alias: 'EB' },
{ name: 'isa-non-book', alias: 'GLO' },
{ name: 'isa-non-book', alias: 'HDL' },
{ name: 'isa-non-book', alias: 'NB' },
{ name: 'isa-non-book', alias: 'SPL' },
{ name: 'isa-calendar', alias: 'KA' },
{ name: 'isa-scroll', alias: 'MA' },
{ name: 'isa-software', alias: 'SW' },
{ name: 'isa-soft-cover', alias: 'TB' },
{ name: 'isa-video', alias: 'VI' },
{ name: 'isa-news-paper', alias: 'ZS' },
],
}),
],
@@ -104,7 +134,7 @@ export function _notificationsHubOptionsFactory(config: Config, auth: AuthServic
provide: APP_INITIALIZER,
useFactory: _appInitializerFactory,
multi: true,
deps: [Config, AuthService, Injector, ScanAdapterService],
deps: [Config, AuthService, Injector, ScanAdapterService, NativeContainerService],
},
{
provide: NOTIFICATIONS_HUB_OPTIONS,

View File

@@ -0,0 +1,11 @@
<div class="odd:bg-slate-200 grid grid-flow-col justify-start" *ngFor="let log of logs$ | async">
<div class="p-2 w-100 grow-0">
{{ log.timestamp | date }}
</div>
<div class="p-2 w-50 grow-0">
{{ log.type }}
</div>
<div class="p-2 grow">
{{ log.args | json }}
</div>
</div>

View File

@@ -0,0 +1,6 @@
:host {
@apply block;
}
:host {
@apply grid grid-flow-row bg-white overflow-scroll;
}

View File

@@ -0,0 +1,17 @@
import { CommonModule } from '@angular/common';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { DebugService } from './debug.service';
@Component({
selector: 'app-debug',
templateUrl: 'debug.component.html',
styleUrls: ['debug.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [CommonModule],
})
export class DebugComponent {
logs$ = this.debugService.logs$;
constructor(private debugService: DebugService) {}
}

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, fromEvent } from 'rxjs';
export interface ConsoleLog {
timestamp?: Date;
type: 'log' | 'warn' | 'error';
args: any[];
}
@Injectable()
export class DebugService {
private _consoleSubject = new BehaviorSubject<ConsoleLog[]>([]);
logs$ = this._consoleSubject.asObservable();
constructor() {
fromEvent(window, 'message').subscribe((event: MessageEvent) => {
this.add({ type: 'log', args: [event.data] });
});
}
add(log: ConsoleLog) {
this._consoleSubject.next([...this._consoleSubject.value, { ...log, timestamp: new Date() }]);
}
}

View File

@@ -0,0 +1,24 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { ApplicationService } from '@core/application';
import { Config } from '@core/config';
import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateAssortmentGuard implements CanActivate {
constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(this._config.get('process.ids.assortment')).pipe(first()).toPromise();
if (!process) {
await this._applicationService.createProcess({
id: this._config.get('process.ids.assortment'),
type: 'assortment',
section: 'branch',
name: 'Sortiment',
});
}
this._applicationService.activateProcess(this._config.get('process.ids.assortment'));
return true;
}
}

View File

@@ -52,11 +52,7 @@ export class IsAuthenticatedGuard implements CanActivate {
return undefined;
}
const result = await this._scanService
.scan({
exclude: ['Dev', 'Native'],
})
?.toPromise();
const result = await this._scanService.scan()?.toPromise();
if (typeof result === 'string') {
try {

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { NEVER, Observable, throwError } from 'rxjs';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { catchError, mergeMap, tap } from 'rxjs/operators';
import { AuthService } from '@core/auth';
@@ -25,6 +25,7 @@ export class HttpErrorInterceptor implements HttpInterceptor {
})
.afterClosed$.pipe(mergeMap(() => throwError(error)));
} else if (error.status === 401) {
console.log('401', error);
return this._modal
.open({
content: UiMessageModalComponent,
@@ -35,7 +36,7 @@ export class HttpErrorInterceptor implements HttpInterceptor {
tap(() => {
this._auth.login();
}),
mergeMap(() => throwError(error))
mergeMap(() => NEVER)
);
}

View File

@@ -1 +1,10 @@
<shared-branch-selector [value]="selectedBranch$ | async" (valueChange)="setNewBranch($event)"></shared-branch-selector>
<h1>Platform: {{ platform | json }}</h1>
<br />
<h1>{{ appVersion }}</h1>
<br />
<h1>{{ userAgent }}</h1>
<br />
<h1>Navigator: {{ navigator | json }}</h1>
<br />
<br />
<h1>Device: {{ device }}</h1>

View File

@@ -1,3 +1,4 @@
import { Platform, PlatformModule } from '@angular/cdk/platform';
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { BranchSelectorComponent } from '@shared/components/branch-selector';
@@ -8,13 +9,48 @@ import { BehaviorSubject } from 'rxjs';
selector: 'app-preview',
templateUrl: 'preview.component.html',
styleUrls: ['preview.component.css'],
imports: [CommonModule, BranchSelectorComponent],
imports: [CommonModule, BranchSelectorComponent, PlatformModule],
standalone: true,
})
export class PreviewComponent implements OnInit {
selectedBranch$ = new BehaviorSubject<BranchDTO>({});
constructor() {}
get appVersion() {
return 'App Version: ' + (window.navigator as any).appVersion;
}
get userAgent() {
return 'User Agent: ' + (window.navigator as any).userAgent;
}
get navigator() {
const nav = {};
for (let i in window.navigator) nav[i] = navigator[i];
return nav;
}
get platform() {
return this._platform;
}
get device() {
const isIpadNative = this._platform.IOS && !this._platform.SAFARI;
const isIpadMini6Native = window?.navigator?.userAgent?.includes('Macintosh') && !this._platform.SAFARI;
const isNative = isIpadNative || isIpadMini6Native;
const isPWA = this._platform.IOS && this._platform.SAFARI;
const isDesktop = !isNative && !isPWA;
if (isNative) {
if (isIpadMini6Native) {
return 'IPAD mini 6 Native App';
} else if (isIpadNative) {
return 'IPAD mini 2 Native App or IPAD mini 5 Native App';
}
} else if (isPWA) {
return 'IPAD Safari PWA';
} else if (isDesktop) return 'Desktop or Macintosh';
}
constructor(private readonly _platform: Platform) {}
ngOnInit() {}

View File

@@ -39,7 +39,7 @@
<div class="shell-footer-wrapper">
<shell-footer *ngIf="section$ | async; let section">
<ng-container *ngIf="section === 'customer'">
<a (click)="resetSelectedBranch()" [routerLink]="[customerBasePath$ | async, 'product']" routerLinkActive="active">
<a [routerLink]="[customerBasePath$ | async, 'product']" routerLinkActive="active">
<ui-icon icon="catalog" size="30px"></ui-icon>
Artikelsuche
</a>
@@ -47,12 +47,20 @@
<ui-icon icon="customer" size="24px"></ui-icon>
Kundensuche
</a>
<a [routerLink]="[customerBasePath$ | async, 'goods', 'out']" routerLinkActive="active">
<a *ifRole="'Store'" [routerLink]="[customerBasePath$ | async, 'goods', 'out']" routerLinkActive="active">
<ui-icon icon="box_out" size="24px"></ui-icon>
Warenausgabe
</a>
<a *ifRole="'CallCenter'" [routerLink]="[customerBasePath$ | async, 'order']" routerLinkActive="active">
<ui-svg-icon icon="package-variant-closed" [size]="28"></ui-svg-icon>
Kundenbestellungen
</a>
</ng-container>
<ng-container *ngIf="section === 'branch'">
<a [routerLink]="['/filiale/assortment']" routerLinkActive="active">
<ui-svg-icon icon="shape-outline" [size]="24"></ui-svg-icon>
Sortiment
</a>
<a [routerLink]="['/filiale/task-calendar']" routerLinkActive="active">
<ui-icon icon="calendar_check" size="24px"></ui-icon>
Tätigkeitskalender
@@ -72,3 +80,9 @@
</ng-container>
</shell-footer>
</div>
<button *ngIf="isDevelopment" class="block absolute bottom-0 right-0 z-tooltip p-4 opacity-5" (click)="debugOpen = !debugOpen">
<ui-svg-icon icon="bug-outline"></ui-svg-icon>
</button>
<app-debug *ngIf="debugOpen" class="absolute inset-x-0 top-0 max-h-[calc(100vh-80px)]"></app-debug>

View File

@@ -1,5 +1,5 @@
:host {
@apply block;
@apply block relative min-h-screen;
}
.main-wrapper {

View File

@@ -4,7 +4,7 @@ import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { ApplicationProcess, ApplicationService } from '@core/application';
import { AuthService } from '@core/auth';
import { AuthModule, AuthService } from '@core/auth';
import { Config } from '@core/config';
import { BreadcrumbService } from '@core/breadcrumb';
import { DomainAvailabilityService } from '@domain/availability';
@@ -16,7 +16,7 @@ import { DashboardComponent } from '@page/dashboard';
import { ShellFooterComponent } from '@shell/footer';
import { ShellHeaderComponent } from '@shell/header';
import { ShellProcessComponent, ShellProcessTabComponent } from '@shell/process';
import { UiIconComponent } from '@ui/icon';
import { IconRegistry, UiIconComponent, UiIconModule } from '@ui/icon';
import { UiModalService } from '@ui/modal';
import { EnvelopeDTO, MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs';
import { MockComponent } from 'ng-mocks';
@@ -46,19 +46,28 @@ describe('ShellComponent', () => {
const createComponent = createComponentFactory({
component: ShellComponent,
imports: [
UiIconModule,
RouterTestingModule.withRoutes([
{ path: 'kunde', component: DummyComponent },
{ path: 'kunde/dashboard', component: DashboardComponent },
]),
AuthModule,
],
declarations: [
MockComponent(ShellHeaderComponent),
MockComponent(ShellFooterComponent),
MockComponent(ShellProcessComponent),
MockComponent(ShellProcessTabComponent),
MockComponent(UiIconComponent),
],
mocks: [BreadcrumbService, DomainAvailabilityService, AuthService, DomainDashboardService, Config, WrongDestinationModalService],
mocks: [
BreadcrumbService,
DomainAvailabilityService,
AuthService,
DomainDashboardService,
Config,
WrongDestinationModalService,
IconRegistry,
],
});
beforeEach(() => {
@@ -174,11 +183,13 @@ describe('ShellComponent', () => {
expect(spectator.query('shell-footer')).not.toBeVisible();
});
it('should display the menu items for section customer', () => {
xit('should display the menu items for section customer', () => {
applicationServiceMock.getSection$.and.returnValue(of('customer'));
spectator.component.customerBasePath$ = of('/kunde/1');
spectator.detectComponentChanges();
authServiceMock.hasRole.and.returnValue(true);
const anchors = spectator.queryAll('shell-footer a');
expect(anchors[0]).toHaveText('Artikelsuche');
expect(anchors[0]).toHaveAttribute('href', '/kunde/1/product');
@@ -193,12 +204,16 @@ describe('ShellComponent', () => {
spectator.detectComponentChanges();
const anchors = spectator.queryAll('shell-footer a');
expect(anchors[0]).toHaveText('Tätigkeitskalender');
expect(anchors[0]).toHaveAttribute('href', '/filiale/task-calendar');
expect(anchors[1]).toHaveText('Abholfach');
expect(anchors[1]).toHaveAttribute('href', '/filiale/goods/in');
expect(anchors[2]).toHaveText('Remission');
expect(anchors[2]).toHaveAttribute('href', '/filiale/remission');
expect(anchors[0]).toHaveText('Sortiment');
expect(anchors[0]).toHaveAttribute('href', '/filiale/assortment');
expect(anchors[1]).toHaveText('Tätigkeitskalender');
expect(anchors[1]).toHaveAttribute('href', '/filiale/task-calendar');
expect(anchors[2]).toHaveText('Abholfach');
expect(anchors[2]).toHaveAttribute('href', '/filiale/goods/in');
expect(anchors[3]).toHaveText('Remission');
expect(anchors[3]).toHaveAttribute('href', '/filiale/remission');
// expect(anchors[4]).toHaveText('Wareneingang');
// expect(anchors[4]).toHaveAttribute('href', '/filiale/package-inspection');
});
});

View File

@@ -20,12 +20,18 @@ import { WrongDestinationModalService } from 'apps/page/package-inspection/src/l
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShellComponent {
isDevelopment = Boolean(this._config.get('debug'));
debugOpen = false;
@ViewChildren('processTabs')
readonly processTabs: QueryList<ShellProcessTabComponent>;
notifications$ = this._notificationsHub.notifications$;
notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length));
notificationCount$ = this.notifications$.pipe(
map((notifications) => Object.values(notifications).reduce((acc, val) => acc + val?.length ?? 0, 0))
);
get activatedProcessId$() {
return this._appService.getActivatedProcessId$().pipe(
@@ -164,13 +170,6 @@ export class ShellComponent {
});
}
async resetSelectedBranch() {
const processId = await this.activatedProcessId$.pipe(take(1)).toPromise();
if (!!processId) {
this._appService.patchProcessData(processId, { selectedBranch: undefined });
}
}
trackByIdFn: TrackByFunction<ApplicationProcess> = (_, process) => process.id;
fetchAndOpenPackages = () => this._wrongDestinationModalService.fetchAndOpen();

View File

@@ -9,9 +9,21 @@ import { ShellFooterModule } from '@shell/footer';
import { ShellComponent } from './shell.component';
import { UiIconModule } from '@ui/icon';
import { RouterModule } from '@angular/router';
import { AuthModule } from '@core/auth';
import { DebugComponent } from '../debug/debug.component';
@NgModule({
imports: [RouterModule, CommonModule, ShellHeaderModule, ShellProcessModule, ShellFooterModule, UiIconModule, OverlayModule],
imports: [
RouterModule,
CommonModule,
ShellHeaderModule,
ShellProcessModule,
ShellFooterModule,
UiIconModule,
OverlayModule,
AuthModule,
DebugComponent,
],
exports: [ShellComponent],
declarations: [ShellComponent],
providers: [],

View File

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="18px" viewBox="0 0 22 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Icon_HC</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Hugendubel_Icons" transform="translate(-29.000000, -177.000000)" fill="#000000" fill-rule="nonzero" stroke="#000000" stroke-width="0.3">
<path d="M32.725699,178.000628 C32.7430505,177.999791 32.7604307,177.999791 32.7777823,178.000628 L37.5260449,178.000628 C38.53109,178.000628 39.44018,178.547926 40.0000026,179.340692 C40.5598253,178.547926 41.4689153,178.000628 42.4739603,178.000628 L47.222223,178.000628 C47.529035,178.00066 47.7777477,178.256627 47.7777784,178.572389 L47.7777784,179.71591 L49.4444446,179.71591 C49.7512567,179.715942 49.9999694,179.971909 50,180.287671 L50,192.008763 C49.9999694,192.324524 49.7512567,192.580492 49.4444446,192.580523 L42.4739603,192.580523 C41.4766486,192.580523 40.8524541,192.949423 40.4947942,193.688309 C40.3998428,193.879608 40.208727,194 40.0000026,194 C39.7912783,194 39.6001624,193.879608 39.5052111,193.688309 C39.1475512,192.949423 38.5233566,192.580523 37.5260449,192.580523 L30.5555607,192.580523 C30.2487486,192.580492 30.0000359,192.324524 30.0000053,192.008763 L30.0000053,180.287671 C29.9987652,179.991708 30.2171687,179.743681 30.5034773,179.71591 C30.5208289,179.715072 30.5382091,179.715072 30.5555607,179.71591 L32.2222269,179.71591 L32.2222269,178.572389 C32.2209869,178.276426 32.4393904,178.028399 32.725699,178.000628 Z M33.3333377,179.14415 L33.3333377,189.43584 L36.6232674,189.43584 C37.6190746,189.43584 38.5547188,189.729711 39.2621556,190.356017 C39.3270606,190.413476 39.3849347,190.480265 39.4444472,190.543626 L39.4444472,180.957703 C39.4433388,180.936873 39.4433388,180.915996 39.4444472,180.895166 C39.4444472,180.068361 38.4768339,179.14415 37.5260449,179.14415 L33.3333377,179.14415 Z M42.4739603,179.14415 C41.5231714,179.14415 40.555558,180.068361 40.555558,180.895166 C40.5555806,180.898144 40.5555806,180.901122 40.555558,180.9041 L40.555558,190.543626 C40.6150705,190.480265 40.6729447,190.413476 40.7378497,190.356017 C41.4452864,189.729711 42.3809306,189.43584 43.3767379,189.43584 L46.6666675,189.43584 L46.6666675,179.14415 L42.4739603,179.14415 Z M31.1111161,180.859431 L31.1111161,191.437002 L37.5260449,191.437002 C38.0365968,191.437002 38.5189494,191.531483 38.9496557,191.713949 C38.8273679,191.527334 38.6888269,191.36055 38.5329891,191.222592 C38.0547048,190.799175 37.405738,190.579361 36.6232674,190.579361 L32.7777823,190.579361 C32.4709702,190.57933 32.2222575,190.323362 32.2222269,190.007601 L32.2222269,180.859431 L31.1111161,180.859431 Z M47.7777784,180.859431 L47.7777784,190.007601 C47.7777477,190.323362 47.529035,190.57933 47.222223,190.579361 L43.3767379,190.579361 C42.5942672,190.579361 41.9453004,190.799175 41.4670161,191.222592 C41.3107359,191.360944 41.1725734,191.526903 41.0503496,191.713949 C41.4810558,191.531483 41.9634085,191.437002 42.4739603,191.437002 L48.8888892,191.437002 L48.8888892,180.859431 L47.7777784,180.859431 Z M35.5,182.116677 L37.5,182.116677 C37.7761424,182.116677 38,182.340535 38,182.616677 L38,182.645847 C38,182.921989 37.7761424,183.145847 37.5,183.145847 L35.5,183.145847 C35.2238576,183.145847 35,182.921989 35,182.645847 L35,182.616677 C35,182.340535 35.2238576,182.116677 35.5,182.116677 Z M35.5,184.175016 L37.5,184.175016 C37.7761424,184.175016 38,184.398874 38,184.675016 L38,184.704185 C38,184.980328 37.7761424,185.204185 37.5,185.204185 L35.5,185.204185 C35.2238576,185.204185 35,184.980328 35,184.704185 L35,184.675016 C35,184.398874 35.2238576,184.175016 35.5,184.175016 Z M35.5,186.233355 L37.5,186.233355 C37.7761424,186.233355 38,186.457212 38,186.733355 L38,186.762524 C38,187.038666 37.7761424,187.262524 37.5,187.262524 L35.5,187.262524 C35.2238576,187.262524 35,187.038666 35,186.762524 L35,186.733355 C35,186.457212 35.2238576,186.233355 35.5,186.233355 Z M42.5,182.116677 L44.5,182.116677 C44.7761424,182.116677 45,182.340535 45,182.616677 L45,182.645847 C45,182.921989 44.7761424,183.145847 44.5,183.145847 L42.5,183.145847 C42.2238576,183.145847 42,182.921989 42,182.645847 L42,182.616677 C42,182.340535 42.2238576,182.116677 42.5,182.116677 Z M42.5,184.175016 L44.5,184.175016 C44.7761424,184.175016 45,184.398874 45,184.675016 L45,184.704185 C45,184.980328 44.7761424,185.204185 44.5,185.204185 L42.5,185.204185 C42.2238576,185.204185 42,184.980328 42,184.704185 L42,184.675016 C42,184.398874 42.2238576,184.175016 42.5,184.175016 Z M42.5,186.233355 L44.5,186.233355 C44.7761424,186.233355 45,186.457212 45,186.733355 L45,186.762524 C45,187.038666 44.7761424,187.262524 44.5,187.262524 L42.5,187.262524 C42.2238576,187.262524 42,187.038666 42,186.762524 L42,186.733355 C42,186.457212 42.2238576,186.233355 42.5,186.233355 Z" id="Icon_HC"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="18px" viewBox="0 0 22 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Icon_HC</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Hugendubel_Icons" transform="translate(-29.000000, -177.000000)" fill="#000000" fill-rule="nonzero" stroke="#000000" stroke-width="0.3">
<path d="M32.725699,178.000628 C32.7430505,177.999791 32.7604307,177.999791 32.7777823,178.000628 L37.5260449,178.000628 C38.53109,178.000628 39.44018,178.547926 40.0000026,179.340692 C40.5598253,178.547926 41.4689153,178.000628 42.4739603,178.000628 L47.222223,178.000628 C47.529035,178.00066 47.7777477,178.256627 47.7777784,178.572389 L47.7777784,179.71591 L49.4444446,179.71591 C49.7512567,179.715942 49.9999694,179.971909 50,180.287671 L50,192.008763 C49.9999694,192.324524 49.7512567,192.580492 49.4444446,192.580523 L42.4739603,192.580523 C41.4766486,192.580523 40.8524541,192.949423 40.4947942,193.688309 C40.3998428,193.879608 40.208727,194 40.0000026,194 C39.7912783,194 39.6001624,193.879608 39.5052111,193.688309 C39.1475512,192.949423 38.5233566,192.580523 37.5260449,192.580523 L30.5555607,192.580523 C30.2487486,192.580492 30.0000359,192.324524 30.0000053,192.008763 L30.0000053,180.287671 C29.9987652,179.991708 30.2171687,179.743681 30.5034773,179.71591 C30.5208289,179.715072 30.5382091,179.715072 30.5555607,179.71591 L32.2222269,179.71591 L32.2222269,178.572389 C32.2209869,178.276426 32.4393904,178.028399 32.725699,178.000628 Z M33.3333377,179.14415 L33.3333377,189.43584 L36.6232674,189.43584 C37.6190746,189.43584 38.5547188,189.729711 39.2621556,190.356017 C39.3270606,190.413476 39.3849347,190.480265 39.4444472,190.543626 L39.4444472,180.957703 C39.4433388,180.936873 39.4433388,180.915996 39.4444472,180.895166 C39.4444472,180.068361 38.4768339,179.14415 37.5260449,179.14415 L33.3333377,179.14415 Z M42.4739603,179.14415 C41.5231714,179.14415 40.555558,180.068361 40.555558,180.895166 C40.5555806,180.898144 40.5555806,180.901122 40.555558,180.9041 L40.555558,190.543626 C40.6150705,190.480265 40.6729447,190.413476 40.7378497,190.356017 C41.4452864,189.729711 42.3809306,189.43584 43.3767379,189.43584 L46.6666675,189.43584 L46.6666675,179.14415 L42.4739603,179.14415 Z M31.1111161,180.859431 L31.1111161,191.437002 L37.5260449,191.437002 C38.0365968,191.437002 38.5189494,191.531483 38.9496557,191.713949 C38.8273679,191.527334 38.6888269,191.36055 38.5329891,191.222592 C38.0547048,190.799175 37.405738,190.579361 36.6232674,190.579361 L32.7777823,190.579361 C32.4709702,190.57933 32.2222575,190.323362 32.2222269,190.007601 L32.2222269,180.859431 L31.1111161,180.859431 Z M47.7777784,180.859431 L47.7777784,190.007601 C47.7777477,190.323362 47.529035,190.57933 47.222223,190.579361 L43.3767379,190.579361 C42.5942672,190.579361 41.9453004,190.799175 41.4670161,191.222592 C41.3107359,191.360944 41.1725734,191.526903 41.0503496,191.713949 C41.4810558,191.531483 41.9634085,191.437002 42.4739603,191.437002 L48.8888892,191.437002 L48.8888892,180.859431 L47.7777784,180.859431 Z M35.5,182.116677 L37.5,182.116677 C37.7761424,182.116677 38,182.340535 38,182.616677 L38,182.645847 C38,182.921989 37.7761424,183.145847 37.5,183.145847 L35.5,183.145847 C35.2238576,183.145847 35,182.921989 35,182.645847 L35,182.616677 C35,182.340535 35.2238576,182.116677 35.5,182.116677 Z M35.5,184.175016 L37.5,184.175016 C37.7761424,184.175016 38,184.398874 38,184.675016 L38,184.704185 C38,184.980328 37.7761424,185.204185 37.5,185.204185 L35.5,185.204185 C35.2238576,185.204185 35,184.980328 35,184.704185 L35,184.675016 C35,184.398874 35.2238576,184.175016 35.5,184.175016 Z M35.5,186.233355 L37.5,186.233355 C37.7761424,186.233355 38,186.457212 38,186.733355 L38,186.762524 C38,187.038666 37.7761424,187.262524 37.5,187.262524 L35.5,187.262524 C35.2238576,187.262524 35,187.038666 35,186.762524 L35,186.733355 C35,186.457212 35.2238576,186.233355 35.5,186.233355 Z M42.5,182.116677 L44.5,182.116677 C44.7761424,182.116677 45,182.340535 45,182.616677 L45,182.645847 C45,182.921989 44.7761424,183.145847 44.5,183.145847 L42.5,183.145847 C42.2238576,183.145847 42,182.921989 42,182.645847 L42,182.616677 C42,182.340535 42.2238576,182.116677 42.5,182.116677 Z M42.5,184.175016 L44.5,184.175016 C44.7761424,184.175016 45,184.398874 45,184.675016 L45,184.704185 C45,184.980328 44.7761424,185.204185 44.5,185.204185 L42.5,185.204185 C42.2238576,185.204185 42,184.980328 42,184.704185 L42,184.675016 C42,184.398874 42.2238576,184.175016 42.5,184.175016 Z M42.5,186.233355 L44.5,186.233355 C44.7761424,186.233355 45,186.457212 45,186.733355 L45,186.762524 C45,187.038666 44.7761424,187.262524 44.5,187.262524 L42.5,187.262524 C42.2238576,187.262524 42,187.038666 42,186.762524 L42,186.733355 C42,186.457212 42.2238576,186.233355 42.5,186.233355 Z" id="Icon_HC"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="18px" viewBox="0 0 22 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Icon_HC</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Hugendubel_Icons" transform="translate(-29.000000, -177.000000)" fill="#000000" fill-rule="nonzero" stroke="#000000" stroke-width="0.3">
<path d="M32.725699,178.000628 C32.7430505,177.999791 32.7604307,177.999791 32.7777823,178.000628 L37.5260449,178.000628 C38.53109,178.000628 39.44018,178.547926 40.0000026,179.340692 C40.5598253,178.547926 41.4689153,178.000628 42.4739603,178.000628 L47.222223,178.000628 C47.529035,178.00066 47.7777477,178.256627 47.7777784,178.572389 L47.7777784,179.71591 L49.4444446,179.71591 C49.7512567,179.715942 49.9999694,179.971909 50,180.287671 L50,192.008763 C49.9999694,192.324524 49.7512567,192.580492 49.4444446,192.580523 L42.4739603,192.580523 C41.4766486,192.580523 40.8524541,192.949423 40.4947942,193.688309 C40.3998428,193.879608 40.208727,194 40.0000026,194 C39.7912783,194 39.6001624,193.879608 39.5052111,193.688309 C39.1475512,192.949423 38.5233566,192.580523 37.5260449,192.580523 L30.5555607,192.580523 C30.2487486,192.580492 30.0000359,192.324524 30.0000053,192.008763 L30.0000053,180.287671 C29.9987652,179.991708 30.2171687,179.743681 30.5034773,179.71591 C30.5208289,179.715072 30.5382091,179.715072 30.5555607,179.71591 L32.2222269,179.71591 L32.2222269,178.572389 C32.2209869,178.276426 32.4393904,178.028399 32.725699,178.000628 Z M33.3333377,179.14415 L33.3333377,189.43584 L36.6232674,189.43584 C37.6190746,189.43584 38.5547188,189.729711 39.2621556,190.356017 C39.3270606,190.413476 39.3849347,190.480265 39.4444472,190.543626 L39.4444472,180.957703 C39.4433388,180.936873 39.4433388,180.915996 39.4444472,180.895166 C39.4444472,180.068361 38.4768339,179.14415 37.5260449,179.14415 L33.3333377,179.14415 Z M42.4739603,179.14415 C41.5231714,179.14415 40.555558,180.068361 40.555558,180.895166 C40.5555806,180.898144 40.5555806,180.901122 40.555558,180.9041 L40.555558,190.543626 C40.6150705,190.480265 40.6729447,190.413476 40.7378497,190.356017 C41.4452864,189.729711 42.3809306,189.43584 43.3767379,189.43584 L46.6666675,189.43584 L46.6666675,179.14415 L42.4739603,179.14415 Z M31.1111161,180.859431 L31.1111161,191.437002 L37.5260449,191.437002 C38.0365968,191.437002 38.5189494,191.531483 38.9496557,191.713949 C38.8273679,191.527334 38.6888269,191.36055 38.5329891,191.222592 C38.0547048,190.799175 37.405738,190.579361 36.6232674,190.579361 L32.7777823,190.579361 C32.4709702,190.57933 32.2222575,190.323362 32.2222269,190.007601 L32.2222269,180.859431 L31.1111161,180.859431 Z M47.7777784,180.859431 L47.7777784,190.007601 C47.7777477,190.323362 47.529035,190.57933 47.222223,190.579361 L43.3767379,190.579361 C42.5942672,190.579361 41.9453004,190.799175 41.4670161,191.222592 C41.3107359,191.360944 41.1725734,191.526903 41.0503496,191.713949 C41.4810558,191.531483 41.9634085,191.437002 42.4739603,191.437002 L48.8888892,191.437002 L48.8888892,180.859431 L47.7777784,180.859431 Z M35.5,182.116677 L37.5,182.116677 C37.7761424,182.116677 38,182.340535 38,182.616677 L38,182.645847 C38,182.921989 37.7761424,183.145847 37.5,183.145847 L35.5,183.145847 C35.2238576,183.145847 35,182.921989 35,182.645847 L35,182.616677 C35,182.340535 35.2238576,182.116677 35.5,182.116677 Z M35.5,184.175016 L37.5,184.175016 C37.7761424,184.175016 38,184.398874 38,184.675016 L38,184.704185 C38,184.980328 37.7761424,185.204185 37.5,185.204185 L35.5,185.204185 C35.2238576,185.204185 35,184.980328 35,184.704185 L35,184.675016 C35,184.398874 35.2238576,184.175016 35.5,184.175016 Z M35.5,186.233355 L37.5,186.233355 C37.7761424,186.233355 38,186.457212 38,186.733355 L38,186.762524 C38,187.038666 37.7761424,187.262524 37.5,187.262524 L35.5,187.262524 C35.2238576,187.262524 35,187.038666 35,186.762524 L35,186.733355 C35,186.457212 35.2238576,186.233355 35.5,186.233355 Z M42.5,182.116677 L44.5,182.116677 C44.7761424,182.116677 45,182.340535 45,182.616677 L45,182.645847 C45,182.921989 44.7761424,183.145847 44.5,183.145847 L42.5,183.145847 C42.2238576,183.145847 42,182.921989 42,182.645847 L42,182.616677 C42,182.340535 42.2238576,182.116677 42.5,182.116677 Z M42.5,184.175016 L44.5,184.175016 C44.7761424,184.175016 45,184.398874 45,184.675016 L45,184.704185 C45,184.980328 44.7761424,185.204185 44.5,185.204185 L42.5,185.204185 C42.2238576,185.204185 42,184.980328 42,184.704185 L42,184.675016 C42,184.398874 42.2238576,184.175016 42.5,184.175016 Z M42.5,186.233355 L44.5,186.233355 C44.7761424,186.233355 45,186.457212 45,186.733355 L45,186.762524 C45,187.038666 44.7761424,187.262524 44.5,187.262524 L42.5,187.262524 C42.2238576,187.262524 42,187.038666 42,186.762524 L42,186.733355 C42,186.457212 42.2238576,186.233355 42.5,186.233355 Z" id="Icon_HC"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -1,5 +1,8 @@
{
"title": "ISA - Feature",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -8,7 +11,7 @@
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi"
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
@@ -38,7 +41,7 @@
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v1"
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
@@ -60,11 +63,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AQZyKCc+BEkNL00Y3h3FjawGLF+INUj7cVb0My91hl8ffiW873T8FTV1k4TIZJx5RwcJlYxhgsxHVcnM4AJgSwJhbAfxJmP/3XGijLlLp3XUIRjQwFtf7UlZAFZ7Vrt1/WSf7kxxrFQ2SE2AQwLqPg9DL+hHEfd4xT/15n8p2q7qUlCKLsV6jF12Pd7koFNSWNL3ZIkRtd1ma99/321dnwAJHFGXqWg5nprJ7sYtqUqNQ8Er9SlvKbhnw3AipHzKpz0O3oNfUsr6NlZivRBhMhCZLo5WpXo1m9uIU8zLEWMNDJ+wGUctcGxE3eCptP2zLXUgxxjB+0EXOUtT/GWUc/Ip61CMiyUf7Paz026E2eYil2yWgfkTP5CUgDMNGZFuAA1T5PhB9FRW51CjAIvwOKVMCvfixJiVoUsXHnWH2ZnXqtbDR/uEZBE7OKoBlaPL4G3Lvgdqym5EjROAztUXb6wOmVDiGzzqgizyZnIcxFBSKJAownGj9Vh4/Y/Ag1xzGzNtjz3ngSRfMfIIq/q2Q51uiLiv7mBVliPvPWMUTfTjnqnK/OSBlR2ID+COJqnUKpQMedPyOT3IMznmM6gQCmyYO5KE0MkfhFh6+pdNi6oJM2iZsxK1Z1V+GRSOIwrJEoajjDJkh439XjXk8NExFvplrLjK/oL/dsHIZiG6U5GVWW92kGkuXkJCeUz1CET3paxbGqwrd53r5d6gFABbC12CtcP2JeH4YYCpHYyPQacf0prj9Hdq3wDztShC9tH+4UQS/GbaDHKcS1ANIyPuTxHmBFtPuCJ9Uagy5QBEc8eAz2nfsbfaUxYzco6u/zhNsFbqp6zgQIxs5OcqDQ=="
"scandit": "AZ7zLw2eLmFWHbYP4RDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YewGQYEESXjpRwGX1NYmKY3pXHnAn2DeqIzh2an+FUu9socQlbQnJiHJHoWBAqcqWSua+P12tc95P3s9aaEEYvSjUy7Md88f7N+sk6zZbUmqbMXeXqmZwdkmRoUY/2w0CiiiA4gBFHgu4sMeNQ9dWyfxKTUPf5AnsxnuYpCt5KLxJWSYDv8HHj0mx8DCJTe1m2ony97Lge3JbJ5Dd+Zz6SCwqik7fv53Qole9s/3m66lYFWKAzWRKkHN1zts78CmPxPb+AAHVoqlBM3duvYmnCxxGOmlXabKUNuDR2ExaMu/nlo532jqqy25Cet/FP1UAs96ZGRgzEcHxGPp6kA53lJ15zd+cxz6G93E83AmYJkhddXBQElWEaGtQRfrEzRGmvcksR+V8MMYjGmhkVbQxGGqpnfP4IxbuEFcef6bxxTiulzo75gXoqZTt+7C1qpDcrMM3Yp0Z8RBw3JlV2tLk4FYFZpxY8QrXIcjvRYKExtQ9e5sSbST4Vx95YhEUd6iX0SBPDzcmgR4/Ef6gvJfoWgz68+rqhBGckphdHi2Mf/pYuAlh2jbwtrkErE2xWARBejR/UcU/A3F7k9RkFd5/QZC7qhsE6bZH7uhpkptIbi5XkXagwYy1oJD7yJs4VLOJteYWferRm8h1auxXew5tL8VLHciF+lLj6h8PTUDt2blLgUjHtualqlCwdSTzJyYwk4oswGGDk6E48X7LXpzuhtR8TYTOi2REN0uuTbO/slFBRw+CaYUnD0LjB9p2lb8ndcdV9adzBKmwPxiOtlOELQ=="
}
}

View File

@@ -1,14 +1,16 @@
{
"title": "ISA - Integration",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi"
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
@@ -38,7 +40,7 @@
"rootUrl": "https://filialinformationsystem-integration.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-integration.paragon-data.net/inv/v1"
"rootUrl": "https://isa-integration.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-integration.paragon-data.net/wws/v1"
@@ -60,11 +62,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AQZyKCc+BEkNL00Y3h3FjawGLF+INUj7cVb0My91hl8ffiW873T8FTV1k4TIZJx5RwcJlYxhgsxHVcnM4AJgSwJhbAfxJmP/3XGijLlLp3XUIRjQwFtf7UlZAFZ7Vrt1/WSf7kxxrFQ2SE2AQwLqPg9DL+hHEfd4xT/15n8p2q7qUlCKLsV6jF12Pd7koFNSWNL3ZIkRtd1ma99/321dnwAJHFGXqWg5nprJ7sYtqUqNQ8Er9SlvKbhnw3AipHzKpz0O3oNfUsr6NlZivRBhMhCZLo5WpXo1m9uIU8zLEWMNDJ+wGUctcGxE3eCptP2zLXUgxxjB+0EXOUtT/GWUc/Ip61CMiyUf7Paz026E2eYil2yWgfkTP5CUgDMNGZFuAA1T5PhB9FRW51CjAIvwOKVMCvfixJiVoUsXHnWH2ZnXqtbDR/uEZBE7OKoBlaPL4G3Lvgdqym5EjROAztUXb6wOmVDiGzzqgizyZnIcxFBSKJAownGj9Vh4/Y/Ag1xzGzNtjz3ngSRfMfIIq/q2Q51uiLiv7mBVliPvPWMUTfTjnqnK/OSBlR2ID+COJqnUKpQMedPyOT3IMznmM6gQCmyYO5KE0MkfhFh6+pdNi6oJM2iZsxK1Z1V+GRSOIwrJEoajjDJkh439XjXk8NExFvplrLjK/oL/dsHIZiG6U5GVWW92kGkuXkJCeUz1CET3paxbGqwrd53r5d6gFABbC12CtcP2JeH4YYCpHYyPQacf0prj9Hdq3wDztShC9tH+4UQS/GbaDHKcS1ANIyPuTxHmBFtPuCJ9Uagy5QBEc8eAz2nfsbfaUxYzco6u/zhNsFbqp6zgQIxs5OcqDQ=="
"scandit": "AZ7zLw2eLmFWHbYP4RDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YewGQYEESXjpRwGX1NYmKY3pXHnAn2DeqIzh2an+FUu9socQlbQnJiHJHoWBAqcqWSua+P12tc95P3s9aaEEYvSjUy7Md88f7N+sk6zZbUmqbMXeXqmZwdkmRoUY/2w0CiiiA4gBFHgu4sMeNQ9dWyfxKTUPf5AnsxnuYpCt5KLxJWSYDv8HHj0mx8DCJTe1m2ony97Lge3JbJ5Dd+Zz6SCwqik7fv53Qole9s/3m66lYFWKAzWRKkHN1zts78CmPxPb+AAHVoqlBM3duvYmnCxxGOmlXabKUNuDR2ExaMu/nlo532jqqy25Cet/FP1UAs96ZGRgzEcHxGPp6kA53lJ15zd+cxz6G93E83AmYJkhddXBQElWEaGtQRfrEzRGmvcksR+V8MMYjGmhkVbQxGGqpnfP4IxbuEFcef6bxxTiulzo75gXoqZTt+7C1qpDcrMM3Yp0Z8RBw3JlV2tLk4FYFZpxY8QrXIcjvRYKExtQ9e5sSbST4Vx95YhEUd6iX0SBPDzcmgR4/Ef6gvJfoWgz68+rqhBGckphdHi2Mf/pYuAlh2jbwtrkErE2xWARBejR/UcU/A3F7k9RkFd5/QZC7qhsE6bZH7uhpkptIbi5XkXagwYy1oJD7yJs4VLOJteYWferRm8h1auxXew5tL8VLHciF+lLj6h8PTUDt2blLgUjHtualqlCwdSTzJyYwk4oswGGDk6E48X7LXpzuhtR8TYTOi2REN0uuTbO/slFBRw+CaYUnD0LjB9p2lb8ndcdV9adzBKmwPxiOtlOELQ=="
}
}

View File

@@ -1,14 +1,18 @@
{
"title": "ISA - Local",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
@@ -38,7 +42,7 @@
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v1"
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
@@ -60,11 +64,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AQZyKCc+BEkNL00Y3h3FjawGLF+INUj7cVb0My91hl8ffiW873T8FTV1k4TIZJx5RwcJlYxhgsxHVcnM4AJgSwJhbAfxJmP/3XGijLlLp3XUIRjQwFtf7UlZAFZ7Vrt1/WSf7kxxrFQ2SE2AQwLqPg9DL+hHEfd4xT/15n8p2q7qUlCKLsV6jF12Pd7koFNSWNL3ZIkRtd1ma99/321dnwAJHFGXqWg5nprJ7sYtqUqNQ8Er9SlvKbhnw3AipHzKpz0O3oNfUsr6NlZivRBhMhCZLo5WpXo1m9uIU8zLEWMNDJ+wGUctcGxE3eCptP2zLXUgxxjB+0EXOUtT/GWUc/Ip61CMiyUf7Paz026E2eYil2yWgfkTP5CUgDMNGZFuAA1T5PhB9FRW51CjAIvwOKVMCvfixJiVoUsXHnWH2ZnXqtbDR/uEZBE7OKoBlaPL4G3Lvgdqym5EjROAztUXb6wOmVDiGzzqgizyZnIcxFBSKJAownGj9Vh4/Y/Ag1xzGzNtjz3ngSRfMfIIq/q2Q51uiLiv7mBVliPvPWMUTfTjnqnK/OSBlR2ID+COJqnUKpQMedPyOT3IMznmM6gQCmyYO5KE0MkfhFh6+pdNi6oJM2iZsxK1Z1V+GRSOIwrJEoajjDJkh439XjXk8NExFvplrLjK/oL/dsHIZiG6U5GVWW92kGkuXkJCeUz1CET3paxbGqwrd53r5d6gFABbC12CtcP2JeH4YYCpHYyPQacf0prj9Hdq3wDztShC9tH+4UQS/GbaDHKcS1ANIyPuTxHmBFtPuCJ9Uagy5QBEc8eAz2nfsbfaUxYzco6u/zhNsFbqp6zgQIxs5OcqDQ=="
"scandit": "AZ7zLw2eLmFWHbYP4RDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YewGQYEESXjpRwGX1NYmKY3pXHnAn2DeqIzh2an+FUu9socQlbQnJiHJHoWBAqcqWSua+P12tc95P3s9aaEEYvSjUy7Md88f7N+sk6zZbUmqbMXeXqmZwdkmRoUY/2w0CiiiA4gBFHgu4sMeNQ9dWyfxKTUPf5AnsxnuYpCt5KLxJWSYDv8HHj0mx8DCJTe1m2ony97Lge3JbJ5Dd+Zz6SCwqik7fv53Qole9s/3m66lYFWKAzWRKkHN1zts78CmPxPb+AAHVoqlBM3duvYmnCxxGOmlXabKUNuDR2ExaMu/nlo532jqqy25Cet/FP1UAs96ZGRgzEcHxGPp6kA53lJ15zd+cxz6G93E83AmYJkhddXBQElWEaGtQRfrEzRGmvcksR+V8MMYjGmhkVbQxGGqpnfP4IxbuEFcef6bxxTiulzo75gXoqZTt+7C1qpDcrMM3Yp0Z8RBw3JlV2tLk4FYFZpxY8QrXIcjvRYKExtQ9e5sSbST4Vx95YhEUd6iX0SBPDzcmgR4/Ef6gvJfoWgz68+rqhBGckphdHi2Mf/pYuAlh2jbwtrkErE2xWARBejR/UcU/A3F7k9RkFd5/QZC7qhsE6bZH7uhpkptIbi5XkXagwYy1oJD7yJs4VLOJteYWferRm8h1auxXew5tL8VLHciF+lLj6h8PTUDt2blLgUjHtualqlCwdSTzJyYwk4oswGGDk6E48X7LXpzuhtR8TYTOi2REN0uuTbO/slFBRw+CaYUnD0LjB9p2lb8ndcdV9adzBKmwPxiOtlOELQ=="
}
}

View File

@@ -1,5 +1,8 @@
{
"title": "ISA - Production",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -8,7 +11,7 @@
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi"
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
@@ -38,10 +41,10 @@
"rootUrl": "https://filialinformationsystem.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa.paragon-systems.de/inv/v1"
"rootUrl": "https://isa.paragon-systems.de/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa.paragon-data.net/wws/v1"
"rootUrl": "https://isa.paragon-systems.de/wws/v1"
},
"hubs": {
"notifications": {
@@ -60,11 +63,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": ""
"scandit": "AZZzfQ+eLFl3Dzf1QSBag1lDibIoOPh4W33erRIRe3SDUMkHDX8eczEjd2TnfRMWoE5lXOBGtESCWICN9EbrmI1S9Lu5APsvvEOD+K54ADwIVawx0HNZRAc8/+9Vf/izcEGOFQFGBQJyR6vzdzFv5HcjznhxI9E3LiF+uVQPtCqsVYzpkMWIrC5VCg2uwNrj9Bw6f8zYi/lZPrDMS5yVKVcajeK7sh9QAq17dR0opjIIuP5t5nDEJ7hnITwtTR5HaM6cX/KhKpTILOgKexvLYqrK6QJWpU85sDwqwn6T7av4V68qL3XrUo60dScop4QsvraQe1HkRsffl6DkAEoX0RNMS5qVWjGerW7lvA/DQd9hsAO3jWFDR9hVDyt2VvmzzFKnHYqTYxC5qG4bCEJ0RJjy6tEP5Q7vL5SxWygVadmjPv+TwDOCS7DxzxIjcO+BXQY7gW6qn0hx9fXzyvO3avrGWqyImMlgEApZq+36ANqtRcPD/stEe4i0N9dSPhYoHPcc/9/9jpts43FozlgfY4wY8Wt5ybB3X0caISMmB/klFIJKKN7num439z3+Xk7ENB/Xvb0XAtnOt/cuxQYsGQ7fb62GOO/7Va5fdE9ZfaIJsS5ToE6oIbV04pLUssJf9cUMsyPFVELYSJmyGPQQFRz0TTxxRvPapIWrfa2x5x3hYUpNTAdY3v0fN9l/1ZqNSBmIBLH/LoXaVJQ2DydGD1/QFZ2Z/S7zTYKg5/cSEpUgiYtbwutNZSjRH29ucSizC524k+Zst95T8G7LJaWCT8SQAcKXqCnjpiEGWzD++h0jXjn6BWjUnIHi0te+27vF/z6UQL00sWco5hUIqF66EiU="
}
}

View File

@@ -1,5 +1,8 @@
{
"title": "ISA - Staging",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -8,7 +11,7 @@
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi"
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
@@ -38,10 +41,10 @@
"rootUrl": "https://filialinformationsystem-staging.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-staging.paragon-systems.de/inv/v1"
"rootUrl": "https://isa-staging.paragon-systems.de/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-staging.paragon-data.net/wws/v1"
"rootUrl": "https://isa-staging.paragon-systems.de/wws/v1"
},
"hubs": {
"notifications": {
@@ -60,11 +63,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": ""
"scandit": "AZZzfQ+eLFl3Dzf1QSBag1lDibIoOPh4W33erRIRe3SDUMkHDX8eczEjd2TnfRMWoE5lXOBGtESCWICN9EbrmI1S9Lu5APsvvEOD+K54ADwIVawx0HNZRAc8/+9Vf/izcEGOFQFGBQJyR6vzdzFv5HcjznhxI9E3LiF+uVQPtCqsVYzpkMWIrC5VCg2uwNrj9Bw6f8zYi/lZPrDMS5yVKVcajeK7sh9QAq17dR0opjIIuP5t5nDEJ7hnITwtTR5HaM6cX/KhKpTILOgKexvLYqrK6QJWpU85sDwqwn6T7av4V68qL3XrUo60dScop4QsvraQe1HkRsffl6DkAEoX0RNMS5qVWjGerW7lvA/DQd9hsAO3jWFDR9hVDyt2VvmzzFKnHYqTYxC5qG4bCEJ0RJjy6tEP5Q7vL5SxWygVadmjPv+TwDOCS7DxzxIjcO+BXQY7gW6qn0hx9fXzyvO3avrGWqyImMlgEApZq+36ANqtRcPD/stEe4i0N9dSPhYoHPcc/9/9jpts43FozlgfY4wY8Wt5ybB3X0caISMmB/klFIJKKN7num439z3+Xk7ENB/Xvb0XAtnOt/cuxQYsGQ7fb62GOO/7Va5fdE9ZfaIJsS5ToE6oIbV04pLUssJf9cUMsyPFVELYSJmyGPQQFRz0TTxxRvPapIWrfa2x5x3hYUpNTAdY3v0fN9l/1ZqNSBmIBLH/LoXaVJQ2DydGD1/QFZ2Z/S7zTYKg5/cSEpUgiYtbwutNZSjRH29ucSizC524k+Zst95T8G7LJaWCT8SQAcKXqCnjpiEGWzD++h0jXjn6BWjUnIHi0te+27vF/z6UQL00sWco5hUIqF66EiU="
}
}

View File

@@ -1,14 +1,18 @@
{
"title": "ISA - Test",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi"
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
@@ -38,7 +42,7 @@
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v1"
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
@@ -60,11 +64,12 @@
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000
"packageInspection": 5000,
"assortment": 6000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "AQZyKCc+BEkNL00Y3h3FjawGLF+INUj7cVb0My91hl8ffiW873T8FTV1k4TIZJx5RwcJlYxhgsxHVcnM4AJgSwJhbAfxJmP/3XGijLlLp3XUIRjQwFtf7UlZAFZ7Vrt1/WSf7kxxrFQ2SE2AQwLqPg9DL+hHEfd4xT/15n8p2q7qUlCKLsV6jF12Pd7koFNSWNL3ZIkRtd1ma99/321dnwAJHFGXqWg5nprJ7sYtqUqNQ8Er9SlvKbhnw3AipHzKpz0O3oNfUsr6NlZivRBhMhCZLo5WpXo1m9uIU8zLEWMNDJ+wGUctcGxE3eCptP2zLXUgxxjB+0EXOUtT/GWUc/Ip61CMiyUf7Paz026E2eYil2yWgfkTP5CUgDMNGZFuAA1T5PhB9FRW51CjAIvwOKVMCvfixJiVoUsXHnWH2ZnXqtbDR/uEZBE7OKoBlaPL4G3Lvgdqym5EjROAztUXb6wOmVDiGzzqgizyZnIcxFBSKJAownGj9Vh4/Y/Ag1xzGzNtjz3ngSRfMfIIq/q2Q51uiLiv7mBVliPvPWMUTfTjnqnK/OSBlR2ID+COJqnUKpQMedPyOT3IMznmM6gQCmyYO5KE0MkfhFh6+pdNi6oJM2iZsxK1Z1V+GRSOIwrJEoajjDJkh439XjXk8NExFvplrLjK/oL/dsHIZiG6U5GVWW92kGkuXkJCeUz1CET3paxbGqwrd53r5d6gFABbC12CtcP2JeH4YYCpHYyPQacf0prj9Hdq3wDztShC9tH+4UQS/GbaDHKcS1ANIyPuTxHmBFtPuCJ9Uagy5QBEc8eAz2nfsbfaUxYzco6u/zhNsFbqp6zgQIxs5OcqDQ=="
"scandit": "AZ7zLw2eLmFWHbYP4RDq8VAEgAxmNGYcPU8YpOc3DryEXj4zMzYQFrQuUm0YewGQYEESXjpRwGX1NYmKY3pXHnAn2DeqIzh2an+FUu9socQlbQnJiHJHoWBAqcqWSua+P12tc95P3s9aaEEYvSjUy7Md88f7N+sk6zZbUmqbMXeXqmZwdkmRoUY/2w0CiiiA4gBFHgu4sMeNQ9dWyfxKTUPf5AnsxnuYpCt5KLxJWSYDv8HHj0mx8DCJTe1m2ony97Lge3JbJ5Dd+Zz6SCwqik7fv53Qole9s/3m66lYFWKAzWRKkHN1zts78CmPxPb+AAHVoqlBM3duvYmnCxxGOmlXabKUNuDR2ExaMu/nlo532jqqy25Cet/FP1UAs96ZGRgzEcHxGPp6kA53lJ15zd+cxz6G93E83AmYJkhddXBQElWEaGtQRfrEzRGmvcksR+V8MMYjGmhkVbQxGGqpnfP4IxbuEFcef6bxxTiulzo75gXoqZTt+7C1qpDcrMM3Yp0Z8RBw3JlV2tLk4FYFZpxY8QrXIcjvRYKExtQ9e5sSbST4Vx95YhEUd6iX0SBPDzcmgR4/Ef6gvJfoWgz68+rqhBGckphdHi2Mf/pYuAlh2jbwtrkErE2xWARBejR/UcU/A3F7k9RkFd5/QZC7qhsE6bZH7uhpkptIbi5XkXagwYy1oJD7yJs4VLOJteYWferRm8h1auxXew5tL8VLHciF+lLj6h8PTUDt2blLgUjHtualqlCwdSTzJyYwk4oswGGDk6E48X7LXpzuhtR8TYTOi2REN0uuTbO/slFBRw+CaYUnD0LjB9p2lb8ndcdV9adzBKmwPxiOtlOELQ=="
}
}

View File

@@ -5,12 +5,36 @@ import * as moment from 'moment';
moment.locale('de');
import { AppModule } from './app/app.module';
import { DebugService } from './app/debug/debug.service';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
const debugService = new DebugService();
const consoleLog = console.log;
console.log = (...args) => {
debugService.add({ type: 'log', args });
consoleLog(...args);
};
const consoleWarn = console.warn;
console.warn = (...args) => {
debugService.add({ type: 'warn', args });
consoleWarn(...args);
};
const consoleError = console.error;
console.error = (...args) => {
debugService.add({ type: 'error', args });
consoleError(...args);
};
platformBrowserDynamic([{ provide: DebugService, useValue: debugService }])
.bootstrapModule(AppModule)
.catch((err) => console.error(err));

View File

@@ -16,7 +16,14 @@
/***************************************************************************************************
* BROWSER POLYFILLS
*/ // Run `npm install --save web-animations-js`.
*/
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents

View File

@@ -1,7 +1,29 @@
<html>
<body>
<script>
parent.postMessage(location.hash, location.origin);
var checks = [/[\?|&|#]code=/, /[\?|&|#]error=/, /[\?|&|#]token=/, /[\?|&|#]id_token=/];
function isResponse(str) {
if (!str) return false;
for (var i = 0; i < checks.length; i++) {
if (str.match(checks[i])) return true;
}
return false;
}
var message = isResponse(location.hash) ? location.hash : '#' + location.search;
if (window.parent && window.parent !== window) {
// if loaded as an iframe during silent refresh
window.parent.postMessage(message, location.origin);
} else if (window.opener && window.opener !== window) {
// if loaded as a popup during initial login
window.opener.postMessage(message, location.origin);
} else {
// last resort for a popup which has been through redirects and can't use window.opener
localStorage.setItem('auth_hash', message);
localStorage.removeItem('auth_hash');
}
</script>
</body>
</html>

View File

@@ -32,7 +32,7 @@ body {
}
.desktop .scroll-bar::-webkit-scrollbar-track {
@apply my-4;
// @apply my-4;
-webkit-box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.1);
border-radius: 10px;
background-color: white;

View File

@@ -7,6 +7,7 @@ import { combineLatest } from 'rxjs';
import { filter, map, shareReplay, switchMap, tap } from 'rxjs/operators';
import { geoDistance } from '@utils/common';
import { BehaviorSubject } from 'rxjs';
import { ApplicationService } from '@core/application';
@Component({
selector: 'modal-availabilities',
@@ -20,7 +21,11 @@ export class ModalAvailabilitiesComponent {
stockFetching$ = new BehaviorSubject(true);
item = this.modalRef.data.item;
itemId = this.modalRef.data.itemId || this.modalRef.data.item.id;
userbranch$ = this.domainAvailabilityService.getDefaultBranch();
userbranch$ = combineLatest([
this.applicationService.getSelectedBranch$(this.applicationService.activatedProcessId),
this.domainAvailabilityService.getDefaultBranch(),
]).pipe(map(([selectedBranch, defaultBranch]) => selectedBranch || defaultBranch));
branches$ = this.domainAvailabilityService.getBranches();
filteredBranches$ = combineLatest([this.branches$, this.userbranch$, this.search$]).pipe(
@@ -66,7 +71,8 @@ export class ModalAvailabilitiesComponent {
constructor(
private modalRef: UiModalRef<BranchDTO, { item: ItemDTO; itemId: number }>,
private domainAvailabilityService: DomainAvailabilityService
private domainAvailabilityService: DomainAvailabilityService,
private applicationService: ApplicationService
) {}
filter(query: string) {

View File

@@ -1,8 +1,11 @@
<div class="notification-headline">
<h1>{{ item.headline }}</h1>
<button class="notification-edit-cta" (click)="itemSelected.emit(item)">
Bearbeiten
</button>
<div class="grid grid-cols-[1fr_auto] items-center gap-4">
<div class="grid grid-flow-row gap-4">
<h1 class="text-left font-bold text-lg">{{ item.headline }}</h1>
<div class="notification-text">{{ item.text }}</div>
</div>
<div>
<button *ngIf="editButton" class="notification-edit-cta text-brand font-bold text-lg px-4 py-3" (click)="itemSelected.emit(item)">
{{ editButtonLabel }}
</button>
</div>
</div>
<div class="notification-text">{{ item.text }}</div>

View File

@@ -13,5 +13,11 @@ export class ModalNotificationsListItemComponent {
@Output()
itemSelected = new EventEmitter<MessageBoardItemDTO>();
@Input()
editButton = true;
@Input()
editButtonLabel = 'Bearbeiten';
constructor() {}
}

View File

@@ -0,0 +1,11 @@
<div class="notification-list scroll-bar">
<ng-container *ngFor="let notification of notifications">
<modal-notifications-list-item
(click)="itemSelected(notification)"
[editButtonLabel]="'Packstück-Prüfung'"
[item]="notification"
(itemSelected)="itemSelected($event)"
></modal-notifications-list-item>
<hr />
</ng-container>
</div>

View File

@@ -0,0 +1,101 @@
import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator';
import { CommonModule } from '@angular/common';
import { RouterTestingModule } from '@angular/router/testing';
import { UiIconModule } from '@ui/icon';
import { Router } from '@angular/router';
import { UiFilter } from '@ui/filter';
import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs';
import { Component } from '@angular/core';
import { ModalNotificationsListItemComponent } from '../notifications-list-item/notifications-list-item.component';
import { ModalNotificationsRemissionGroupComponent } from './notifications-remission-group.component';
// DummyComponent Class
@Component({
selector: 'dummy-component',
template: '<div></div>',
})
class DummyComponent {
constructor() {}
}
describe('ModalNotificationsRemissionGroupComponent', () => {
let spectator: Spectator<ModalNotificationsRemissionGroupComponent>;
let uiFilterMock: SpyObject<UiFilter>;
let router: Router;
const createComponent = createComponentFactory({
component: ModalNotificationsRemissionGroupComponent,
declarations: [ModalNotificationsListItemComponent],
imports: [
CommonModule,
RouterTestingModule.withRoutes([
{ path: 'filiale/goods/in/results', component: DummyComponent },
{ path: 'filiale/remission/create', component: DummyComponent },
]),
UiIconModule,
],
providers: [],
mocks: [UiFilter],
});
beforeEach(() => {
spectator = createComponent({ props: { notifications: [] } });
router = spectator.inject(Router);
uiFilterMock = spectator.inject(UiFilter);
});
it('should create', () => {
expect(spectator.component).toBeTruthy();
});
describe('notifications input', () => {
it('should display the right notification-counter value based on the length of the input array', () => {
spectator.setInput({ notifications: [{}, {}, {}] });
expect(spectator.query('.notification-counter')).toHaveText('3');
});
it('should not display notification-counter if input array has length 0', () => {
spectator.setInput({ notifications: [] });
expect(spectator.query('.notification-counter')).toHaveText('');
});
it('should render modal-notifications-list-item based on the input array', () => {
const notifications = [{}, {}];
spectator.setInput({ notifications });
spectator.detectComponentChanges();
expect(spectator.queryAll('modal-notifications-list-item')).toHaveLength(notifications.length);
});
});
describe('itemSelected()', () => {
it('should navigate to results with queryParams from UiFilter.getQueryParamsFromQueryTokenDTO()', () => {
const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } };
spyOn(UiFilter, 'getQueryParamsFromQueryTokenDTO').and.returnValue(item.queryToken.input);
spyOn(router, 'navigate');
spectator.component.itemSelected(item);
expect(router.navigate).toHaveBeenCalledWith(['/filiale/goods/in/results'], { queryParams: item.queryToken.input });
});
it('should emit the navigated event after select item', () => {
const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } };
spyOn(spectator.component.navigated, 'emit');
spectator.component.itemSelected(item);
expect(spectator.component.navigated.emit).toHaveBeenCalled();
});
});
describe('actions CTA', () => {
it('should navigate to remission page after clicking the CTA', () => {
const cta = spectator.query('.cta-primary');
expect(cta).toHaveText('Zur Remission');
expect(cta).toHaveAttribute('href', '/filiale/remission/create');
});
it('should emit the navigated event after clicking the CTA', () => {
const cta = spectator.query('.cta-primary') as HTMLAnchorElement;
spyOn(spectator.component.navigated, 'emit');
spectator.click(cta);
expect(spectator.component.navigated.emit).toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,23 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Router } from '@angular/router';
import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs';
@Component({
selector: 'modal-notifications-package-inspection-group',
templateUrl: 'notifications-package-inspection-group.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ModalNotificationsPackageInspectionGroupComponent {
@Input()
notifications: MessageBoardItemDTO[];
@Output()
navigated = new EventEmitter<void>();
constructor(private _router: Router) {}
itemSelected(item: MessageBoardItemDTO) {
this._router.navigate(['/filiale/package-inspection/packages']);
this.navigated.emit();
}
}

View File

@@ -1,13 +1,3 @@
<div class="header">
<div class="notification-icon">
<span class="notification-counter">{{ notifications.length }}</span>
<ui-icon icon="notification" size="26px"></ui-icon>
</div>
<h2>Remission</h2>
</div>
<hr />
<div class="notification-list scroll-bar">
<ng-container *ngFor="let notification of notifications">
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>

View File

@@ -1,13 +1,3 @@
<div class="header">
<div class="notification-icon">
<div class="notification-counter">{{ notifications.length }}</div>
<ui-icon icon="notification" size="26px"></ui-icon>
</div>
<h2>Reservierungsanfragen</h2>
</div>
<hr />
<div class="notification-list scroll-bar">
<ng-container *ngFor="let notification of notifications">
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>

View File

@@ -1,13 +1,3 @@
<div class="header">
<div class="notification-icon">
<span class="notification-counter">{{ notifications.length }}</span>
<ui-icon icon="notification" size="26px"></ui-icon>
</div>
<h2>Tätigkeitskalender</h2>
</div>
<hr />
<div class="notification-list scroll-bar">
<ng-container *ngFor="let notification of notifications">
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>

View File

@@ -1,13 +1,3 @@
<div class="header">
<div class="notification-icon">
<span class="notification-counter">{{ notifications.length }}</span>
<ui-icon icon="notification" size="26px"></ui-icon>
</div>
<h2>ISA-Update</h2>
</div>
<hr />
<div class="notification-list scroll-bar">
<ng-container *ngFor="let notification of notifications">
<div class="notification-headline">

View File

@@ -1,34 +1,39 @@
<h1>Sie haben neue Nachrichten</h1>
<ng-container *ngFor="let notification of groupedNotifications$ | async">
<button
*ngIf="notification.group !== (activeCard$ | async)"
type="button"
class="notification-card"
(click)="activeCard = notification.group"
>
{{ notification.group }}
<ng-container *ngFor="let notification of notifications$ | async | keyvalue">
<button type="button" class="notification-card" (click)="selectArea(notification.key)">
<div class="notification-icon">
<div class="notification-counter">{{ notification.value?.length }}</div>
<ui-icon icon="notification" size="26px"></ui-icon>
</div>
<span>{{ notification.value?.[0]?.category }}</span>
</button>
</ng-container>
<ng-container [ngSwitch]="activeCard$ | async">
<modal-notifications-update-group
*ngSwitchCase="'ISA-Update'"
[notifications]="activeNotifications$ | async"
></modal-notifications-update-group>
<modal-notifications-reservation-group
*ngSwitchCase="'Reservierungsanfragen'"
[notifications]="activeNotifications$ | async"
(navigated)="close()"
></modal-notifications-reservation-group>
<modal-notifications-remission-group
*ngSwitchCase="'Remission'"
[notifications]="activeNotifications$ | async"
(navigated)="close()"
></modal-notifications-remission-group>
<modal-notifications-task-calendar-group
*ngSwitchCase="'Tätigkeitskalender'"
[notifications]="activeNotifications$ | async"
(navigated)="close()"
></modal-notifications-task-calendar-group>
<hr class="-mx-4" />
<ng-container *ngIf="notification.key === selectedArea" [ngSwitch]="notification.value?.[0]?.category">
<modal-notifications-update-group
*ngSwitchCase="'ISA-Update'"
[notifications]="notifications[selectedArea]"
></modal-notifications-update-group>
<modal-notifications-reservation-group
*ngSwitchCase="'Reservierungsanfragen'"
[notifications]="notifications[selectedArea]"
(navigated)="close()"
></modal-notifications-reservation-group>
<modal-notifications-remission-group
*ngSwitchCase="'Remission'"
[notifications]="notifications[selectedArea]"
(navigated)="close()"
></modal-notifications-remission-group>
<modal-notifications-task-calendar-group
*ngSwitchCase="'Tätigkeitskalender'"
[notifications]="notifications[selectedArea]"
(navigated)="close()"
></modal-notifications-task-calendar-group>
<modal-notifications-package-inspection-group
*ngSwitchCase="'Wareneingang Lagerware'"
[notifications]="notifications[selectedArea]"
(navigated)="close()"
>
</modal-notifications-package-inspection-group>
</ng-container>
</ng-container>

View File

@@ -2,46 +2,44 @@ modal-notifications {
@apply flex flex-col relative h-full;
h1 {
@apply text-xl font-bold text-center mb-10;
@apply text-xl font-bold text-center;
}
// .notification-card {
// @apply text-center text-xl text-inactive-branch block bg-white rounded-t-card font-bold no-underline py-4 border-none outline-none shadow-card -ml-4;
// width: calc(100% + 2rem);
// }
.notification-card {
@apply text-center text-xl text-inactive-branch block bg-white rounded-t-card font-bold no-underline py-4 border-none outline-none shadow-card -ml-4;
width: calc(100% + 2rem);
@apply grid grid-flow-col items-center justify-center gap-4;
@apply text-inactive-branch bg-white;
@apply font-bold text-xl -mx-4 py-4;
.notification-icon {
@apply relative;
.notification-counter {
@apply absolute font-normal text-base -top-2 -right-1 bg-brand text-white rounded-full w-5 h-5 flex items-center justify-center;
z-index: 10;
}
ui-icon {
@apply text-inactive-branch;
}
}
h2 {
@apply font-bold text-2xl ml-4;
}
}
modal-notifications-remission-group,
modal-notifications-reservation-group,
modal-notifications-task-calendar-group,
modal-notifications-update-group {
modal-notifications-update-group,
modal-notifications-package-inspection-group {
@apply flex flex-col relative pb-2;
.header {
@apply flex flex-row justify-center items-center mt-5;
.notification-icon {
@apply relative;
.notification-counter {
@apply absolute -top-2 -right-1 bg-brand text-white rounded-full w-5 h-5 flex items-center justify-center;
z-index: 10;
}
ui-icon {
@apply text-inactive-branch;
}
}
h2 {
@apply font-bold text-2xl ml-4;
}
}
hr {
@apply bg-disabled-branch h-px-2 -ml-4 my-4;
width: calc(100% + 2rem);
}
.notification-list {
@apply overflow-y-scroll -ml-4;
max-height: calc(100vh - 450px);
@@ -60,7 +58,7 @@ modal-notifications {
modal-notifications-list-item,
modal-notifications-update-group {
@apply flex flex-col relative py-1 px-4;
@apply flex flex-col relative p-4;
.notification-headline {
@apply flex flex-row justify-between items-start;

View File

@@ -1,14 +1,11 @@
import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ComponentStore } from '@ngrx/component-store';
import { Group, groupBy } from '@ui/common';
import { UiModalRef } from '@ui/modal';
import { EnvelopeDTO, MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs';
interface ModalNotificationComponentState {
activeCard: string;
groupedNotifications: Group<string, MessageBoardItemDTO>[];
selectedArea: string;
notifications: Record<string, MessageBoardItemDTO[]>;
}
@Component({
@@ -18,44 +15,61 @@ interface ModalNotificationComponentState {
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
})
export class ModalNotificationsComponent extends ComponentStore<ModalNotificationComponentState> implements OnInit {
set activeCard(activeCard: string) {
if (this.activeCard !== activeCard) {
this.patchState({ activeCard });
export class ModalNotificationsComponent extends ComponentStore<ModalNotificationComponentState> {
private _selectedAreaSelector = (state: ModalNotificationComponentState) => {
if (state.selectedArea) {
return state.selectedArea;
}
const keys = Object.keys(state.notifications);
for (const key of keys) {
if (state.notifications[key]?.length > 0) {
return key;
}
}
return undefined;
};
get selectedArea() {
return this.get(this._selectedAreaSelector);
}
activeCard$ = this.select((s) => s.activeCard);
selectedArea$ = this.select(this._selectedAreaSelector);
get activeCard() {
return this.get((s) => s.activeCard);
private _categorySelector = (state: ModalNotificationComponentState) => {
const selectedArea = this._selectedAreaSelector(state);
console.log('_categorySelector', state.notifications[selectedArea]?.[0]?.category);
return state.notifications[selectedArea]?.[0]?.category;
};
get category() {
return this.get(this._categorySelector);
}
get groupedNotifications() {
return this.get((s) => s.groupedNotifications);
}
groupedNotifications$ = this.select((s) => s.groupedNotifications);
category$ = this.select(this._categorySelector);
set groupedNotifications(groupedNotifications: Group<string, MessageBoardItemDTO>[]) {
this.patchState({ groupedNotifications });
get notifications() {
return this.get((s) => s.notifications);
}
activeNotifications$ = combineLatest([this.activeCard$, this.groupedNotifications$]).pipe(
map(([activeCard, notifications]) => notifications.find((n) => n.group === activeCard)?.items)
);
notifications$ = this.select((s) => s.notifications);
constructor(private _modalRef: UiModalRef<any, EnvelopeDTO<MessageBoardItemDTO[]>>) {
constructor(private _modalRef: UiModalRef<any, Record<string, MessageBoardItemDTO[]>>) {
super({
activeCard: undefined,
groupedNotifications: groupBy(_modalRef.data.data, (item: MessageBoardItemDTO) => item.category),
selectedArea: undefined,
notifications: _modalRef.data,
});
}
ngOnInit() {
this.patchState({ activeCard: this.groupedNotifications?.find((_) => true)?.group });
}
close() {
this._modalRef.close();
}
selectArea(area: string) {
this.patchState({
selectedArea: area,
});
}
}

View File

@@ -9,6 +9,7 @@ import { ModalNotificationsReservationGroupComponent } from './notifications-res
import { ModalNotificationsTaskCalendarGroupComponent } from './notifications-task-calendar-group/notifications-task-calendar-group.component';
import { ModalNotificationsUpdateGroupComponent } from './notifications-update-group/notifications-update-group.component';
import { ModalNotificationsComponent } from './notifications.component';
import { ModalNotificationsPackageInspectionGroupComponent } from './notifications-package-inspection-group/notifications-package-inspection-group.component';
@NgModule({
imports: [CommonModule, UiCommonModule, UiIconModule, RouterModule],
@@ -19,6 +20,7 @@ import { ModalNotificationsComponent } from './notifications.component';
ModalNotificationsTaskCalendarGroupComponent,
ModalNotificationsUpdateGroupComponent,
ModalNotificationsListItemComponent,
ModalNotificationsPackageInspectionGroupComponent,
],
exports: [
ModalNotificationsComponent,
@@ -27,6 +29,7 @@ import { ModalNotificationsComponent } from './notifications.component';
ModalNotificationsTaskCalendarGroupComponent,
ModalNotificationsUpdateGroupComponent,
ModalNotificationsListItemComponent,
ModalNotificationsPackageInspectionGroupComponent,
],
})
export class ModalNotificationsModule {}

View File

@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ApplicationService } from '@core/application';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainOmsService } from '@domain/oms';
import { ComponentStore } from '@ngrx/component-store';
@@ -68,17 +69,15 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
)
);
readonly currentBranch$ = this.domainAvailabilityService.getDefaultBranch();
readonly storeAvailabilities$ = combineLatest([this.orderItem$, this.currentBranch$]).pipe(
switchMap(([item, branch]) =>
readonly storeAvailabilities$ = this.orderItem$.pipe(
switchMap((item) =>
this.domainAvailabilityService
.getPickUpAvailabilities([
{
qty: item.quantity,
ean: item.product.ean,
itemId: item.product?.catalogProductNumber,
shopId: branch.id,
shopId: item?.targetBranchId,
price: item.retailPrice,
},
])
@@ -99,6 +98,7 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
eans: [item.product.ean],
quantity: item.quantity,
price: item.retailPrice,
branchId: item.targetBranchId,
})
.pipe(
catchError(() => {
@@ -141,7 +141,8 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
constructor(
public modalRef: UiModalRef<ReorderResult, { item: OrderItemListItemDTO; showReasons: boolean }>,
private domainAvailabilityService: DomainAvailabilityService,
private _omsService: DomainOmsService
private _omsService: DomainOmsService,
private _applicationService: ApplicationService
) {
super({
orderItem: modalRef.data?.item,

View File

@@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { Observable, fromEvent, Subject } from 'rxjs';
import { Observable, fromEvent, Subject, ReplaySubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { WindowRef } from './window-ref.service';
import { ScanRequestType } from './scan-request.type';
import { EnvironmentService } from '@core/environment';
import { Platform } from '@angular/cdk/platform';
@Injectable({
providedIn: 'root',
})
@@ -11,11 +11,15 @@ export class NativeContainerService {
private wm: Observable<any>;
public windowMessages = new Subject<any>();
private webViewDetected = false;
private webViewEventRecieved = false;
private browserDetected = false;
constructor(private windowRef: WindowRef, private _environmentService: EnvironmentService) {
private _init$ = new ReplaySubject<boolean>(1);
get isNative() {
return this.webViewEventRecieved;
}
constructor(private windowRef: WindowRef, private _platform: Platform) {
this.defineWindowCallback();
this.wm = fromEvent(this.windowRef.nativeWindow, 'message').pipe(
@@ -27,11 +31,16 @@ export class NativeContainerService {
this.wm.subscribe((data) => {
if (data.status === 'INIT') {
this.webViewEventRecieved = true;
this._init$.next(true);
}
this.windowMessages.next(data);
});
}
init() {
return this._init$.asObservable();
}
public openScanner(scanRequestType: ScanRequestType) {
const scanRequest = {
[scanRequestType]: true,
@@ -46,7 +55,6 @@ export class NativeContainerService {
this.windowRef.nativeWindow.postMessage({ status: 'IN_PROGRESS', data: 'Scan Started' }, '*');
try {
// if (this.isUiWebview() && this.isUiWebview().isNative) {
(this.windowRef.nativeWindow as any).webkit.messageHandlers.scanRequest.postMessage(message);
} catch (error) {
this.windowRef.nativeWindow.postMessage({ status: 'ERROR', data: 'Not a WebView' }, '*');
@@ -54,30 +62,6 @@ export class NativeContainerService {
}
}
public isUiWebview() {
// const navigator = this.windowRef.nativeWindow.navigator as Navigator;
// alert(this.deviceDetector.browser);
// const standalone = (navigator as any).standalone,
// userAgent = navigator.userAgent.toLowerCase(),
// safari = /safari/.test(userAgent),
// ios = /iphone|ipod|ipad/.test(userAgent),
// chrome = /chrome/.test(userAgent) && /Google Inc/.test(navigator.vendor),
// crios = /crios/.test(userAgent);
// this.webViewDetected = ios && !standalone && !safari;
// this.browserDetected = !standalone && (safari || chrome) && !crios;
return {
isSafari: this._environmentService.isSafari(),
isNative: this.webViewEventRecieved,
};
}
public isIpadMini6() {
const width = window.innerWidth > 0 ? window.innerWidth : screen.width;
return width === 744;
}
private defineWindowCallback() {
if (this.windowRef.nativeWindow['scanResults'] === undefined) {
this.windowRef.nativeWindow['scanResults'] = (result) => window.postMessage(result, '*');
@@ -91,6 +75,7 @@ export class NativeContainerService {
try {
(this.windowRef.nativeWindow as any).webkit.messageHandlers.scanRequest.postMessage('PING');
} catch (error) {
this._init$.next(false);
this.windowRef.nativeWindow.postMessage({ status: 'ERROR', data: 'Not a WebView' }, '*');
this.windowRef.nativeWindow.postMessage('PING', '*');
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../../dist/page/yellow-pages",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,3 @@
:host {
@apply block;
}

View File

@@ -0,0 +1,3 @@
<shared-breadcrumb class="my-4" [key]="breadcrumbKey"></shared-breadcrumb>
<router-outlet></router-outlet>

View File

@@ -0,0 +1,67 @@
import { RouterTestingModule } from '@angular/router/testing';
import { BreadcrumbService } from '@core/breadcrumb';
import { Config } from '@core/config';
import { Spectator, createComponentFactory } from '@ngneat/spectator';
import { BreadcrumbComponent } from '@shared/components/breadcrumb';
import { MockComponent } from 'ng-mocks';
import { AssortmentComponent } from './assortment.component';
describe('AssortmentComponent', () => {
let spectator: Spectator<AssortmentComponent>;
const createComponent = createComponentFactory({
component: AssortmentComponent,
imports: [RouterTestingModule],
mocks: [Config, BreadcrumbService],
declarations: [MockComponent(BreadcrumbComponent)],
});
let configMock: jasmine.SpyObj<Config>;
let breadcrumbServiceMock: jasmine.SpyObj<BreadcrumbService>;
beforeEach(async () => {
spectator = createComponent();
await spectator.fixture.whenStable();
configMock = spectator.inject(Config);
breadcrumbServiceMock = spectator.inject(BreadcrumbService);
});
it('should create', () => {
expect(spectator.component).toBeTruthy();
});
describe('get breadcrumbKey(): string', () => {
it('should call Config.get("process.ids.assortment") and return the result', () => {
const expected = 'expected';
configMock.get.and.returnValue(expected);
const actual = spectator.component.breadcrumbKey;
expect(configMock.get).toHaveBeenCalledWith('process.ids.assortment');
expect(actual).toBe(expected);
});
});
describe('ngOnInit()', () => {
it('should call createBreadcrumbIfNotExists()', () => {
const spy = spyOn(spectator.component, 'createBreadcrumbIfNotExists');
spectator.component.ngOnInit();
expect(spy).toHaveBeenCalled();
});
});
describe('template', () => {
it('should render the ShellBreadcrumbComponent and pass the breadcrumbKey as @Input() key', () => {
const expected = 'expected';
configMock.get.and.returnValue(expected);
spectator.detectComponentChanges();
const shellBreadcrumb = spectator.query(BreadcrumbComponent);
expect(shellBreadcrumb).toBeTruthy();
expect(shellBreadcrumb.key).toBe(expected);
});
});
});

View File

@@ -0,0 +1,33 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { Config } from '@core/config';
@Component({
selector: 'page-assortment',
templateUrl: 'assortment.component.html',
styleUrls: ['assortment.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AssortmentComponent implements OnInit {
get breadcrumbKey(): string {
return this._config.get('process.ids.assortment');
}
constructor(private _config: Config, private _breadcrumb: BreadcrumbService) {}
ngOnInit() {
this.createBreadcrumbIfNotExists();
}
async createBreadcrumbIfNotExists(): Promise<void> {
const crumb: Breadcrumb = {
key: this.breadcrumbKey,
name: 'Sortiment',
path: '/filiale/assortment',
section: 'branch',
tags: ['main'],
};
await this._breadcrumb.addBreadcrumbIfNotExists(crumb);
}
}

View File

@@ -0,0 +1,16 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { BreadcrumbModule } from '@shared/components/breadcrumb';
import { routes } from './routes';
import { AssortmentComponent } from './assortment.component';
import { PriceUpdateModule } from './price-update/price-update.module';
@NgModule({
imports: [CommonModule, RouterModule.forChild(routes), BreadcrumbModule, PriceUpdateModule],
exports: [AssortmentComponent],
declarations: [AssortmentComponent],
providers: [],
})
export class AssortmentModule {}

View File

@@ -0,0 +1,7 @@
// start:ng42.barrel
export * from './price-update.component.state';
export * from './price-update.component.store';
export * from './price-update.component';
export * from './price-update.module';
export * from './price-update-list';
// end:ng42.barrel

View File

@@ -0,0 +1,6 @@
// start:ng42.barrel
export * from './price-update-item-loader.component';
export * from './price-update-item.component';
export * from './price-update-list.component';
export * from './price-update-list.module';
// end:ng42.barrel

View File

@@ -0,0 +1,9 @@
:host {
@apply flex flex-row items-center bg-white rounded px-4;
height: 53px;
}
.skeleton {
@apply block bg-gray-300 h-6;
animation: load 1s ease-in-out infinite;
}

Some files were not shown because too many files have changed in this diff Show More