320 Commits

Author SHA1 Message Date
b993700ae4 Merge pull request 'feature/employee_login_defaults' (#124) from feature/employee_login_defaults into master
Reviewed-on: #124
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 19:00:36 +02:00
5d506a1526 Merge pull request 'Fix display order in various locations. closes #120' (#121) from feateure/display_order_intern into master
Reviewed-on: #121
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:59:14 +02:00
e87d0d8cdd Merge pull request 'feature/optimize_create_order_whbookings' (#117) from feature/optimize_create_order_whbookings into master
Reviewed-on: #117
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:57:45 +02:00
fb222c9213 Merge branch 'master' into feature/employee_login_defaults 2020-06-24 18:57:05 +02:00
cf9ab66e68 Hide /intern/ link if non employee. closes #76 2020-06-24 18:40:57 +02:00
cdc372d574 Redirect to intern if employee. 2020-06-24 18:40:22 +02:00
2290f01a8f Merge pull request 'feature/fix_delivery_api' (#116) from feature/fix_delivery_api into master
Reviewed-on: #116
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:38:03 +02:00
3bc0996c73 Merge pull request 'Add & generate tracking information to (Supplier-)OrderConfirmation. Fixes #110' (#114) from feature/supplier_tracking into master
Reviewed-on: #114
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:37:15 +02:00
758c6810ab Merge pull request 'Clean Shopping Cart on Logout' (#123) from feature/clean_shopping_on_logout into master
Reviewed-on: #123
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 18:27:24 +02:00
577a4f72f0 Clean Shopping Cart on Logout for real 2020-06-24 18:25:42 +02:00
a2aca7a8ca Clean Shopping Cart on Logout 2020-06-24 18:23:01 +02:00
3b7317a831 Fix typo 7 2020-06-24 18:19:14 +02:00
32921a3f87 Merge pull request 'Remove WarehouseBookingReason it was not used.' (#115) from feature/remove_unused_warehouse_booking_reason into master
Reviewed-on: #115
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 15:03:47 +02:00
63e9bc2336 Merge pull request 'Fix in stock calculation. closes #118' (#119) from feature/display_article_shop_in_stock into master
Reviewed-on: #119
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 15:03:04 +02:00
31bff55a20 Merge pull request 'Show advertisements distinct.' (#122) from feature/show_ads_distinct into master
Reviewed-on: #122
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-24 14:59:50 +02:00
5861b61565 Merge branch 'master' into feature/supplier_tracking 2020-06-24 10:44:38 +02:00
f0c9823e45 Clearify Inventory view with text. 2020-06-24 09:53:19 +02:00
6fbb16fea2 Show advertisements distinct. 2020-06-24 02:20:24 +02:00
0d547c8903 Fix display order in various locations. closes #120 2020-06-24 02:15:31 +02:00
5162e3fc19 Fix in stock calculation. closes #118 2020-06-24 02:05:25 +02:00
8bfe8b37f5 Maybe Fix warehouse booking repo 2020-06-24 02:00:37 +02:00
2e07d03908 Fix warehouse bookings on CreateOrderAction 2020-06-24 01:48:33 +02:00
b9e87f5bd4 Add additional infos for order history in /user/... fixes #109 2020-06-24 01:19:37 +02:00
cd641d6cc4 Fix #113 by fixing initialization of RestServiceForDelivery. 2020-06-24 01:18:16 +02:00
4e9f7800be Remove WarehouseBookingReason it was not used. 2020-06-24 00:42:55 +02:00
8d27c486d5 Add & generate tracking information to (Supplier-)OrderConfirmation. Fixes #110 2020-06-24 00:37:58 +02:00
714ac4f1de Fix issues with class loading in Cronjobs 2020-06-24 00:35:18 +02:00
186db31b17 Merge pull request 'Fix showing CustomerOrder all CustomerOrders in my orders. fix displaying orders in random order' (#108) from feature/fix_show_orders into master
reviewed by Lukas
2020-06-21 22:22:53 +02:00
fb53c68be3 Merge pull request 'implement Config for Supplier and Delivery Service' (#107) from feature/reading_config into master
reviewed by Lukas
2020-06-21 22:22:43 +02:00
19cf6ccfc5 Merge pull request 'Implement Supplier AutoSupplierPayment. closes #100' (#105) from feature/supplier_auto_payment into master 2020-06-21 22:13:41 +02:00
7fafd14715 Fix variable name 2020-06-21 19:01:25 +02:00
d5181f149a Fix showing CustomerOrder all CustomerOrders in my orders. fix displaying orders in random order 2020-06-21 18:58:15 +02:00
a950ecb2b9 Make delivery/gradlew executable 2020-06-21 18:42:33 +02:00
f42a50d24d fix CustomerOrder find by order 2020-06-21 18:32:04 +02:00
33118ddb2f Mark lost delivery as delivered. #spaghetticode 2020-06-21 18:25:33 +02:00
a6a2adc7db implement Config for Supplier and Delivery Service 2020-06-21 16:49:35 +02:00
62df2346c3 fix template crashing when defaultAddr == null 2020-06-21 16:28:00 +02:00
c1a0ecdcf3 fix delivery server crashing when tracking id not found 2020-06-21 16:13:48 +02:00
a1abbf5329 Implement Supplier AutoSupplierPayment. closes #100 2020-06-21 15:50:27 +02:00
deaf283a98 remove db blob 2020-06-21 01:08:58 +02:00
ffe9af0633 Prevent duplicate Articles from ArticleOffer 2020-06-21 01:07:04 +02:00
2989b54593 Merge pull request 'fix_change_user_name' (#104) from fix_change_user_name into master 2020-06-21 00:15:57 +02:00
b846b919b7 code clean up 2020-06-21 00:14:00 +02:00
1f83a82f17 removed user.name and use user.defaultDeliveryAddress.name instead 2020-06-21 00:13:10 +02:00
cd1a8568e6 show only one entry per article suggestions fix #96 2020-06-21 00:06:15 +02:00
53cdb20091 Merge pull request 'Fix the warehouse diagrams on the dashboard' (#103) from fix/dashboard_warehouse_statistics into master 2020-06-21 00:02:50 +02:00
9389675b5c Do not show dupliactes in category search. fixes #85 2020-06-21 00:01:28 +02:00
97a7f3a8ad Fix the warehouse diagrams on the dashboard
- Show the correct utilization
- Display values as correct percentages

Closes #99
2020-06-20 23:54:33 +02:00
aa2f42a10a Add password restrictions fixes #91 2020-06-20 23:47:53 +02:00
f128a8b9ed Make reason for manuel booking required. fixes #90 2020-06-20 23:35:56 +02:00
ffea25c082 Merge pull request 'Fix bad query for article slot sum' (#102) from feature/89_fix_lagerstand into master 2020-06-20 23:30:12 +02:00
d77b8c046a Fix bad query for article slot sum 2020-06-20 23:24:06 +02:00
8fdfe9cc69 Catch failing delivery server 2020-06-20 22:51:18 +02:00
e5332a3348 Fix String -> UUIDs -> String in method causing crash 2020-06-20 22:25:52 +02:00
c3eaf5ac11 Remove unused db fields 2020-06-20 22:10:53 +02:00
fa75beb9c4 Fix 6284a4d7bf altering template 2020-06-20 22:09:25 +02:00
4bef17dc0a Merge pull request 'feature/dashboard' (#83) from feature/dashboard into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-19 14:12:44 +02:00
79e4986880 delete blank line 2020-06-19 14:11:32 +02:00
181a09e16b Merge pull request 'feature/config' (#78) from feature/config into master
Reviewed-by: CodeSteak <codesteak@shellf.art>
2020-06-18 14:09:46 +02:00
f4aae9c581 ignore config.yml 2020-06-18 14:07:59 +02:00
ea0c8c45d6 rename eCommerce_config.yml to config.yml
* if no config file is present, create a default config file
2020-06-18 14:04:54 +02:00
8416783e0a merged 2020-06-18 01:13:03 +02:00
bcbebe8e8d master merged into feature/customer_orders 2020-06-18 01:11:11 +02:00
a570696795 feature/customer_orders merged into master 2020-06-18 01:07:11 +02:00
187656814e String Error -> Enum ErrorHandling Logic removed from model class 2020-06-18 00:50:43 +02:00
8c0652b26b exit if the settings file has not been found
* use a final variable for the settings file name
2020-06-17 22:32:48 +02:00
ef07447abc fix json-file 2020-06-17 20:42:08 +02:00
bb7b35dc28 Increase the amount of commits 2020-06-17 19:00:49 +02:00
93cfbb4a27 Merge remote-tracking branch 'origin/master' 2020-06-17 18:59:10 +02:00
68cc35cec3 Fix typo 6 2020-06-17 18:57:42 +02:00
b2ee048f25 Fix typo 5 2020-06-17 18:57:23 +02:00
020a30474e Fix typo 4 2020-06-17 18:56:32 +02:00
4bfc8a2cc6 Fix typo 3 2020-06-17 18:56:02 +02:00
0ff873baaa Fix typo 2 2020-06-17 18:55:50 +02:00
1812c89994 Fix typo 2020-06-17 18:55:29 +02:00
2442a477c8 add missing "|" 2020-06-17 18:43:06 +02:00
ebb3c8c235 removes unused Imports and fomattet 2020-06-17 17:23:29 +02:00
e6c068e71a Secretly fix number 0 showing on 'Warenkorb' 2020-06-17 00:23:05 +02:00
86e3ced19b Secretly fix searchbar not showing term when searching 2020-06-17 00:17:50 +02:00
44df9e4b4f Secretly add Favicon 2020-06-17 00:11:55 +02:00
18cbfbf148 Fix template strings with a starting "/" 2020-06-17 00:02:46 +02:00
e037b75f7c Merge pull request 'fix creditcard num not being set leading to crash' (#82) from feature/80_fix_creditcardnum_null_usersettings into master 2020-06-16 23:51:25 +02:00
486f237135 fix creditcard num not being set leading to crash 2020-06-16 23:50:10 +02:00
396d3fda14 Merge pull request 'Fix not creating directory when uploading file' (#80) from feature/fix_uploading_images into master 2020-06-16 23:40:12 +02:00
3b1d378968 Fix not creating directory when uploading file 2020-06-16 23:38:35 +02:00
5a0b303601 Merge branch 'master' into feature/config 2020-06-16 20:44:10 +02:00
d5825ba7a0 use @Autowired to get AppSettings Object 2020-06-16 20:42:40 +02:00
8199bd8a23 Merge pull request 'Allow employees to trigger cronjobs manually' (#74) from feature/manual_cronjobs into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-16 19:29:38 +02:00
774e62f892 add suppliers to YAMLData, store data in AppSettings() 2020-06-16 19:19:58 +02:00
e1c00eca8f fix ApplicationSettings.readConfig() 2020-06-16 18:52:33 +02:00
e904eba7a4 Merge remote-tracking branch 'origin/feature/manual_cronjobs' into feature/dashboard
Butfixes
2020-06-15 21:41:54 +02:00
6284a4d7bf Dashboard working 2020-06-15 17:47:55 +02:00
3b364ae815 Allow employees to trigger cronjobs manually 2020-06-15 16:23:42 +02:00
2ba4968556 Merge pull request 'feature/create_admin' (#73) from feature/create_admin into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-15 11:18:59 +02:00
18f35c43e7 remove spaces 2020-06-15 10:46:04 +02:00
709c80fa37 add fist admin user after start 2020-06-15 10:43:16 +02:00
87bade71b4 Dateien hochladen nach „supplier/config“ 2020-06-15 10:20:40 +02:00
b6eb66e348 Dateien hochladen nach „supplier/config“ 2020-06-15 10:19:16 +02:00
0a7722d612 removed register user type and ad 2020-06-15 09:40:32 +02:00
d121532fba Merge remote-tracking branch 'origin/master' into feature/config 2020-06-14 21:15:37 +02:00
985169117d fix nullpointer in settings 2020-06-14 18:19:59 +02:00
fa610cae3b if no orders show hint 2020-06-14 17:56:09 +02:00
3bb3c5b513 [HOTFIX] of HOTFIX: Use CanonicalPath for check 2020-06-14 16:51:59 +02:00
15616e05f3 [HOTFIX] Prevent path directory traversal attack for deploy 2020-06-14 15:54:56 +02:00
0ab185f143 impl trackingID for the UserController 2020-06-13 21:59:18 +02:00
1dd4b5cfa5 del update query and impl error for trackigID 2020-06-13 20:05:22 +02:00
266bba095a merged feature/customer_orders with master 2020-06-13 19:58:30 +02:00
fa7bdf2f7e impl error handling 2020-06-13 19:37:34 +02:00
76550be9e7 Merge pull request 'Allow partial discount on supplier orders' (#68) from fix/discount_calculation_in_supplier_service into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-13 12:21:26 +02:00
ef8aa826a3 Merge pull request 'Fix the cash booking sum on customer order' (#67) from fix/wrong_bookings_on_customer_order into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-13 12:19:18 +02:00
114453b5b9 Merge pull request 'Implement Arrival of Supplier Order.' (#62) from feature/supplier_order_arrived_button into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-13 11:59:17 +02:00
7397698929 fix check for missing article 2020-06-13 11:54:25 +02:00
9fa2c1f3a7 Allow partial discount on supplier orders
Closes #46
2020-06-13 11:46:16 +02:00
327ba182f5 Fix the cash booking sum on customer order
Closes #57
2020-06-13 11:10:20 +02:00
30226c5c19 Merge pull request 'fixes_ui' (#66) from fixes_ui into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-13 11:03:20 +02:00
e0e853a575 fixes from PR review 2020-06-13 10:41:59 +02:00
d2b98f083b Merge pull request 'fix missing cells in TODO-Warehouse. fixes #63' (#64) from feature/63_fix_todo_ui into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-13 10:26:36 +02:00
a41889b2cb login in extern loginController, RequestController cleanup, login right after register 2020-06-12 23:48:28 +02:00
0f7d248d40 add radio BTNs to register 2020-06-12 23:31:57 +02:00
11ade7b8f9 fix missing cells in TODO-Warehouse. fixes #63 2020-06-12 19:57:50 +02:00
a664b42853 Allow reuse of empty warehouse slots. Also fixes #59 2020-06-12 19:48:21 +02:00
56f4ec0dda Implement Arrival of Supplier Order. 2020-06-12 19:22:36 +02:00
1590821f64 delete bonuspoints and notifications html 2020-06-12 11:15:05 +02:00
16803d2b12 Merge pull request 'feature/customers' (#58) from feature/customers into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-12 00:52:53 +02:00
938d16301d fix stuff 2020-06-10 20:10:12 +02:00
5a59ac11b0 code cleanup 2020-06-10 19:28:12 +02:00
c194f44308 implement customers detail bookings 2020-06-10 19:28:12 +02:00
fc68e231d3 Allow the user to create manual bookings 2020-06-10 19:28:07 +02:00
6d8a36b7ac implement customers detail part 2 2020-06-10 19:27:38 +02:00
49e26039a3 implement customers detail part 1 2020-06-10 19:27:38 +02:00
28834431ce Display financial bookings in the ui 2020-06-10 19:27:38 +02:00
5691f0eeec implement customers index 2020-06-10 19:27:16 +02:00
7f643987f3 fix wrong closing tags in customerOrder/id.html 2020-06-10 19:12:34 +02:00
6b8bf25e0e Merge pull request 'feature/cash_bookings' (#55) from feature/cash_bookings into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-10 19:04:45 +02:00
ebeba72278 impl tracking ID 2020-06-10 17:03:59 +02:00
f671baf7f0 Error with package path 2020-06-10 14:51:12 +02:00
691916894f Apply some changes according to comments on the pullrequest 2020-06-10 11:48:38 +02:00
e7e2017825 Merge pull request 'Show a hyphen in supplier column if an article is not offered by anyone' (#56) from fix/null_pointer_deref_in_SupplierOfferController into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-09 14:06:51 +02:00
03d557d67b Show a hyphen in supplier column if an article is not offered by anyone
Closes #54
2020-06-08 18:56:35 +02:00
8fff0e2dca Merge remote-tracking branch 'origin/master' into feature/cash_bookings 2020-06-08 18:15:34 +02:00
6317780dfa Merge pull request 'Implement Warehouse....' (#47) from feature/intern_warehouse into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-08 13:40:10 +02:00
78a7ad053a Merge branch 'master' into feature/intern_warehouse 2020-06-08 09:22:06 +02:00
f6554629d7 Merge branch 'master' into feature/intern_warehouse 2020-06-08 09:14:50 +02:00
eb82d05f4f Use Spring model for template attributes 2020-06-08 09:14:11 +02:00
41320473d9 Merge pull request 'feature/supplier_page' (#52) from feature/supplier_page into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-07 15:00:56 +02:00
bd6be54149 removed space in SupplierOrderRepo 2020-06-07 11:45:53 +02:00
99612165f5 fixed review requests 2020-06-07 11:44:50 +02:00
9dff6adb3d rebase to master 2020-06-05 21:25:38 +02:00
445b504188 code cleanup 2020-06-05 21:20:54 +02:00
bd8b3db7f3 added post form for marking order as delivered 2020-06-05 21:18:04 +02:00
8a4ff4cfa5 added supplier bookings table 2020-06-05 21:18:04 +02:00
2245309198 added supplier balance label 2020-06-05 21:18:04 +02:00
d078c44027 finished supplier orders 2020-06-05 21:18:04 +02:00
bd8b07990b finished show orders based on supplier 2020-06-05 21:18:04 +02:00
2f4c45ed71 show orders based on supplier 2020-06-05 21:18:04 +02:00
b600040bf3 basic detail page for supplier 2020-06-05 21:18:04 +02:00
4f5e1fe185 list suppliers page 2020-06-05 21:18:04 +02:00
f348d33c94 Show the balances of the main and vat account in the dashboard 2020-06-05 19:17:44 +02:00
6f90b9c941 Fix a bug where bookings were invisible if they affected only one account 2020-06-05 18:58:16 +02:00
e53c1e0872 Allow the user to create manual bookings 2020-06-05 18:45:25 +02:00
e138476d76 code cleanup 2020-06-05 18:05:10 +02:00
c1b9588189 added post form for marking order as delivered 2020-06-05 17:37:19 +02:00
5e05100d21 added supplier bookings table 2020-06-05 15:43:46 +02:00
93a5863fbe added supplier balance label 2020-06-05 13:10:16 +02:00
7f652e38f0 fix nullpointer in category search 2020-06-05 12:32:34 +02:00
0061d25082 Display financial bookings in the ui 2020-06-05 07:49:11 +02:00
d4ddd8a9a3 Merge pull request 'feature/user' (#38) from feature/user into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-03 18:36:06 +02:00
a192f0ac51 Set Tracing Id in Warehouse/finish not in Checkout. This was wrong. 2020-06-03 16:55:36 +02:00
9660e52f34 Merge pull request 'feature/31_js_magic_article_edit' (#50) from feature/31_js_magic_article_edit into master 2020-06-02 07:47:28 +02:00
72ddfa42ef Merge pull request 'Show Tax in Article Edit View. Fixes #48' (#49) from feature/48_not_showing_tax_edit_view into master 2020-06-02 07:47:04 +02:00
fc239983a7 Fix article links. 2020-06-01 21:15:27 +02:00
5e1170839e Update Gross Price in Article Edit View. Fixes #31 2020-06-01 21:11:15 +02:00
2ef9eb29ac Show Tax in Article Edit View. Fixes #48 2020-06-01 20:46:00 +02:00
f4ba79a4cd add id's to form inputs 2020-06-01 20:27:19 +02:00
976e2d1949 Implement Warehouse....
Sry, only one commit. No time to make history ;D
2020-06-01 20:25:51 +02:00
b7134a60da rename CustomerOrderRepo in Cronjob 2020-06-01 20:25:31 +02:00
ff4c984f54 fix error message 2020-06-01 20:24:45 +02:00
21502a0b1c Merge branch 'master' into feature/user 2020-06-01 19:03:42 +02:00
b38ffdea34 Merge pull request 'feature/offered_article_supplier' (#40) from feature/offered_article_supplier into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-01 17:10:21 +02:00
6988d3f213 minor code clean up 2020-06-01 17:10:04 +02:00
f5ad05f49f Merge branch 'master' into feature/offered_article_supplier 2020-06-01 17:04:52 +02:00
e539a9433a Merge pull request 'implement better search' (#43) from feature/search into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-01 16:59:37 +02:00
b14c565e81 Merge pull request 'Store the cheapest supplier for each offered article in the database' (#41) from feature/reorder_cronjob into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-01 16:53:58 +02:00
b04d627695 Merge pull request 'check if article.image is null' (#44) from fix/image_null into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-06-01 16:51:23 +02:00
839bebcd89 trim searchterm 2020-06-01 11:58:08 +02:00
3775f96f3f implement getCustomerOrderDetail 2020-06-01 11:53:29 +02:00
2394157be8 implement getCustomerOrder 2020-06-01 11:31:12 +02:00
922e3cadef Bugfix register 2020-06-01 11:13:03 +02:00
0fb1d5704e Bufix: After register, you now logged in 2020-06-01 11:11:34 +02:00
dfa528484c Merge branch 'master' into feature/user 2020-06-01 10:57:55 +02:00
38fcda708f check if article.image is null 2020-06-01 10:53:10 +02:00
9238162a4c implement better search 2020-06-01 10:52:19 +02:00
1cb52a20d4 finished supplier orders 2020-05-30 23:32:01 +02:00
67c101ebea finished show orders based on supplier 2020-05-30 21:13:25 +02:00
657d315850 show orders based on supplier 2020-05-30 20:16:27 +02:00
e7e3864b4c Store the cheapest supplier for each offered article in the database 2020-05-30 18:25:23 +02:00
407229f15d changed cheapest supplier ref in offered article entity 2020-05-30 16:49:20 +02:00
5d7ecbdfb7 basic detail page for supplier 2020-05-30 16:43:07 +02:00
c8d3ab3ff0 list suppliers page 2020-05-30 16:16:14 +02:00
1b2e75a82c updates scripts 2020-05-30 12:29:40 +02:00
286edb33ed show supplier name at offered article page 2020-05-30 11:52:30 +02:00
1a1502b38c Merge pull request 'feature/reorder_cronjob' (#27) from feature/reorder_cronjob into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-29 16:20:04 +02:00
d55a50ab29 [supplier] update gradle to version 6.4 2020-05-29 16:18:22 +02:00
e4489ff3a6 Merge branch 'feature/supplier_api' into feature/reorder_cronjob 2020-05-29 16:13:29 +02:00
2c2b5c1d0f Add Missing Gradle Folder to Repo 2020-05-29 16:12:28 +02:00
e9a3cb51ba Merge pull request 'fix/intern_articles' (#39) from fix/intern_articles into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-28 13:23:01 +02:00
f4b0d6a576 fix html warnings [2] 2020-05-28 13:22:28 +02:00
2bf383b113 fix html warnings 2020-05-28 13:20:42 +02:00
ff5b0afc93 Merge pull request 'feature/delivery_api' (#25) from feature/delivery_api into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-28 12:19:29 +02:00
d5540c1979 Small changes according to comments on the pullrequest 2020-05-28 10:58:11 +02:00
e1df56147b Merge remote-tracking branch 'origin/master' into feature/reorder_cronjob 2020-05-28 10:39:38 +02:00
35861d3664 fixed styling for edit/add btn 2020-05-27 21:53:58 +02:00
08dc4bcb47 fixed spaces in textarea 2020-05-27 21:43:26 +02:00
a38b817f20 fixed empty input fields fixes #32 2020-05-27 21:07:31 +02:00
8a2eff1fe9 code cleanup 2020-05-27 19:03:18 +02:00
e4d6642d61 order orders by newest first 2020-05-27 15:52:51 +02:00
9406bbc4ac fix nullpointer again 2020-05-27 15:10:12 +02:00
e5b28250c2 fix payment method null pointer 2020-05-27 14:56:15 +02:00
db1d3d31b2 fix register 2020-05-27 14:55:51 +02:00
42a02effa6 fixed pull request #25 2020-05-27 14:24:05 +02:00
819cca7f2a Merge remote-tracking branch 'origin/feature/user' into feature/user 2020-05-27 13:41:04 +02:00
846e448f01 code cleanup 2020-05-27 13:40:44 +02:00
614a929592 code cleanup 2020-05-27 13:40:44 +02:00
60699ed847 fix error handling 2020-05-27 13:40:44 +02:00
83ad1f3999 remove advertisementflag 2020-05-27 13:40:44 +02:00
5b4c0be1c8 add Errorhandling to UpdateSettingsAction 2020-05-27 13:40:44 +02:00
0256c19e8f implement change User Settings 2020-05-27 13:40:44 +02:00
adcd840154 remove notification center 2020-05-27 13:40:44 +02:00
b5495bda3d show all Orders 2020-05-27 13:40:44 +02:00
106b00a907 show usersettings from db 2020-05-27 13:40:44 +02:00
7c73f69865 remove bonuspoints 2020-05-27 13:40:44 +02:00
7c467c8641 bugfix register 2020-05-27 12:22:24 +02:00
ffb683bdd0 Set the advertise flag if at least one supplier wants it to be set 2020-05-25 17:19:25 +02:00
8386629088 Implement the reorder cronjob 2020-05-25 17:08:15 +02:00
86a2e5d8c2 code cleanup 2020-05-25 12:17:09 +02:00
747eb1cde2 Merge pull request 'feature/register' (#24) from feature/register into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-25 10:51:33 +02:00
8d65998ac2 code cleanup 2020-05-25 10:21:36 +02:00
178f98d664 fix error handling 2020-05-25 10:10:42 +02:00
33ff754b9d remove advertisementflag 2020-05-25 09:36:40 +02:00
ac79e1cf22 Check for inactive accounts
Check for employee by access on /intern
2020-05-24 21:51:40 +02:00
40013f0812 Merge remote-tracking branch 'origin/feature/delivery_api' into feature/delivery_api 2020-05-24 20:06:43 +02:00
77892c9dfc fixed pull request #25 2020-05-24 20:05:50 +02:00
cff9417dc5 fixed pull request #24 2020-05-24 20:04:03 +02:00
e571e21281 Merge pull request 'feature/image' (#26) from feature/image into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-23 18:51:15 +02:00
019f5d8f1b code cleanup 2020-05-23 14:19:43 +02:00
620e163a14 added feature to change an article image 2020-05-23 14:18:33 +02:00
6892a11688 Merge branch 'feature/supplier_api' into feature/reorder_cronjob 2020-05-23 11:16:46 +02:00
fcdf0e776d set default img if article is added to listed articles 2020-05-23 10:45:14 +02:00
a3efa4092a removed old static product images 2020-05-23 09:40:52 +02:00
32c6b8cb68 Merge pull request 'feature/offeredArticle' (#23) from feature/offeredArticle into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-21 20:25:53 +02:00
525a5f7eb1 Merge branch 'master' into feature/offeredArticle 2020-05-21 20:23:47 +02:00
ba570852a7 added id´s to inputs 2020-05-21 10:27:26 +02:00
8ede86fdce add Errorhandling to UpdateSettingsAction 2020-05-20 12:08:57 +02:00
caf5430a06 delivery_api finished 2020-05-19 20:38:22 +02:00
9a24fa306d gitignore push 2020-05-19 20:37:26 +02:00
da78ab4990 register working 2020-05-19 18:47:15 +02:00
9cfb1cd0ad add offered article to listed article 2020-05-19 16:30:27 +02:00
7686c4cda6 Create rudimentary register class 2020-05-19 15:41:43 +02:00
39eefbdd3e implement change User Settings 2020-05-19 15:18:48 +02:00
6e3d1cbcf5 remove notification center 2020-05-19 14:17:38 +02:00
1ce9a971ba show all Orders 2020-05-19 11:52:41 +02:00
d8032c2bd7 load offeredArticles from DB and display basic info 2020-05-19 00:12:26 +02:00
4711527971 show usersettings from db 2020-05-18 14:41:33 +02:00
b7b5c7a6b3 remove bonuspoints 2020-05-18 12:49:37 +02:00
5c5df83872 Merge pull request 'feature/search' (#19) from feature/search into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-18 09:42:06 +02:00
7b6882b12b format code 2020-05-18 09:41:02 +02:00
d739b15410 fix missing import 2020-05-17 13:11:32 +02:00
0c66a713b4 add some comments 2020-05-17 13:10:04 +02:00
d0785308d4 code cleanup 2020-05-17 13:09:51 +02:00
bc350b3177 implement only show Searchresults which are in Stock 2020-05-17 13:09:26 +02:00
8d6a1f4053 implement only show advertisements which are in stock 2020-05-17 13:09:26 +02:00
6b9db3eed5 implement errorhandling 2020-05-17 13:09:26 +02:00
3f5b1efa0c implement search 2020-05-17 13:08:18 +02:00
d4be2b9e94 Revert "Revert "added link from listedArticles page to related offered article""
This reverts commit 3c69f38367.
2020-05-16 22:48:40 +02:00
14bef592c4 Revert "Revert "UImodel for offeredArticle page, html cleanup""
This reverts commit 0840eda369.
2020-05-16 22:48:20 +02:00
0840eda369 Revert "UImodel for offeredArticle page, html cleanup"
This reverts commit e0f638f0bd.
2020-05-16 22:46:42 +02:00
3c69f38367 Revert "added link from listedArticles page to related offered article"
This reverts commit 39fb735f21.
2020-05-16 22:46:18 +02:00
39fb735f21 added link from listedArticles page to related offered article 2020-05-16 21:49:31 +02:00
e0f638f0bd UImodel for offeredArticle page, html cleanup 2020-05-16 13:42:07 +02:00
4f6388e4b9 fixed links to offered articles page 2020-05-15 20:57:56 +02:00
2bbf8260f4 Merge pull request 'WIP feature/listedArticles' (#15) from feature/listedArticles into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-15 19:48:21 +02:00
b1840dd845 code cleanup 2020-05-15 19:47:26 +02:00
dc49bc2e8d code cleanuo 2020-05-15 19:45:10 +02:00
8c777ca1ff fixed merge bug: cascade = CascadeType.ALL in Article to Categories 2020-05-15 19:31:31 +02:00
c73435b2a0 Merge remote-tracking branch 'origin/master' into feature/listedArticles 2020-05-15 19:04:02 +02:00
ea8aaa3931 removed PW bypass 2020-05-15 18:16:03 +02:00
56efbd7940 code cleanup 2020-05-15 18:13:12 +02:00
a98782bc01 added new route in external controller 2020-05-15 14:14:29 +02:00
0de35f1a51 Implement scheduled execution of cronjobs 2020-05-15 13:47:54 +02:00
f403a6e1c4 fix in stock bug 2020-05-15 12:33:32 +02:00
d06fd57447 added new route for offered articles page 2020-05-15 11:57:20 +02:00
197a774fb2 removed data folder 2020-05-14 23:14:33 +02:00
af99e8107d added change for stock, price and autobuy values on article edit page 2020-05-14 22:16:06 +02:00
f2fc51384e Merge pull request 'update dependencies & gradle' (#12) from feature/spring-boot-2.2.7 into master
Reviewed-by: CodeSteak <codesteak@shellf.art>
2020-05-13 19:45:46 +02:00
922e21a454 update dependencies & gradle
* gradle 5.6.2 -> 6.4
* spring boot 2.2.2 -> 2.2.7
* sqlite-jdbc 3.28.0 -> 3.31.1
2020-05-13 18:28:21 +02:00
c69b20b914 added POST that changes the article WIP 2020-05-12 22:52:01 +02:00
c92e485018 Implement API Bindings for SupplierService 2020-05-12 22:42:25 +02:00
62d811db1d Implement First Draft of Supplier API 2020-05-12 22:41:08 +02:00
fd9dcf141e fixes in listed article page and converted form to POST 2020-05-11 15:32:39 +02:00
09c85fe8cb fixed route for /intern/articles and /intern/suppliers/articles 2020-05-11 14:32:07 +02:00
22d2744c8c GET article edit page 2020-05-11 11:58:26 +02:00
bba4173269 fixed inputstream once more 2020-05-10 22:19:58 +02:00
e1d7becc2e added yaml dump&parse class 2020-05-10 21:23:44 +02:00
89eaeaed40 Merge pull request 'feature/basic_functions' (#7) from feature/basic_functions into master
Reviewed-by: Jannik Seiler <seil0@mosad.xyz>
2020-05-10 17:48:31 +02:00
edcdb2e9cf fix use Math.min for loopcount 2020-05-10 17:25:48 +02:00
530034a40e fix unused imports and unneeded empty lines 2020-05-10 17:19:18 +02:00
8f24aee350 fix add should_be_advertised to script 2020-05-10 17:16:17 +02:00
bd2aeb63f6 Remove unneeded comment 2020-05-10 17:05:19 +02:00
713c8ebe86 Comment empty constructors 2020-05-10 17:03:40 +02:00
e406918ca2 Remove commend out code 2020-05-10 16:57:33 +02:00
e167832164 Merge branch 'feature/basic_functions' of git.mosad.xyz:localhorst/e-commerce into feature/basic_functions 2020-05-10 15:39:35 +02:00
777786f5b6 Remove old missleading comment 2020-05-10 15:39:24 +02:00
a62e3e8a63 fix only show ordered Articles from specific Customer 2020-05-10 15:28:39 +02:00
7f95cef13b fix personalized suggestions 2020-05-10 15:03:45 +02:00
95e52cc79d fix non-formatted and non-vatted price 2020-05-10 14:57:02 +02:00
d4d1426438 Fix decimals in checkout page 2020-05-10 14:34:20 +02:00
d1301f6988 Fix naming-convension-error in ArticleOffer 2020-05-10 14:30:00 +02:00
731f4761fa Show error if articles are not avail during checkout. 2020-05-10 14:29:08 +02:00
69b5b33af8 fix typo in 8dd4cc4459 2020-05-09 19:58:31 +02:00
8dd4cc4459 use application.properties when run via bootRun 2020-05-09 19:31:57 +02:00
193 changed files with 7239 additions and 2132 deletions

2
.gitignore vendored
View File

@ -91,3 +91,5 @@ local.properties
prototype/*.db
prototype/images
prototype/data

32
delivery/.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

30
delivery/build.gradle Normal file
View File

@ -0,0 +1,30 @@
plugins {
id 'org.springframework.boot' version '2.2.7.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'org.hso.ecommerce'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
compile 'org.json:json:20190722'
}
test {
useJUnitPlatform()
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Tue May 19 15:50:06 CEST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

183
delivery/gradlew vendored Executable file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

100
delivery/gradlew.bat vendored Normal file
View File

@ -0,0 +1,100 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
delivery/settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'delivery'

View File

@ -0,0 +1,11 @@
package org.hso.ecommerce.supplier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@ -0,0 +1,37 @@
package org.hso.ecommerce.supplier;
import org.hso.ecommerce.supplier.data.Delivery;
import org.hso.ecommerce.supplier.data.DeliveryManager;
import org.hso.ecommerce.supplier.data.ReturnStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
public class RequestController {
@PostMapping("/newDelivery")
public String supplier(HttpServletResponse response, HttpServletRequest request, @RequestBody Delivery delivery) {
DeliveryManager.getInstance().add(delivery);
return delivery.getUuid();
}
@GetMapping(value = "/status", produces = MediaType.APPLICATION_JSON_VALUE)
public ReturnStatus searchArticles(@RequestParam(value = "trackingID") String trackingID, HttpServletRequest request, HttpServletResponse response) {
Delivery delivery = DeliveryManager.getInstance().getDeliveryByID(trackingID);
if (delivery == null) {
Delivery lostDelivery = Delivery.lostDelivery(trackingID);
DeliveryManager.getInstance().add(lostDelivery);
delivery = lostDelivery;
}
return new ReturnStatus(delivery.getStatus(), delivery.getEstimatedArrival());
}
}

View File

@ -0,0 +1,78 @@
package org.hso.ecommerce.supplier.data;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
public class Delivery {
private String[] states = {"Bestellung eingegangen", "Bestellung auf dem Weg", "Lieferung erfolgreich"};
private int[] timeBorder = {4, 24};
private String name;
private String address;
// Why is this a string and creationTime a Date?!
private String estimatedArrival;
private Date creationTime;
private String uuid;
public Delivery(String name, String address) {
this.name = name;
this.address = address;
this.uuid = UUID.randomUUID().toString();
this.creationTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
this.estimatedArrival = formatter.format(addDays((Date) this.creationTime.clone(), 1));
}
public static Delivery lostDelivery(String uuid) {
Delivery delivery = new Delivery("", "");
delivery.uuid = uuid;
delivery.creationTime = addDays(new Date(), -1);
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
delivery.estimatedArrival = formatter.format(addDays((Date) delivery.creationTime.clone(), 1));
return delivery;
}
public String getStatus() {
Date now = new Date();
long timeNow = now.getTime();
long creationTime = this.creationTime.getTime();
// Wow, that's how calculate date diffs.
long diff = timeNow - creationTime;
double hour = (((diff / 1000.0) / 3600.0));
for (int i = 0; i < timeBorder.length; i++) {
if (hour < timeBorder[i])
return states[i];
}
return states[timeBorder.length];
}
public String getEstimatedArrival() {
return estimatedArrival;
}
private static Date addDays(Date date, int days) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, days);
return cal.getTime();
}
public String getUuid() {
return uuid;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,30 @@
package org.hso.ecommerce.supplier.data;
import java.util.HashMap;
public class DeliveryManager {
private HashMap<String, Delivery> deliveryList;
private static DeliveryManager deliveryManager;
private DeliveryManager()
{
deliveryList = new HashMap<>();
}
public static DeliveryManager getInstance() {
if (DeliveryManager.deliveryManager == null) {
DeliveryManager.deliveryManager = new DeliveryManager();
}
return DeliveryManager.deliveryManager;
}
public void add(Delivery delivery) {
deliveryList.put(delivery.getUuid(), delivery);
}
public Delivery getDeliveryByID(String uuid) {
return deliveryList.getOrDefault(uuid, null);
}
}

View File

@ -0,0 +1,20 @@
package org.hso.ecommerce.supplier.data;
public class ReturnStatus {
private String status;
private String estimatedArrival;
public ReturnStatus(String status, String estimatedArrival) {
this.status = status;
this.estimatedArrival = estimatedArrival;
}
public String getStatus() {
return status;
}
public String getEstimatedArrival() {
return estimatedArrival;
}
}

View File

@ -0,0 +1,2 @@
server.address=::1
server.port=8082

View File

@ -5,3 +5,5 @@ e-commerce.db
./e-commerce.iml
./e-commerce.ipr
./e-commerce.iws
config.yml

View File

@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.2.RELEASE")
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.7.RELEASE")
}
}
@ -27,7 +27,8 @@ dependencies {
// implementation 'org.springframework.session:spring-session-jdbc'
implementation 'com.github.gwenn:sqlite-dialect:0.1.0'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'org.xerial:sqlite-jdbc:3.28.0'
implementation 'org.xerial:sqlite-jdbc:3.31.1'
implementation 'org.yaml:snakeyaml:1.26'
testCompile("org.springframework.boot:spring-boot-starter-test")
}

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

22
prototype/gradlew vendored
View File

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`

18
prototype/gradlew.bat vendored
View File

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

View File

@ -1,6 +1,15 @@
INSERT INTO article_offers ("manufacturer", "article_number", "vat_percent")
VALUES ("McDonalds", "1", 7);
/*
* add a supplier first
*/
INSERT INTO article_offers ("manufacturer", "article_number", "price_per_unit_net", "title", "vat_percent", "should_be_advertised", "cheapest_supplier_id")
VALUES ("McDonalds", "1", 4242, "McPizza", 7, 1, 1);
/*
* There is no need for the add article, you can add one form the UI on the offerd article page
*/
INSERT INTO articles ("related_id", "shop_price_per_unit_net_cent", "warehouse_units_per_slot", "should_reorder", "reorder_max_price", "title", "description", "image_id")
VALUES (1, 19.99, 10, 1, 15, "Huge Hamburger", "This huge Hamburger is awesome!", NULL);

View File

@ -0,0 +1,6 @@
INSERT INTO suppliers ("api_url", "name", "uuid")
VALUES ("https://api.com", "Conrad", "fdfdfg4gdfgdf4gfg");

View File

@ -0,0 +1,3 @@
INSERT INTO supplier_orders ("created", "delivered", "number_of_units", "price_per_unit_net_cent", "total_price_net", "ordered_id", "supplier_id")
VALUES ('0', '0', '42', '42', '42', '1', '1');

View File

@ -10,6 +10,7 @@ public class CreateBookingAction {
public CreateBookingAction(BookingAccountEntry source, BookingAccountEntry destination, BookingReason reason, int amountCent) {
booking = new Booking();
booking.created = new java.sql.Timestamp(System.currentTimeMillis());
booking.reason = reason;
booking.amountCent = amountCent;

View File

@ -0,0 +1,83 @@
package org.hso.ecommerce.action.cronjob;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import org.hso.ecommerce.api.SupplierService;
import org.hso.ecommerce.api.data.Article;
import org.hso.ecommerce.api.data.Supplier;
public class ReadSupplierDataAction {
private List<org.hso.ecommerce.entities.supplier.Supplier> suppliers;
public static class ArticleIdentifier {
public final String manufacturer;
public final String articleNumber;
public ArticleIdentifier(String manufacturer, String articleNumber) {
this.manufacturer = manufacturer;
this.articleNumber = articleNumber;
}
@Override
public int hashCode() {
return Objects.hash(manufacturer, articleNumber);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ArticleIdentifier)) {
return false;
}
ArticleIdentifier otherId = (ArticleIdentifier) other;
return this.manufacturer.equals(otherId.manufacturer) && this.articleNumber.equals(otherId.articleNumber);
}
}
public ReadSupplierDataAction(List<org.hso.ecommerce.entities.supplier.Supplier> suppliers) {
this.suppliers = suppliers;
}
public static class Offer {
public final org.hso.ecommerce.entities.supplier.Supplier dbSupplier;
public final Supplier apiSupplier;
public Offer(org.hso.ecommerce.entities.supplier.Supplier dbSupplier, Supplier apiSupplier) {
this.dbSupplier = dbSupplier;
this.apiSupplier = apiSupplier;
}
}
public static class Result {
public final ArrayList<Supplier> supplierData;
public final HashMap<ArticleIdentifier, Offer> cheapestOffer;
public Result(ArrayList<Supplier> supplierData, HashMap<ArticleIdentifier, Offer> cheapestOffer) {
this.supplierData = supplierData;
this.cheapestOffer = cheapestOffer;
}
}
public Result finish() {
ArrayList<Supplier> suppliers = new ArrayList<>();
HashMap<ArticleIdentifier, Integer> price = new HashMap<>();
HashMap<ArticleIdentifier, Offer> cheapest = new HashMap<>();
for (org.hso.ecommerce.entities.supplier.Supplier supplier : this.suppliers) {
SupplierService service = new SupplierService(supplier.apiUrl);
Supplier apiSupplier = service.getSupplier();
suppliers.add(apiSupplier);
for (Article article : apiSupplier.articles) {
ArticleIdentifier identifier = new ArticleIdentifier(article.manufacturer, article.articleNumber);
Integer previousPrice = price.get(identifier);
if (previousPrice == null || article.pricePerUnitNet < previousPrice) {
price.put(identifier, article.pricePerUnitNet);
cheapest.put(identifier, new Offer(supplier, apiSupplier));
}
}
}
return new Result(suppliers, cheapest);
}
}

View File

@ -0,0 +1,111 @@
package org.hso.ecommerce.action.cronjob;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer;
import org.hso.ecommerce.api.SupplierService;
import org.hso.ecommerce.api.data.Order;
import org.hso.ecommerce.api.data.OrderConfirmation;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.supplier.ArticleOffer;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Timestamp;
import java.util.HashMap;
public class ReorderAction {
private static final Logger log = LoggerFactory.getLogger(ReorderAction.class);
private Article article;
private Integer[] orderedAmounts;
private Integer undeliveredReorders;
private int amountInStock;
private HashMap<ArticleIdentifier, Offer> cheapestOffer;
private HashMap<ArticleIdentifier, ArticleOffer> articleOffers;
public ReorderAction(
Article article, Integer[] orderedAmounts,
Integer undeliveredReorders,
int amountInStock,
HashMap<ArticleIdentifier, Offer> cheapestOffer,
HashMap<ArticleIdentifier, ArticleOffer> articleOffers
) {
this.article = article;
this.orderedAmounts = orderedAmounts;
this.undeliveredReorders = undeliveredReorders;
this.amountInStock = amountInStock;
this.cheapestOffer = cheapestOffer;
this.articleOffers = articleOffers;
}
private int null_to_zero(Integer input) {
return input == null ? 0 : input;
}
private int calculateAmountToReorder() {
// Algorithm as described in the documentation
int a = null_to_zero(orderedAmounts[0]);
int b = null_to_zero(orderedAmounts[1]);
int c = null_to_zero(orderedAmounts[2]);
int x = Math.max(Math.max(a, b), c);
int y = Math.min(Math.min(a, b), c);
int n = 6 * x - 2 * y;
if (n < 3) {
n = 3;
}
int i = null_to_zero(undeliveredReorders);
int l = amountInStock;
return n - i - l;
}
public SupplierOrder finish() {
if (!article.shouldReorder) {
return null;
}
int amount = calculateAmountToReorder();
if (amount <= 0) {
return null;
}
ArticleIdentifier identifier = new ArticleIdentifier(article.related.manufacturer, article.related.articleNumber);
Offer offer = cheapestOffer.get(identifier);
if (offer == null) {
log.info("Could not order \"" + article.title + "\" because there is no supplier delivering it.");
return null;
}
ArticleOffer articleOffer = articleOffers.get(identifier);
org.hso.ecommerce.api.data.Article apiArticle = offer.apiSupplier.findArticle(identifier.manufacturer,
identifier.articleNumber);
if (apiArticle.pricePerUnitNet > article.reorderMaxPrice) {
log.info("Could not order \"" + article.title + "\" because it is currently too expensive.");
return null;
}
Order order = new Order();
order.manufacturer = articleOffer.manufacturer;
order.articleNumber = articleOffer.articleNumber;
order.quantity = amount;
order.maxTotalPriceCentNet = apiArticle.pricePerUnitNet * amount;
OrderConfirmation confirm = new SupplierService(offer.dbSupplier.apiUrl).order(order);
SupplierOrder createdOrder = new SupplierOrder();
createdOrder.created = new Timestamp(System.currentTimeMillis());
createdOrder.supplier = offer.dbSupplier;
createdOrder.ordered = articleOffer;
createdOrder.numberOfUnits = confirm.quantity;
createdOrder.pricePerUnitNetCent = confirm.pricePerUnitNetCent;
createdOrder.totalPriceNet = confirm.totalPriceNetCharged;
createdOrder.carrier = confirm.carrier;
createdOrder.trackingId = confirm.trackingId;
createdOrder.estimatedArrival = Timestamp.valueOf(confirm.estimatedArrival);
return createdOrder;
}
}

View File

@ -0,0 +1,63 @@
package org.hso.ecommerce.action.cronjob;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.ArticleIdentifier;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction.Offer;
import org.hso.ecommerce.api.data.Article;
import org.hso.ecommerce.entities.supplier.ArticleOffer;
public class UpdateOffersAction {
private List<ArticleOffer> offers;
private HashMap<ArticleIdentifier, Offer> cheapestOffer;
public UpdateOffersAction(List<ArticleOffer> offers, HashMap<ArticleIdentifier, Offer> cheapestOffer) {
this.offers = offers;
this.cheapestOffer = cheapestOffer;
}
private HashMap<ArticleIdentifier, ArticleOffer> mapOffers() {
HashMap<ArticleIdentifier, ArticleOffer> map = new HashMap<>();
for (ArticleOffer offer : offers) {
ArticleIdentifier identifier = new ArticleIdentifier(offer.manufacturer, offer.articleNumber);
map.put(identifier, offer);
}
return map;
}
public List<ArticleOffer> finish() {
HashMap<ArticleIdentifier, ArticleOffer> availableOffers = mapOffers();
// Reset all advertise-flags and supplier relations first. They are set again below.
for (ArticleOffer offer : availableOffers.values()) {
offer.shouldBeAdvertised = false;
offer.cheapestSupplier = null;
}
for (Entry<ArticleIdentifier, Offer> cheapestOffer : cheapestOffer.entrySet()) {
String manufacturer = cheapestOffer.getKey().manufacturer;
String articleNumber = cheapestOffer.getKey().articleNumber;
ArticleOffer currentOffer = availableOffers.get(cheapestOffer.getKey());
if (currentOffer == null) {
currentOffer = new ArticleOffer();
currentOffer.manufacturer = manufacturer;
currentOffer.articleNumber = articleNumber;
offers.add(currentOffer);
}
Article currentOfferedArticle = cheapestOffer.getValue().apiSupplier.findArticle(manufacturer,
articleNumber);
currentOffer.title = currentOfferedArticle.title;
currentOffer.vatPercent = currentOfferedArticle.vatPercent;
currentOffer.cheapestSupplier = cheapestOffer.getValue().dbSupplier;
currentOffer.pricePerUnitNet = currentOfferedArticle.pricePerUnitNet;
// Set advertise-flag if any supplier wants it to be set
if (currentOfferedArticle.shouldBeAdvertised) {
currentOffer.shouldBeAdvertised = true;
}
}
return offers;
}
}

View File

@ -13,6 +13,7 @@ import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
@ -68,14 +69,21 @@ public class CreateOrderAction {
totalVatCent += article.getVat() * quantity;
}
public Result finish() {
public Result finish() throws ArticleNotInStockException {
CustomerOrder order = createOrder();
CustomerPayment payment = createPayment();
List<Booking> bookingList = new ArrayList<>();
bookingList.add(new CreateBookingAction(latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish());
bookingList.add(new CreateBookingAction(null, latestUserBooking, new BookingReason(payment), order.totalGrossCent).finish());
bookingList.add(new CreateBookingAction(latestMainBooking, latestVatBooking, new BookingReason(order), order.totalVatCent).finish());
Booking purchaseBooking = new CreateBookingAction(
latestUserBooking, latestMainBooking, new BookingReason(order), order.totalGrossCent).finish();
Booking paymentBooking = new CreateBookingAction(
null, purchaseBooking.source /* userAccount */, new BookingReason(payment), order.totalGrossCent).finish();
Booking vatBooking = new CreateBookingAction(
purchaseBooking.destination /* mainAccount */, latestVatBooking, new BookingReason(order), order.totalVatCent).finish();
bookingList.add(purchaseBooking);
bookingList.add(paymentBooking);
bookingList.add(vatBooking);
WarehouseBooking warehouseBooking = createWarehouseBooking(order);
@ -86,7 +94,7 @@ public class CreateOrderAction {
);
}
private WarehouseBooking createWarehouseBooking(CustomerOrder order) {
private WarehouseBooking createWarehouseBooking(CustomerOrder order) throws ArticleNotInStockException {
WarehouseBooking booking = new WarehouseBooking();
booking.created = new Timestamp(new Date().getTime());
booking.reason = new BookingReason(order);
@ -94,7 +102,14 @@ public class CreateOrderAction {
for (OrderItem item : orderItems) {
int needed = item.quantity;
// Sort for most empty slot first;
item.availableSlots.sort(Comparator.comparingInt(a -> a.newSumSlot));
for (WarehouseBookingPositionSlotEntry slot : item.availableSlots) {
if (slot.newSumSlot == 0) {
continue;
}
int remove = Math.min(slot.newSumSlot, needed);
needed -= remove;
@ -102,7 +117,7 @@ public class CreateOrderAction {
bookingPosition.article = item.article;
bookingPosition.amount = -remove;
bookingPosition.slotEntry = slot.copyAddAmount(-remove);
bookingPosition.slotEntry = slot.copyAddAmount(-remove, item.article);
bookingPosition.booking = booking;
booking.positions.add(bookingPosition);
@ -111,6 +126,10 @@ public class CreateOrderAction {
break;
}
}
if (needed > 0) {
throw new ArticleNotInStockException(item.article);
}
}
return booking;
@ -174,4 +193,17 @@ public class CreateOrderAction {
this.quantity = quantity;
}
}
public static class ArticleNotInStockException extends Exception {
private Article article;
public ArticleNotInStockException(Article article) {
super("The quantity of article '" + article.title + "' is not in stock.");
this.article = article;
}
public Article getArticle() {
return article;
}
}
}

View File

@ -1,11 +1,16 @@
package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.springframework.web.client.ResourceAccessException;
import java.sql.Timestamp;
import java.util.Date;
public class EnableTrackingAction {
public static void addTrackingInfo(CustomerOrder customerOrder) {
// TODO:
customerOrder.trackingId = "555-NASE";
public static void addTrackingInfo(RestServiceForDelivery deliveryService, CustomerOrder customerOrder) throws ResourceAccessException {
customerOrder.inDeliverySince = new Timestamp(new Date().getTime());
customerOrder.trackingId = deliveryService.getDeliveryID(customerOrder);
}
}

View File

@ -9,7 +9,7 @@ public class GetRandomArticlesAction {
public static List<Article> getRandomArticles(int quantity, List<Article> advertisedArticles) {
List<Article> randomisedArticles = new ArrayList<Article>();
int loopcount = quantity > advertisedArticles.size() ? advertisedArticles.size() : quantity;
int loopcount = Math.min(quantity, advertisedArticles.size());
for (int i = 0; i < loopcount; i++) {
int index = (int) (Math.random() * advertisedArticles.size());
randomisedArticles.add(advertisedArticles.remove(index));

View File

@ -0,0 +1,40 @@
package org.hso.ecommerce.action.shop;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SearchByTermAction {
public static List<Article> searchByTerm(String sourceTerm, ArticleRepository repository) {
List<String> terms = Arrays.asList(sourceTerm.split(" "));
List<Article> resultArticles = new ArrayList<>();
terms.forEach(term -> {
List<Article> titleArticles = repository.getArticlesByTermInTitle(term); //search in Title
titleArticles.forEach(article -> {
if(!resultArticles.contains(article)){
resultArticles.add(article);
}
});
});
terms.forEach(term -> {
List<Article> descArticles = repository.getArticlesByTermInDescription(term); //search by Term
descArticles.forEach(article -> {
if(!resultArticles.contains(article)){
resultArticles.add(article);
}
});
});
return resultArticles;
}
}

View File

@ -0,0 +1,48 @@
package org.hso.ecommerce.action.user;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.hso.ecommerce.uimodel.DeliveryDataEnum;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class CreateDeliveryData {
public static DeliveryData getDeliveryDataFromCustomerOrder(CustomerOrder customerOrder, CustomerOrderRepository customerOrderRepository, RestServiceForDelivery restServiceForDelivery)
{
if(customerOrder.trackingId == null)
return new DeliveryData("", "", DeliveryDataEnum.NO_TRACKING_ID);
if(customerOrder.deliveredAt == null)
{
DeliveryData deliveryData = restServiceForDelivery.getDeliveryData(customerOrder.trackingId);
if(deliveryData.isDelivered())
{
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
try {
calendar.setTime(simpleDateFormat.parse(deliveryData.getEstimatedArrival()));
} catch (ParseException e) {
e.printStackTrace();
}
customerOrder.deliveredAt = new Timestamp(calendar.getTimeInMillis());
customerOrderRepository.save(customerOrder);
}
return deliveryData;
}
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
return new DeliveryData("Lieferung erfolgreich", formatter.format(customerOrder.deliveredAt), DeliveryDataEnum.OK);
}
}

View File

@ -0,0 +1,83 @@
package org.hso.ecommerce.action.user;
import org.hso.ecommerce.entities.booking.PaymentMethod;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository;
public class UpdateUserSettingsAction {
private User user;
private UserRepository repository;
public UpdateUserSettingsAction(User user, UserRepository repository) {
this.user = user;
this.repository = repository;
}
public UpdateResult updateEmail(String newMail) {
UpdateResult result = new UpdateResult(false);
if (!newMail.contains("@")) {
result.errorString = "Ändern der Email-Addresse nicht möglich. Bitte versuchen Sie es erneut.";
} else {
this.user.email = newMail;
this.repository.save(this.user);
result.updated = true;
}
return result;
}
public UpdateResult updatePassword(String oldPassword, String password1, String password2) {
UpdateResult result = new UpdateResult(false);
if (this.user.validatePassword(oldPassword)) {
if (password1.equals(password2)) {
if (!password1.equals(oldPassword)) {
this.user.setPassword(password1);
this.repository.save(this.user);
result.updated = true;
} else {
result.errorString = "Das neue Passwort entspricht dem alten Passwort.";
}
} else {
result.errorString = "Die beiden neuen Passwörter stimmen nicht überein. Bitte versuchen Sie es erneut.";
}
} else {
result.errorString = "Das eingegebene alte Passwort stimmt nicht mit dem momentan gespeicherten Passwort überein. Bitte versuchen Sie es erneut.";
}
return result;
}
public UpdateResult updateShippingInfo(String salutation, String name, String address) {
this.user.salutation = salutation;
this.user.defaultDeliveryAddress.name = name;
this.user.defaultDeliveryAddress.addressString = address;
this.repository.save(this.user);
return new UpdateResult(true);
}
public UpdateResult updatePaymentInfo(String creditCardNumber) {
UpdateResult result = new UpdateResult(false);
if (creditCardNumber.matches("[0-9]+")) {
this.user.defaultPayment = PaymentMethod.fromCreditCardNumber(creditCardNumber);
this.repository.save(this.user);
result.updated = true;
} else {
result.errorString = "Kreditkartennummer darf nur Zahlen enthalten. Bitte versuchen Sie es erneut.";
}
return result;
}
public class UpdateResult {
public boolean updated; //if true worked, if false not worked
public String errorString;
public UpdateResult(boolean updated, String errorString) {
this.updated = updated;
this.errorString = errorString;
}
public UpdateResult(boolean updated) {
this.updated = updated;
this.errorString = "";
}
}
}

View File

@ -0,0 +1,73 @@
package org.hso.ecommerce.action.warehouse;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.util.HashSet;
import java.util.List;
public class CalculateWarehouseStatsAction {
private List<WarehouseBookingPositionSlotEntry> entryList;
public CalculateWarehouseStatsAction(List<WarehouseBookingPositionSlotEntry> everyCurrentEntry) {
this.entryList = everyCurrentEntry;
}
public WarehouseStats finish() {
int numArticles = calculateNumArticles();
double efficiency = calculateEfficiency();
double ratioUsedSlots = calculateRatioSlotsUsed();
return new WarehouseStats(numArticles, efficiency, ratioUsedSlots);
}
private double calculateRatioSlotsUsed() {
int used = 0;
for (WarehouseBookingPositionSlotEntry entry : entryList) {
if (entry.newSumSlot > 0) {
used++;
}
}
return ((double) used) / entryList.size();
}
private double calculateEfficiency() {
double e = 0;
for (WarehouseBookingPositionSlotEntry entry : entryList) {
if (entry.newSumSlot > 0) {
e += entry.newSumSlot / (double) entry.article.warehouseUnitsPerSlot;
} else {
e += 0;
}
}
return e / entryList.size();
}
private int calculateNumArticles() {
HashSet<Long> articleIds = new HashSet<>();
for (WarehouseBookingPositionSlotEntry entry : entryList) {
if (entry.newSumSlot > 0) {
articleIds.add(entry.article.id);
}
}
return articleIds.size();
}
public static class WarehouseStats {
public int numArticles;
public double efficiency;
public double ratioUsedSlots;
WarehouseStats(int numArticles, double efficiency, double ratioUsedSlots) {
this.numArticles = numArticles;
this.efficiency = efficiency;
this.ratioUsedSlots = ratioUsedSlots;
}
}
}

View File

@ -0,0 +1,92 @@
package org.hso.ecommerce.action.warehouse;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Optional;
public class CreateManuelBookingAction {
private Article article;
private int amount;
private Optional<WarehouseBookingPositionSlotEntry> source;
private Optional<WarehouseBookingPositionSlotEntry> destination;
private String reason;
public CreateManuelBookingAction(Article article, int amount, Optional<WarehouseBookingPositionSlotEntry> source, Optional<WarehouseBookingPositionSlotEntry> destination, String reason) {
this.article = article;
this.amount = amount;
this.source = source;
this.destination = destination;
this.reason = reason;
}
public WarehouseBooking finish() throws ArticleSlotConstraintFailedException {
WarehouseBooking booking = new WarehouseBooking();
booking.created = new Timestamp(new Date().getTime());
booking.reason = new BookingReason(reason);
if (source.isPresent()) {
if (source.get().article.id != article.id) {
throw new ArticleSlotConstraintArticleTypeFailedException();
}
WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition();
bookingPosition.booking = booking;
bookingPosition.article = article;
bookingPosition.amount = -amount;
bookingPosition.slotEntry = source.get().copyAddAmount(-amount, article);
if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) {
throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds.");
}
booking.positions.add(bookingPosition);
}
if (destination.isPresent()) {
if (destination.get().article.id != article.id && destination.get().newSumSlot > 0) {
throw new ArticleSlotConstraintArticleTypeFailedException();
}
WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition();
bookingPosition.booking = booking;
bookingPosition.article = article;
bookingPosition.amount = amount;
bookingPosition.slotEntry = destination.get().copyAddAmount(amount, article);
if (bookingPosition.slotEntry.newSumSlot < 0 || bookingPosition.slotEntry.newSumSlot > article.warehouseUnitsPerSlot) {
throw new ArticleSlotConstraintFailedException("The quantity of article can only be set in bounds.");
}
booking.positions.add(bookingPosition);
}
return booking;
}
public static class ArticleSlotConstraintFailedException extends Exception {
public ArticleSlotConstraintFailedException(String s) {
super(s);
}
public ArticleSlotConstraintFailedException() {
super("The quantity of article can only be set in bounds.");
}
}
public static class ArticleSlotConstraintArticleTypeFailedException extends ArticleSlotConstraintFailedException {
public ArticleSlotConstraintArticleTypeFailedException() {
super("The Article in the slot entry does not match.");
}
}
}

View File

@ -0,0 +1,103 @@
package org.hso.ecommerce.action.warehouse;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPosition;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class SupplierOrderArrivedAction {
private final Article article;
private final ArrayList<WarehouseBookingPositionSlotEntry> warehouseCandidates;
private final SupplierOrder order;
public SupplierOrderArrivedAction(List<WarehouseBookingPositionSlotEntry> warehouseCandidates, SupplierOrder order, Article article) {
this.warehouseCandidates = new ArrayList<>(warehouseCandidates);
this.order = order;
this.article = article;
}
public Result finish() throws NoSpaceInWarehouseException {
// Sort for most filled slot first;
warehouseCandidates.sort((b, a) -> Integer.compare(a.newSumSlot, b.newSumSlot));
int needed = order.numberOfUnits;
WarehouseBooking booking = new WarehouseBooking();
booking.created = new Timestamp(new Date().getTime());
booking.reason = new BookingReason(order);
for (WarehouseBookingPositionSlotEntry entry : warehouseCandidates) {
int canBeAdded = article.warehouseUnitsPerSlot - entry.newSumSlot;
if (canBeAdded == 0) {
// this slot is full, skip
continue;
}
int willBeAdded = Math.min(canBeAdded, needed);
needed -= willBeAdded;
WarehouseBookingPosition bookingPosition = new WarehouseBookingPosition();
bookingPosition.article = article;
bookingPosition.amount = willBeAdded;
bookingPosition.slotEntry = entry.copyAddAmount(willBeAdded, article);
bookingPosition.booking = booking;
booking.positions.add(bookingPosition);
if (needed == 0) {
break;
}
}
if (needed > 0) {
throw new NoSpaceInWarehouseException(article);
}
order.delivered = new Timestamp(new Date().getTime());
return new Result(order, booking);
}
public static class Result extends Exception {
private final SupplierOrder order;
private final WarehouseBooking booking;
public Result(SupplierOrder order, WarehouseBooking booking) {
this.order = order;
this.booking = booking;
}
public SupplierOrder getOrder() {
return order;
}
public WarehouseBooking getBooking() {
return booking;
}
}
public static class NoSpaceInWarehouseException extends Exception {
private Article article;
public NoSpaceInWarehouseException(Article article) {
super("The quantity of article '" + article.title + "' does not fit in warehouse.");
this.article = article;
}
public Article getArticle() {
return article;
}
}
}

View File

@ -0,0 +1,83 @@
package org.hso.ecommerce.api;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.hso.ecommerce.uimodel.DeliveryDataEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.util.HashMap;
import java.util.Map;
@Service
public class RestServiceForDelivery {
@Autowired
private final RestTemplateBuilder restTemplateBuilder = null;
@Autowired
private final AppSettings settings = null;
private String getDeliveryEndpoint() {
return settings.getParcelServiceApiURL();
}
public String getDeliveryID(CustomerOrder customerOrder) throws ResourceAccessException {
String url = getDeliveryEndpoint() + "/newDelivery";
RestTemplate restTemplate = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Map<String, String> requestBody = new HashMap<>();
requestBody.put("name", customerOrder.destination.name);
requestBody.put("address", customerOrder.destination.addressString);
HttpEntity<Map<String, String>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
throw new ResourceAccessException("Http Status wrong");
}
}
public DeliveryData getDeliveryData(String trackingID) {
String url = getDeliveryEndpoint() + "/status";
RestTemplate restTemplate = restTemplateBuilder.build();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url).queryParam("trackingID", trackingID);
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<DeliveryData> response;
try {
response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity, DeliveryData.class);
if (response.getStatusCode() == HttpStatus.OK) {
return response.getBody();
} else {
return new DeliveryData("", "", DeliveryDataEnum.NO_DATA);
}
} catch (Exception e) {
return new DeliveryData("", "", DeliveryDataEnum.NO_DATA);
}
}
}

View File

@ -0,0 +1,27 @@
package org.hso.ecommerce.api;
import org.hso.ecommerce.api.data.Order;
import org.hso.ecommerce.api.data.OrderConfirmation;
import org.hso.ecommerce.api.data.Supplier;
import org.springframework.web.client.RestTemplate;
public class SupplierService {
private final String url;
public SupplierService(String url) {
this.url = url;
}
public Supplier getSupplier() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, Supplier.class);
}
public OrderConfirmation order(Order order) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject(url + "/order", order, OrderConfirmation.class);
}
}

View File

@ -0,0 +1,11 @@
package org.hso.ecommerce.api.data;
public class Article {
public String title;
public String manufacturer;
public String articleNumber;
public int vatPercent;
public int pricePerUnitNet;
public boolean shouldBeAdvertised;
}

View File

@ -0,0 +1,9 @@
package org.hso.ecommerce.api.data;
public class Order {
public String manufacturer;
public String articleNumber;
public int quantity;
public int maxTotalPriceCentNet;
}

View File

@ -0,0 +1,18 @@
package org.hso.ecommerce.api.data;
import java.time.LocalDateTime;
public class OrderConfirmation {
public String manufacturer;
public String articleNumber;
public int quantity;
public int pricePerUnitNetCent;
public int discountNetCent;
public int totalPriceNetCharged;
public String carrier;
public String trackingId;
public LocalDateTime estimatedArrival;
}

View File

@ -0,0 +1,21 @@
package org.hso.ecommerce.api.data;
import java.util.List;
public class Supplier {
public String id;
public String name;
public SupplierDiscount discount;
public List<Article> articles;
public Article findArticle(String manufacturer, String articleNumber) {
for (Article a : articles) {
if (a.manufacturer.equals(manufacturer) && a.articleNumber.equals(articleNumber)) {
return a;
}
}
return null;
}
}

View File

@ -0,0 +1,6 @@
package org.hso.ecommerce.api.data;
public class SupplierDiscount {
public int minimumDailySalesVolumeNetCent;
public int percentDiscount;
}

View File

@ -13,6 +13,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}

View File

@ -1,278 +0,0 @@
package org.hso.ecommerce.app;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.entities.user.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Optional;
/**
* TODO clean up this class
*/
@Controller
public class RequestController {
@Autowired
private final UserRepository userRepository = null;
static int notSoRandom = 0;
// @GetMapping("/")
// public String home() {
// return "redirect:/shop/";
// }
@GetMapping("/login")
public String login() {
return "login";
}
@PostMapping("/login")
public String loginPost(
HttpServletRequest request,
HttpServletResponse response,
@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session
) {
String gto = (String) session.getAttribute("afterLogin");
Optional<User> user = userRepository.findByEmail(username);
if (!user.isPresent()) {
request.setAttribute("error", "Email Adresse falsch.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login";
}
if (!user.get().validatePassword(password)) {
request.setAttribute("error", "Passwort falsch.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login";
}
session.setAttribute("userId", user.get().getId());
if (gto != null && gto.startsWith("/")) {
return "redirect:" + gto;
} else {
return "redirect:/";
}
}
@PostMapping("/logout")
public String logoutPost(HttpServletResponse response,
HttpSession session
) {
session.removeAttribute("userId");
return "redirect:/";
}
@GetMapping("/register")
public String register() {
return "register";
}
@PostMapping("/register")
public String registerPost(
@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("password2") String password2,
@RequestParam("type") String type
) {
return "redirect:/";
}
// @GetMapping("/shop/")
// public String shop() {
// return "shop/index";
// }
@GetMapping("/shop/search")
public String shopSearch() {
return "shop/search";
}
/* @GetMapping("/shop/checkout")
public String shopCheckout(HttpSession session, HttpServletRequest request) {
session.setAttribute("afterLogin", request.getRequestURI());
return "shop/checkout";
}
@PostMapping("/shop/checkoutFinish")
public String shopCheckoutFinish() {
return "shop/checkoutFinish";
}
@GetMapping("/shop/checkoutFinish")
public String shopCheckoutFinishGET() {
return "shop/checkoutFinish";
}*/
// @GetMapping("/shop/articles/{id}")
// public String shopArticlesById() {
// return "shop/articles/id";
// }
//
// @PostMapping("/shop/articles/{id}")
// public String shopArticlesByIdBuy(HttpSession session,
// @RequestAttribute(value = "user", required = false) User customer,
// @PathVariable("id") Integer id,
// @RequestParam("fastcheckout") Boolean fastcheckout
// ) {
// if (customer != null) {
// if (!fastcheckout) {
// return "shop/articles/post_add";
// } else {
// return "shop/checkout";
// }
// } else {
// session.setAttribute("afterLogin", "/shop/articles/" + id);
// return "redirect:/login";
// }
// }
// @GetMapping("/about")
// public String about() {
// return "about";
// }
//
// @GetMapping("/terms")
// public String terms() {
// return "terms";
// }
//
// @GetMapping("/privacy")
// public String privacy() {
// return "privacy";
// }
@GetMapping("/intern/")
public String intern() {
return "intern/index";
}
/*
@GetMapping("/intern/listedArticles/")
public String internListedArticles() {
return "intern/listedArticles/index";
}
@GetMapping("/intern/listedArticles/{id}")
public String internListedArticlesId() {
return "intern/listedArticles/id";
}
*/
@GetMapping("/intern/articles/")
public String internArticles() {
return "intern/articles/index";
}
@GetMapping("/intern/articles/{id}")
public String internArticlesId() {
return "intern/articles/id";
}
@GetMapping("/intern/customers/")
public String internCustomers() {
return "intern/customers/index";
}
@GetMapping("/intern/customers/{id}")
public String internCustomersId() {
return "intern/customers/id";
}
@GetMapping("/intern/customerOrders/")
public String internCustomerOrder() {
return "intern/customerOrders/index";
}
@GetMapping("/intern/customerOrders/{id}")
public String internCustomerOrdersId() {
return "intern/customerOrders/id";
}
@GetMapping("/intern/suppliers/")
public String internSuppliers() {
return "intern/suppliers/index";
}
@GetMapping("/intern/suppliers/{id}")
public String internSuppliersId() {
return "intern/suppliers/id";
}
@GetMapping("/intern/supplierOrders/")
public String internSupplierOrders() {
return "intern/supplierOrders/index";
}
@GetMapping("/intern/supplierOrders/{id}")
public String internSupplierOrdersId() {
return "intern/supplierOrders/id";
}
@GetMapping("/intern/accounting/")
public String accounting() {
return "intern/accounting/index";
}
@GetMapping("/intern/accounting/vat")
public String accountingVat() {
return "intern/accounting/vat";
}
@GetMapping("/intern/accounting/main")
public String accountingIntern() {
return "intern/accounting/main";
}
@GetMapping("/intern/accounting/addManual")
public String accountingAddManual() {
return "intern/accounting/addManual";
}
@GetMapping("/intern/warehouse/")
public String accountingWarehouse() {
return "intern/warehouse/index";
}
@GetMapping("/intern/warehouse/todo")
public String accountingWarehouseTodo() {
return "intern/warehouse/todo";
}
@GetMapping("/intern/warehouse/addManual")
public String accountingWarehouseAddManual() {
return "intern/warehouse/addManual";
}
@PostMapping("/intern/warehouse/progress/{id}")
public String accountingWarehouseProgressIdPost(HttpServletResponse response) {
if ((notSoRandom++) % 2 == 1) {
return "redirect:/intern/warehouse/progress/450";
} else {
response.setStatus(409);
return "intern/warehouse/error_progress_failed";
}
}
@GetMapping("/intern/warehouse/progress/{id}")
public String accountingWarehouseProgressId() {
return "intern/warehouse/id_progress";
}
@GetMapping("/intern/warehouse/slots/")
public String accountingWarehouseSlots() {
return "intern/warehouse/slots/index";
}
}

View File

@ -1,36 +0,0 @@
package org.hso.ecommerce.app;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("user")
public class UserRequestController {
@GetMapping("/")
public String user() {
return "redirect:/user/settings";
}
@GetMapping("/settings")
public String userSettings() {
return "user/settings";
}
@GetMapping("/orders/")
public String userOrdeers() {
return "user/orders/index";
}
@GetMapping("/bonuspoints")
public String userBonuspoints() {
return "user/bonuspoints";
}
@GetMapping("/notifications/")
public String userNotifications() {
return "user/notifications/index";
}
}

View File

@ -0,0 +1,161 @@
package org.hso.ecommerce.app.config;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import org.hso.ecommerce.app.config.YAMLData.Address;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import javax.annotation.PostConstruct;
@Component("appSettings")
public class AppSettings {
private YAMLData data;
private final String configFile = "config.yml";
private String installationName;
private String companyName;
private Address companyAddress;
private int numberOfStorageSpaces;
private List<YAMLData.Supplier> suppliers;
private String parcelServiceName;
private String parcelServiceApiURL;
/**
* on initialization read the config and store the data in static objects
*/
@PostConstruct
public void init() {
data = readConfig();
installationName = data.getInstallationName();
companyName = data.getCompanyName();
companyAddress = data.getCompanyAddress();
numberOfStorageSpaces = data.getNumberOfStorageSpaces();
suppliers = data.getSuppliers();
parcelServiceName = data.getParcelServiceName();
parcelServiceApiURL = data.getParcelServiceApiURL();
System.out.println("Initialised Settings!");
}
/**
* write the default config file
*/
public void writeDefaultConfig() {
YAMLData data = new YAMLData();
data.setInstallationName("eCommerce");
data.setCompanyName("eCommerce Shop UG");
data.setCompanyAddress(new Address(
"Musterstraße",
"1",
"12345",
"Musterstadt",
"Germany"
));
data.setNumberOfStorageSpaces(128);
List<YAMLData.Supplier> suppliers = new ArrayList<>();
suppliers.add(new YAMLData.Supplier(
"Bank of Chees",
"d41d8cd98f00b204e9800998ecf8427e",
"http://[::1]:8081/bank/",
4,
new Address(
"Musterstraße",
"2",
"12345",
"Musterstadt",
"Germany"
)
));
suppliers.add(new YAMLData.Supplier(
"MDA",
"18a17da5bac1cf00551b08c3e98720f5",
"http://[::1]:8081/mda/",
5,
new Address(
"Musterstraße",
"3",
"12345",
"Musterstadt",
"Germany"
)
));
data.setSuppliers(suppliers);
data.setParcelServiceName("Parcel Service");
data.setParcelServiceApiURL("http://[::1]:8082/");
try (FileWriter writer = new FileWriter("./" + configFile)) {
Yaml yaml = new Yaml();
yaml.dump(data, writer);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* read a config file named "eCommerce_config.yml" from the applications root directory
* @return the settings as YAMLData object
*/
public YAMLData readConfig() {
YAMLData data = new YAMLData();
File file = new File("./" + configFile);
if (!file.exists()) {
writeDefaultConfig();
}
try (InputStream inputStream = new FileInputStream("./" + configFile)) {
Yaml yaml = new Yaml(new Constructor(YAMLData.class));
data = yaml.load(inputStream);
} catch (FileNotFoundException e) {
System.err.println("The file \"" + configFile + "\" has not been found, please create a valid Configuration file.");
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
return data;
}
public YAMLData getData() {
return data;
}
public String getInstallationName() {
return installationName;
}
public String getCompanyName() {
return companyName;
}
public Address getCompanyAddress() {
return companyAddress;
}
public int getNumberOfStorageSpaces() {
return numberOfStorageSpaces;
}
public List<YAMLData.Supplier> getSuppliers() {
return suppliers;
}
public String getParcelServiceName() {
return parcelServiceName;
}
public String getParcelServiceApiURL() {
return parcelServiceApiURL;
}
}

View File

@ -0,0 +1,183 @@
package org.hso.ecommerce.app.config;
import java.util.List;
public class YAMLData {
private String installationName;
private String companyName;
private Address companyAddress;
private int numberOfStorageSpaces;
private List<Supplier> suppliers;
private String parcelServiceName;
private String parcelServiceApiURL;
public String getInstallationName() {
return installationName;
}
public void setInstallationName(String installationName) {
this.installationName = installationName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Address getCompanyAddress() {
return companyAddress;
}
public void setCompanyAddress(Address companyAddress) {
this.companyAddress = companyAddress;
}
public int getNumberOfStorageSpaces() {
return numberOfStorageSpaces;
}
public void setNumberOfStorageSpaces(int numberOfStorageSpaces) {
this.numberOfStorageSpaces = numberOfStorageSpaces;
}
public List<Supplier> getSuppliers() {
return suppliers;
}
public void setSuppliers(List<Supplier> suppliers) {
this.suppliers = suppliers;
}
public String getParcelServiceName() {
return parcelServiceName;
}
public void setParcelServiceName(String parcelServiceName) {
this.parcelServiceName = parcelServiceName;
}
public String getParcelServiceApiURL() {
return parcelServiceApiURL;
}
public void setParcelServiceApiURL(String parcelServiceApiURL) {
this.parcelServiceApiURL = parcelServiceApiURL;
}
public static class Address {
public String streetName;
public String houseNumber;
public String zipCode;
public String cityName;
public String countryName;
public Address() {
// needed by snakeyaml
}
public Address(String streetName, String houseNumber, String zipCode, String cityName, String countryName) {
this.streetName = streetName;
this.houseNumber = houseNumber;
this.zipCode = zipCode;
this.cityName = cityName;
this.countryName = countryName;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getHouseNumber() {
return houseNumber;
}
public void setHouseNumber(String houseNumber) {
this.houseNumber = houseNumber;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
public static class Supplier {
public String name;
public String id;
public String apiURL;
public int deliveryTime;
public Address companyAddress;
public Supplier() {
// needed by snakeyaml
}
public Supplier(String name, String id, String apiURL, int deliveryTime, Address companyAddress) {
this.name = name;
this.id = id;
this.apiURL = apiURL;
this.deliveryTime = deliveryTime;
this.companyAddress = companyAddress;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getApiURL() {
return apiURL;
}
public void setApiURL(String apiURL) {
this.apiURL = apiURL;
}
public int getDeliveryTime() {
return deliveryTime;
}
public void setDeliveryTime(int deliveryTime) {
this.deliveryTime = deliveryTime;
}
public Address getCompanyAddress() {
return companyAddress;
}
public void setCompanyAddress(Address companyAddress) {
this.companyAddress = companyAddress;
}
}
}

View File

@ -0,0 +1,38 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.entities.booking.PaymentMethod;
import org.hso.ecommerce.entities.shop.Address;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.Timestamp;
import java.util.Optional;
@Component
public class AdminInitializer {
@Autowired
private final UserRepository userRepository = null;
@PostConstruct
public void init() {
Optional<Integer> numberOfEmployees = userRepository.numberOfEmployees();
if (numberOfEmployees.orElse(0) == 0) {
// create first admin user
User firstAdmin = new User();
firstAdmin.created = new Timestamp(System.currentTimeMillis());
firstAdmin.defaultDeliveryAddress = new Address();
firstAdmin.defaultDeliveryAddress.name = "admin";
firstAdmin.defaultPayment = new PaymentMethod();
firstAdmin.defaultPayment.creditCardNumber = ""; //set empty number
firstAdmin.email = "admin";
firstAdmin.isActive = true;
firstAdmin.isEmployee = true;
firstAdmin.setPassword("admin");
userRepository.save(firstAdmin); //save to DB
}
}
}

View File

@ -24,6 +24,7 @@ public class LoginIntercepter implements HandlerInterceptor {
HttpSession session = request.getSession();
Object userId = session.getAttribute("userId");
Optional<User> user = null;
if (request.getRequestURI().startsWith("/user/")) {
System.out.println("USER");
@ -43,10 +44,24 @@ public class LoginIntercepter implements HandlerInterceptor {
response.sendRedirect("/login");
return false;
}
user = userRepository.findById((Long) userId);
if(user.isPresent() && !user.get().isEmployee)
{
session.setAttribute("afterLogin", request.getRequestURI());
response.sendRedirect("/");
return false;
}
}
if (!request.getRequestURI().startsWith("/login")) {
session.removeAttribute("afterLogin");
}
if (userId != null) {
Optional<User> user = userRepository.findById((Long) userId);
if (user == null)
user = userRepository.findById((Long) userId);
user.ifPresent(value -> request.setAttribute("user", value));
}

View File

@ -1,5 +1,6 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.warehouse.Slot;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.springframework.beans.factory.annotation.Autowired;
@ -10,14 +11,16 @@ import javax.annotation.PostConstruct;
@Component
public class SlotInitializer {
@Autowired
private final SlotRepository slotRepository = null;
@Autowired
private final SlotRepository slotRepository = null;
// TODO: use values form cfg.
private final int NUM_SLOTS = 50;
@Autowired
private final AppSettings appSettings = null;
@PostConstruct
public void init() {
int NUM_SLOTS = appSettings.getNumberOfStorageSpaces();
for (int i = 1; i <= NUM_SLOTS; i++) {
if (!slotRepository.findBySlotNum(i).isPresent()) {
Slot slotAdded = new Slot();

View File

@ -0,0 +1,42 @@
package org.hso.ecommerce.components;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.app.config.YAMLData;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Optional;
@Component
public class SupplierInitializer {
@Autowired
private final SupplierRepository supplierRepository = null;
@Autowired
private final AppSettings appSettings = null;
@PostConstruct
public void init() {
for (YAMLData.Supplier cfg : appSettings.getSuppliers()) {
Optional<Supplier> sup = supplierRepository.findByUuid(cfg.id);
Supplier supplier;
if (sup.isPresent()) {
supplier = sup.get();
supplier.name = cfg.name;
supplier.apiUrl = cfg.apiURL;
} else {
supplier = new Supplier();
supplier.uuid = cfg.id;
supplier.apiUrl = cfg.apiURL;
supplier.name = cfg.name;
}
supplierRepository.save(supplier);
}
}
}

View File

@ -1,8 +0,0 @@
package org.hso.ecommerce.controller;
import org.springframework.stereotype.Controller;
@Controller
//@RequestMapping("...")
public class BookingController {
}

View File

@ -1,8 +1,70 @@
package org.hso.ecommerce.controller;
import org.hso.ecommerce.entities.shop.ShoppingCart;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Optional;
@Controller
//@RequestMapping("...")
@RequestMapping("/")
public class LoginController {
@Autowired
private final UserRepository userRepository = null;
@GetMapping("login")
public String login() {
return "login";
}
@PostMapping("login")
public String loginPost(HttpServletRequest request, HttpServletResponse response,
@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session) {
String gto = (String) session.getAttribute("afterLogin");
Optional<User> user = userRepository.findByEmail(username);
if (!user.isPresent()) {
request.setAttribute("error", "Die Email Adresse falsch.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login";
}
if (!user.get().validatePassword(password)) {
request.setAttribute("error", "Das Passwort ist falsch.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login";
}
if (!user.get().isActive) {
request.setAttribute("error", "Dieses Konto ist deaktiviert..");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "login";
}
session.setAttribute("userId", user.get().getId());
if (gto != null && gto.startsWith("/")) {
return "redirect:" + gto;
} else if (user.get().isEmployee) {
return "redirect:/intern/";
} else {
return "redirect:/";
}
}
@PostMapping("logout")
public String logoutPost(@RequestAttribute(value = "shoppingCart") ShoppingCart shoppingCart, HttpSession session) {
session.removeAttribute("userId");
shoppingCart.clear();
return "redirect:/";
}
}

View File

@ -0,0 +1,80 @@
package org.hso.ecommerce.controller;
import org.hso.ecommerce.entities.booking.PaymentMethod;
import org.hso.ecommerce.entities.shop.Address;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Optional;
@Controller
public class RegisterController {
@Autowired
private final UserRepository userRepository = null;
@PostMapping("/register")
public String registerPost(HttpServletRequest request, HttpServletResponse response,
@RequestParam("username") String username, @RequestParam("password") String password,
@RequestParam("password2") String password2, @RequestParam("salutation") String salutation,
@RequestParam("name") String name, @RequestParam("address") String address,
HttpSession session) {
Optional<User> user = userRepository.findByEmail(username);
if (user.isPresent()) {
request.setAttribute("error", "Die Email Adresse existiert bereits.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "register";
}
if (!password.equals(password2)) {
request.setAttribute("error", "Die Passwörter stimmen nicht überein.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "register";
}
// set values for new user
User newUser = new User();
newUser.email = username;
newUser.setPassword(password);
newUser.email = username;
newUser.isEmployee = false;
newUser.salutation = salutation;
newUser.defaultPayment = PaymentMethod.fromCreditCardNumber("");
newUser.isActive = true;
newUser.created = new java.sql.Timestamp(System.currentTimeMillis());
Address newAddress = new Address();
newAddress.name = name;
newAddress.addressString = address;
newUser.defaultDeliveryAddress = newAddress;
userRepository.save(newUser); // save newUser
user = userRepository.findByEmail(username);
session.setAttribute("userId", user.get().getId());
String gto = (String) session.getAttribute("afterLogin");
//login after register
if (gto != null && gto.startsWith("/")) {
return "redirect:" + gto;
} else {
return "redirect:/";
}
}
@GetMapping("/register")
public String register() {
return "register";
}
}

View File

@ -1,8 +1,159 @@
package org.hso.ecommerce.controller;
import org.hso.ecommerce.action.user.CreateDeliveryData;
import org.hso.ecommerce.action.user.UpdateUserSettingsAction;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.app.config.AppSettings;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.stream.Collectors;
@Controller
//@RequestMapping("...")
@RequestMapping("/user")
public class UserController {
@Autowired
private final UserRepository userRepository = null;
@Autowired
private final CustomerOrderRepository customerOrderRepository = null;
@Autowired
private final RestServiceForDelivery restServiceForDelivery = null;
@Autowired
private final AppSettings appSettings = null;
@GetMapping("/")
public String user() {
return "redirect:/user/settings";
}
@GetMapping("/settings")
public String userSettings(Model model,
@RequestAttribute("user") User user
) {
model.addAttribute("user", user);
return "user/settings";
}
@GetMapping("/orders/")
public String userOrders(
@RequestAttribute("user") User user,
Model model
) {
List<CustomerOrder> orders = customerOrderRepository.getOrdersByUserId(user.id);
List<CustomerOrderDelivery> customerOrderDeliveryDataMap = orders
.stream()
.map(o -> new CustomerOrderDelivery(o, CreateDeliveryData.getDeliveryDataFromCustomerOrder(o, customerOrderRepository, restServiceForDelivery)))
.collect(Collectors.toList());
model.addAttribute("orderDeliveryDataMap", customerOrderDeliveryDataMap);
model.addAttribute("deliveryService", appSettings.getParcelServiceName());
return "user/orders/index";
}
static class CustomerOrderDelivery {
private CustomerOrder customerOrder;
private DeliveryData deliveryData;
public CustomerOrderDelivery(CustomerOrder customerOrder, DeliveryData deliveryData) {
this.customerOrder = customerOrder;
this.deliveryData = deliveryData;
}
public CustomerOrder getCustomerOrder() {
return customerOrder;
}
public DeliveryData getDeliveryData() {
return deliveryData;
}
}
@PostMapping("/settings/changeMail")
public String changeMail(HttpSession session,
@RequestParam("email") String email,
HttpServletRequest request
) {
User user = userRepository.findById((long) session.getAttribute("userId")).get();
UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository);
UpdateUserSettingsAction.UpdateResult result = cusa.updateEmail(email);
if (result.updated == false) {
request.setAttribute("error", result.errorString);
return "user/settings";
}
return "redirect:/user/settings";
}
@PostMapping("/settings/changePwd")
public String changePwd(HttpSession session,
@RequestParam("old-password") String oldPassword,
@RequestParam("password1") String password1,
@RequestParam("password2") String password2,
HttpServletRequest request
) {
User user = userRepository.findById((long) session.getAttribute("userId")).get();
UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository);
UpdateUserSettingsAction.UpdateResult result = cusa.updatePassword(oldPassword, password1, password2);
if (result.updated == false) {
request.setAttribute("error", result.errorString);
return "user/settings";
}
return "redirect:/user/settings";
}
@PostMapping("/settings/changeAddress")
public String changeAddress(HttpSession session,
@RequestParam("salutation") String salutation,
@RequestParam("name") String name,
@RequestParam("address") String address,
HttpServletRequest request
) {
User user = userRepository.findById((long) session.getAttribute("userId")).get();
UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository);
UpdateUserSettingsAction.UpdateResult result = cusa.updateShippingInfo(salutation, name, address);
if (result.updated == false) {
request.setAttribute("error", result.errorString);
return "user/settings";
}
return "redirect:/user/settings";
}
@PostMapping("/settings/changePaymentInfo")
public String changePaymentInfo(HttpSession session,
@RequestParam("creditCardNumber") String creditCardNumber,
HttpServletRequest request
) {
User user = userRepository.findById((long) session.getAttribute("userId")).get();
UpdateUserSettingsAction cusa = new UpdateUserSettingsAction(user, userRepository);
UpdateUserSettingsAction.UpdateResult result = cusa.updatePaymentInfo(creditCardNumber);
if (result.updated == false) {
request.setAttribute("error", result.errorString);
return "user/settings";
}
return "redirect:/user/settings";
}
}

View File

@ -0,0 +1,77 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Calendar;
@Component
public class AutoSupplierPayment implements ICronjob {
@Autowired
private SupplierRepository supplierRepository = null;
@Autowired
private BookingAccountEntryRepository bookingAccountEntryRepository = null;
@Autowired
private BookingRepository bookingRepository = null;
@Override
public Calendar nextExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) >= 10) {
reference.add(Calendar.DAY_OF_MONTH, 1);
}
reference.set(Calendar.HOUR_OF_DAY, 10);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public Calendar previousExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) < 10) {
reference.add(Calendar.DAY_OF_MONTH, -1);
}
reference.set(Calendar.HOUR_OF_DAY, 10);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public void executeAt(Calendar time, CronjobController controller) {
for (Supplier supplier : supplierRepository.findAll()) {
int debts = bookingAccountEntryRepository
.getBySupplier(supplier.id)
.map(entry -> entry.newSumCent).orElse(0);
if (debts > 0) {
Booking booking = new CreateBookingAction(
bookingAccountEntryRepository.getBySupplier(supplier.id).orElseGet(() -> BookingAccountEntry.newSupplier(supplier)),
null,
new BookingReason(supplier),
debts
).finish();
bookingRepository.save(booking);
}
}
}
@Override
public String getDisplayName() {
return "Supplier Auto Payment";
}
}

View File

@ -0,0 +1,201 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.entities.cron.BackgroundJob;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.cronjob.BackgroundJobRepository;
import org.hso.ecommerce.repos.dashboard.DashboardSummaryRepository;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.supplier.ArticleOfferRepository;
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.PostConstruct;
import java.sql.Timestamp;
import java.util.*;
import java.util.Map.Entry;
@Component
@RequestMapping("intern/cronjobs")
class CronjobController {
private static final Logger log = LoggerFactory.getLogger(CronjobController.class);
private Map<String, ICronjob> cronjobs;
@Autowired
private final BackgroundJobRepository cronjobRepository = null;
@Autowired
final ArticleRepository articleRepository = null;
@Autowired
final ArticleOfferRepository articleOfferRepository = null;
@Autowired
final CustomerOrderRepository customerOrderRepository = null;
@Autowired
final BookingRepository bookingRepository = null;
@Autowired
final BookingAccountEntryRepository bookingAccountEntryRepository = null;
@Autowired
final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
final DashboardSummaryRepository dashboardSummaryRepository = null;
@Autowired
final SupplierRepository supplierRepository = null;
@Autowired
final SupplierOrderRepository supplierOrderRepository = null;
@Autowired
final Reorder reorderJob = null;
@Autowired
final DashboardCronjob dashboardCronjob = null;
@Autowired
final AutoSupplierPayment autoSupplierPaymentJob = null;
@PostConstruct
public void init() {
cronjobs = getCronjobs();
}
private Map<String, ICronjob> getCronjobs() {
HashMap<String, ICronjob> map = new HashMap<>();
// Register all existing cronjobs
map.put(BackgroundJob.JOB_REORDER, reorderJob);
map.put(BackgroundJob.JOB_DASHBOARD, dashboardCronjob);
map.put(BackgroundJob.JOB_SUPPLIER_AUTO_PAYMENT, autoSupplierPaymentJob);
return Collections.unmodifiableMap(map);
}
private ScheduledCronjob getNextCronjob() {
Calendar currentTime = new GregorianCalendar();
Iterable<BackgroundJob> jobs = cronjobRepository.findAll();
HashMap<String, BackgroundJob> alreadyExecuted = new HashMap<>();
for (BackgroundJob job : jobs) {
alreadyExecuted.put(job.jobName, job);
}
ScheduledCronjob earliestJob = null;
for (Entry<String, ICronjob> entry : cronjobs.entrySet()) {
ScheduledCronjob resultingJob;
BackgroundJob dbEntry = alreadyExecuted.get(entry.getKey());
if (dbEntry != null) {
Calendar previousExecution = new GregorianCalendar();
previousExecution.setTimeInMillis(dbEntry.lastExecution.getTime());
Calendar followingSchedule = entry.getValue().nextExecution((Calendar) previousExecution.clone());
Calendar lastSchedule = entry.getValue().previousExecution((Calendar) currentTime.clone());
if (lastSchedule.getTimeInMillis() > followingSchedule.getTimeInMillis()) {
// This happens, if more than one execution was missed.
// In this case, run the job only once.
followingSchedule = lastSchedule;
}
resultingJob = new ScheduledCronjob(followingSchedule, entry.getValue(), dbEntry);
} else {
// This cronjob has never been executed before.
Calendar lastScheduleTime = entry.getValue().previousExecution((Calendar) currentTime.clone());
BackgroundJob model = new BackgroundJob();
model.jobName = entry.getKey();
resultingJob = new ScheduledCronjob(lastScheduleTime, entry.getValue(), model);
}
// Look for the job with earliest executionTime - it will run next
if (earliestJob == null
|| resultingJob.executionTime.getTimeInMillis() < earliestJob.executionTime.getTimeInMillis()) {
earliestJob = resultingJob;
}
}
return earliestJob;
}
private void runCronjobExecutionLoop() {
Thread.currentThread().setName("Cronjob");
try {
while (true) {
ScheduledCronjob nextJob = getNextCronjob();
if (nextJob == null) {
// In case there are no cronjobs
return;
}
long waitingTime = nextJob.executionTime.getTimeInMillis() - System.currentTimeMillis();
if (waitingTime > 0) {
Thread.sleep(waitingTime);
}
try {
nextJob.cronjob.executeAt(nextJob.executionTime, this);
} catch (Throwable t) {
log.error("Failed to execute cronjob " + nextJob.cronjob.getClass() + ":");
t.printStackTrace();
}
nextJob.model.lastExecution = new Timestamp(nextJob.executionTime.getTimeInMillis());
cronjobRepository.save(nextJob.model);
}
} catch (InterruptedException e) {
log.error("The cronjob execution thread has been interrupted");
}
}
@PostConstruct
public void onPostConstruct() {
new Thread(this::runCronjobExecutionLoop).start();
}
class ManualCronjob {
public final String identifier;
public final String visibleName;
public ManualCronjob(String identifier, String visibleName) {
this.identifier = identifier;
this.visibleName = visibleName;
}
}
private List<ManualCronjob> listAllCronjobs() {
ArrayList<ManualCronjob> entries = new ArrayList<>();
for (Entry<String, ICronjob> job : cronjobs.entrySet()) {
entries.add(new ManualCronjob(job.getKey(), job.getValue().getDisplayName()));
}
return entries;
}
@GetMapping("/")
public String cronjobOverview(Model model) {
model.addAttribute("jobs", listAllCronjobs());
return "intern/cronjobs/index";
}
@PostMapping("/run/{identifier}")
public String runCronjob(Model model, @PathVariable("identifier") String identifier) {
ICronjob jobToExecute = cronjobs.get(identifier);
if (jobToExecute != null) {
jobToExecute.executeAt(new GregorianCalendar(), this);
model.addAttribute("info",
String.format("Der Cronjob \"%s\" wurde ausgeführt.", jobToExecute.getDisplayName()));
} else {
model.addAttribute("error", "Der Cronjob konnte nicht gefunden werden.");
}
model.addAttribute("jobs", listAllCronjobs());
return "intern/cronjobs/index";
}
}

View File

@ -0,0 +1,100 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.action.warehouse.CalculateWarehouseStatsAction;
import org.hso.ecommerce.entities.dashboard.DashboardSummary;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class DashboardCronjob implements ICronjob {
@Autowired
private SlotRepository slotRepository = null;
@Autowired
private UserRepository userRepository = null;
@Autowired
private WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private CustomerOrderRepository customerOrderRepository = null;
@Override
public Calendar nextExecution(Calendar reference) {
reference.add(Calendar.DAY_OF_MONTH, 1);
reference.set(Calendar.HOUR_OF_DAY, 0);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public Calendar previousExecution(Calendar reference) {
reference.set(Calendar.HOUR_OF_DAY, 0);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public void executeAt(Calendar time, CronjobController controller) {
Calendar oneDayBefore = (Calendar) time.clone();
oneDayBefore.add(Calendar.DAY_OF_MONTH, -1);
DashboardSummary dashboardSummary = new DashboardSummary();
List<WarehouseBookingPositionSlotEntry> entries = slotRepository.findAll().stream().map(
s -> warehouseBookingPositionSlotEntryRepository
.getBySlotNum(s.slotNum)
.orElseGet(() -> WarehouseBookingPositionSlotEntry.empty(null, s))
).collect(Collectors.toList());
CalculateWarehouseStatsAction.WarehouseStats warehouseStats = new CalculateWarehouseStatsAction(entries).finish();
dashboardSummary.created = new java.sql.Date(time.getTimeInMillis());
dashboardSummary.todaysCustomersOrders = nullToZero(getSales(oneDayBefore, time));
dashboardSummary.todaysNewCustomers = nullToZero(getNewUsers(oneDayBefore, time));
dashboardSummary.todaysWarehouseCapacity = warehouseStats.efficiency;
dashboardSummary.currentWarehouseCapacity = warehouseStats.ratioUsedSlots;
dashboardSummary.todaysSalesCent = nullToZero(getTurnover(oneDayBefore, time));
controller.dashboardSummaryRepository.save(dashboardSummary);
}
@Override
public String getDisplayName() {
return "Dashboard refresh";
}
private Integer getSales(Calendar begin, Calendar end) {
return customerOrderRepository.countOrdersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getTurnover(Calendar begin, Calendar end) {
return customerOrderRepository.countTurnoverInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private Integer getNewUsers(Calendar begin, Calendar end) {
return userRepository.countUsersInTimespan(
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
private int nullToZero(Integer input) {
return input == null ? 0 : input;
}
}

View File

@ -0,0 +1,38 @@
package org.hso.ecommerce.controller.cronjob;
import java.util.Calendar;
interface ICronjob {
/**
* Calculate the earliest cronjob execution time that happens after the given reference time.
*
* @param reference Position in time to start searching. The implementor is allowed to modify the reference time.
* @return A new Calendar instance (or the same) containing the time for next execution.
*/
Calendar nextExecution(Calendar reference);
/**
* Calculate the latest cronjob execution time that happens before or exactly at the given refernce time.
*
* @param reference Position in time to start searching. The implementor is allowed to modify the reference time.
* @return A new Calendar instance (or the same) containing the time of the last execution.
*/
Calendar previousExecution(Calendar reference);
/**
* Execute this cronjob.
*
* @param time The point in time this execution was scheduled. In case of a missed cronjob, the actual time of
* this call might be much later.
* @param controller Back-reference that allows to use repositories.
*/
void executeAt(Calendar time, CronjobController controller);
/**
* Get a name for this cronjob, that can be presented to the user in the frontend.
*
* @return A german name of this cronjob.
*/
String getDisplayName();
}

View File

@ -0,0 +1,164 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.action.cronjob.ReadSupplierDataAction;
import org.hso.ecommerce.action.cronjob.ReorderAction;
import org.hso.ecommerce.action.cronjob.UpdateOffersAction;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.supplier.ArticleOffer;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.springframework.stereotype.Component;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
@Component
class Reorder implements ICronjob {
@Override
public String getDisplayName() {
return "Nachbestellung";
}
@Override
public Calendar nextExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) >= 8) {
reference.add(Calendar.DAY_OF_MONTH, 1);
}
reference.set(Calendar.HOUR_OF_DAY, 8);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
@Override
public Calendar previousExecution(Calendar reference) {
if (reference.get(Calendar.HOUR_OF_DAY) < 8) {
reference.add(Calendar.DAY_OF_MONTH, -1);
}
reference.set(Calendar.HOUR_OF_DAY, 8);
reference.set(Calendar.MINUTE, 0);
reference.set(Calendar.SECOND, 0);
reference.set(Calendar.MILLISECOND, 0);
return reference;
}
/**
* Calculates the amount of ordered articles by customers for the given article type in the time between begin and
* end.
*
* @param article The article to search orders for.
* @param begin The start time for the search (included)
* @param end The end time for the search (excluded)
* @return The number of articles that were ordered by customers in the given range.
*/
private Integer getOrderedAmounts(Article article, Calendar begin, Calendar end, CronjobController controller) {
return controller.customerOrderRepository.countOrdersOfArticleInTimespan(
article.id,
new Timestamp(begin.getTimeInMillis()), new Timestamp(end.getTimeInMillis()));
}
/**
* Calculates the amount of ordered articles by customers for the given article type in the three days before the
* given reference time. The return-array contains 3 fields: Index 0: Orders 72 to 48 hours ago; Index 1: Orders 48
* to 24 hours ago; Index 2: Orders 24 to 0 hours ago.
*
* @param article The article for which the customer orders are checked.
* @param time The reference time to use for calculation of the last orders.
* @return A 3-element array containing the orders of the last three days.
*/
private Integer[] getOrderedAmounts(Article article, Calendar time, CronjobController controller) {
Calendar oneDayBefore = (Calendar) time.clone();
oneDayBefore.add(Calendar.DAY_OF_MONTH, -1);
Calendar twoDaysBefore = (Calendar) time.clone();
twoDaysBefore.add(Calendar.DAY_OF_MONTH, -2);
Calendar threeDaysBefore = (Calendar) time.clone();
threeDaysBefore.add(Calendar.DAY_OF_MONTH, -3);
return new Integer[]{ //
getOrderedAmounts(article, threeDaysBefore, twoDaysBefore, controller), //
getOrderedAmounts(article, twoDaysBefore, oneDayBefore, controller), //
getOrderedAmounts(article, oneDayBefore, time, controller), //
};
}
private HashMap<ReadSupplierDataAction.ArticleIdentifier, ArticleOffer> mapArticleOffers(List<ArticleOffer> articleOffers) {
HashMap<ReadSupplierDataAction.ArticleIdentifier, ArticleOffer> map = new HashMap<>();
for (ArticleOffer articleOffer : articleOffers) {
ReadSupplierDataAction.ArticleIdentifier identifier = new ReadSupplierDataAction.ArticleIdentifier(articleOffer.manufacturer, articleOffer.articleNumber);
map.put(identifier, articleOffer);
}
return map;
}
@Override
public void executeAt(Calendar time, CronjobController controller) {
List<Supplier> suppliers = controller.supplierRepository.findAll();
ReadSupplierDataAction.Result supplierData = new ReadSupplierDataAction(suppliers).finish();
// Save the new offers in the database
List<ArticleOffer> allOffers = controller.articleOfferRepository.findAll();
allOffers = new UpdateOffersAction(allOffers, supplierData.cheapestOffer).finish();
controller.articleOfferRepository.saveAll(allOffers);
HashMap<ReadSupplierDataAction.ArticleIdentifier, ArticleOffer> mappedOffers = mapArticleOffers(allOffers);
// Reorder
List<Article> allArticles = controller.articleRepository.findAll();
for (Article article : allArticles) {
Integer[] orderedAmounts = getOrderedAmounts(article, time, controller);
Integer undeliveredReorders = controller.supplierOrderRepository
.countUndeliveredReorders(article.related.articleNumber);
int amountInStock = controller.warehouseBookingPositionSlotEntryRepository
.getByArticle(article.id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum();
ReorderAction action = new ReorderAction(article, orderedAmounts,
undeliveredReorders,
amountInStock,
supplierData.cheapestOffer, mappedOffers);
SupplierOrder order = action.finish();
if (order != null) {
controller.supplierOrderRepository.save(order);
// Create bookings for this order
int netPrice = order.totalPriceNet;
int vatPercent = order.ordered.vatPercent;
int vatAmount = netPrice * vatPercent / 100;
int grossPrice = netPrice + vatAmount;
// Obligation towards the supplier
BookingAccountEntry mainAccount = controller.bookingAccountEntryRepository.getByMain()
.orElseGet(BookingAccountEntry::newMain);
BookingAccountEntry supplierAccount = controller.bookingAccountEntryRepository
.getBySupplier(order.supplier.id)
.orElseGet(() -> BookingAccountEntry.newSupplier(order.supplier));
BookingReason obligationReason = new BookingReason(order);
Booking obligationBooking = new CreateBookingAction(mainAccount,
supplierAccount,
obligationReason,
grossPrice).finish();
controller.bookingRepository.save(obligationBooking);
// Input Tax
BookingAccountEntry vatAccount = controller.bookingAccountEntryRepository.getByVat()
.orElseGet(BookingAccountEntry::newVat);
mainAccount = controller.bookingAccountEntryRepository.getByMain().get();
BookingReason inputTaxReason = new BookingReason(order);
Booking inputTaxBooking = new CreateBookingAction(vatAccount, mainAccount, inputTaxReason, vatAmount)
.finish();
controller.bookingRepository.save(inputTaxBooking);
}
}
}
}

View File

@ -0,0 +1,17 @@
package org.hso.ecommerce.controller.cronjob;
import org.hso.ecommerce.entities.cron.BackgroundJob;
import java.util.Calendar;
class ScheduledCronjob {
public final Calendar executionTime;
public final ICronjob cronjob;
public final BackgroundJob model;
public ScheduledCronjob(Calendar executionTime, ICronjob cronjob, BackgroundJob model) {
this.executionTime = executionTime;
this.cronjob = cronjob;
this.model = model;
}
}

View File

@ -0,0 +1,25 @@
package org.hso.ecommerce.controller.intern;
import org.hso.ecommerce.entities.dashboard.DashboardSummary;
import org.hso.ecommerce.repos.dashboard.DashboardSummaryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DashboardController {
@Autowired
private DashboardSummaryRepository dashboardSummaryRepository;
@GetMapping("/intern/dashboardsummary")
public List<DashboardSummary> getDashboardEntries()
{
List<DashboardSummary> inTimespan = dashboardSummaryRepository.findInTimespan(PageRequest.of(0, 7) );
return inTimespan;
}
}

View File

@ -1,36 +1,63 @@
package org.hso.ecommerce.controller.intern;
import java.util.ArrayList;
import java.util.List;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.shop.Category;
import org.hso.ecommerce.entities.shop.Image;
import org.hso.ecommerce.entities.supplier.ArticleOffer;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CategoryRepository;
import org.hso.ecommerce.repos.shop.ImageRepository;
import org.hso.ecommerce.repos.shop.OffersRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.view.RedirectView;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Controller
@RequestMapping("intern/listedArticles")
public class InternArticleController
{
@RequestMapping("intern/articles")
public class InternArticleController {
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseEntryRepository = null;
@GetMapping("/")
public String internListedArticles(Model model)
{
@Autowired
private final CategoryRepository categoryRepository = null;
List<ListedArticlesListTotals> totals = new ArrayList<ListedArticlesListTotals>();
@Autowired
private final OffersRepository offersRepository = null;
@Autowired
private final ImageRepository imageRepository = null;
@GetMapping("/")
public String internListedArticles(Model model) {
List<UImodelArticles> totals = new ArrayList<UImodelArticles>();
for (Article article : articleRepository.findAll()) {
ListedArticlesListTotals tmp = new ListedArticlesListTotals();
tmp.addListedArticle(article,
warehouseEntryRepository.getArticleStock(article.id).orElse(0));
UImodelArticles tmp = new UImodelArticles();
tmp.addListedArticle(article, warehouseEntryRepository
.getByArticle(article.id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum());
totals.add(tmp);
}
@ -39,43 +66,229 @@ public class InternArticleController
}
@GetMapping("/{id}")
public String internListedArticlesId()
{
public String internListedArticlesId(Model model, @PathVariable String id) {
long articleId = Long.parseLong(id);
UImodelArticle total = new UImodelArticle();
total.addArticle(
articleRepository.findById(articleId).get(),
warehouseEntryRepository.getByArticle(articleId)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum());
model.addAttribute("ArticleID", total);
return "intern/listedArticles/id";
}
public static class ListedArticlesListTotals
{
@PostMapping("/{id}/saveChanges")
public RedirectView saveChanges(@PathVariable(required = true) int id,
@RequestParam(value = "title", required = true) String title,
@RequestParam(value = "description", required = true) String description,
@RequestParam(value = "units-per-slot", required = true) String warehouseUnitsPerSlot,
@RequestParam(value = "priceNet", required = true) String pricenetto,
@RequestParam(value = "reorderMaxPrice", required = true) String reorderMaxPrice,
@RequestParam(value = "autobuy", required = true) Boolean shouldReorder,
@RequestParam(value = "categorie", required = true) String categories,
@RequestParam(value = "img", required = true) MultipartFile imgFile) {
Article tmpArticle = articleRepository.findArticleById(id); // get the old article
String[] separatedCategories = categories.split("\n");
tmpArticle.categories.clear();
// loop through all categories strings and create a new category if a new one;
// also adds the categories to the article
for (String category : separatedCategories) {
tmpArticle.categories.add(categoryRepository.findCategoryByName(category.trim())
.orElseGet(() -> new Category(category.trim())));
}
tmpArticle.shouldReorder = shouldReorder;
tmpArticle.reorderMaxPrice = (int) (Float.parseFloat(reorderMaxPrice) * 100);
tmpArticle.shopPricePerUnitNetCent = (int) (Float.parseFloat(pricenetto) * 100);
tmpArticle.warehouseUnitsPerSlot = Integer.parseInt(warehouseUnitsPerSlot);
tmpArticle.title = title;
updateImage(tmpArticle, imgFile);
tmpArticle.description = description;
articleRepository.save(tmpArticle); // save updated article
return new RedirectView("../"); // return to overview page
}
@PostMapping("/addArticle/{id}")
public RedirectView addArticle(@PathVariable(required = true) String id) {
// article is not already listed, create new one
long offeredArticleID = Long.parseLong(id);
Article tmpArticle = new Article();
ArticleOffer offeredArticle = offersRepository.findById(offeredArticleID).get();
// Check for duplicates
Optional<Article> related = articleRepository.findArticleByArticleOffer(offeredArticle);
if (related.isPresent()) {
return new RedirectView("../" + related.get().id);
}
// set default values
tmpArticle.description = "";
tmpArticle.reorderMaxPrice = 0;
tmpArticle.shopPricePerUnitNetCent = offeredArticle.pricePerUnitNet;
tmpArticle.shouldReorder = false;
tmpArticle.title = offeredArticle.title;
tmpArticle.warehouseUnitsPerSlot = 1;
setDefaultImage(tmpArticle);
tmpArticle.related = offeredArticle;
articleRepository.save(tmpArticle); // save new article
// return to edit article page
return new RedirectView("../" + articleRepository.findArticleIDByRelatedID(offeredArticleID).get());
}
private void setDefaultImage(Article article) {
String defaultImagePath = "./data/img/no_product_img.jpg"; // path + name of default img
Optional<Integer> imageID = imageRepository.findImageIDByPath(defaultImagePath); // get default img
if (imageID.isPresent()) {
// default img is in DB
article.image = imageRepository.findImageById(imageID.get()); // set default img to new article
} else {
// default img is not in DB
File tmpFile = new File(defaultImagePath);
// test if default img file exits
if (!tmpFile.exists()) {
// fallback if the file not exists
// create new file
BufferedImage bufferedImage = new BufferedImage(422, 428, BufferedImage.TYPE_INT_RGB);
try {
ImageIO.write(bufferedImage, "jpg", new File(defaultImagePath)); // save new file on disk
} catch (IOException e) {
e.printStackTrace();
}
}
Image defaultImage = new Image();
defaultImage.path = defaultImagePath; // set new file to default img
imageRepository.save(defaultImage); // save default img
article.image = defaultImage; // set default img to new article
}
}
private void updateImage(Article article, MultipartFile imgFile) {
// check if a file is present
if (imgFile.getSize() > 0) {
try {
// get file name based on MD5 hash
String fileName = DigestUtils.md5DigestAsHex(imgFile.getBytes()) + ".jpg";
// check if img is already in system
Optional<Integer> imageID = imageRepository.findImageIDByPath("./data/img/" + fileName);
if (imageID.isPresent()) {
article.image = imageRepository.findImageById(imageID.get()); // add existing img to article
} else {
Path targetPath = Paths.get("./data/img/");
if (Files.notExists(targetPath)) {
Files.createDirectories(targetPath);
}
// write new img file to disk
Files.newOutputStream(Paths.get(targetPath.toString(), fileName)).write(imgFile.getBytes());
// create new img
Image newImage = new Image();
newImage.path = "./data/img/" + fileName; // set new file to new img
imageRepository.save(newImage); // save new img
article.image = newImage; // set new img to article
}
} catch (IOException e) {
e.printStackTrace();
setDefaultImage(article); // if upload failed, reset to default img
}
}
}
public static class UImodelArticles {
public String imgPath;
public String title;
public String price;
public String price_netto;
public String priceNet;
public String categorie;
public int stock;
public long offer_id;
public long offerID;
public long id;
void addListedArticle(Article article, int stock)
{
this.imgPath = article.image.path;
void addListedArticle(Article article, int stock) {
if (article.image != null) {
this.imgPath = article.image.path;
}
this.title = article.title;
this.price_netto = String.format("%.2f",
((float) article.shopPricePerUnitNetCent / 100));
this.price = String.format("%.2f",
((float) article.getPriceGross() / 100));
this.categorie = article.getCategories();
this.priceNet = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100));
this.price = String.format("%.2f", ((float) article.getPriceGross() / 100));
StringBuilder result = new StringBuilder();
for (Category temp : article.categories) {
result.append(temp.name + " ");
}
this.categorie = result.toString();
this.stock = stock;
this.offer_id = article.related.id;
this.offerID = article.related.id;
this.id = article.id;
}
}
public static class UImodelArticle {
public String imgPath;
public String title;
public String price;
public String priceNet;
public String reorderMaxPrice;
public String categorie;
public int stock;
public long offerID;
public long id;
public boolean shouldReorder;
public String warehouseUnitsPerSlot;
public String description;
public int vatPercent;
public String getCategorie() {
return categorie;
}
public String getDescription() {
return description;
}
void addArticle(Article article, int stock) {
if (article.image != null) {
this.imgPath = article.image.path;
}
this.title = article.title;
this.priceNet = String.format("%.2f", ((float) article.shopPricePerUnitNetCent / 100));
this.price = String.format("%.2f", ((float) article.getPriceGross() / 100));
StringBuilder result = new StringBuilder();
for (Category temp : article.categories) {
result.append(temp.name);
result.append("\n");
}
this.categorie = result.toString();
this.stock = stock;
this.offerID = article.related.id;
this.id = article.id;
this.reorderMaxPrice = String.format("%.2f", ((float) article.reorderMaxPrice / 100));
this.shouldReorder = article.shouldReorder;
this.warehouseUnitsPerSlot = String.valueOf(article.warehouseUnitsPerSlot);
this.description = article.description;
this.vatPercent = article.related.vatPercent;
}
}
}

View File

@ -1,8 +1,34 @@
package org.hso.ecommerce.controller.intern;
import java.util.Optional;
import org.hso.ecommerce.controller.intern.accounting.AccountingController;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern")
public class InternIndexController {
@Autowired
private final BookingAccountEntryRepository bookingAccountEntryRepository = null;
@GetMapping("/")
public String intern(Model model) {
Optional<BookingAccountEntry> mainAccount = bookingAccountEntryRepository.getByMain();
int mainAccountBalance = mainAccount.map(entry -> entry.newSumCent).orElse(0);
Optional<BookingAccountEntry> vatAccount = bookingAccountEntryRepository.getByVat();
int vatAccountBalance = vatAccount.map(entry -> entry.newSumCent).orElse(0);
model.addAttribute("mainAccountBalance", AccountingController.fmtEuro(mainAccountBalance));
model.addAttribute("vatAccountBalance", AccountingController.fmtEuro(vatAccountBalance));
return "intern/index";
}
}

View File

@ -1,8 +1,27 @@
package org.hso.ecommerce.controller.intern;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/warehouse/")
public class WarehouseController {
@Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null;
@GetMapping("/")
public String accountingWarehouse(
Model model,
HttpServletRequest request
) {
model.addAttribute("bookings", warehouseBookingRepository.findAllDesc());
return "intern/warehouse/index";
}
}

View File

@ -0,0 +1,229 @@
package org.hso.ecommerce.controller.intern.accounting;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@Controller
public class AccountingController {
@Autowired
private BookingRepository bookingRepository = null;
/**
* A description used to render the html template for the overview of all bookings
*/
static class TemplateBooking {
public String datetime;
public String amount;
public String source;
public String sourceAddr;
public String sourceBalance;
public String destination;
public String destinationAddr;
public String destinationBalance;
public String reason;
public String reference;
public String referenceAddr;
}
/**
* A description used to render the html template for bookings on a specific account
*/
public static class ShortTemplateBooking {
public String datetime;
public String amount;
public String source;
public String sourceAddr;
public String balance;
public String reason;
public String reference;
public String referenceAddr;
}
public static class ShortTemplateBookingResult {
public final String balance;
public final List<ShortTemplateBooking> bookings;
public ShortTemplateBookingResult(String balance, List<ShortTemplateBooking> bookings) {
this.balance = balance;
this.bookings = bookings;
}
}
/**
* Used to check which side of the booking (source or destination) is the account we are currently looking at.
*/
public interface AccountFilter {
boolean matches(BookingAccountEntry account);
}
/**
* Renders template information for the bookings of one specific account.
*
* @param bookings The (already fetched) booking entities to display
* @param currentAccount A filter matching the account that is to be displayed
* @return A collection result containing the final balance and all booking entries.
*/
public ShortTemplateBookingResult buildShortTemplate(List<Booking> bookings, AccountFilter currentAccount) {
List<ShortTemplateBooking> templateBookings = new ArrayList<>();
for (Booking booking : bookings) {
ShortTemplateBooking tb = new ShortTemplateBooking();
tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created);
tb.reason = booking.reason.comment;
tb.reference = describeReference(booking.reason);
tb.referenceAddr = linkToReference(booking.reason);
if (booking.destination != null && currentAccount.matches(booking.destination)) {
// Money was transferred onto the account we are looking at.
tb.amount = fmtEuro(booking.amountCent);
if (booking.source != null) {
tb.source = describeAccount(booking.source);
tb.sourceAddr = linkAddrOfAccount(booking.source);
} else {
tb.source = "-";
tb.sourceAddr = null;
}
tb.balance = fmtEuro(booking.destination.newSumCent);
} else if (booking.source != null && currentAccount.matches(booking.source)) {
// Money was transferred away from the account we are looking at.
tb.amount = fmtEuro(-booking.amountCent); // Negative because of the transfer away
if (booking.destination != null) {
tb.source = describeAccount(booking.destination); // 'source' means 'the other account' in this case
tb.sourceAddr = linkAddrOfAccount(booking.destination);
} else {
tb.source = "-";
tb.sourceAddr = null;
}
tb.balance = fmtEuro(booking.source.newSumCent);
} else {
throw new RuntimeException(
"This booking should not appear in this list, because neither source nor destination is the account we are looking at.");
}
templateBookings.add(tb);
}
String balance = templateBookings.isEmpty() ? fmtEuro(0) : templateBookings.get(0).balance;
return new ShortTemplateBookingResult(balance, templateBookings);
}
public static String fmtEuro(long amountCent) {
return String.format("%.2f EUR", amountCent / 100.0);
}
private String describeAccount(BookingAccountEntry account) {
if (account.isMainAccount) {
return "Hauptkonto";
} else if (account.isVATAccount) {
return "Mehrwertsteuer";
} else if (account.supplierAccount != null) {
return "Lieferant " + account.supplierAccount.id;
} else if (account.userAccount != null) {
return "Kunde " + account.userAccount.id;
} else {
return "- unbekannt -";
}
}
private String linkAddrOfAccount(BookingAccountEntry account) {
if (account.isMainAccount) {
return "/intern/accounting/main";
} else if (account.isVATAccount) {
return "/intern/accounting/vat";
} else if (account.supplierAccount != null) {
return "/intern/suppliers/" + account.supplierAccount.id;
} else if (account.userAccount != null) {
return "/intern/customers/" + account.userAccount.id;
} else {
return null;
}
}
private String describeReference(BookingReason reason) {
if (reason.customerOrder != null) {
return reason.customerOrder.id + "";
} else if (reason.customerPayment != null) {
return "Bezahlung mit Kreditkarte " + reason.customerPayment.payment.creditCardNumber;
} else if (reason.supplierOrder != null) {
return "Lieferanten-Bestellung " + reason.supplierOrder.supplier.name;
} else if (reason.supplierPayment != null) {
return "Lieferanten-Zahlung " + reason.supplierPayment.name;
} else {
return "-";
}
}
private String linkToReference(BookingReason reason) {
if (reason.customerOrder != null) {
return "/intern/customerOrders/" + reason.customerOrder.id;
} else if (reason.supplierPayment != null) {
return "/intern/suppliers/#q=" + reason.supplierPayment.id;
} else if (reason.supplierOrder != null) {
return "/intern/supplierOrders/#q=" + reason.supplierOrder.id;
} else {
return null;
}
}
@GetMapping("/intern/accounting/")
public String accounting(Model model) {
List<Booking> bookings = bookingRepository.allBookingsReverseChronologically();
List<TemplateBooking> templateBookings = new ArrayList<>();
for (Booking booking : bookings) {
TemplateBooking tb = new TemplateBooking();
tb.datetime = new SimpleDateFormat("dd.MM.yyyy HH:mm").format(booking.created);
tb.amount = fmtEuro(booking.amountCent);
if (booking.source != null) {
tb.source = describeAccount(booking.source);
tb.sourceAddr = linkAddrOfAccount(booking.source);
tb.sourceBalance = fmtEuro(booking.source.newSumCent);
} else {
tb.source = "-";
tb.sourceAddr = null;
tb.sourceBalance = "-";
}
if (booking.destination != null) {
tb.destination = describeAccount(booking.destination);
tb.destinationAddr = linkAddrOfAccount(booking.destination);
tb.destinationBalance = fmtEuro(booking.destination.newSumCent);
} else {
tb.destination = "-";
tb.destinationAddr = null;
tb.destinationBalance = "-";
}
tb.reason = booking.reason.comment;
tb.reference = describeReference(booking.reason);
tb.referenceAddr = linkToReference(booking.reason);
templateBookings.add(tb);
}
model.addAttribute("bookings", templateBookings);
return "intern/accounting/index";
}
@GetMapping("/intern/accounting/vat")
public String accountingVat(Model model) {
List<Booking> bookings = bookingRepository.vatBookingsReverseChronologically();
ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isVATAccount);
model.addAttribute("balance", result.balance);
model.addAttribute("bookings", result.bookings);
return "intern/accounting/vat";
}
@GetMapping("/intern/accounting/main")
public String accountingIntern(Model model) {
List<Booking> bookings = bookingRepository.mainBookingsReverseChronologically();
ShortTemplateBookingResult result = buildShortTemplate(bookings, account -> account.isMainAccount);
model.addAttribute("balance", result.balance);
model.addAttribute("bookings", result.bookings);
return "intern/accounting/main";
}
}

View File

@ -0,0 +1,270 @@
package org.hso.ecommerce.controller.intern.accounting;
import java.util.Optional;
import org.hso.ecommerce.action.booking.CreateBookingAction;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.BookingReason;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class ManualAccountingController {
@Autowired
final BookingRepository bookingRepository = null;
@Autowired
final BookingAccountEntryRepository bookingAccountEntryRepository = null;
@Autowired
final UserRepository userRepository = null;
@Autowired
final SupplierRepository supplierRepository = null;
/**
* Collection for the form values used to create a manual booking
*/
public static class ManualAccounting {
String amount;
String source;
String sourceCustomer;
String sourceSupplier;
String destination;
String destinationCustomer;
String destinationSupplier;
String reason;
String reasonText;
/**
* Default constructor with default values for the form
*/
public ManualAccounting() {
amount = "0.00";
reason = "Manual";
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getSourceCustomer() {
return sourceCustomer;
}
public void setSourceCustomer(String sourceCustomer) {
this.sourceCustomer = sourceCustomer;
}
public String getSourceSupplier() {
return sourceSupplier;
}
public void setSourceSupplier(String sourceSupplier) {
this.sourceSupplier = sourceSupplier;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getDestinationCustomer() {
return destinationCustomer;
}
public void setDestinationCustomer(String destinationCustomer) {
this.destinationCustomer = destinationCustomer;
}
public String getDestinationSupplier() {
return destinationSupplier;
}
public void setDestinationSupplier(String destinationSupplier) {
this.destinationSupplier = destinationSupplier;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public String getReasonText() {
return reasonText;
}
public void setReasonText(String reasonText) {
this.reasonText = reasonText;
}
}
/**
* An exception to represent errors that can be shown to the user
*/
public static class InvalidFormDataException extends Exception {
public InvalidFormDataException(String msg) {
super(msg);
}
private static final long serialVersionUID = 1L;
}
private boolean sameAccountBothSides(ManualAccounting formData) {
// No need to check for NumberFormatException because the numbers were already parsed before.
if (!formData.getSource().equals(formData.getDestination())) {
return false;
} else if (formData.getSource().equals("Cust")
&& Long.parseLong(formData.getSourceCustomer()) != Long.parseLong(formData.getDestinationCustomer())) {
return false;
} else if (formData.getSource().equals("Sup")
&& Long.parseLong(formData.getSourceSupplier()) != Long.parseLong(formData.getDestinationSupplier())) {
return false;
} else {
return true;
}
}
public Booking createBooking(ManualAccounting formData) throws InvalidFormDataException {
if (formData.getSource() == null) {
throw new InvalidFormDataException("Bitte wählen Sie ein Quellkonto aus.");
} else if (formData.getDestination() == null) {
throw new InvalidFormDataException("Bitte wählen Sie ein Zielkonto aus.");
}
BookingAccountEntry source = getAccountFromFormData(formData.getSource(), formData.getSourceCustomer(),
formData.getSourceSupplier());
BookingAccountEntry destination = getAccountFromFormData(formData.getDestination(),
formData.getDestinationCustomer(), formData.getDestinationSupplier());
if (sameAccountBothSides(formData)) {
throw new InvalidFormDataException("Quell- und Zielkonto dürfen nicht das selbe sein.");
}
double doubleAmount;
try {
doubleAmount = Double.parseDouble(formData.amount);
} catch (NumberFormatException e) {
throw new InvalidFormDataException("Der angegebene Betrag ist ungültig.");
}
int amountCent = (int) Math.round(doubleAmount * 100);
BookingReason reason = new BookingReason();
if (formData.getReason().equals("Start")) {
reason.isStartBooking = true;
} else if (formData.getReason().equals("Manual")) {
reason.isManuel = true;
} else {
throw new RuntimeException("Invalid form value for the booking reason: " + formData.getReason());
}
reason.comment = formData.reasonText;
CreateBookingAction action = new CreateBookingAction(source, destination, reason, amountCent);
return action.finish();
}
/**
* Retrieve the corresponding account based on user-input
*
* @param account The chosen value on the radio buttons
* @param customer The given customer id from the corresponding text field
* @param supplier the given supplier id from the corresponding text field
* @return The account entry that can be used to create the new booking
* @throws InvalidFormDataException If the user provided incorrect data in any way
*/
private BookingAccountEntry getAccountFromFormData(String account, String customer, String supplier)
throws InvalidFormDataException {
if (account.equals("None")) {
return null;
} else if (account.equals("Main")) {
return bookingAccountEntryRepository.getByMain().orElseGet(BookingAccountEntry::newMain);
} else if (account.equals("Vat")) {
return bookingAccountEntryRepository.getByVat().orElseGet(BookingAccountEntry::newVat);
} else if (account.equals("Cust")) {
long userId;
try {
userId = Long.parseLong(customer);
} catch (NumberFormatException e) {
throw new InvalidFormDataException("Die angegebene Kunden-Nr. ist ungültig.");
}
Optional<BookingAccountEntry> bookingAccount = bookingAccountEntryRepository.getByUser(userId);
if (!bookingAccount.isPresent()) {
bookingAccount = userRepository.findById(userId).map((user) -> BookingAccountEntry.newUser(user));
}
if (bookingAccount.isPresent()) {
return bookingAccount.get();
} else {
throw new InvalidFormDataException("Der Kunde Nr. " + userId + " konnte nicht gefunden werden.");
}
} else if (account.equals("Sup")) {
long supplierId;
try {
supplierId = Long.parseLong(supplier);
} catch (NumberFormatException e) {
throw new InvalidFormDataException("Die angegebene Lieferanten-Nr. ist ungültig.");
}
Optional<BookingAccountEntry> bookingAccount = bookingAccountEntryRepository.getBySupplier(supplierId);
if (!bookingAccount.isPresent()) {
bookingAccount = supplierRepository.findById(supplierId)
.map((sup) -> BookingAccountEntry.newSupplier(sup));
}
if (bookingAccount.isPresent()) {
return bookingAccount.get();
} else {
throw new InvalidFormDataException(
"Der Lieferant Nr. " + supplierId + " konnte nicht gefunden werden.");
}
} else {
throw new RuntimeException("Invalid form value for an account: " + account);
}
}
@GetMapping("/intern/accounting/addManual")
public String accountingAddManual(Model model) {
model.addAttribute("form_vals", new ManualAccounting());
return "intern/accounting/addManual";
}
@PostMapping("/intern/accounting/addManual")
public String accountingAddManualSubmit(Model model, @ModelAttribute ManualAccounting formData) {
Booking booking;
try {
booking = createBooking(formData);
bookingRepository.save(booking);
model.addAttribute("info", "Die Buchung wurde erfolgreich erstellt.");
model.addAttribute("form_vals", new ManualAccounting()); // Reply with empty form on success
} catch (InvalidFormDataException e) {
model.addAttribute("error", e.getMessage());
model.addAttribute("form_vals", formData);
}
return "intern/accounting/addManual";
}
}

View File

@ -1,8 +1,71 @@
package org.hso.ecommerce.controller.intern.customers;
import org.hso.ecommerce.action.user.CreateDeliveryData;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.uimodel.DeliveryData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
import java.util.stream.Collectors;
@Controller
//@RequestMapping("...")
@RequestMapping("intern/customerOrders")
public class CustomerOrderController {
@Autowired
private final CustomerOrderRepository customerOrderRepository = null;
@Autowired
private final RestServiceForDelivery restServiceForDelivery = null;
@GetMapping("")
public String internCustomerOrder(Model model) {
List<CustomerOrder> orders = customerOrderRepository.getAllOrders();
List<CustomerOrderDelivery> customerOrderDeliveryDataMap = orders
.stream()
.map(o -> new CustomerOrderDelivery(o, CreateDeliveryData.getDeliveryDataFromCustomerOrder(o, customerOrderRepository, restServiceForDelivery)))
.collect(Collectors.toList());
model.addAttribute("orderDeliveryDataMap", customerOrderDeliveryDataMap);
return "intern/customerOrders/index";
}
static class CustomerOrderDelivery {
private CustomerOrder customerOrder;
private DeliveryData deliveryData;
public CustomerOrderDelivery(CustomerOrder customerOrder, DeliveryData deliveryData) {
this.customerOrder = customerOrder;
this.deliveryData = deliveryData;
}
public CustomerOrder getCustomerOrder() {
return customerOrder;
}
public DeliveryData getDeliveryData() {
return deliveryData;
}
}
@GetMapping("/{id}")
public String internCustomerOrdersId(Model model,
@PathVariable("id") String id
) {
CustomerOrder order = customerOrderRepository.findById(Long.parseLong(id)).get();
DeliveryData deliveryData = CreateDeliveryData.getDeliveryDataFromCustomerOrder(order, customerOrderRepository, restServiceForDelivery);
model.addAttribute("order", order);
model.addAttribute("deliveryData", deliveryData);
return "intern/customerOrders/id";
}
}

View File

@ -1,8 +1,118 @@
package org.hso.ecommerce.controller.intern.customers;
import org.hso.ecommerce.controller.intern.accounting.AccountingController;
import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/customers")
public class CustomersIndexController {
@Autowired
private BookingRepository bookingRepository = null;
@Autowired
private final CustomerOrderRepository customerOrderRepository = null;
@Autowired
private final UserRepository userRepository = null;
@Autowired
private AccountingController accountingController = null;
@GetMapping("")
public String internCustomers(Model model) {
List<User> users = userRepository.findAll();
model.addAttribute("users", users);
return "intern/customers/index";
}
@GetMapping("/{id}")
public String internCustomersId(Model model,
@PathVariable("id") Long id,
HttpServletResponse response,
HttpServletRequest request
) {
Optional<User> optUser = userRepository.findById(id);
if (!optUser.isPresent()) {
request.setAttribute("error", "Der User wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error/404";
}
User user = optUser.get();
model.addAttribute("user", user);
List<CustomerOrder> orders = customerOrderRepository.getOrdersByUserId(id);
model.addAttribute("orders", orders);
List<Booking> bookings = bookingRepository.customerBookingsReverseChronologically(id);
ShortTemplateBookingResult result = accountingController.buildShortTemplate(
bookings,
account -> account.userAccount != null && account.userAccount.id == id);
model.addAttribute("balance", result.balance);
model.addAttribute("bookings", result.bookings);
return "intern/customers/id";
}
@PostMapping("/{id}/changeState")
public String changeState(@PathVariable("id") Long id,
@RequestParam(value = "active", required = false) String active,
@RequestParam(value = "ma", required = false) String ma
) {
User user = userRepository.findById(id).get();
if (active == null)
user.isActive = false;
else
user.isActive = true;
if (ma == null)
user.isEmployee = false;
else
user.isEmployee = true;
userRepository.save(user);
return "redirect:/intern/customers/" + id.toString();
}
@PostMapping("/{id}/resetPassword")
public String resetPassword(@PathVariable("id") Long id,
@RequestParam("password") String password,
@RequestParam("password2") String password2,
HttpServletRequest request
) {
if (!password.equals(password2)) {
request.setAttribute("error", "Passwörter stimmen nicht überein!");
return "intern/customers/id";
}
User user = userRepository.findById(id).get();
if (!user.validatePassword(password)) {
request.setAttribute("error", "Die Passwörter stimmen nicht mit dem Original überein!");
return "intern/customers/id";
}
user.setPassword("12345");
userRepository.save(user);
request.setAttribute("info", "Passwort wurde auf 12345 geändert!");
return "intern/customers/id";
}
}

View File

@ -1,8 +1,157 @@
package org.hso.ecommerce.controller.intern.suppliers;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hso.ecommerce.controller.intern.accounting.AccountingController;
import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBooking;
import org.hso.ecommerce.controller.intern.accounting.AccountingController.ShortTemplateBookingResult;
import org.hso.ecommerce.entities.booking.Booking;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
import org.hso.ecommerce.repos.supplier.SupplierRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/")
public class SupplierIndexController {
@Autowired
private final SupplierRepository supplierRepository = null;
@Autowired
private final SupplierOrderRepository supplierOrderRepository = null;
@Autowired
private final BookingRepository bookingRepository = null;
@Autowired
private final AccountingController accountingController = null;
@GetMapping("suppliers")
public String listSuppliers(Model model) {
List<UImodelSuppliers> totals = new ArrayList<UImodelSuppliers>();
for (Supplier supplier : supplierRepository.findAll()) {
UImodelSuppliers tmp = new UImodelSuppliers(supplier.id, supplier.name);
totals.add(tmp);
}
model.addAttribute("suppliers", totals);
return "intern/suppliers/index";
}
@GetMapping("/suppliers/{id}")
public String supplierDetail(Model model, @PathVariable String id) {
long supplierId = Long.parseLong(id);
// add orders from supplier to UImodel
List<UImodelSupplierDetailOrders> orders = new ArrayList<UImodelSupplierDetailOrders>();
for (SupplierOrder supplierOrder : supplierOrderRepository.findOrderBySupplierID(supplierId)) {
orders.add(new UImodelSupplierDetailOrders(supplierOrder));
}
// Table of bookings
List<Booking> bookings = bookingRepository.supplierBookingsReverseChronologically(supplierId);
ShortTemplateBookingResult bookingResult = accountingController.buildShortTemplate(bookings,
account -> account.supplierAccount != null && account.supplierAccount.id == supplierId);
UImodelSupplierDetail total = new UImodelSupplierDetail(supplierRepository.findSupplierById(supplierId).name,
bookingResult.balance, orders, bookingResult.bookings);
model.addAttribute("SupplierDetail", total);
return "intern/suppliers/id";
}
public class UImodelSuppliers {
public long id;
public String name;
public UImodelSuppliers(long id, String name) {
this.id = id;
this.name = name;
}
}
public class UImodelSupplierDetail {
public String name;
public String balance;
public List<UImodelSupplierDetailOrders> orders;
public List<ShortTemplateBooking> bookings;
public UImodelSupplierDetail(String name, String balance, List<UImodelSupplierDetailOrders> orders,
List<ShortTemplateBooking> bookings
) {
this.name = name;
this.balance = balance;
this.orders = orders;
this.bookings = bookings;
}
}
public class UImodelSupplierDetailOrders {
public long id;
public String dateOrder;
public String articleName;
public long articleId;
public String priceNet;
public String quantity;
public String priceTotal;
public boolean arrived;
public UImodelSupplierDetailOrders(SupplierOrder order) {
this.id = order.id;
this.articleName = order.ordered.title;
this.articleId = order.ordered.id;
this.priceNet = String.format("%.2f", ((float) order.pricePerUnitNetCent / 100));
this.quantity = String.valueOf(order.numberOfUnits);
this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100));
Date date = new Date();
date.setTime(order.created.getTime());
this.dateOrder = new SimpleDateFormat("dd.MM.yyyy").format(date);
if (order.delivered != null) {
arrived = true;
} else {
arrived = false;
}
}
}
public class UImodelSupplierDetailBookings {
public String dateBooking;
public String price;
public String srcName;
public String balance;
public String reason;
public long orderID;
public UImodelSupplierDetailBookings(Booking booking) {
Date date = new Date();
date.setTime(booking.reason.supplierOrder.created.getTime());
this.dateBooking = new SimpleDateFormat("dd.MM.yyyy").format(date);
this.price = String.format("%.2f", ((float) booking.amountCent / 100));
this.srcName = ((booking.source.isMainAccount) ? "Hauptkonto" : booking.source.supplierAccount.name);
this.balance = String.format("%.2f", ((float) booking.destination.newSumCent / 100));
this.reason = booking.reason.comment;
this.orderID = booking.reason.supplierOrder.id;
}
}
}

View File

@ -1,8 +1,77 @@
package org.hso.ecommerce.controller.intern.suppliers;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.hso.ecommerce.entities.supplier.ArticleOffer;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.OffersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/")
public class SupplierOfferController {
@Autowired
private final OffersRepository offersRepository = null;
@Autowired
private final ArticleRepository articleRepository = null;
@GetMapping("supplierOffers")
public String internListedArticles(Model model) {
List<UImodelOfferedArticle> totals = new ArrayList<>();
for (ArticleOffer article : offersRepository.findAll()) {
UImodelOfferedArticle tmp = new UImodelOfferedArticle(article,
articleRepository.findArticleIDByRelatedID(article.id));
totals.add(tmp);
}
model.addAttribute("OfferedArticles", totals);
return "intern/offeredArticles/index";
}
public class UImodelOfferedArticle {
public long offerId;
public String title;
public String manufacturer;
public String articleNumber;
public String supplierName;
public String price;
public String ads;
public int listedArticleId;
public boolean offerIsListed; // true --> offered article is listed
public UImodelOfferedArticle(ArticleOffer article, Optional<Integer> listedArticleId) {
this.offerId = article.id;
this.title = article.title;
this.manufacturer = article.manufacturer;
this.articleNumber = article.articleNumber;
if (article.cheapestSupplier != null) {
this.supplierName = article.cheapestSupplier.name;
} else {
this.supplierName = "-";
}
this.price = String.format("%.2f", ((float) article.pricePerUnitNet / 100));
this.ads = (article.shouldBeAdvertised) ? "Ja" : "Nein";
if (listedArticleId.isPresent()) {
// this offer is listed --> show link
this.listedArticleId = listedArticleId.get();
offerIsListed = true;
} else {
// this offer is not listed
offerIsListed = false;
}
}
}
}

View File

@ -1,8 +1,142 @@
package org.hso.ecommerce.controller.intern.suppliers;
import org.hso.ecommerce.action.warehouse.SupplierOrderArrivedAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.supplier.SupplierOrderRepository;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Controller
//@RequestMapping("...")
@RequestMapping("/intern/")
public class SupplierOrderController {
@Autowired
private final SupplierOrderRepository supplierOrderRepository = null;
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null;
@Autowired
private final SlotRepository slotRepository = null;
@GetMapping("supplierOrders")
public String listSuppliers(Model model) {
List<UImodelSupplierOrder> totals = new ArrayList<UImodelSupplierOrder>();
for (SupplierOrder order : supplierOrderRepository.findAllDesc()) {
final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null);
totals.add(new UImodelSupplierOrder(order, article));
}
model.addAttribute("orders", totals);
return "intern/supplierOrders/index";
}
@PostMapping("/supplierOrders/store/{id}")
public String storeOrder(@PathVariable("id") Long supplierOrderID, Model model, HttpServletResponse response) {
SupplierOrder order = supplierOrderRepository.findById(supplierOrderID).orElse(null);
if (order == null) {
model.addAttribute("error", "Die ausgewählte Lieferung konnte nicht gefunden werden.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return listSuppliers(model);
}
if (order.wasDelivered()) {
model.addAttribute("error", "Die ausgewählte Lieferung wurde schon zugestellt.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return listSuppliers(model);
}
final Article article = articleRepository.findArticleByArticleOffer(order.ordered).orElse(null);
if (article == null) {
model.addAttribute("error", "Der bestellte Artikel wurde nicht angelegt, er hätte nicht bestellt werden dürfen.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return listSuppliers(model);
}
// Hard to do efficiently, this should be fine.
List<WarehouseBookingPositionSlotEntry> candidates = slotRepository
.findAll()
.stream()
.map(slot ->
warehouseBookingPositionSlotEntryRepository.getBySlotNum(slot.slotNum).orElseGet(() ->
WarehouseBookingPositionSlotEntry.empty(article, slot)
)
)
.filter(entry -> entry.article.id == article.id || entry.newSumSlot == 0)
.collect(Collectors.toList());
SupplierOrderArrivedAction action = new SupplierOrderArrivedAction(candidates, order, article);
try {
SupplierOrderArrivedAction.Result result = action.finish();
supplierOrderRepository.save(result.getOrder());
warehouseBookingRepository.save(result.getBooking());
} catch (SupplierOrderArrivedAction.NoSpaceInWarehouseException e) {
e.printStackTrace();
}
return "redirect:/intern/warehouse/todo";
}
public class UImodelSupplierOrder {
public long id;
public String dateOrder;
public String supplierName;
public String articleName;
public long articleId;
public String priceNet;
public String quantity;
public String priceTotal;
public boolean arrived;
public String carrier;
public String trackingId;
public String estimatedArrival;
public UImodelSupplierOrder(SupplierOrder order, Article article) {
this.id = order.id;
this.supplierName = order.supplier.name;
this.articleName = article != null ? article.title : "error";
this.articleId = article != null ? article.id : 0;
this.priceNet = String.format("%.2f", ((float) order.pricePerUnitNetCent / 100));
this.quantity = String.valueOf(order.numberOfUnits);
this.priceTotal = String.format("%.2f", ((float) order.totalPriceNet / 100));
this.carrier = order.carrier != null ? order.carrier : " - ";
this.trackingId = order.trackingId != null ? order.trackingId : " - ";
this.estimatedArrival = order.estimatedArrival != null
? new SimpleDateFormat("yyyy.MM.dd HH:00").format(order.estimatedArrival) + " Uhr"
: " - ";
this.dateOrder = new SimpleDateFormat("yyyy.MM.dd").format(order.created);
arrived = order.delivered != null;
}
}
}

View File

@ -0,0 +1,140 @@
package org.hso.ecommerce.controller.intern.warehouse;
import org.hso.ecommerce.action.warehouse.CreateManuelBookingAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.warehouse.Slot;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
@Controller
@RequestMapping("/intern/warehouse/")
public class ManuelBookingController {
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final SlotRepository slotRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null;
@GetMapping("addManual")
public String warehouseAddManual(
Model model
) {
model.addAttribute("articles", articleRepository.findAll());
model.addAttribute("slots", slotRepository.findAll());
return "intern/warehouse/addManual";
}
@PostMapping("addManual")
public String warehouseAddMaualPost(
Model model,
HttpServletRequest request,
HttpServletResponse response,
@RequestParam("articleId") String articleIdText,
@RequestParam("amount") Integer amount,
@RequestParam("reason") String reason,
@RequestParam("sourceIsSlot") Boolean sourceIsSlot,
@RequestParam("sourceSlot") Integer sourceSlotNum,
@RequestParam("destinationIsSlot") Boolean destinationIsSlot,
@RequestParam("destinationSlot") Integer destinationSlotNum
) {
// The suggestions for articleId in the UI show articles names, seperated by a " - ".
// The Number must be extracted first.
long articleId = -1;
try {
articleId = Long.parseLong(articleIdText.split(" - ", 2)[0].trim());
} catch (NumberFormatException e) {
model.addAttribute("error", "Die Artikel Id konnte nicht erkannt werden.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "intern/warehouse/addManual";
}
Optional<Article> optionalArticle = articleRepository.findById(articleId);
Article article = null;
if (!optionalArticle.isPresent()) {
model.addAttribute("error", "Der Artikel konnte nicht gefunden werden.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
} else {
article = optionalArticle.get();
}
if (amount <= 0) {
model.addAttribute("error", "Eine Anzahl <= 0 kann nicht verbucht werden.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
}
if (sourceIsSlot == false && destinationIsSlot == false) {
model.addAttribute("error", "Jede Buchung benötigt ein Ziel oder eine Quelle.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
}
Optional<WarehouseBookingPositionSlotEntry> sourceSlot = Optional.empty();
if (sourceIsSlot == true) {
sourceSlot = warehouseBookingPositionSlotEntryRepository.getBySlotNum(sourceSlotNum);
if (!sourceSlot.isPresent()) {
request.setAttribute("error", "Quelllagerplatz wurde nicht gefunden oder ist leer.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
}
}
Optional<WarehouseBookingPositionSlotEntry> destinationSlot = Optional.empty();
if (destinationIsSlot == true) {
Optional<Slot> slot = slotRepository.findBySlotNum(destinationSlotNum);
if (!slot.isPresent()) {
request.setAttribute("error", "Ziellagerplatz wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
}
Article finalArticle = article;
destinationSlot = Optional.of(warehouseBookingPositionSlotEntryRepository.getBySlotNum(destinationSlotNum).orElseGet(
() -> WarehouseBookingPositionSlotEntry.empty(finalArticle, slot.get())
));
}
try {
warehouseBookingRepository.save(
new CreateManuelBookingAction(article, amount, sourceSlot, destinationSlot, reason).finish()
);
} catch (CreateManuelBookingAction.ArticleSlotConstraintArticleTypeFailedException e) {
model.addAttribute("error", "Es befindet sich der falsche Artikeltyp in Quell- oder Ziellagerplatz. ");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
} catch (CreateManuelBookingAction.ArticleSlotConstraintFailedException e) {
model.addAttribute("error", "Die maximale Anzahl an lagerbaren Artikeln im Ziellagerplatz wurde überschritten.");
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
return "intern/warehouse/addManual";
}
return "redirect:/intern/warehouse/todo";
}
}

View File

@ -0,0 +1,46 @@
package org.hso.ecommerce.controller.intern.warehouse;
import org.hso.ecommerce.action.warehouse.CalculateWarehouseStatsAction;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.warehouse.SlotRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
@Controller
@RequestMapping("/intern/warehouse/")
public class SlotsController {
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private final SlotRepository slotRepository = null;
@GetMapping("slots/")
public String accountingWarehouseSlots(
Model model,
HttpServletRequest request
) {
// Doing this in a single would be hard and error prone.
// Writing native queries should be minimized.
// Therefore this method was prefered
List<WarehouseBookingPositionSlotEntry> entries = slotRepository.findAll().stream().map(
s -> warehouseBookingPositionSlotEntryRepository
.getBySlotNum(s.slotNum)
.orElseGet(() -> WarehouseBookingPositionSlotEntry.empty(null, s))
).collect(Collectors.toList());
model.addAttribute("entries", entries);
model.addAttribute("stats", new CalculateWarehouseStatsAction(entries).finish());
return "intern/warehouse/slots/index";
}
}

View File

@ -0,0 +1,124 @@
package org.hso.ecommerce.controller.intern.warehouse;
import org.hso.ecommerce.action.shop.EnableTrackingAction;
import org.hso.ecommerce.api.RestServiceForDelivery;
import org.hso.ecommerce.entities.warehouse.WarehouseBooking;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.ResourceAccessException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;
@Controller
@RequestMapping("/intern/warehouse/")
public class TodoController {
@Autowired
private final WarehouseBookingRepository warehouseBookingRepository = null;
@Autowired
private final RestServiceForDelivery deliveryService = null;
@GetMapping("todo")
public String accountingWarehouseTodo(
Model model
) {
model.addAttribute("bookings", warehouseBookingRepository.findNotDone());
return "intern/warehouse/todo";
}
@PostMapping("progress/{id}")
public String postProgressId(
Model model,
HttpServletRequest request,
HttpServletResponse response,
@PathVariable("id") Long id
) {
Optional<WarehouseBooking> booking = warehouseBookingRepository.findById(id);
if (!booking.isPresent()) {
model.addAttribute("error", "Die Buchung wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "error/404";
}
if (booking.get().isInProgress) {
response.setStatus(409);
return "intern/warehouse/error_progress_failed";
}
booking.get().isInProgress = true;
warehouseBookingRepository.save(booking.get());
return "redirect:/intern/warehouse/progress/" + id;
}
@PostMapping("progress/{id}/finish")
public String postProgressIdFinish(
Model model,
HttpServletRequest request,
HttpServletResponse response,
@PathVariable("id") Long id
) {
Optional<WarehouseBooking> booking = warehouseBookingRepository.findById(id);
if (!booking.isPresent()) {
model.addAttribute("error", "Die Buchung wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "error/404";
}
booking.get().isInProgress = true;
booking.get().isDone = true;
// Update Delivery Date
if (booking.get().reason.customerOrder != null) {
try{
EnableTrackingAction.addTrackingInfo(deliveryService, booking.get().reason.customerOrder);
}
catch(ResourceAccessException e)
{
return "error/500";
}
}
warehouseBookingRepository.save(booking.get());
return "redirect:/intern/warehouse/todo";
}
@GetMapping("progress/{id}")
public String getProgressId(Model model,
HttpServletRequest request,
HttpServletResponse response,
@PathVariable("id") Long id) {
Optional<WarehouseBooking> booking = warehouseBookingRepository.findById(id);
if (!booking.isPresent()) {
model.addAttribute("error", "Die Buchung wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return "error/404";
}
if (booking.get().isDone) {
model.addAttribute("info", "Die Buchung wurde schon abgeschlossen.");
}
if (!booking.get().isInProgress) {
// Only reachable if path is manipulated.
model.addAttribute("error", "Die Buchung wurde noch nicht zugewiesen!");
}
model.addAttribute("booking", booking.get());
return "intern/warehouse/id_progress";
}
}

View File

@ -4,8 +4,8 @@ import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.hso.ecommerce.action.shop.GetRandomArticlesAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.shop.ShoppingCart;
import org.hso.ecommerce.entities.warehouse.WarehouseBookingPositionSlotEntry;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CategoryRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@ -16,10 +16,10 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@Controller
@ -32,37 +32,39 @@ public class ShopArticleController {
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@Autowired
private final CategoryRepository categoryRepository = null;
@GetMapping("/{id}")
public String shopArticlesById(Model model,
@PathVariable("id") Long id,
HttpServletRequest request,
HttpServletResponse response
) {
model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar
Article article = articleRepository.findArticleById(id);
if (article == null) {
request.setAttribute("error", "Der Artikel wurde nicht gefunden.");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error/404";
}
model.addAttribute("article", article);
//if (warehouseBookingPositionSlotEntryRepository.getByArticle(id).get(0).newSumSlot > 0) { //TODO: use this as soon as warehouse works
if (true) {
if (warehouseBookingPositionSlotEntryRepository
.getByArticle(id)
.stream()
.mapToInt(e -> e.newSumSlot)
.sum() > 0) { //check if in Stock
model.addAttribute("inStock", true);
} else {
model.addAttribute("inStock", false);
}
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles());
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(3, articleRepository.getAdvertisedArticles()); //get 3 advertised Articles
model.addAttribute("commercialArticles", commercialArticles);
return "shop/articles/id";
}
@ -76,8 +78,6 @@ public class ShopArticleController {
@RequestParam(value = "set_amount", required = false) Boolean setAmount,
@RequestParam("fastcheckout") Boolean fastcheckout
) {
Article article = articleRepository.findArticleById(id);
if (article == null) {
@ -104,14 +104,19 @@ public class ShopArticleController {
HttpServletResponse response,
@PathVariable("id") Long id
) throws IOException {
Article article = articleRepository.findArticleById(id);
InputStream in = new FileInputStream(article.image.path);
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
IOUtils.copy(in, response.getOutputStream());
if(article.image != null) {
File file = new File(article.image.path);
File allowedPath = new File("./data/img/");
if (file.getCanonicalPath().startsWith(allowedPath.getCanonicalPath())) {
InputStream in = new FileInputStream(file);
response.setContentType(MediaType.IMAGE_JPEG_VALUE);
IOUtils.copy(in, response.getOutputStream());
} else {
throw new RuntimeException("Got illegal file path. DB was modified.");
}
}
}
}
}

View File

@ -1,7 +1,6 @@
package org.hso.ecommerce.controller.shop;
import org.hso.ecommerce.action.shop.CreateOrderAction;
import org.hso.ecommerce.action.shop.EnableTrackingAction;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.hso.ecommerce.entities.booking.PaymentMethod;
import org.hso.ecommerce.entities.shop.Address;
@ -11,7 +10,7 @@ import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.booking.BookingAccountEntryRepository;
import org.hso.ecommerce.repos.booking.BookingRepository;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CustomerOderRepository;
import org.hso.ecommerce.repos.shop.CustomerOrderRepository;
import org.hso.ecommerce.repos.user.UserRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingRepository;
@ -45,7 +44,7 @@ public class ShopCheckoutController {
private final WarehouseBookingRepository warehouseBookingRepository = null;
@Autowired
private final CustomerOderRepository customerOderRepository = null;
private final CustomerOrderRepository customerOderRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository wbeseRepo = null;
@ -98,6 +97,7 @@ public class ShopCheckoutController {
@PostMapping("/checkoutFinish")
public String shopCheckoutFinish(
HttpSession session,
HttpServletRequest request,
HttpServletResponse response,
@RequestAttribute(value = "user") User user,
@ -109,7 +109,7 @@ public class ShopCheckoutController {
) {
if (shoppingCart.getRevision() != cartRevision) {
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher die Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
request.setAttribute("error", "Der Warenkorb wurde zwischenzeitlich bearbeitet. Daher konnte der Kaufvorgang nicht abgeschlossen werden. Bitte versuchen Sie es erneut.");
response.setStatus(HttpServletResponse.SC_CONFLICT);
return "shop/checkout";
}
@ -121,7 +121,7 @@ public class ShopCheckoutController {
user,
expectedPrice,
Address.fromString(address),
PaymentMethod.fromCreditCarNumber(cardnumber),
PaymentMethod.fromCreditCardNumber(cardnumber),
bookingEntryRepository.getByUser(user.id).orElse(BookingAccountEntry.newUser(user)),
bookingEntryRepository.getByVat().orElse(BookingAccountEntry.newVat()),
bookingEntryRepository.getByMain().orElse(BookingAccountEntry.newMain())
@ -132,15 +132,21 @@ public class ShopCheckoutController {
action.addArticle(article, item.getAmount(), wbeseRepo.getByArticle(article.id));
}
CreateOrderAction.Result result = action.finish();
CreateOrderAction.Result result = null;
try {
result = action.finish();
EnableTrackingAction.addTrackingInfo(result.customerOrder);
customerOderRepository.save(result.customerOrder);
bookingRepository.saveAll(result.bookings);
warehouseBookingRepository.save(result.warehouseBooking);
customerOderRepository.save(result.customerOrder);
bookingRepository.saveAll(result.bookings);
warehouseBookingRepository.save(result.warehouseBooking);
shoppingCart.clear();
} catch (CreateOrderAction.ArticleNotInStockException e) {
request.setAttribute("error", "Der Artikel '" + e.getArticle().title + "' ist leider nicht mehr in ausreichender Menge verfügbar. Bitte passen Sie die Artikelmenge an.");
return shopCheckout(session, request, shoppingCart);
}
shoppingCart.clear();
return "shop/checkoutFinish";
}

View File

@ -2,8 +2,8 @@ package org.hso.ecommerce.controller.shop;
import org.hso.ecommerce.action.shop.GetRandomArticlesAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.entities.user.User;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.warehouse.WarehouseBookingPositionSlotEntryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -11,7 +11,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
@Controller
@ -21,6 +20,9 @@ public class ShopIndexController {
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final WarehouseBookingPositionSlotEntryRepository warehouseBookingPositionSlotEntryRepository = null;
@GetMapping("/")
public String home() {
return "redirect:/shop/";
@ -29,19 +31,18 @@ public class ShopIndexController {
@GetMapping("/shop/")
public String shop(Model model, HttpSession session) {
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles());
List<Article> commercialArticles = GetRandomArticlesAction.getRandomArticles(8, articleRepository.getAdvertisedArticles()); //get random advertised Articles
model.addAttribute("commercialArticles", commercialArticles);
boolean isLoggedIn = false;
boolean hasOrders = false;
if (session != null && session.getAttribute("id") != null) {
long userId = (long) session.getAttribute("id");
isLoggedIn = true;
}
if (isLoggedIn) {
List<Article> suggestedArticles = articleRepository.getOrderedArticles();
suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0,4) : suggestedArticles; //only latest 4 articles
if (session != null && session.getAttribute("userId") != null) { //check if logged in
long userId = (long) session.getAttribute("userId");
isLoggedIn = true;
List<Article> suggestedArticles = articleRepository.getOrderedArticles(userId);
suggestedArticles = suggestedArticles.size() > 3 ? suggestedArticles.subList(0, 4) : suggestedArticles; //only latest 4 ordered articles
if (suggestedArticles.size() > 0) {
model.addAttribute("suggestedArticles", suggestedArticles);
hasOrders = true;
@ -68,5 +69,4 @@ public class ShopIndexController {
public String privacy() {
return "privacy";
}
}

View File

@ -1,8 +1,55 @@
package org.hso.ecommerce.controller.shop;
import org.hso.ecommerce.action.shop.SearchByTermAction;
import org.hso.ecommerce.entities.shop.Article;
import org.hso.ecommerce.repos.shop.ArticleRepository;
import org.hso.ecommerce.repos.shop.CategoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Controller
//@RequestMapping("...")
@RequestMapping("/shop/search")
public class ShopSearchController {
}
@Autowired
private final ArticleRepository articleRepository = null;
@Autowired
private final CategoryRepository categoryRepository = null;
@GetMapping("")
public String searchArticles(@RequestParam(required = false, value = "term") String term,
@RequestParam(required = false, value = "category") String category,
Model model,
HttpServletRequest request,
HttpServletResponse response
) {
model.addAttribute("categories", categoryRepository.getCategories()); //for sidebar
if (term != null) { //if search by Term
term = term.trim();
List<Article> articles = SearchByTermAction.searchByTerm(term, articleRepository);
model.addAttribute("articles", articles);
} else if (category != null) { //if search by Category
List<Article> articles = articleRepository.getArticlesByCategory(category); //search by Category
model.addAttribute("articles", articles);
} else {
request.setAttribute("error", "Es wurden keine Suchparameter angegeben.");
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return "error/404";
}
// Show term in search box
model.addAttribute("searchterm", term != null ? term : "");
return "shop/search";
}
}

View File

@ -1,6 +1,7 @@
package org.hso.ecommerce.entities.booking;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
@Table(name = "bookings")
@ -14,6 +15,9 @@ public class Booking {
// always >= 0
public int amountCent;
@NotNull
public java.sql.Timestamp created;
@ManyToOne(optional = true, cascade = CascadeType.ALL)
public BookingAccountEntry source;

View File

@ -16,10 +16,10 @@ public class BookingAccountEntry {
public int newSumCent;
@ManyToOne(optional = true, cascade = CascadeType.ALL)
@ManyToOne(optional = true, cascade = CascadeType.MERGE)
public User userAccount;
@ManyToOne(optional = true, cascade = CascadeType.ALL)
@ManyToOne(optional = true, cascade = CascadeType.MERGE)
public Supplier supplierAccount;
public boolean isMainAccount;
@ -46,6 +46,14 @@ public class BookingAccountEntry {
return e;
}
public static BookingAccountEntry newSupplier(Supplier supplier) {
BookingAccountEntry e = new BookingAccountEntry();
e.supplierAccount = supplier;
e.newSumCent = 0;
return e;
}
public static BookingAccountEntry newMain() {
BookingAccountEntry e = new BookingAccountEntry();
e.isMainAccount = true;

View File

@ -1,6 +1,7 @@
package org.hso.ecommerce.entities.booking;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.supplier.Supplier;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import javax.persistence.*;
@ -28,11 +29,13 @@ public class BookingReason {
@ManyToOne(optional = true)
public SupplierOrder supplierOrder;
@ManyToOne(optional = true)
public Supplier supplierPayment;
// Default Constructor is needed for construction by ORM
public BookingReason() {
}
;
public BookingReason(CustomerOrder order) {
this.customerOrder = order;
}
@ -40,4 +43,17 @@ public class BookingReason {
public BookingReason(CustomerPayment customerPayment) {
this.customerPayment = customerPayment;
}
public BookingReason(Supplier supplierPayment) {
this.supplierPayment = supplierPayment;
}
public BookingReason(SupplierOrder supplierOrder) {
this.supplierOrder = supplierOrder;
}
public BookingReason(String comment) {
this.isManuel = true;
this.comment = comment;
}
}

View File

@ -1,14 +1,13 @@
package org.hso.ecommerce.entities.booking;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;
@Embeddable
public class PaymentMethod {
@NotNull
public String creditCardNumber;
public static PaymentMethod fromCreditCarNumber(String cardnumber) {
public static PaymentMethod fromCreditCardNumber(String cardnumber) {
PaymentMethod m = new PaymentMethod();
m.creditCardNumber = cardnumber;

View File

@ -7,8 +7,9 @@ import javax.validation.constraints.NotNull;
@Table(name = "background_jobs")
public class BackgroundJob {
public final String JOB_DASHBOARD = "Dashboard";
public final String JOB_REORDER = "SupplierOrder";
public static final String JOB_DASHBOARD = "Dashboard";
public static final String JOB_REORDER = "SupplierOrder";
public static final String JOB_SUPPLIER_AUTO_PAYMENT = "SupplierAutoPayment";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -15,11 +15,18 @@ public class DashboardSummary {
@NotNull
public java.sql.Date created;
public int todaysCustomers;
public int todaysCustomersOrders;
public int todaysSuppliersOrders;
public int todaysItemsSold;
public int todaysSalesCent;
public int totalWarehouseCapacity;
public int currentWarehouseCapacity;
@NotNull
public Integer todaysNewCustomers;
@NotNull
public Integer todaysCustomersOrders;
@NotNull
public Integer todaysSalesCent;
@NotNull
public Double currentWarehouseCapacity;
@NotNull
public Double todaysWarehouseCapacity;
}

View File

@ -9,55 +9,41 @@ import java.util.Set;
@Entity
@Table(name = "articles")
public class Article
{
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic
public long id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic
public long id;
@ManyToOne(optional = false)
public ArticleOffer related;
@OneToOne(optional = false)
public ArticleOffer related;
public int shopPricePerUnitNetCent;
public int shopPricePerUnitNetCent;
public int warehouseUnitsPerSlot;
public int warehouseUnitsPerSlot;
public boolean shouldReorder;
public int reorderMaxPrice;
public boolean shouldReorder;
@NotNull
public String title;
public int reorderMaxPrice;
@NotNull
public String description;
@NotNull
public String title;
@OneToOne(optional = true)
@Basic(fetch = FetchType.LAZY)
public Image image;
@NotNull
public String description;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "article_categories_bindings")
public Set<Category> categories = new HashSet<>();
@OneToOne(optional = true)
@Basic(fetch = FetchType.LAZY)
public Image image;
public int getVat() {
return (shopPricePerUnitNetCent * related.vatPercent) / 100;
}
@ManyToMany
@JoinTable(name = "article_categories_bindings")
public Set<Category> categories = new HashSet<>();
public String getCategories()
{
StringBuilder result = new StringBuilder();
for (Category temp : categories) {
result.append(temp.name);
}
return result.toString();
}
public int getVat()
{
return (shopPricePerUnitNetCent * related.vatPercent) / 100;
}
public int getPriceGross()
{
return shopPricePerUnitNetCent + getVat();
}
}
public int getPriceGross() {
return shopPricePerUnitNetCent + getVat();
}
}

View File

@ -20,4 +20,15 @@ public class Category {
@ManyToMany(mappedBy = "categories")
public Set<Article> articles = new HashSet<>();
public Category() {
}
public Category (String name) {
this.name = name;
}
}

View File

@ -4,6 +4,7 @@ import org.hso.ecommerce.entities.user.User;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@ -31,7 +32,7 @@ public class CustomerOrder {
@NotNull
public java.sql.Timestamp created;
@NotNull
@Column(nullable = true)
public String trackingId;
@Column(nullable = true)
@ -43,4 +44,16 @@ public class CustomerOrder {
public int totalNetCent;
public int totalGrossCent;
public int totalVatCent;
public String formatInDeliverySince(){
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(inDeliverySince);
}
public String formatCreated(){
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(created);
}
public String formatDeliveredAt(){
return new SimpleDateFormat("dd.MM.yyyy HH:mm").format(deliveredAt);
}
}

View File

@ -19,4 +19,8 @@ public class CustomerOrderPosition {
public int pricePerUnit;
public int quantity;
public int getSumPrice(){
return article.getPriceGross() * quantity;
}
}

View File

@ -12,5 +12,4 @@ public class Image {
public long id;
public String path;
}
}

View File

@ -14,11 +14,20 @@ public class ArticleOffer {
@NotNull
public String manufacturer;
@NotNull
public String title;
@NotNull
public int pricePerUnitNet;
@NotNull
public String articleNumber;
public int vatPercent;
public boolean should_be_advertised;
public boolean shouldBeAdvertised;
@ManyToOne(optional = true)
public Supplier cheapestSupplier;
}

View File

@ -28,6 +28,15 @@ public class SupplierOrder {
// Includes discounts
public int totalPriceNet;
@Column(nullable = true)
public String carrier;
@Column(nullable = true)
public String trackingId;
@Column(nullable = true)
public Timestamp estimatedArrival;
@Column(nullable = true)
public Timestamp delivered;

View File

@ -24,6 +24,8 @@ public class User {
@Column(unique = true)
public String email;
public String salutation;
public String passwordHash;
public boolean isActive;

View File

@ -27,6 +27,7 @@ public class WarehouseBooking {
)
public List<WarehouseBookingPosition> positions = new ArrayList<>();
// TODO FIX ME
@OneToOne(optional = false, cascade = CascadeType.ALL)
public BookingReason reason;
}

View File

@ -18,15 +18,19 @@ public class WarehouseBookingPositionSlotEntry {
@ManyToOne
public Article article;
// Can;t do, does not work when created in action.
//public int newSumArticles;
@NotNull
public int newSumSlot;
@NotNull
@ManyToOne
public Slot slot;
public WarehouseBookingPositionSlotEntry copyAddAmount(int amount) {
public WarehouseBookingPositionSlotEntry copyAddAmount(int amount, Article article) {
// Article can be changed if newSumSlot == 0.
if (this.newSumSlot != 0 && this.article.id != article.id) {
throw new IllegalArgumentException("Article does not match.");
}
WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry();
e.article = article;
@ -39,4 +43,14 @@ public class WarehouseBookingPositionSlotEntry {
return e;
}
public static WarehouseBookingPositionSlotEntry empty(Article article, Slot slot) {
WarehouseBookingPositionSlotEntry e = new WarehouseBookingPositionSlotEntry();
e.article = article;
e.slot = slot;
e.newSumSlot = 0;
return e;
}
}

View File

@ -1,33 +0,0 @@
package org.hso.ecommerce.entities.warehouse;
import org.hso.ecommerce.entities.shop.CustomerOrder;
import org.hso.ecommerce.entities.supplier.SupplierOrder;
import javax.persistence.*;
@Entity
@Table(name = "warehouse_booking_reasons")
public class WarehouseBookingReason {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic
public long id;
public String comment;
@ManyToOne(optional = true)
public SupplierOrder causeSupplierOrder;
@ManyToOne(optional = true)
public CustomerOrder customerOrder;
public boolean isManuel;
public WarehouseBookingReason() {
}
public WarehouseBookingReason(CustomerOrder order) {
this.customerOrder = order;
}
}

View File

@ -1,18 +1,21 @@
package org.hso.ecommerce.repos.booking;
import java.util.Optional;
import org.hso.ecommerce.entities.booking.BookingAccountEntry;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface BookingAccountEntryRepository extends JpaRepository<BookingAccountEntry, Long> {
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.user_account_id = :user ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getByUser(Long user);
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.supplier_account_id = :supplier ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getBySupplier(Long supplier);
@Query(value = "SELECT * FROM booking_account_entries as e WHERE e.is_main_account = 1 ORDER BY e.id DESC LIMIT 1", nativeQuery = true)
Optional<BookingAccountEntry> getByMain();

View File

@ -1,12 +1,28 @@
package org.hso.ecommerce.repos.booking;
import java.util.List;
import org.hso.ecommerce.entities.booking.Booking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@Repository
public interface BookingRepository extends JpaRepository<Booking, Long> {
@Query("SELECT b FROM Booking b ORDER BY b.id DESC")
List<Booking> allBookingsReverseChronologically();
@Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isMainAccount = 1 OR d.isMainAccount = 1 ORDER BY b.id DESC")
List<Booking> mainBookingsReverseChronologically();
@Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.isVATAccount = 1 OR d.isVATAccount = 1 ORDER BY b.id DESC")
List<Booking> vatBookingsReverseChronologically();
@Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.userAccount.id = :customerId OR d.userAccount.id = :customerId ORDER BY b.id DESC")
List<Booking> customerBookingsReverseChronologically(long customerId);
@Query("SELECT b FROM Booking b LEFT JOIN b.source s LEFT JOIN b.destination d WHERE s.supplierAccount.id = :supplierId OR d.supplierAccount.id = :supplierId ORDER BY b.id DESC")
List<Booking> supplierBookingsReverseChronologically(long supplierId);
}

View File

@ -0,0 +1,10 @@
package org.hso.ecommerce.repos.cronjob;
import org.hso.ecommerce.entities.cron.BackgroundJob;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BackgroundJobRepository extends JpaRepository<BackgroundJob, Long> {
}

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