diff --git a/docs/res/AppIcon/AppIcons.zip b/docs/res/AppIcon/AppIcons.zip new file mode 100644 index 00000000..2607c14b Binary files /dev/null and b/docs/res/AppIcon/AppIcons.zip differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-1024@1x.png b/docs/res/AppIcon/AppIcons/icon-ios-1024@1x.png new file mode 100644 index 00000000..8acdd236 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-1024@1x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-20@1x.png b/docs/res/AppIcon/AppIcons/icon-ios-20@1x.png new file mode 100644 index 00000000..5a82f6a2 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-20@1x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-20@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-20@2x.png new file mode 100644 index 00000000..f2d9328d Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-20@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-20@3x.png b/docs/res/AppIcon/AppIcons/icon-ios-20@3x.png new file mode 100644 index 00000000..36907fc9 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-20@3x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-29@1x.png b/docs/res/AppIcon/AppIcons/icon-ios-29@1x.png new file mode 100644 index 00000000..13058643 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-29@1x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-29@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-29@2x.png new file mode 100644 index 00000000..866d7a4e Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-29@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-29@3x.png b/docs/res/AppIcon/AppIcons/icon-ios-29@3x.png new file mode 100644 index 00000000..9a7a17ce Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-29@3x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-40@1x.png b/docs/res/AppIcon/AppIcons/icon-ios-40@1x.png new file mode 100644 index 00000000..f2d9328d Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-40@1x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-40@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-40@2x.png new file mode 100644 index 00000000..cc9c951e Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-40@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-40@3x.png b/docs/res/AppIcon/AppIcons/icon-ios-40@3x.png new file mode 100644 index 00000000..0e62f72a Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-40@3x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-60@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-60@2x.png new file mode 100644 index 00000000..0e62f72a Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-60@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-60@3x.png b/docs/res/AppIcon/AppIcons/icon-ios-60@3x.png new file mode 100644 index 00000000..61aead05 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-60@3x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-76@1x.png b/docs/res/AppIcon/AppIcons/icon-ios-76@1x.png new file mode 100644 index 00000000..1b4730dc Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-76@1x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-76@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-76@2x.png new file mode 100644 index 00000000..e5d83974 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-76@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios-83.5@2x.png b/docs/res/AppIcon/AppIcons/icon-ios-83.5@2x.png new file mode 100644 index 00000000..917a2a63 Binary files /dev/null and b/docs/res/AppIcon/AppIcons/icon-ios-83.5@2x.png differ diff --git a/docs/res/AppIcon/AppIcons/icon-ios.svg b/docs/res/AppIcon/AppIcons/icon-ios.svg new file mode 100644 index 00000000..be6d0dff --- /dev/null +++ b/docs/res/AppIcon/AppIcons/icon-ios.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/res/AppIcon/AppIconsMitB.zip b/docs/res/AppIcon/AppIconsMitB.zip new file mode 100644 index 00000000..7b1b3fcf Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB.zip differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-1024@1x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-1024@1x.png new file mode 100644 index 00000000..f6d8ac2e Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-1024@1x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-20@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-20@2x.png new file mode 100644 index 00000000..32cf9dad Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-20@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-20@3x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-20@3x.png new file mode 100644 index 00000000..eb47edf1 Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-20@3x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-29@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-29@2x.png new file mode 100644 index 00000000..0b704ce5 Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-29@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-29@3x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-29@3x.png new file mode 100644 index 00000000..3dd1d0a9 Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-29@3x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-40@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-40@2x.png new file mode 100644 index 00000000..40f45f69 Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-40@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-40@3x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-40@3x.png new file mode 100644 index 00000000..071a2d9a Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-40@3x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-60@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-60@2x.png new file mode 100644 index 00000000..071a2d9a Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-60@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-60@3x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-60@3x.png new file mode 100644 index 00000000..a02004af Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-60@3x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-76@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-76@2x.png new file mode 100644 index 00000000..38e65c7b Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-76@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios-83.5@2x.png b/docs/res/AppIcon/AppIconsMitB/icon-ios-83.5@2x.png new file mode 100644 index 00000000..a04586ca Binary files /dev/null and b/docs/res/AppIcon/AppIconsMitB/icon-ios-83.5@2x.png differ diff --git a/docs/res/AppIcon/AppIconsMitB/icon-ios.svg b/docs/res/AppIcon/AppIconsMitB/icon-ios.svg new file mode 100644 index 00000000..2fe78812 --- /dev/null +++ b/docs/res/AppIcon/AppIconsMitB/icon-ios.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/res/icons/BankTransfer.pdf b/docs/res/icons/BankTransfer.pdf new file mode 100644 index 00000000..94d9188b --- /dev/null +++ b/docs/res/icons/BankTransfer.pdf @@ -0,0 +1,362 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 12.840942 cm +0.000000 0.000000 0.000000 scn +1.279276 25.159058 m +0.572751 25.159058 0.000000 24.586306 0.000000 23.879782 c +0.000000 17.483404 l +0.000000 1.279245 l +0.000000 0.572721 0.572751 -0.000031 1.279276 -0.000031 c +5.221627 -0.000031 l +5.221627 2.558521 l +2.558551 2.558521 l +2.558551 17.483404 l +2.558551 22.600506 l +36.028580 22.600506 l +36.028580 20.894806 l +38.587132 20.894806 l +38.587132 23.879782 l +38.587132 24.586306 38.014378 25.159058 37.307854 25.159058 c +1.279276 25.159058 l +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 3.942383 9.056763 cm +0.000000 0.000000 0.000000 scn +1.279276 25.105347 m +36.988125 25.105347 l +37.327412 25.105347 37.652798 24.970568 37.892708 24.730656 c +38.132618 24.490746 38.267403 24.165356 38.267403 23.826071 c +38.267403 10.402944 l +37.452763 10.640730 36.596001 10.804845 35.708851 10.885599 c +35.708851 22.546795 l +2.558552 22.546795 l +2.558552 2.558540 l +23.013508 2.558540 l +22.961720 2.192711 22.935091 1.820532 22.935091 1.443214 c +22.935091 0.952564 22.980118 0.470600 23.066944 -0.000010 c +1.279276 -0.000010 l +0.939991 -0.000010 0.614603 0.134769 0.374692 0.374680 c +0.134781 0.614590 0.000000 0.939981 0.000000 1.279266 c +0.000000 23.826071 l +0.000000 24.165356 0.134781 24.490746 0.374692 24.730656 c +0.614603 24.970568 0.939991 25.105347 1.279276 25.105347 c +h +23.255920 6.382050 m +22.035740 5.566751 20.601198 5.131590 19.133701 5.131590 c +17.166679 5.134298 15.280995 5.916893 13.890100 7.307789 c +12.499204 8.698685 11.716607 10.584367 11.713900 12.551389 c +11.713900 14.018887 12.149064 15.453429 12.964362 16.673609 c +13.779660 17.893787 14.938475 18.844801 16.294266 19.406387 c +17.650057 19.967976 19.141930 20.114914 20.581230 19.828619 c +22.020531 19.542324 23.342613 18.835655 24.380291 17.797979 c +25.417969 16.760302 26.124636 15.438218 26.410931 13.998919 c +26.697226 12.559619 26.550285 11.067746 25.988699 9.711955 c +25.427113 8.356165 24.476101 7.197348 23.255920 6.382050 c +h +21.834467 16.593369 m +21.035038 17.127529 20.095165 17.412636 19.133701 17.412636 c +19.133701 17.414341 l +17.844608 17.412762 16.608788 16.899853 15.697420 15.988167 c +14.786053 15.076480 14.273580 13.840483 14.272451 12.551389 c +14.272451 11.589925 14.557560 10.650051 15.091721 9.850623 c +15.625881 9.051195 16.385103 8.428120 17.273380 8.060183 c +18.161657 7.692245 19.139093 7.595976 20.082083 7.783548 c +21.025072 7.971121 21.891266 8.434109 22.571123 9.113967 c +23.250980 9.793824 23.713966 10.660017 23.901539 11.603006 c +24.089111 12.545996 23.992846 13.523432 23.624908 14.411709 c +23.256971 15.299986 22.633896 16.059208 21.834467 16.593369 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 8.500000 27.750000 cm +0.000000 0.000000 0.000000 scn +0.000000 1.250000 m +0.000000 1.940356 0.559644 2.500000 1.250000 2.500000 c +5.250000 2.500000 l +5.940356 2.500000 6.500000 1.940356 6.500000 1.250000 c +6.500000 0.559644 5.940356 0.000000 5.250000 0.000000 c +1.250000 0.000000 l +0.559644 0.000000 0.000000 0.559644 0.000000 1.250000 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 33.000000 12.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 1.000000 m +0.000000 1.552285 0.447715 2.000000 1.000000 2.000000 c +8.000000 2.000000 l +8.552285 2.000000 9.000000 1.552285 9.000000 1.000000 c +9.000000 1.000000 l +9.000000 0.447715 8.552285 0.000000 8.000000 0.000000 c +1.000000 0.000000 l +0.447715 0.000000 0.000000 0.447715 0.000000 1.000000 c +0.000000 1.000000 l +h +f +n +Q +q +q +-1.000000 -0.000000 -0.000000 1.000000 43.000000 6.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 1.000000 m +0.000000 1.552285 0.447715 2.000000 1.000000 2.000000 c +8.000000 2.000000 l +8.552285 2.000000 9.000000 1.552285 9.000000 1.000000 c +9.000000 1.000000 l +9.000000 0.447715 8.552285 0.000000 8.000000 0.000000 c +1.000000 0.000000 l +0.447715 0.000000 0.000000 0.447715 0.000000 1.000000 c +0.000000 1.000000 l +h +f +n +Q +43.000000 7.000000 m +43.000000 7.552284 42.552284 8.000000 42.000000 8.000000 c +35.000000 8.000000 l +34.447716 8.000000 34.000000 7.552284 34.000000 7.000000 c +34.000000 7.000000 l +34.000000 6.447716 34.447716 6.000000 35.000000 6.000000 c +42.000000 6.000000 l +42.552284 6.000000 43.000000 6.447716 43.000000 7.000000 c +43.000000 7.000000 l +h +W* +n +q +-1.000000 -0.000000 -0.000000 1.000000 43.000000 6.000000 cm +0.000000 0.000000 0.000000 scn +1.000000 1.000000 m +8.000000 1.000000 l +8.000000 3.000000 l +1.000000 3.000000 l +1.000000 1.000000 l +h +8.000000 1.000000 m +1.000000 1.000000 l +1.000000 -1.000000 l +8.000000 -1.000000 l +8.000000 1.000000 l +h +1.000000 1.000000 m +1.000000 1.000000 l +-1.000000 1.000000 l +-1.000000 -0.104569 -0.104570 -1.000000 1.000000 -1.000000 c +1.000000 1.000000 l +h +8.000000 1.000000 m +8.000000 1.000000 l +8.000000 -1.000000 l +9.104569 -1.000000 10.000000 -0.104569 10.000000 1.000000 c +8.000000 1.000000 l +h +8.000000 1.000000 m +8.000000 1.000000 l +10.000000 1.000000 l +10.000000 2.104569 9.104569 3.000000 8.000000 3.000000 c +8.000000 1.000000 l +h +1.000000 3.000000 m +-0.104569 3.000000 -1.000000 2.104569 -1.000000 1.000000 c +1.000000 1.000000 l +1.000000 1.000000 l +1.000000 3.000000 l +h +f +n +Q +Q +q +50.000000 10.000000 m +50.000000 4.477154 44.851273 0.000000 38.500000 0.000000 c +32.148727 0.000000 27.000000 4.477154 27.000000 10.000000 c +27.000000 15.522848 32.148727 20.000000 38.500000 20.000000 c +44.851273 20.000000 50.000000 15.522848 50.000000 10.000000 c +h +W* +n +q +1.000000 0.000000 -0.000000 1.000000 27.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +22.000000 10.000000 m +22.000000 5.156100 17.434797 1.000000 11.500000 1.000000 c +11.500000 -1.000000 l +18.267752 -1.000000 24.000000 3.798204 24.000000 10.000000 c +22.000000 10.000000 l +h +11.500000 1.000000 m +5.565202 1.000000 1.000000 5.156100 1.000000 10.000000 c +-1.000000 10.000000 l +-1.000000 3.798204 4.732249 -1.000000 11.500000 -1.000000 c +11.500000 1.000000 l +h +1.000000 10.000000 m +1.000000 14.843899 5.565202 19.000000 11.500000 19.000000 c +11.500000 21.000000 l +4.732249 21.000000 -1.000000 16.201796 -1.000000 10.000000 c +1.000000 10.000000 l +h +11.500000 19.000000 m +17.434797 19.000000 22.000000 14.843899 22.000000 10.000000 c +24.000000 10.000000 l +24.000000 16.201796 18.267752 21.000000 11.500000 21.000000 c +11.500000 19.000000 l +h +f +n +Q +Q +q +0.707107 -0.707107 0.707107 0.707107 28.118925 3.187651 cm +0.000000 0.000000 0.000000 scn +1.000000 6.949707 m +0.447715 6.949707 0.000000 6.501992 0.000000 5.949707 c +0.000000 5.397422 0.447715 4.949707 1.000000 4.949707 c +1.000000 6.949707 l +h +4.000000 4.949707 m +4.552285 4.949707 5.000000 5.397422 5.000000 5.949707 c +5.000000 6.501992 4.552285 6.949707 4.000000 6.949707 c +4.000000 4.949707 l +h +1.000000 4.949707 m +4.000000 4.949707 l +4.000000 6.949707 l +1.000000 6.949707 l +1.000000 4.949707 l +h +f +n +Q +q +0.707107 0.707107 -0.707107 0.707107 36.499969 1.964506 cm +0.000000 0.000000 0.000000 scn +1.000000 6.949707 m +0.447715 6.949707 0.000000 6.501992 0.000000 5.949707 c +0.000000 5.397422 0.447715 4.949707 1.000000 4.949707 c +1.000000 6.949707 l +h +4.000000 4.949707 m +4.552285 4.949707 5.000000 5.397422 5.000000 5.949707 c +5.000000 6.501992 4.552285 6.949707 4.000000 6.949707 c +4.000000 4.949707 l +h +1.000000 4.949707 m +4.000000 4.949707 l +4.000000 6.949707 l +1.000000 6.949707 l +1.000000 4.949707 l +h +f +n +Q +q +-0.707107 0.707107 -0.707107 -0.707107 47.035614 16.964449 cm +0.000000 0.000000 0.000000 scn +1.000000 6.949707 m +0.447715 6.949707 0.000000 6.501992 0.000000 5.949707 c +0.000000 5.397422 0.447715 4.949707 1.000000 4.949707 c +1.000000 6.949707 l +h +4.000000 4.949707 m +4.552285 4.949707 5.000000 5.397422 5.000000 5.949707 c +5.000000 6.501992 4.552285 6.949707 4.000000 6.949707 c +4.000000 4.949707 l +h +1.000000 4.949707 m +4.000000 4.949707 l +4.000000 6.949707 l +1.000000 6.949707 l +1.000000 4.949707 l +h +f +n +Q +q +-0.707107 -0.707107 0.707107 -0.707107 39.035675 17.499971 cm +0.000000 0.000000 0.000000 scn +1.000000 6.949707 m +0.447715 6.949707 0.000000 6.501992 0.000000 5.949707 c +0.000000 5.397422 0.447715 4.949707 1.000000 4.949707 c +1.000000 6.949707 l +h +4.000000 4.949707 m +4.552285 4.949707 5.000000 5.397422 5.000000 5.949707 c +5.000000 6.501992 4.552285 6.949707 4.000000 6.949707 c +4.000000 4.949707 l +h +1.000000 4.949707 m +4.000000 4.949707 l +4.000000 6.949707 l +1.000000 6.949707 l +1.000000 4.949707 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 8320 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 50.000000 38.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000008410 00000 n +0000008433 00000 n +0000008606 00000 n +0000008680 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +8739 +%%EOF \ No newline at end of file diff --git a/docs/res/icons/gear.fill.pdf b/docs/res/icons/gear.fill.pdf new file mode 100644 index 00000000..6591a0b4 --- /dev/null +++ b/docs/res/icons/gear.fill.pdf @@ -0,0 +1,170 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.596078 0.592157 0.572549 scn +12.000000 0.000000 m +18.627417 0.000000 24.000000 5.372583 24.000000 12.000000 c +24.000000 18.627417 18.627417 24.000000 12.000000 24.000000 c +5.372583 24.000000 0.000000 18.627417 0.000000 12.000000 c +0.000000 5.372583 5.372583 0.000000 12.000000 0.000000 c +h +11.356085 20.726612 m +11.080673 20.706541 10.887666 20.454128 10.923384 20.180305 c +11.042957 19.263630 l +10.397048 19.179354 9.777586 19.010939 9.195930 18.769743 c +8.841808 19.623703 l +8.736031 19.878782 8.442759 20.001123 8.194168 19.880884 c +7.804816 19.692562 7.431699 19.476336 7.077398 19.234867 c +6.849212 19.079351 6.808879 18.764158 6.977139 18.545200 c +7.540419 17.812202 l +7.032850 17.422173 6.577827 16.967148 6.187798 16.459579 c +5.454800 17.022860 l +5.235841 17.191120 4.920649 17.150787 4.765132 16.922600 c +4.523664 16.568302 4.307439 16.195185 4.119116 15.805833 c +3.998877 15.557242 4.121218 15.263969 4.376298 15.158192 c +5.230257 14.804070 l +4.989061 14.222414 4.820646 13.602952 4.736370 12.957043 c +3.819693 13.076614 l +3.545871 13.112333 3.293459 12.919325 3.273387 12.643913 c +3.257878 12.431119 3.250000 12.216384 3.250000 12.000000 c +3.250000 11.783616 3.257878 11.568881 3.273387 11.356086 c +3.293460 11.080674 3.545871 10.887668 3.819694 10.923385 c +4.736370 11.042957 l +4.820646 10.397048 4.989061 9.777586 5.230256 9.195931 c +4.376297 8.841809 l +4.121218 8.736032 3.998877 8.442759 4.119116 8.194168 c +4.307439 7.804817 4.523664 7.431700 4.765132 7.077400 c +4.920649 6.849213 5.235842 6.808880 5.454801 6.977140 c +6.187798 7.540421 l +6.577827 7.032852 7.032850 6.577827 7.540419 6.187798 c +6.977141 5.454802 l +6.808880 5.235844 6.849213 4.920650 7.077399 4.765133 c +7.431699 4.523664 7.804815 4.307440 8.194167 4.119116 c +8.442758 3.998877 8.736031 4.121218 8.841808 4.376299 c +9.195930 5.230255 l +9.777586 4.989059 10.397048 4.820644 11.042957 4.736368 c +10.923386 3.819695 l +10.887667 3.545872 11.080674 3.293459 11.356086 3.273388 c +11.568880 3.257879 11.783616 3.250000 12.000000 3.250000 c +12.216384 3.250000 12.431120 3.257879 12.643914 3.273388 c +12.919326 3.293459 13.112333 3.545872 13.076615 3.819695 c +12.957043 4.736368 l +13.602952 4.820644 14.222414 4.989059 14.804070 5.230255 c +15.158191 4.376299 l +15.263968 4.121218 15.557241 3.998877 15.805832 4.119116 c +16.195183 4.307440 16.568300 4.523664 16.922600 4.765133 c +17.150787 4.920650 17.191120 5.235844 17.022858 5.454802 c +16.459581 6.187798 l +16.967150 6.577827 17.422173 7.032850 17.812202 7.540419 c +18.545198 6.977142 l +18.764156 6.808880 19.079350 6.849213 19.234867 7.077400 c +19.476336 7.431700 19.692560 7.804817 19.880884 8.194167 c +20.001123 8.442758 19.878782 8.736032 19.623701 8.841808 c +18.769745 9.195930 l +19.010941 9.777586 19.179356 10.397049 19.263632 11.042958 c +20.180305 10.923386 l +20.454128 10.887668 20.706541 11.080674 20.726612 11.356086 c +20.742121 11.568881 20.750000 11.783616 20.750000 12.000000 c +20.750000 12.216385 20.742121 12.431121 20.726612 12.643916 c +20.706541 12.919327 20.454128 13.112334 20.180305 13.076616 c +19.263632 12.957044 l +19.179356 13.602953 19.010939 14.222414 18.769745 14.804070 c +19.623701 15.158192 l +19.878782 15.263969 20.001123 15.557241 19.880884 15.805832 c +19.692560 16.195185 19.476336 16.568302 19.234867 16.922602 c +19.079350 17.150787 18.764156 17.191120 18.545198 17.022861 c +17.812202 16.459581 l +17.422173 16.967150 16.967148 17.422173 16.459579 17.812202 c +17.022860 18.545200 l +17.191120 18.764158 17.150787 19.079351 16.922600 19.234867 c +16.568300 19.476336 16.195183 19.692562 15.805832 19.880884 c +15.557241 20.001123 15.263968 19.878782 15.158192 19.623703 c +14.804069 18.769743 l +14.222413 19.010939 13.602951 19.179354 12.957042 19.263630 c +13.076614 20.180307 l +13.112332 20.454130 12.919325 20.706541 12.643913 20.726612 c +12.431118 20.742123 12.216383 20.750000 12.000000 20.750000 c +11.783615 20.750000 11.568880 20.742123 11.356085 20.726612 c +h +12.696518 11.185913 m +18.050842 11.185913 l +17.652849 8.199290 15.095482 5.895348 12.000000 5.895348 c +11.021035 5.895348 10.095892 6.125784 9.275852 6.535372 c +11.844859 10.710011 l +12.026858 11.005757 12.349257 11.185913 12.696518 11.185913 c +h +5.895349 12.000000 m +5.895349 10.045127 6.814215 8.304866 8.243605 7.187559 c +10.807167 11.353347 l +10.988450 11.647931 11.004625 12.015280 10.849936 12.324657 c +8.508228 17.008072 l +6.928618 15.904668 5.895349 14.073000 5.895349 12.000000 c +h +12.000000 18.104652 m +11.138405 18.104652 10.318498 17.926159 9.575241 17.604132 c +11.897492 12.959629 l +12.066884 12.620845 12.413148 12.406842 12.791920 12.406842 c +18.091309 12.406842 l +17.881950 15.588663 15.234796 18.104652 12.000000 18.104652 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 4761 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000004851 00000 n +0000004874 00000 n +0000005047 00000 n +0000005121 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +5180 +%%EOF \ No newline at end of file diff --git a/docs/res/icons/new.pdf b/docs/res/icons/new.pdf new file mode 100644 index 00000000..c7d904f6 --- /dev/null +++ b/docs/res/icons/new.pdf @@ -0,0 +1,88 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.596078 0.592157 0.572549 scn +12.000000 0.000000 m +18.627417 0.000000 24.000000 5.372583 24.000000 12.000000 c +24.000000 18.627417 18.627417 24.000000 12.000000 24.000000 c +5.372583 24.000000 0.000000 18.627417 0.000000 12.000000 c +0.000000 5.372583 5.372583 0.000000 12.000000 0.000000 c +h +11.272544 19.262787 m +11.265584 19.754541 11.632695 20.000000 11.999791 20.000000 c +12.366895 20.000000 12.734310 19.754541 12.727037 19.262787 c +12.727037 12.727312 l +19.262306 12.727312 l +20.245783 12.741233 20.245783 11.258884 19.262306 11.272775 c +12.727037 11.272775 l +12.727037 4.737299 l +12.732529 4.336187 12.412436 4.006634 12.006896 4.000090 c +11.601364 3.993753 11.266726 4.327286 11.272544 4.737299 c +11.272544 11.272775 l +4.737278 11.272775 l +4.336182 11.267284 4.006634 11.587388 4.000089 11.992941 c +3.993754 12.398488 4.327280 12.733130 4.737278 12.727312 c +11.272544 12.727312 l +11.272544 19.262787 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1007 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001097 00000 n +0000001120 00000 n +0000001293 00000 n +0000001367 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1426 +%%EOF \ No newline at end of file diff --git a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt b/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt index d45a211f..87c99180 100644 --- a/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt +++ b/persistence/LuceneBankingPersistence/src/main/kotlin/net/dankito/banking/persistence/LuceneBankingPersistence.kt @@ -14,9 +14,7 @@ import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName import net.dankito.banking.LuceneConfig.Companion.UsageFieldName -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.* import net.dankito.banking.util.ISerializer import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.utils.lucene.index.DocumentsWriter @@ -47,7 +45,7 @@ open class LuceneBankingPersistence( protected val fields = FieldBuilder() - override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List) { + override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List) { val writer = getWriter() transactions.forEach { transaction -> @@ -60,7 +58,7 @@ open class LuceneBankingPersistence( writer.flushChangesToDisk() } - protected open fun createFieldsForAccountTransaction(bankAccount: BankAccount, transaction: AccountTransaction): List { + protected open fun createFieldsForAccountTransaction(bankAccount: TypedBankAccount, transaction: IAccountTransaction): List { return listOf( fields.keywordField(BankAccountIdFieldName, bankAccount.technicalId), fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true), @@ -79,7 +77,7 @@ open class LuceneBankingPersistence( } - override fun deleteAccount(customer: Customer, allCustomers: List) { + override fun deleteAccount(customer: TypedCustomer, allCustomers: List) { try { deleteAccountTransactions(customer.accounts) } catch (e: Exception) { @@ -89,7 +87,7 @@ open class LuceneBankingPersistence( super.deleteAccount(customer, allCustomers) } - protected open fun deleteAccountTransactions(bankAccounts: List) { + protected open fun deleteAccountTransactions(bankAccounts: List) { val writer = getWriter() val bankAccountIds = bankAccounts.map { it.technicalId } diff --git a/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneRemitteeSearcherTest.kt b/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneRemitteeSearcherTest.kt index d3681437..12d927f6 100644 --- a/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneRemitteeSearcherTest.kt +++ b/persistence/LuceneBankingPersistence/src/test/kotlin/net/dankito/banking/search/LuceneRemitteeSearcherTest.kt @@ -5,12 +5,14 @@ import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.BankAccount import net.dankito.utils.io.FileUtils +import net.dankito.utils.multiplatform.File +import net.dankito.utils.multiplatform.toBigDecimal +import net.dankito.utils.multiplatform.toDate import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Before import org.junit.Test import org.mockito.Mockito.mock -import java.io.File import java.math.BigDecimal import java.text.SimpleDateFormat import java.util.* @@ -211,7 +213,7 @@ class LuceneRemitteeSearcherTest { otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction { - return AccountTransaction(bankAccount, amount, "EUR", usage, bookingDate, otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate) + return AccountTransaction(bankAccount, amount.toBigDecimal(), "EUR", usage, bookingDate.toDate(), otherPartyName, otherPartyBankCode, otherPartyAccountId, null, bookingDate.toDate()) } private fun randomString(): String { diff --git a/persistence/json/BankingPersistenceJson/build.gradle b/persistence/json/BankingPersistenceJson/build.gradle index c9eb8ad8..5177cf94 100644 --- a/persistence/json/BankingPersistenceJson/build.gradle +++ b/persistence/json/BankingPersistenceJson/build.gradle @@ -17,10 +17,6 @@ compileTestKotlin { dependencies { implementation project(':BankingUiCommon') - implementation("org.mapstruct:mapstruct:$mapStructVersion") - - kapt("org.mapstruct:mapstruct-processor:$mapStructVersion") - testImplementation "junit:junit:$junitVersion" diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt index 909a906d..32769af6 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/BankingPersistenceJson.kt @@ -1,13 +1,9 @@ package net.dankito.banking.persistence -import net.dankito.banking.persistence.mapper.CustomerConverter import net.dankito.banking.persistence.model.CustomerEntity +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.File -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.util.ISerializer -import org.mapstruct.factory.Mappers import java.io.FileOutputStream import java.net.URL @@ -17,39 +13,32 @@ open class BankingPersistenceJson( protected val serializer: ISerializer ) : IBankingPersistence { - protected val mapper = Mappers.getMapper(CustomerConverter::class.java) - - init { jsonFile.absoluteFile.parentFile.mkdirs() } - override fun saveOrUpdateAccount(customer: Customer, allCustomers: List) { + override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List) { saveAllCustomers(allCustomers) } - override fun deleteAccount(customer: Customer, allCustomers: List) { + override fun deleteAccount(customer: TypedCustomer, allCustomers: List) { saveAllCustomers(allCustomers) } - override fun readPersistedAccounts(): List { - val deserializedCustomers = serializer.deserializeListOr(jsonFile, CustomerEntity::class) - - return mapper.mapCustomerEntities(deserializedCustomers) + override fun readPersistedAccounts(): List { + return serializer.deserializeListOr(jsonFile, CustomerEntity::class).map { it as TypedCustomer } } - override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List) { + override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List) { // done when called saveOrUpdateAccount() // TODO: or also call saveAllCustomers()? } - protected open fun saveAllCustomers(allCustomers: List) { - val mappedCustomers = mapper.mapCustomers(allCustomers) - - serializer.serializeObject(mappedCustomers, jsonFile) + protected open fun saveAllCustomers(allCustomers: List) { + serializer.serializeObject(allCustomers, jsonFile) } diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CustomerConverter.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CustomerConverter.kt deleted file mode 100644 index 2a1936cb..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CustomerConverter.kt +++ /dev/null @@ -1,90 +0,0 @@ -package net.dankito.banking.persistence.mapper - -import net.dankito.banking.persistence.model.AccountTransactionEntity -import net.dankito.banking.persistence.model.BankAccountEntity -import net.dankito.banking.persistence.model.CustomerEntity -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.Customer -import org.mapstruct.* - - -@Mapper -abstract class CustomerConverter { - - // Context is needed to fix cycle dependencies issue - - protected val bankAccountCustomerField = BankAccount::class.java.getDeclaredField(BankAccount::customer.name) - - - init { - bankAccountCustomerField.isAccessible = true - } - - - @Mapping(source = "technicalId", target = "id") - abstract fun mapToEntity(customer: Customer, @Context context: CycleAvoidingMappingContext): CustomerEntity - - @InheritInverseConfiguration - abstract fun mapCustomer(customer: CustomerEntity, @Context context: CycleAvoidingMappingContext): Customer - - abstract fun mapCustomers(customers: List, @Context context: CycleAvoidingMappingContext): List - - open fun mapCustomers(customers: List): List { - // create a new context instance each time as otherwise just cached instance would be taken und BankAccounts and AccountTransactions would never get updated - return mapCustomers(customers, CycleAvoidingMappingContext()) - } - - abstract fun mapCustomerEntities(customers: List, @Context context: CycleAvoidingMappingContext): List - - open fun mapCustomerEntities(customers: List): List { - // create a new context instance each time as otherwise just cached instance would be taken und BankAccounts and AccountTransactions would never get updated - return mapCustomerEntities(customers, CycleAvoidingMappingContext()) - } - - - @Mapping(source = "technicalId", target = "id") - abstract fun mapBankAccount(account: BankAccount, @Context context: CycleAvoidingMappingContext): BankAccountEntity - - @InheritInverseConfiguration - abstract fun mapBankAccount(account: BankAccountEntity, @Context context: CycleAvoidingMappingContext): BankAccount - - abstract fun mapBankAccounts(accounts: List, @Context context: CycleAvoidingMappingContext): List - - abstract fun mapBankAccountEntities(accounts: List, @Context context: CycleAvoidingMappingContext): List - - @AfterMapping - open fun mapBankAccountCustomer(serializedAccount: BankAccountEntity, @MappingTarget account: BankAccount, @Context context: CycleAvoidingMappingContext) { - val mappedCustomer = mapCustomer(serializedAccount.customer, context) - - bankAccountCustomerField.set(account, mappedCustomer) - } - - - - @Mapping(source = "technicalId", target = "id") - abstract fun mapTransaction(transaction: AccountTransaction, @Context context: CycleAvoidingMappingContext): AccountTransactionEntity - - @InheritInverseConfiguration - fun mapTransaction(transaction: AccountTransactionEntity, @Context context: CycleAvoidingMappingContext): AccountTransaction { - val account = mapBankAccount(transaction.bankAccount, context) - - val mappedTransaction = AccountTransaction(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate, - transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, - transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, - transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode, - transaction.compensationAmount, transaction.originalAmount, transaction.sepaUsage, transaction.deviantOriginator, transaction.deviantRecipient, - transaction.usageWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement, transaction.currencyType, transaction.bookingKey, - transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails, - transaction.transactionReferenceNumber, transaction.relatedReferenceNumber) - - mappedTransaction.technicalId = transaction.id - - return mappedTransaction - } - - abstract fun mapTransactions(transactions: List, @Context context: CycleAvoidingMappingContext): List - - abstract fun mapTransactionEntities(transactions: List, @Context context: CycleAvoidingMappingContext): List - -} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CycleAvoidingMappingContext.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CycleAvoidingMappingContext.kt deleted file mode 100644 index fb1cdc35..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/CycleAvoidingMappingContext.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.dankito.banking.persistence.mapper - -import org.mapstruct.BeforeMapping -import org.mapstruct.MappingTarget -import org.mapstruct.TargetType -import java.util.* - - -open class CycleAvoidingMappingContext { - - private val knownInstances: MutableMap = IdentityHashMap() - - - /** - * Gets an instance out of this context if it is already mapped. - */ - @BeforeMapping - open fun getMappedInstance(source: Any, @TargetType targetType: Class): T { - return targetType.cast(knownInstances[source]) - } - - /** - * Puts an instance into the cache, so that it can be remembered to avoid endless mapping. - */ - @BeforeMapping - open fun storeMappedInstance(source: Any, @MappingTarget target: Any) { - knownInstances[source] = target - } - -} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt deleted file mode 100644 index b00d1336..00000000 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesMapper.kt +++ /dev/null @@ -1,136 +0,0 @@ -package net.dankito.banking.persistence.mapper - -import net.dankito.banking.persistence.model.AccountTransactionEntity -import net.dankito.banking.persistence.model.BankAccountEntity -import net.dankito.banking.persistence.model.CustomerEntity -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.Customer - - -open class EntitiesMapper { - - open fun mapCustomers(customers: List): List { - return customers.map { mapCustomer(it) } - } - - open fun mapCustomer(customer: Customer): CustomerEntity { - val mappedCustomer = CustomerEntity( - customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, - customer.bankName, customer.bic, customer.customerName, customer.userId, customer.iconUrl, - listOf(), customer.supportedTanProcedures, customer.selectedTanProcedure, customer.tanMedia - ) - - mappedCustomer.id = customer.technicalId - mappedCustomer.userSetDisplayName = customer.userSetDisplayName - - mappedCustomer.accounts = mapBankAccounts(customer.accounts, mappedCustomer) - - return mappedCustomer - } - - - open fun mapCustomerEntities(customers: List): List { - return customers.map { mapCustomer(it) } - } - - open fun mapCustomer(customer: CustomerEntity): Customer { - val mappedCustomer = Customer( - customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, - customer.bankName, customer.bic, customer.customerName, customer.userId, customer.iconUrl - ) - - mappedCustomer.technicalId = customer.id - mappedCustomer.userSetDisplayName = customer.userSetDisplayName - - mappedCustomer.accounts = mapBankAccounts(customer.accounts, mappedCustomer) - - - mappedCustomer.supportedTanProcedures = customer.supportedTanProcedures - mappedCustomer.selectedTanProcedure = customer.selectedTanProcedure - mappedCustomer.tanMedia = customer.tanMedia - - return mappedCustomer - } - - - open fun mapBankAccounts(transactions: List, customer: CustomerEntity): List { - return transactions.map { mapBankAccount(it, customer) } - } - - open fun mapBankAccount(account: BankAccount, customer: CustomerEntity): BankAccountEntity { - val mappedAccount = BankAccountEntity( - customer, account.identifier, account.accountHolderName, account.iban, account.subAccountNumber, - account.customerId, account.balance, account.currency, account.type, account.productName, - account.accountLimit, account.lastRetrievedTransactionsTimestamp, - account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, - account.supportsTransferringMoney, account.supportsInstantPaymentMoneyTransfer - ) - - mappedAccount.id = account.technicalId - mappedAccount.userSetDisplayName = account.userSetDisplayName - - mappedAccount.bookedTransactions = mapTransactions(account.bookedTransactions, mappedAccount) - - return mappedAccount - } - - - open fun mapBankAccounts(transactions: List, customer: Customer): List { - return transactions.map { mapBankAccount(it, customer) } - } - - open fun mapBankAccount(account: BankAccountEntity, customer: Customer): BankAccount { - val mappedAccount = BankAccount( - customer, account.identifier, account.accountHolderName, account.iban, account.subAccountNumber, - account.customerId, account.balance, account.currency, account.type, account.productName, - account.accountLimit, account.lastRetrievedTransactionsTimestamp, - account.supportsRetrievingAccountTransactions, account.supportsRetrievingBalance, - account.supportsTransferringMoney, account.supportsInstantPaymentMoneyTransfer - ) - - mappedAccount.technicalId = account.id - mappedAccount.userSetDisplayName = account.userSetDisplayName - - mappedAccount.bookedTransactions = mapTransactions(account.bookedTransactions, mappedAccount) - - return mappedAccount - } - - - open fun mapTransactions(transactions: List, account: BankAccountEntity): List { - return transactions.map { mapTransaction(it, account) } - } - - open fun mapTransaction(transaction: AccountTransaction, account: BankAccountEntity): AccountTransactionEntity { - return AccountTransactionEntity(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate, - transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, - transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, - transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode, - transaction.compensationAmount, transaction.originalAmount, transaction.sepaUsage, transaction.deviantOriginator, transaction.deviantRecipient, - transaction.usageWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement, transaction.currencyType, transaction.bookingKey, - transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails, - transaction.transactionReferenceNumber, transaction.relatedReferenceNumber, transaction.technicalId) - } - - - open fun mapTransactions(transactions: List, account: BankAccount): List { - return transactions.map { mapTransaction(it, account) } - } - - open fun mapTransaction(transaction: AccountTransactionEntity, account: BankAccount): AccountTransaction { - val mappedTransaction = AccountTransaction(account, transaction.amount, transaction.currency, transaction.unparsedUsage, transaction.bookingDate, - transaction.otherPartyName, transaction.otherPartyBankCode, transaction.otherPartyAccountId, transaction.bookingText, - transaction.valueDate, transaction.statementNumber, transaction.sequenceNumber, transaction.openingBalance, transaction.closingBalance, - transaction.endToEndReference, transaction.customerReference, transaction.mandateReference, transaction.creditorIdentifier, transaction.originatorsIdentificationCode, - transaction.compensationAmount, transaction.originalAmount, transaction.sepaUsage, transaction.deviantOriginator, transaction.deviantRecipient, - transaction.usageWithNoSpecialType, transaction.primaNotaNumber, transaction.textKeySupplement, transaction.currencyType, transaction.bookingKey, - transaction.referenceForTheAccountOwner, transaction.referenceOfTheAccountServicingInstitution, transaction.supplementaryDetails, - transaction.transactionReferenceNumber, transaction.relatedReferenceNumber) - - mappedTransaction.technicalId = transaction.id - - return mappedTransaction - } - -} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt new file mode 100644 index 00000000..c63cbfdb --- /dev/null +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/mapper/EntitiesModelCreator.kt @@ -0,0 +1,72 @@ +package net.dankito.banking.persistence.mapper + +import net.dankito.banking.persistence.model.AccountTransactionEntity +import net.dankito.banking.persistence.model.BankAccountEntity +import net.dankito.banking.persistence.model.CustomerEntity +import net.dankito.banking.ui.model.IAccountTransaction +import net.dankito.banking.ui.model.TypedBankAccount +import net.dankito.banking.ui.model.TypedCustomer +import net.dankito.banking.ui.model.mapper.IModelCreator +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date + + +open class EntitiesModelCreator : IModelCreator { + + override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String, + customerName: String, userId: String, iconUrl: String?): TypedCustomer { + + return CustomerEntity(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) as TypedCustomer + } + + + override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount { + return BankAccountEntity(customer as CustomerEntity, identifier, "", null, null, "", productName = productName) as TypedBankAccount + } + + override fun createTransaction( + bankAccount: TypedBankAccount, + amount: BigDecimal, + currency: String, + unparsedUsage: String, + bookingDate: Date, + otherPartyName: String?, + otherPartyBankCode: String?, + otherPartyAccountId: String?, + bookingText: String?, + valueDate: Date, + statementNumber: Int, + sequenceNumber: Int?, + openingBalance: BigDecimal?, + closingBalance: BigDecimal?, + endToEndReference: String?, + customerReference: String?, + mandateReference: String?, + creditorIdentifier: String?, + originatorsIdentificationCode: String?, + compensationAmount: String?, + originalAmount: String?, + sepaUsage: String?, + deviantOriginator: String?, + deviantRecipient: String?, + usageWithNoSpecialType: String?, + primaNotaNumber: String?, + textKeySupplement: String?, + currencyType: String?, + bookingKey: String, + referenceForTheAccountOwner: String, + referenceOfTheAccountServicingInstitution: String?, + supplementaryDetails: String?, + transactionReferenceNumber: String, + relatedReferenceNumber: String? + ) : IAccountTransaction { + + return AccountTransactionEntity(bankAccount as BankAccountEntity, amount, currency, unparsedUsage, bookingDate, + otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber, + openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, + originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient, + usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, + referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) + } + +} \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt index 2d0039d9..df72e5bb 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/AccountTransactionEntity.kt @@ -2,57 +2,63 @@ package net.dankito.banking.persistence.model import com.fasterxml.jackson.annotation.JsonIdentityInfo import com.fasterxml.jackson.annotation.ObjectIdGenerators +import net.dankito.banking.ui.model.IAccountTransaction import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.UUID -@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references // had to define all properties as 'var' 'cause MapStruct cannot handle vals open class AccountTransactionEntity( - open var bankAccount: BankAccountEntity, - open var amount: BigDecimal, - open var currency: String, - open var unparsedUsage: String, - open var bookingDate: Date, - open var otherPartyName: String?, - open var otherPartyBankCode: String?, - open var otherPartyAccountId: String?, - open var bookingText: String?, - open var valueDate: Date, - open var statementNumber: Int, - open var sequenceNumber: Int?, - open var openingBalance: BigDecimal?, - open var closingBalance: BigDecimal?, + override var bankAccount: BankAccountEntity, + override var amount: BigDecimal, + override var currency: String, + override var unparsedUsage: String, + override var bookingDate: Date, + override var otherPartyName: String?, + override var otherPartyBankCode: String?, + override var otherPartyAccountId: String?, + override var bookingText: String?, + override var valueDate: Date, + override var statementNumber: Int, + override var sequenceNumber: Int?, + override var openingBalance: BigDecimal?, + override var closingBalance: BigDecimal?, - open var endToEndReference: String?, - open var customerReference: String?, - open var mandateReference: String?, - open var creditorIdentifier: String?, - open var originatorsIdentificationCode: String?, - open var compensationAmount: String?, - open var originalAmount: String?, - open var sepaUsage: String?, - open var deviantOriginator: String?, - open var deviantRecipient: String?, - open var usageWithNoSpecialType: String?, - open var primaNotaNumber: String?, - open var textKeySupplement: String?, + override var endToEndReference: String?, + override var customerReference: String?, + override var mandateReference: String?, + override var creditorIdentifier: String?, + override var originatorsIdentificationCode: String?, + override var compensationAmount: String?, + override var originalAmount: String?, + override var sepaUsage: String?, + override var deviantOriginator: String?, + override var deviantRecipient: String?, + override var usageWithNoSpecialType: String?, + override var primaNotaNumber: String?, + override var textKeySupplement: String?, - open var currencyType: String?, - open var bookingKey: String, - open var referenceForTheAccountOwner: String, - open var referenceOfTheAccountServicingInstitution: String?, - open var supplementaryDetails: String?, + override var currencyType: String?, + override var bookingKey: String, + override var referenceForTheAccountOwner: String, + override var referenceOfTheAccountServicingInstitution: String?, + override var supplementaryDetails: String?, - open var transactionReferenceNumber: String, - open var relatedReferenceNumber: String?, - var id: String = UUID.random().toString() -) { + override var transactionReferenceNumber: String, + override var relatedReferenceNumber: String?, + override var technicalId: String = UUID.random() +) : IAccountTransaction { // for object deserializers internal constructor() : this(BankAccountEntity(), BigDecimal.Zero, "", "", Date(), null, null, null, null, Date(), -1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null) + constructor(bankAccount: BankAccountEntity, otherPartyName: String?, unparsedUsage: String, amount: BigDecimal, valueDate: Date, bookingText: String?) + : this(bankAccount, amount, "EUR", unparsedUsage, valueDate, otherPartyName, null, null, bookingText, valueDate, 0, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, + null, "", "", null, null, "", null) + } \ No newline at end of file diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt index e03dc2b6..56d24334 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/BankAccountEntity.kt @@ -2,37 +2,39 @@ package net.dankito.banking.persistence.model import com.fasterxml.jackson.annotation.JsonIdentityInfo import com.fasterxml.jackson.annotation.ObjectIdGenerators -import net.dankito.banking.ui.model.BankAccountType +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.UUID -@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references // had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableBankAccountBuilder would fail with @Context) open class BankAccountEntity( - open var customer: CustomerEntity, - open var identifier: String, - open var accountHolderName: String, - open var iban: String?, - open var subAccountNumber: String?, - open var customerId: String, - open var balance: BigDecimal = BigDecimal.Zero, - open var currency: String = "EUR", - open var type: BankAccountType = BankAccountType.Girokonto, - open var productName: String? = null, - open var accountLimit: String? = null, - open var lastRetrievedTransactionsTimestamp: Date? = null, - open var supportsRetrievingAccountTransactions: Boolean = false, - open var supportsRetrievingBalance: Boolean = false, - open var supportsTransferringMoney: Boolean = false, - open var supportsInstantPaymentMoneyTransfer: Boolean = false, - open var bookedTransactions: List = listOf(), - open var unbookedTransactions: List = listOf(), - open var id: String = UUID.random().toString(), - open var userSetDisplayName: String? = null + override var customer: CustomerEntity, + override var identifier: String, + override var accountHolderName: String, + override var iban: String?, + override var subAccountNumber: String?, + override var customerId: String, + override var balance: BigDecimal = BigDecimal.Zero, + override var currency: String = "EUR", + override var type: BankAccountType = BankAccountType.Girokonto, + override var productName: String? = null, + override var accountLimit: String? = null, + override var lastRetrievedTransactionsTimestamp: Date? = null, + override var supportsRetrievingAccountTransactions: Boolean = false, + override var supportsRetrievingBalance: Boolean = false, + override var supportsTransferringMoney: Boolean = false, + override var supportsInstantPaymentMoneyTransfer: Boolean = false, + override var bookedTransactions: List = listOf(), + override var unbookedTransactions: List = listOf(), + override var technicalId: String = UUID.random(), + override var userSetDisplayName: String? = null, + override var haveAllTransactionsBeenFetched: Boolean = false, + override var displayIndex: Int = 0 -) { +) : IBankAccount { internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers diff --git a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt index 1af860a1..9aa46cc8 100644 --- a/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt +++ b/persistence/json/BankingPersistenceJson/src/main/kotlin/net/dankito/banking/persistence/model/CustomerEntity.kt @@ -1,30 +1,32 @@ package net.dankito.banking.persistence.model import com.fasterxml.jackson.annotation.* +import net.dankito.banking.ui.model.ICustomer import net.dankito.banking.ui.model.tan.TanMedium import net.dankito.banking.ui.model.tan.TanProcedure import java.util.* -@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references +@JsonIdentityInfo(property = "technicalId", generator = ObjectIdGenerators.PropertyGenerator::class) // to avoid stack overflow due to circular references // had to define all properties as 'var' 'cause MapStruct cannot handle vals (and cannot use Pozo's mapstruct-kotlin as SerializableCustomerBuilder would fail with @Context) open class CustomerEntity( - var bankCode: String, - var customerId: String, - var password: String, - var finTsServerAddress: String, - var bankName: String, - var bic: String, - var customerName: String, - var userId: String = customerId, - var iconUrl: String? = null, - var accounts: List = listOf(), - var supportedTanProcedures: List = listOf(), - var selectedTanProcedure: TanProcedure? = null, - var tanMedia: List = listOf(), - var id: String = UUID.randomUUID().toString(), - var userSetDisplayName: String? = null -) { + override var bankCode: String, + override var customerId: String, + override var password: String, + override var finTsServerAddress: String, + override var bankName: String, + override var bic: String, + override var customerName: String, + override var userId: String = customerId, + override var iconUrl: String? = null, + override var accounts: List = listOf(), + override var supportedTanProcedures: List = listOf(), + override var selectedTanProcedure: TanProcedure? = null, + override var tanMedia: List = listOf(), + override var technicalId: String = UUID.randomUUID().toString(), + override var userSetDisplayName: String? = null, + override var displayIndex: Int = 0 +) : ICustomer { internal constructor() : this("", "", "", "", "", "", "") // for object deserializers diff --git a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt index 1eb72b0c..62732c25 100644 --- a/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt +++ b/persistence/json/BankingPersistenceJson/src/test/kotlin/net/dankito/banking/persistence/BankingPersistenceJsonTest.kt @@ -1,13 +1,9 @@ package net.dankito.banking.persistence -import net.dankito.banking.persistence.mapper.CustomerConverter -import net.dankito.banking.persistence.mapper.CycleAvoidingMappingContext import net.dankito.banking.persistence.model.AccountTransactionEntity import net.dankito.banking.persistence.model.BankAccountEntity import net.dankito.banking.persistence.model.CustomerEntity -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.* import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.Date @@ -15,7 +11,6 @@ import net.dankito.utils.multiplatform.File import org.assertj.core.api.Assertions.assertThat import org.junit.Assert import org.junit.Test -import org.mapstruct.factory.Mappers import kotlin.random.Random @@ -72,7 +67,7 @@ class BankingPersistenceJsonTest { // when - underTest.saveOrUpdateAccount(customers.first(), customers) + underTest.saveOrUpdateAccount(customers.first() as TypedCustomer, customers.map { it as TypedCustomer }) // then @@ -89,13 +84,13 @@ class BankingPersistenceJsonTest { // when - underTest.saveOrUpdateAccount(customer, listOf(customer)) + underTest.saveOrUpdateAccount(customer as TypedCustomer, listOf(customer).map { it as TypedCustomer }) // then val result = serializer.deserializeListOr(file, CustomerEntity::class) - assertCustomersEqual(result, listOf(customer)) + assertCustomersEqual(result, listOf(customer) as List) } @@ -107,9 +102,8 @@ class BankingPersistenceJsonTest { createCustomer(2), createCustomer(3) ) - val serializableCustomers = Mappers.getMapper(CustomerConverter::class.java).mapCustomers(customers, CycleAvoidingMappingContext()) - serializer.serializeObject(serializableCustomers, file) + serializer.serializeObject(customers, file) // when @@ -117,19 +111,19 @@ class BankingPersistenceJsonTest { // then - assertCustomersEqual(serializableCustomers, result) + assertCustomersEqual(customers, result as List) } - private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): Customer { - val result = Customer(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl) + private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): CustomerEntity { + val result = CustomerEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl) result.accounts = createBankAccounts(countBankAccounts, result) return result } - private fun createBankAccounts(count: Int, customer: Customer): List { + private fun createBankAccounts(count: Int, customer: CustomerEntity): List { val random = Random(System.nanoTime()) return IntRange(1, count).map { accountIndex -> @@ -137,8 +131,8 @@ class BankingPersistenceJsonTest { } } - private fun createBankAccount(productName: String, customer: Customer, countTransactions: Int = 0): BankAccount { - val result = BankAccount(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null, + private fun createBankAccount(productName: String, customer: CustomerEntity, countTransactions: Int = 0): BankAccountEntity { + val result = BankAccountEntity(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null, customer.customerId, BigDecimal(84.25), productName = productName) result.bookedTransactions = createAccountTransactions(countTransactions, result) @@ -146,14 +140,14 @@ class BankingPersistenceJsonTest { return result } - private fun createAccountTransactions(countTransactions: Int, account: BankAccount): List { + private fun createAccountTransactions(countTransactions: Int, account: BankAccountEntity): List { return IntRange(1, countTransactions).map { transactionIndex -> createAccountTransaction(transactionIndex, account) } } - private fun createAccountTransaction(transactionIndex: Int, account: BankAccount): AccountTransaction { - return AccountTransaction(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) + private fun createAccountTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity { + return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) } private fun createDate(): Date { @@ -161,11 +155,11 @@ class BankingPersistenceJsonTest { } - private fun assertCustomersEqual(deserializedCustomers: List, customers: List) { + private fun assertCustomersEqual(deserializedCustomers: List, customers: List) { assertThat(deserializedCustomers.size).isEqualTo(customers.size) deserializedCustomers.forEach { deserializedCustomer -> - val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.id } + val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.technicalId } if (customer == null) { Assert.fail("Could not find matching customer for deserialized customer $deserializedCustomer. customers = $customers") @@ -176,7 +170,7 @@ class BankingPersistenceJsonTest { } } - private fun assertCustomersEqual(deserializedCustomer: CustomerEntity, customer: Customer) { + private fun assertCustomersEqual(deserializedCustomer: CustomerEntity, customer: CustomerEntity) { assertThat(deserializedCustomer.bankCode).isEqualTo(customer.bankCode) assertThat(deserializedCustomer.customerId).isEqualTo(customer.customerId) assertThat(deserializedCustomer.password).isEqualTo(customer.password) @@ -191,11 +185,11 @@ class BankingPersistenceJsonTest { assertBankAccountsEqual(deserializedCustomer.accounts, customer.accounts) } - private fun assertBankAccountsEqual(deserializedAccounts: List, accounts: List) { + private fun assertBankAccountsEqual(deserializedAccounts: List, accounts: List) { assertThat(deserializedAccounts.size).isEqualTo(accounts.size) deserializedAccounts.forEach { deserializedAccount -> - val account = accounts.firstOrNull { it.technicalId == deserializedAccount.id } + val account = accounts.firstOrNull { it.technicalId == deserializedAccount.technicalId } if (account == null) { Assert.fail("Could not find matching account for deserialized account $deserializedAccount. accounts = $accounts") @@ -206,9 +200,9 @@ class BankingPersistenceJsonTest { } } - private fun assertBankAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccount) { + private fun assertBankAccountsEqual(deserializedAccount: BankAccountEntity, account: BankAccountEntity) { // to check if MapStruct created reference correctly - assertThat(deserializedAccount.customer.id).isEqualTo(account.customer.technicalId) + assertThat(deserializedAccount.customer.technicalId).isEqualTo(account.customer.technicalId) assertThat(deserializedAccount.identifier).isEqualTo(account.identifier) assertThat(deserializedAccount.iban).isEqualTo(account.iban) @@ -219,11 +213,11 @@ class BankingPersistenceJsonTest { assertAccountTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions) } - private fun assertAccountTransactionsEqual(deserializedTransactions: List, transactions: List) { + private fun assertAccountTransactionsEqual(deserializedTransactions: List, transactions: List) { assertThat(deserializedTransactions.size).isEqualTo(transactions.size) deserializedTransactions.forEach { deserializedTransaction -> - val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.id } + val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.technicalId } if (transaction == null) { Assert.fail("Could not find matching transaction for deserialized transaction $deserializedTransaction. transactions = $transactions") @@ -234,9 +228,9 @@ class BankingPersistenceJsonTest { } } - private fun assertAccountTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransaction) { + private fun assertAccountTransactionsEqual(deserializedTransaction: AccountTransactionEntity, transaction: AccountTransactionEntity) { // to check if MapStruct created reference correctly - assertThat(deserializedTransaction.bankAccount.id).isEqualTo(transaction.bankAccount.technicalId) + assertThat(deserializedTransaction.bankAccount.technicalId).isEqualTo(transaction.bankAccount.technicalId) assertThat(deserializedTransaction.otherPartyName).isEqualTo(transaction.otherPartyName) assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage) diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt index 61dc1953..196e5178 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/RouterAndroid.kt @@ -3,7 +3,7 @@ package net.dankito.banking.ui.android import net.dankito.banking.ui.android.util.CurrentActivityTracker import net.dankito.banking.ui.IRouter import net.dankito.banking.ui.android.dialogs.* -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanResult @@ -20,7 +20,7 @@ open class RouterAndroid(protected val activityTracker: CurrentActivityTracker) } } - override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) { + override fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) { activityTracker.currentOrNextActivity { activity -> activity.runOnUiThread { EnterTanDialog().show(customer, tanChallenge, activity, false) { result -> diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt index e1abcc77..7eaa22a8 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/AccountTransactionAdapter.kt @@ -6,7 +6,7 @@ import android.view.View import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.adapter.viewholder.AccountTransactionViewHolder import net.dankito.banking.ui.android.extensions.showAmount -import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.IAccountTransaction import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter @@ -14,14 +14,14 @@ import java.text.DateFormat open class AccountTransactionAdapter(protected val presenter: BankingPresenter) - : ListRecyclerAdapter() { + : ListRecyclerAdapter() { companion object { val ValueDateFormat = DateFormat.getDateInstance(DateFormat.SHORT) } - var selectedTransaction: AccountTransaction? = null + var selectedTransaction: IAccountTransaction? = null override fun getListItemLayoutId() = R.layout.list_item_account_transaction @@ -34,7 +34,7 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter) return viewHolder } - override fun bindItemToView(viewHolder: AccountTransactionViewHolder, item: AccountTransaction) { + override fun bindItemToView(viewHolder: AccountTransactionViewHolder, item: IAccountTransaction) { viewHolder.txtvwDate.text = ValueDateFormat.format(item.valueDate) val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt index 46483671..0cc617b4 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/adapter/BankAccountsAdapter.kt @@ -8,11 +8,11 @@ import android.view.ViewGroup import android.widget.ImageView import kotlinx.android.synthetic.main.list_item_bank_account.view.* import net.dankito.banking.ui.android.R -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.utils.android.ui.adapter.ListAdapter -open class BankAccountsAdapter(bankAccounts: List) : ListAdapter(bankAccounts) { +open class BankAccountsAdapter(bankAccounts: List) : ListAdapter(bankAccounts) { override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { @@ -30,7 +30,7 @@ open class BankAccountsAdapter(bankAccounts: List) : ListAdapter Unit) { this.customer = customer diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt index bc99e0ce..b9d09435 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/dialogs/TransferMoneyDialog.kt @@ -27,7 +27,7 @@ import net.dankito.banking.ui.android.listener.ListItemSelectedListener import net.dankito.banking.ui.android.util.StandardAutocompleteCallback import net.dankito.banking.ui.android.util.StandardTextWatcher import net.dankito.banking.search.Remittee -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.presenter.BankingPresenter @@ -52,7 +52,7 @@ open class TransferMoneyDialog : DialogFragment() { } - protected lateinit var bankAccount: BankAccount + protected lateinit var bankAccount: TypedBankAccount protected var preselectedValues: TransferMoneyData? = null diff --git a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt index 38b14ae0..988ed5c6 100644 --- a/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt +++ b/ui/BankingAndroidApp/src/main/java/net/dankito/banking/ui/android/views/DrawerView.kt @@ -20,7 +20,7 @@ import com.mikepenz.materialdrawer.util.removeItemByPosition import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.extensions.withIcon -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.presenter.BankingPresenter import org.slf4j.LoggerFactory @@ -139,7 +139,7 @@ open class DrawerView( }.flatten() } - private fun createAccountDrawerItem(customer: Customer): IDrawerItem<*> { + private fun createAccountDrawerItem(customer: TypedCustomer): IDrawerItem<*> { val accountItem = AccountDrawerItem() .withName(customer.displayName) @@ -160,7 +160,7 @@ open class DrawerView( return accountItem } - private fun createBankAccountsDrawerItems(customer: Customer): List> { + private fun createBankAccountsDrawerItems(customer: TypedCustomer): List> { return customer.accounts.map { bankAccount -> SecondaryDrawerItem() .withName(bankAccount.displayName) @@ -176,13 +176,13 @@ open class DrawerView( return false } - private fun closeDrawerAndEditAccount(customer: Customer) { + private fun closeDrawerAndEditAccount(customer: TypedCustomer) { closeDrawer() editAccount(customer) } - private fun editAccount(customer: Customer) { + private fun editAccount(customer: TypedCustomer) { // TODO: implement editing account (e.g. displayed name etc.) AlertDialog.Builder(activity) diff --git a/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt b/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt index 60066a33..e7a96376 100755 --- a/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt +++ b/ui/BankingJavaFxApp/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/mainwindow/MainWindow.kt @@ -11,6 +11,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.util.BankIconFinder import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.persistence.LuceneBankingPersistence +import net.dankito.banking.persistence.mapper.EntitiesModelCreator import net.dankito.banking.search.LuceneRemitteeSearcher import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.extraction.JavaTextExtractorRegistry @@ -32,6 +33,8 @@ class MainWindow : View(messages["application.title"]) { private val indexFolder = ensureFolderExists(dataFolder, "index") + private val modelCreator = EntitiesModelCreator() + private val serializer = JacksonJsonSerializer() private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German))) @@ -42,11 +45,11 @@ class MainWindow : View(messages["application.title"]) { tesseractTextExtractor, TikaTextExtractor() ))) - private val presenter = BankingPresenter(fints4kBankingClientCreator(serializer), + private val presenter = BankingPresenter(fints4kBankingClientCreator(modelCreator, serializer), LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), - RouterJavaFx(), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry) + RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry) // private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), -// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), LuceneRemitteeSearcher(indexFolder), +// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder), // BankIconFinder(), textExtractorRegistry) diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt index e29d5ef6..b6dcb06c 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/RouterJavaFx.kt @@ -4,7 +4,7 @@ import net.dankito.banking.ui.IRouter import net.dankito.banking.ui.javafx.dialogs.AddAccountDialog import net.dankito.banking.ui.javafx.dialogs.cashtransfer.TransferMoneyDialog import net.dankito.banking.ui.javafx.dialogs.tan.EnterTanDialog -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanResult @@ -22,7 +22,7 @@ open class RouterJavaFx : IRouter { AddAccountDialog(presenter).show(messages["add.account.dialog.title"]) } - override fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) { + override fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) { FX.runAndWait { EnterTanDialog(customer, tanChallenge, presenter) { result -> callback(result) diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsControlView.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsControlView.kt index 019a1afe..d920b866 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsControlView.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsControlView.kt @@ -141,7 +141,7 @@ open class AccountTransactionsControlView( protected open fun updateAccountTransactions(processingIndicatorButton: ProcessingIndicatorButton) { // TODO: or only update transactions of selected accounts? - presenter.updateAccountsTransactionsAsync { transactions -> + presenter.updateAccountsTransactionsAsync { runLater { processingIndicatorButton.resetIsProcessing() } diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsTable.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsTable.kt index 8372e8b0..9b79bfac 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsTable.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsTable.kt @@ -11,7 +11,7 @@ import javafx.scene.control.TableView import javafx.scene.layout.Priority import javafx.scene.paint.Color import javafx.util.Callback -import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.IAccountTransaction import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible import tornadofx.* @@ -21,8 +21,8 @@ import java.text.DateFormat open class AccountTransactionsTable @JvmOverloads constructor( protected val presenter: BankingPresenter, - transactions: ObservableList = FXCollections.emptyObservableList() -) : TableView(transactions) { + transactions: ObservableList = FXCollections.emptyObservableList() +) : TableView(transactions) { companion object { @@ -38,7 +38,7 @@ open class AccountTransactionsTable @JvmOverloads constructor( protected open fun initUi() { - column(messages["account.transactions.table.column.header.value.date"], AccountTransaction::valueDate) { + column(messages["account.transactions.table.column.header.value.date"], IAccountTransaction::valueDate) { prefWidth = 115.0 cellFormat { @@ -48,7 +48,7 @@ open class AccountTransactionsTable @JvmOverloads constructor( } } - columns.add(TableColumn(messages["account.transactions.table.column.header.usage"]).apply { + columns.add(TableColumn(messages["account.transactions.table.column.header.usage"]).apply { this.cellFormat { contentDisplay = ContentDisplay.GRAPHIC_ONLY @@ -80,8 +80,8 @@ open class AccountTransactionsTable @JvmOverloads constructor( } } - cellValueFactory = Callback { object : ObjectBinding() { - override fun computeValue(): AccountTransaction { + cellValueFactory = Callback { object : ObjectBinding() { + override fun computeValue(): IAccountTransaction { return it.value } @@ -90,7 +90,7 @@ open class AccountTransactionsTable @JvmOverloads constructor( weightedWidth(4.0) }) - columns.add(TableColumn(messages["account.transactions.table.column.header.amount"]).apply { + columns.add(TableColumn(messages["account.transactions.table.column.header.amount"]).apply { prefWidth = 85.0 this.cellFormat { diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsView.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsView.kt index 7a906bb3..b80ec828 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsView.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountTransactionsView.kt @@ -8,8 +8,8 @@ import javafx.scene.input.ContextMenuEvent import javafx.scene.input.MouseButton import javafx.scene.input.MouseEvent import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.IAccountTransaction +import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.banking.ui.presenter.BankingPresenter @@ -24,7 +24,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi protected val balance = SimpleStringProperty("") - protected val transactionsToDisplay = FXCollections.observableArrayList(listOf()) + protected val transactionsToDisplay = FXCollections.observableArrayList(listOf()) protected var currentMenu: ContextMenu? = null @@ -56,7 +56,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi } - protected open fun tableClicked(event: MouseEvent, selectedItem: AccountTransaction?) { + protected open fun tableClicked(event: MouseEvent, selectedItem: IAccountTransaction?) { if (event.button == MouseButton.PRIMARY || event.button == MouseButton.MIDDLE) { currentMenu?.hide() } @@ -79,7 +79,7 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi } } - protected open fun createContextMenuForItems(selectedItem: AccountTransaction): ContextMenu { + protected open fun createContextMenuForItems(selectedItem: IAccountTransaction): ContextMenu { val contextMenu = ContextMenu() contextMenu.apply { @@ -105,21 +105,21 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi return contextMenu } - protected open fun showTransactionDetailsDialog(transaction: AccountTransaction) { + protected open fun showTransactionDetailsDialog(transaction: IAccountTransaction) { // TODO: // presenter.showTransactionDetailsWindow(transaction.item) } - protected open fun newTransferToSameRemittee(transaction: AccountTransaction) { + protected open fun newTransferToSameRemittee(transaction: IAccountTransaction) { presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(transaction)) } - protected open fun newTransferWithSameData(transaction: AccountTransaction) { + protected open fun newTransferWithSameData(transaction: IAccountTransaction) { presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransaction(transaction)) } - protected open fun handleSelectedBankAccountsChanged(selectedBankAccounts: List) { + protected open fun handleSelectedBankAccountsChanged(selectedBankAccounts: List) { runLater { isAccountSelected.value = selectedBankAccounts.isNotEmpty() diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsTreeView.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsTreeView.kt index fbaa34a2..b939dcf6 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsTreeView.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/controls/AccountsTreeView.kt @@ -10,13 +10,13 @@ import javafx.scene.input.KeyCode import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.model.AccountsAccountTreeItem import net.dankito.banking.ui.javafx.model.AccountsRootTreeItem -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.presenter.BankingPresenter import tornadofx.* import tornadofx.FX.Companion.messages -open class AccountsTreeView(customers: ObservableList, protected val presenter: BankingPresenter) +open class AccountsTreeView(customers: ObservableList, protected val presenter: BankingPresenter) : TreeView(AccountsRootTreeItem(customers)) { protected var currentMenu: ContextMenu? = null diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt index 906d81c7..6bcf6431 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/cashtransfer/TransferMoneyDialog.kt @@ -12,7 +12,7 @@ import javafx.scene.image.ImageView import javafx.scene.layout.Priority import kotlinx.coroutines.* import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedBankAccount import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.presenter.BankingPresenter @@ -49,7 +49,7 @@ open class TransferMoneyDialog @JvmOverloads constructor( protected val bankAccountsSupportingTransferringMoney = FXCollections.observableArrayList(presenter.bankAccounts.filter { it.supportsTransferringMoney }) - protected val selectedBankAccount = SimpleObjectProperty(preselectedValues?.account ?: bankAccountsSupportingTransferringMoney.firstOrNull()) + protected val selectedBankAccount = SimpleObjectProperty(preselectedValues?.account ?: bankAccountsSupportingTransferringMoney.firstOrNull()) protected val showBankAccounts = SimpleBooleanProperty(bankAccountsSupportingTransferringMoney.size > 1) @@ -268,7 +268,7 @@ open class TransferMoneyDialog @JvmOverloads constructor( } - private fun selectedBankAccountChanged(newValue: BankAccount?) { + private fun selectedBankAccountChanged(newValue: TypedBankAccount?) { supportsInstantPayment.value = newValue?.supportsInstantPaymentMoneyTransfer ?: false if (supportsInstantPayment.value == false) { @@ -345,7 +345,7 @@ open class TransferMoneyDialog @JvmOverloads constructor( } protected open fun transferMoney() { - remitteeBank.value?.let { remitteeBank -> + remitteeBank.value?.let { val bankAccount = selectedBankAccount.value val data = TransferMoneyData( @@ -366,7 +366,7 @@ open class TransferMoneyDialog @JvmOverloads constructor( } } - protected open fun handleTransferMoneyResultOnUiThread(bankAccount: BankAccount, transferData: TransferMoneyData, response: BankingClientResponse) { + protected open fun handleTransferMoneyResultOnUiThread(bankAccount: TypedBankAccount, transferData: TransferMoneyData, response: BankingClientResponse) { val currency = bankAccount.currency if (response.isSuccessful) { diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt index 99bd53d1..806198f4 100644 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/dialogs/tan/EnterTanDialog.kt @@ -10,7 +10,7 @@ import javafx.scene.text.FontWeight import net.dankito.banking.ui.javafx.dialogs.tan.controls.ChipTanFlickerCodeView import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.dialogs.tan.controls.TanImageView -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.tan.* import net.dankito.banking.ui.presenter.BankingPresenter @@ -20,7 +20,7 @@ import tornadofx.* open class EnterTanDialog( - protected val customer: Customer, + protected val customer: TypedCustomer, protected val challenge: TanChallenge, protected val presenter: BankingPresenter, protected val tanEnteredCallback: (EnterTanResult) -> Unit diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt index d60246e2..a7e19a67 100755 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsAccountTreeItem.kt @@ -2,10 +2,10 @@ package net.dankito.banking.ui.javafx.model import javafx.scene.Node import javafx.scene.image.ImageView -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer -open class AccountsAccountTreeItem(val customer: Customer) : AccountsTreeItemBase(customer.displayName) { +open class AccountsAccountTreeItem(val customer: TypedCustomer) : AccountsTreeItemBase(customer.displayName) { companion object { private const val IconSize = 16.0 diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsBankAccountTreeItem.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsBankAccountTreeItem.kt index 02c8ed54..1ca6fced 100755 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsBankAccountTreeItem.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsBankAccountTreeItem.kt @@ -1,6 +1,6 @@ package net.dankito.banking.ui.javafx.model -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedBankAccount -open class AccountsBankAccountTreeItem(val bankAccount: BankAccount) : AccountsTreeItemBase(bankAccount.displayName) \ No newline at end of file +open class AccountsBankAccountTreeItem(val bankAccount: TypedBankAccount) : AccountsTreeItemBase(bankAccount.displayName) \ No newline at end of file diff --git a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsRootTreeItem.kt b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsRootTreeItem.kt index afe36f63..215cae6f 100755 --- a/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsRootTreeItem.kt +++ b/ui/BankingJavaFxControls/src/main/kotlin/net/dankito/banking/ui/javafx/model/AccountsRootTreeItem.kt @@ -2,13 +2,13 @@ package net.dankito.banking.ui.javafx.model import javafx.collections.ListChangeListener import javafx.collections.ObservableList -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import tornadofx.FX.Companion.messages import tornadofx.get import tornadofx.runLater -open class AccountsRootTreeItem(customers: ObservableList) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) { +open class AccountsRootTreeItem(customers: ObservableList) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) { init { setAccounts(customers) @@ -18,7 +18,7 @@ open class AccountsRootTreeItem(customers: ObservableList) : AccountsT }) } - protected open fun setAccounts(customers: List) { + protected open fun setAccounts(customers: List) { isExpanded = customers.isNotEmpty() children.setAll(customers.map { AccountsAccountTreeItem(it) }) diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt index b51b2e33..350ee25d 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/IBankingPersistence.kt @@ -1,21 +1,19 @@ package net.dankito.banking.persistence +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.File -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount interface IBankingPersistence { - fun saveOrUpdateAccount(customer: Customer, allCustomers: List) + fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List) - fun deleteAccount(customer: Customer, allCustomers: List) + fun deleteAccount(customer: TypedCustomer, allCustomers: List) - fun readPersistedAccounts(): List + fun readPersistedAccounts(): List - fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List) + fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List) fun saveUrlToFile(url: String, file: File) diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt index c607a575..939c2221 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/persistence/NoOpBankingPersistence.kt @@ -1,27 +1,25 @@ package net.dankito.banking.persistence -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.File open class NoOpBankingPersistence : IBankingPersistence { - override fun saveOrUpdateAccount(customer: Customer, allCustomers: List) { + override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List) { } - override fun deleteAccount(customer: Customer, allCustomers: List) { + override fun deleteAccount(customer: TypedCustomer, allCustomers: List) { } - override fun readPersistedAccounts(): List { + override fun readPersistedAccounts(): List { return listOf() } - override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List) { + override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List) { } diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/BankingClientCallback.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/BankingClientCallback.kt index f7e85ccc..614103d3 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/BankingClientCallback.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/BankingClientCallback.kt @@ -1,6 +1,6 @@ package net.dankito.banking.ui -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.tan.TanChallenge @@ -9,7 +9,7 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium interface BankingClientCallback { - fun enterTan(customer: Customer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) + fun enterTan(customer: TypedCustomer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) /** * This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator. diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClient.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClient.kt index 1a122888..8dab3924 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClient.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClient.kt @@ -1,8 +1,6 @@ package net.dankito.banking.ui -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.MessageLogEntry +import net.dankito.banking.ui.model.* import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.AddAccountResponse @@ -18,7 +16,7 @@ interface IBankingClient { fun addAccountAsync(callback: (AddAccountResponse) -> Unit) fun getTransactionsAsync( - bankAccount: BankAccount, + bankAccount: TypedBankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit ) @@ -26,6 +24,6 @@ interface IBankingClient { fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) - fun dataChanged(customer: Customer) + fun dataChanged(customer: TypedCustomer) } \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClientCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClientCreator.kt index bab34274..381bf098 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClientCreator.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IBankingClientCreator.kt @@ -1,14 +1,14 @@ package net.dankito.banking.ui import net.dankito.utils.multiplatform.File -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.util.IAsyncRunner interface IBankingClientCreator { fun createClient( - customer: Customer, + customer: TypedCustomer, dataFolder: File, asyncRunner: IAsyncRunner, callback: BankingClientCallback diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IRouter.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IRouter.kt index 202b6d19..ebc99d00 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IRouter.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/IRouter.kt @@ -1,7 +1,6 @@ package net.dankito.banking.ui -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanResult @@ -14,7 +13,7 @@ interface IRouter { fun showAddAccountDialog(presenter: BankingPresenter) - fun getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) + fun getTanFromUserFromNonUiThread(customer: TypedCustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: (EnterTanResult) -> Unit) fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit) diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt index 95fae62b..0e69c2fd 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/AccountTransaction.kt @@ -7,49 +7,44 @@ import net.dankito.utils.multiplatform.DateFormatter open class AccountTransaction( - open val bankAccount: BankAccount, - open val amount: BigDecimal, - open val currency: String, - open val unparsedUsage: String, - open val bookingDate: Date, - open val otherPartyName: String?, - open val otherPartyBankCode: String?, - open val otherPartyAccountId: String?, - open val bookingText: String?, - open val valueDate: Date, - open val statementNumber: Int, - open val sequenceNumber: Int?, - open val openingBalance: BigDecimal?, - open val closingBalance: BigDecimal?, + override val bankAccount: TypedBankAccount, + override val amount: BigDecimal, + override val currency: String, + override val unparsedUsage: String, + override val bookingDate: Date, + override val otherPartyName: String?, + override val otherPartyBankCode: String?, + override val otherPartyAccountId: String?, + override val bookingText: String?, + override val valueDate: Date, + override val statementNumber: Int, + override val sequenceNumber: Int?, + override val openingBalance: BigDecimal?, + override val closingBalance: BigDecimal?, - open val endToEndReference: String?, - open val customerReference: String?, - open val mandateReference: String?, - open val creditorIdentifier: String?, - open val originatorsIdentificationCode: String?, - open val compensationAmount: String?, - open val originalAmount: String?, - open val sepaUsage: String?, - open val deviantOriginator: String?, - open val deviantRecipient: String?, - open val usageWithNoSpecialType: String?, - open val primaNotaNumber: String?, - open val textKeySupplement: String?, + override val endToEndReference: String?, + override val customerReference: String?, + override val mandateReference: String?, + override val creditorIdentifier: String?, + override val originatorsIdentificationCode: String?, + override val compensationAmount: String?, + override val originalAmount: String?, + override val sepaUsage: String?, + override val deviantOriginator: String?, + override val deviantRecipient: String?, + override val usageWithNoSpecialType: String?, + override val primaNotaNumber: String?, + override val textKeySupplement: String?, - open val currencyType: String?, - open val bookingKey: String, - open val referenceForTheAccountOwner: String, - open val referenceOfTheAccountServicingInstitution: String?, - open val supplementaryDetails: String?, - - open val transactionReferenceNumber: String, - open val relatedReferenceNumber: String? -) { - - companion object { - val IdDateFormat = DateFormatter("yyyy.MM.dd") - } + override val currencyType: String?, + override val bookingKey: String, + override val referenceForTheAccountOwner: String, + override val referenceOfTheAccountServicingInstitution: String?, + override val supplementaryDetails: String?, + override val transactionReferenceNumber: String, + override val relatedReferenceNumber: String? +) : IAccountTransaction { // for object deserializers internal constructor() : this(BankAccount(), null, "", BigDecimal.Zero, Date(), null) @@ -69,29 +64,7 @@ open class AccountTransaction( 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "", "", null, null, "", null) - open var technicalId: String = buildTransactionIdentifier() - - open val transactionIdentifier: String - get() = buildTransactionIdentifier() - - protected fun buildTransactionIdentifier() : String { - if (bankAccount != null) { - return "${bankAccount.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" - } - else { // happens for derived classes during initialization. These have to set technicalId after initialization by themselves - return " ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" - } - } - - - open val showOtherPartyName: Boolean - get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO - - open val canCreateMoneyTransferFrom: Boolean - get() = otherPartyAccountId != null && bankAccount.supportsTransferringMoney - - open val usage: String - get() = sepaUsage ?: unparsedUsage + override var technicalId: String = buildTransactionIdentifier() override fun equals(other: Any?): Boolean { diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt index 1a263eac..3b3df0a0 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/BankAccount.kt @@ -7,69 +7,47 @@ import kotlin.jvm.JvmOverloads open class BankAccount @JvmOverloads constructor( - open val customer: Customer, - open var identifier: String, - open var accountHolderName: String, - open var iban: String?, - open var subAccountNumber: String?, - open var customerId: String, - open var balance: BigDecimal = BigDecimal.Zero, - open var currency: String = "EUR", - open var type: BankAccountType = BankAccountType.Girokonto, - open var productName: String? = null, - open var accountLimit: String? = null, - open var lastRetrievedTransactionsTimestamp: Date? = null, - open var supportsRetrievingAccountTransactions: Boolean = false, - open var supportsRetrievingBalance: Boolean = false, - open var supportsTransferringMoney: Boolean = false, - open var supportsInstantPaymentMoneyTransfer: Boolean = false, - open var bookedTransactions: List = listOf(), - open var unbookedTransactions: List = listOf() -) : OrderedDisplayable { + override val customer: TypedCustomer, + override var identifier: String, + override var accountHolderName: String, + override var iban: String?, + override var subAccountNumber: String?, + override var customerId: String, + override var balance: BigDecimal = BigDecimal.Zero, + override var currency: String = "EUR", + override var type: BankAccountType = BankAccountType.Girokonto, + override var productName: String? = null, + override var accountLimit: String? = null, + override var lastRetrievedTransactionsTimestamp: Date? = null, + override var supportsRetrievingAccountTransactions: Boolean = false, + override var supportsRetrievingBalance: Boolean = false, + override var supportsTransferringMoney: Boolean = false, + override var supportsInstantPaymentMoneyTransfer: Boolean = false, + override var bookedTransactions: List = listOf(), + override var unbookedTransactions: List = listOf() +) : TypedBankAccount { internal constructor() : this(Customer(), null, "") // for object deserializers /* convenience constructors for languages not supporting default values */ - constructor(customer: Customer, productName: String?, identifier: String) : this(customer, productName, identifier, BankAccountType.Girokonto) + constructor(customer: TypedCustomer, productName: String?, identifier: String) : this(customer, productName, identifier, BankAccountType.Girokonto) - constructor(customer: Customer, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero) + constructor(customer: TypedCustomer, productName: String?, identifier: String, type: BankAccountType = BankAccountType.Girokonto, balance: BigDecimal = BigDecimal.Zero) : this(customer, identifier, "", null, null, "", balance, "EUR", type, productName) - open var technicalId: String = UUID.random() + override var technicalId: String = UUID.random() - open var haveAllTransactionsBeenFetched: Boolean = false + override var haveAllTransactionsBeenFetched: Boolean = false - open var userSetDisplayName: String? = null - - override val displayName: String - get() { - return userSetDisplayName ?: productName ?: subAccountNumber ?: identifier - } + override var userSetDisplayName: String? = null override var displayIndex: Int = 0 - open fun addBookedTransactions(retrievedBookedTransactions: List) { - val uniqueTransactions = this.bookedTransactions.toMutableSet() - - uniqueTransactions.addAll(retrievedBookedTransactions) - - this.bookedTransactions = uniqueTransactions.toList() - } - - open fun addUnbookedTransactions(retrievedUnbookedTransactions: List) { - val uniqueUnbookedTransactions = this.unbookedTransactions.toMutableSet() - - uniqueUnbookedTransactions.addAll(retrievedUnbookedTransactions) - - this.unbookedTransactions = uniqueUnbookedTransactions.toList() - } - - override fun toString(): String { return "$accountHolderName ($identifier)" } diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt index cbac8a6e..bf71546f 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt @@ -1,26 +1,22 @@ package net.dankito.banking.ui.model -import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.utils.multiplatform.sum import net.dankito.banking.ui.model.tan.TanMedium -import net.dankito.banking.ui.model.tan.TanMediumStatus import net.dankito.banking.ui.model.tan.TanProcedure -import net.dankito.banking.util.sortedByDisplayIndex import net.dankito.utils.multiplatform.UUID open class Customer( - open var bankCode: String, - open var customerId: String, - open var password: String, - open var finTsServerAddress: String, - open var bankName: String, - open var bic: String, - open var customerName: String, - open var userId: String = customerId, - open var iconUrl: String? = null, - open var accounts: List = listOf() -) : OrderedDisplayable { + override var bankCode: String, + override var customerId: String, + override var password: String, + override var finTsServerAddress: String, + override var bankName: String, + override var bic: String, + override var customerName: String, + override var userId: String = customerId, + override var iconUrl: String? = null, + override var accounts: List = listOf() +) : TypedCustomer { internal constructor() : this("", "", "", "", "", "", "") // for object deserializers @@ -31,40 +27,23 @@ open class Customer( : this(bankCode, customerId, password, finTsServerAddress, "", "", "") - open var technicalId: String = UUID.random() + override var technicalId: String = UUID.random() - open var supportedTanProcedures: List = listOf() + override var supportedTanProcedures: List = listOf() - open var selectedTanProcedure: TanProcedure? = null + override var selectedTanProcedure: TanProcedure? = null - open var tanMedia: List = listOf() - - open val tanMediaSorted: List - get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used } + override var tanMedia: List = listOf() - open var userSetDisplayName: String? = null - - override val displayName: String - get() = userSetDisplayName ?: bankName + override var userSetDisplayName: String? = null override var displayIndex: Int = 0 - open val accountsSorted: List - get() = accounts.sortedByDisplayIndex() - - - open val balance: BigDecimal - get() = accounts.map { it.balance }.sum() - - open val transactions: List - get() = accounts.flatMap { it.bookedTransactions } - - override fun toString(): String { - return "$customerName ($customerId)" + return "$bankName $customerId" } } \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt new file mode 100644 index 00000000..07262874 --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IAccountTransaction.kt @@ -0,0 +1,76 @@ +package net.dankito.banking.ui.model + +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date +import net.dankito.utils.multiplatform.DateFormatter + + +interface IAccountTransaction { + + companion object { + val IdDateFormat = DateFormatter("yyyy.MM.dd") + } + + + val bankAccount: IBankAccount<*> + val amount: BigDecimal + val currency: String + val unparsedUsage: String + val bookingDate: Date + val otherPartyName: String? + val otherPartyBankCode: String? + val otherPartyAccountId: String? + val bookingText: String? + val valueDate: Date + val statementNumber: Int + val sequenceNumber: Int? + val openingBalance: BigDecimal? + val closingBalance: BigDecimal? + + val endToEndReference: String? + val customerReference: String? + val mandateReference: String? + val creditorIdentifier: String? + val originatorsIdentificationCode: String? + val compensationAmount: String? + val originalAmount: String? + val sepaUsage: String? + val deviantOriginator: String? + val deviantRecipient: String? + val usageWithNoSpecialType: String? + val primaNotaNumber: String? + val textKeySupplement: String? + + val currencyType: String? + val bookingKey: String + val referenceForTheAccountOwner: String + val referenceOfTheAccountServicingInstitution: String? + val supplementaryDetails: String? + + val transactionReferenceNumber: String + val relatedReferenceNumber: String? + + + var technicalId: String + + + val showOtherPartyName: Boolean + get() = otherPartyName.isNullOrBlank() == false /* && type != "ENTGELTABSCHLUSS" && type != "AUSZAHLUNG" */ // TODO + + val canCreateMoneyTransferFrom: Boolean + get() = otherPartyAccountId != null && bankAccount.supportsTransferringMoney + + val usage: String + get() = sepaUsage ?: unparsedUsage + + + fun buildTransactionIdentifier() : String { + if (bankAccount != null) { + return "${bankAccount.technicalId} ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" + } + else { // happens for derived classes during initialization. These have to set technicalId after initialization by themselves + return " ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId" + } + } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt new file mode 100644 index 00000000..d1ffa9f4 --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/IBankAccount.kt @@ -0,0 +1,56 @@ +package net.dankito.banking.ui.model + +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date + + +typealias TypedBankAccount = IBankAccount + + +interface IBankAccount : OrderedDisplayable { + val customer: ICustomer<*, *> + var identifier: String + var accountHolderName: String + var iban: String? + var subAccountNumber: String? + var customerId: String + var balance: BigDecimal + var currency: String + var type: BankAccountType + var productName: String? + var accountLimit: String? + var lastRetrievedTransactionsTimestamp: Date? + var supportsRetrievingAccountTransactions: Boolean + var supportsRetrievingBalance: Boolean + var supportsTransferringMoney: Boolean + var supportsInstantPaymentMoneyTransfer: Boolean + var bookedTransactions: List + var unbookedTransactions: List + var technicalId: String + var haveAllTransactionsBeenFetched: Boolean + var userSetDisplayName: String? + + + override val displayName: String + get() { + return userSetDisplayName ?: productName ?: subAccountNumber ?: identifier + } + + + fun addBookedTransactions(retrievedBookedTransactions: List) { + val uniqueTransactions = this.bookedTransactions.toMutableSet() + + uniqueTransactions.addAll(retrievedBookedTransactions) + + this.bookedTransactions = uniqueTransactions.toList() + } + + fun addUnbookedTransactions(retrievedUnbookedTransactions: List) { + val uniqueUnbookedTransactions = this.unbookedTransactions.toMutableSet() + + uniqueUnbookedTransactions.addAll(retrievedUnbookedTransactions) + + this.unbookedTransactions = uniqueUnbookedTransactions.toList() + } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt new file mode 100644 index 00000000..29ed0a76 --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/ICustomer.kt @@ -0,0 +1,56 @@ +package net.dankito.banking.ui.model + +import net.dankito.banking.ui.model.tan.TanMedium +import net.dankito.banking.ui.model.tan.TanMediumStatus +import net.dankito.banking.ui.model.tan.TanProcedure +import net.dankito.banking.util.sortedByDisplayIndex +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.sum + + +typealias TypedCustomer = ICustomer, IAccountTransaction> + + +interface ICustomer, TAccountTransaction: IAccountTransaction> : OrderedDisplayable { + + var bankCode: String + var customerId: String + var password: String + var finTsServerAddress: String + + var bankName: String + var bic: String + var customerName: String + var userId: String + + var iconUrl: String? + + var accounts: List + + var supportedTanProcedures: List + var selectedTanProcedure: TanProcedure? + var tanMedia: List + + var userSetDisplayName: String? + + var technicalId: String + + + override val displayName: String + get() = userSetDisplayName ?: bankName + + + val accountsSorted: List + get() = accounts.sortedByDisplayIndex() + + + val balance: BigDecimal + get() = accounts.map { it.balance }.sum() + + val transactions: List + get() = accounts.flatMap { it.bookedTransactions } + + val tanMediaSorted: List + get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/MessageLogEntry.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/MessageLogEntry.kt index ff5050d6..abed10af 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/MessageLogEntry.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/MessageLogEntry.kt @@ -6,7 +6,7 @@ import net.dankito.utils.multiplatform.Date open class MessageLogEntry( val message: String, val time: Date, - val customer: Customer + val customer: TypedCustomer ) { override fun toString(): String { diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt new file mode 100644 index 00000000..50d2091a --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/DefaultModelCreator.kt @@ -0,0 +1,66 @@ +package net.dankito.banking.ui.model.mapper + +import net.dankito.banking.ui.model.* +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date + + +open class DefaultModelCreator : IModelCreator { + + override fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String, + customerName: String, userId: String, iconUrl: String?): TypedCustomer { + + return Customer(bankCode, customerId, password, finTsServerAddress, bankName, bic, customerName, userId, iconUrl) + } + + + override fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String): TypedBankAccount { + return BankAccount(customer, productName, identifier) + } + + override fun createTransaction( + bankAccount: TypedBankAccount, + amount: BigDecimal, + currency: String, + unparsedUsage: String, + bookingDate: Date, + otherPartyName: String?, + otherPartyBankCode: String?, + otherPartyAccountId: String?, + bookingText: String?, + valueDate: Date, + statementNumber: Int, + sequenceNumber: Int?, + openingBalance: BigDecimal?, + closingBalance: BigDecimal?, + endToEndReference: String?, + customerReference: String?, + mandateReference: String?, + creditorIdentifier: String?, + originatorsIdentificationCode: String?, + compensationAmount: String?, + originalAmount: String?, + sepaUsage: String?, + deviantOriginator: String?, + deviantRecipient: String?, + usageWithNoSpecialType: String?, + primaNotaNumber: String?, + textKeySupplement: String?, + currencyType: String?, + bookingKey: String, + referenceForTheAccountOwner: String, + referenceOfTheAccountServicingInstitution: String?, + supplementaryDetails: String?, + transactionReferenceNumber: String, + relatedReferenceNumber: String? + ) : IAccountTransaction { + + return AccountTransaction(bankAccount, amount, currency, unparsedUsage, bookingDate, + otherPartyName, otherPartyBankCode, otherPartyAccountId, bookingText, valueDate, statementNumber, sequenceNumber, + openingBalance, closingBalance, endToEndReference, customerReference, mandateReference, creditorIdentifier, + originatorsIdentificationCode, compensationAmount, originalAmount, sepaUsage, deviantOriginator, deviantRecipient, + usageWithNoSpecialType, primaNotaNumber, textKeySupplement, currencyType, bookingKey, referenceForTheAccountOwner, + referenceOfTheAccountServicingInstitution, supplementaryDetails, transactionReferenceNumber, relatedReferenceNumber) + } + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt new file mode 100644 index 00000000..3de107cb --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/mapper/IModelCreator.kt @@ -0,0 +1,57 @@ +package net.dankito.banking.ui.model.mapper + +import net.dankito.banking.ui.model.* +import net.dankito.utils.multiplatform.BigDecimal +import net.dankito.utils.multiplatform.Date + + +interface IModelCreator { + + fun createCustomer(bankCode: String, customerId: String, password: String, finTsServerAddress: String, bankName: String, bic: String, + customerName: String = "", userId: String = customerId, iconUrl: String? = null): TypedCustomer + + + fun createBankAccount(customer: TypedCustomer, productName: String?, identifier: String) : TypedBankAccount + + + fun createTransaction( + bankAccount: TypedBankAccount, + amount: BigDecimal, + currency: String, + unparsedUsage: String, + bookingDate: Date, + otherPartyName: String?, + otherPartyBankCode: String?, + otherPartyAccountId: String?, + bookingText: String?, + valueDate: Date, + statementNumber: Int, + sequenceNumber: Int?, + openingBalance: BigDecimal?, + closingBalance: BigDecimal?, + + endToEndReference: String?, + customerReference: String?, + mandateReference: String?, + creditorIdentifier: String?, + originatorsIdentificationCode: String?, + compensationAmount: String?, + originalAmount: String?, + sepaUsage: String?, + deviantOriginator: String?, + deviantRecipient: String?, + usageWithNoSpecialType: String?, + primaNotaNumber: String?, + textKeySupplement: String?, + + currencyType: String?, + bookingKey: String, + referenceForTheAccountOwner: String, + referenceOfTheAccountServicingInstitution: String?, + supplementaryDetails: String?, + + transactionReferenceNumber: String, + relatedReferenceNumber: String? + ) : IAccountTransaction + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt index 222c7733..0f6f8aa4 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/GetTransactionsParameter.kt @@ -1,7 +1,7 @@ package net.dankito.banking.ui.model.parameters import net.dankito.utils.multiplatform.Date -import net.dankito.banking.ui.model.AccountTransaction +import net.dankito.banking.ui.model.IAccountTransaction open class GetTransactionsParameter( @@ -9,7 +9,7 @@ open class GetTransactionsParameter( val fromDate: Date? = null, val toDate: Date? = null, val abortIfTanIsRequired: Boolean = false, - val retrievedChunkListener: ((List) -> Unit)? = null + val retrievedChunkListener: ((List) -> Unit)? = null ) { constructor() : this(true, null, null) // for Java diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt index 9f336196..c3a0a263 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/parameters/TransferMoneyData.kt @@ -1,12 +1,11 @@ package net.dankito.banking.ui.model.parameters +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount open class TransferMoneyData( - val account: BankAccount, + val account: TypedBankAccount, val creditorName: String, val creditorIban: String, val creditorBic: String, @@ -17,9 +16,9 @@ open class TransferMoneyData( companion object { - fun fromAccountTransactionWithoutAmountAndUsage(transaction: AccountTransaction): TransferMoneyData { + fun fromAccountTransactionWithoutAmountAndUsage(transaction: IAccountTransaction): TransferMoneyData { return TransferMoneyData( - transaction.bankAccount, + transaction.bankAccount as TypedBankAccount, transaction.otherPartyName ?: "", transaction.otherPartyAccountId ?: "", transaction.otherPartyBankCode ?: "", @@ -28,9 +27,9 @@ open class TransferMoneyData( ) } - fun fromAccountTransaction(transaction: AccountTransaction): TransferMoneyData { + fun fromAccountTransaction(transaction: IAccountTransaction): TransferMoneyData { return TransferMoneyData( - transaction.bankAccount, + transaction.bankAccount as TypedBankAccount, transaction.otherPartyName ?: "", transaction.otherPartyAccountId ?: "", transaction.otherPartyBankCode ?: "", diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/AddAccountResponse.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/AddAccountResponse.kt index e56815cb..8962823c 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/AddAccountResponse.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/AddAccountResponse.kt @@ -1,19 +1,17 @@ package net.dankito.banking.ui.model.responses +import net.dankito.banking.ui.model.* import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount open class AddAccountResponse( isSuccessful: Boolean, errorToShowToUser: String?, - val customer: Customer, + val customer: TypedCustomer, val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false, - val bookedTransactionsOfLast90Days: Map> = mapOf(), - val unbookedTransactionsOfLast90Days: Map> = mapOf(), - val balances: Map = mapOf(), + val bookedTransactionsOfLast90Days: Map> = mapOf(), + val unbookedTransactionsOfLast90Days: Map> = mapOf(), + val balances: Map = mapOf(), userCancelledAction: Boolean = false ) : BankingClientResponse(isSuccessful, errorToShowToUser, userCancelledAction) { diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt index 9ef13977..2ea46a37 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/responses/GetTransactionsResponse.kt @@ -1,15 +1,15 @@ package net.dankito.banking.ui.model.responses import net.dankito.utils.multiplatform.BigDecimal -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.IAccountTransaction +import net.dankito.banking.ui.model.TypedBankAccount open class GetTransactionsResponse( - val bankAccount: BankAccount, + val bankAccount: TypedBankAccount, isSuccessful: Boolean, errorToShowToUser: String?, - val bookedTransactions: List = listOf(), + val bookedTransactions: List = listOf(), val unbookedTransactions: List = listOf(), val balance: BigDecimal? = null, userCancelledAction: Boolean = false, diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt index e256ad08..221f3125 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt @@ -15,6 +15,8 @@ import net.dankito.banking.bankfinder.BankInfo import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.NoOpRemitteeSearcher import net.dankito.banking.search.Remittee +import net.dankito.banking.ui.model.mapper.DefaultModelCreator +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResult import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType import net.dankito.banking.ui.model.parameters.GetTransactionsParameter @@ -30,12 +32,13 @@ import net.dankito.utils.multiplatform.log.LoggerFactory import kotlin.collections.ArrayList -open class BankingPresenter( +open class BankingPresenter constructor( protected val bankingClientCreator: IBankingClientCreator, protected val bankFinder: IBankFinder, protected val dataFolder: File, protected val persister: IBankingPersistence, protected val router: IRouter, + protected val modelCreator: IModelCreator = DefaultModelCreator(), protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(), protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(), protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(), @@ -56,25 +59,25 @@ open class BankingPresenter( } - protected val bankingClientsForAccounts = mutableMapOf() + protected val bankingClientsForAccounts = mutableMapOf() - protected var selectedBankAccountsField = mutableListOf() + protected var selectedBankAccountsField = mutableListOf() protected var selectedAccountType = SelectedAccountType.AllAccounts protected var saveAccountOnNextEnterTanInvocation = false - protected val accountsChangedListeners = mutableListOf<(List) -> Unit>() + protected val accountsChangedListeners = mutableListOf<(List) -> Unit>() protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(GetTransactionsResponse) -> Unit>() - protected val selectedBankAccountsChangedListeners = mutableListOf<(List) -> Unit>() + protected val selectedBankAccountsChangedListeners = mutableListOf<(List) -> Unit>() protected val callback: BankingClientCallback = object : BankingClientCallback { - override fun enterTan(customer: Customer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) { + override fun enterTan(customer: TypedCustomer, tanChallenge: TanChallenge, callback: (EnterTanResult) -> Unit) { if (saveAccountOnNextEnterTanInvocation) { persistAccount(customer) saveAccountOnNextEnterTanInvocation = false @@ -131,14 +134,14 @@ open class BankingPresenter( } } - protected open fun addClientForAccount(customer: Customer, client: IBankingClient) { + protected open fun addClientForAccount(customer: TypedCustomer, client: IBankingClient) { bankingClientsForAccounts.put(customer, client) } // TODO: move BankInfo out of fints4k - open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, callback: (AddAccountResponse) -> Unit) { - val customer = Customer(bankInfo.bankCode, customerId, pin, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "") + open fun addAccountAsync(bankInfo: BankInfo, customerId: String, password: String, callback: (AddAccountResponse) -> Unit) { + val customer = modelCreator.createCustomer(bankInfo.bankCode, customerId, password, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "") val newClient = bankingClientCreator.createClient(customer, dataFolder, asyncRunner, this.callback) @@ -180,7 +183,7 @@ open class BankingPresenter( } } - protected open fun findIconForBankAsync(customer: Customer) { + protected open fun findIconForBankAsync(customer: TypedCustomer) { bankIconFinder.findIconForBankAsync(customer.bankName) { bankIconUrl -> bankIconUrl?.let { try { @@ -192,7 +195,7 @@ open class BankingPresenter( } } - protected open fun handleFindIconForBankResult(customer: Customer, bankIconUrl: String) { + protected open fun handleFindIconForBankResult(customer: TypedCustomer, bankIconUrl: String) { val bankIconFile = saveBankIconToDisk(customer, bankIconUrl) var iconFilePath = bankIconFile.getAbsolutePath() @@ -208,7 +211,7 @@ open class BankingPresenter( callAccountsChangedListeners() } - protected open fun saveBankIconToDisk(customer: Customer, bankIconUrl: String): File { + protected open fun saveBankIconToDisk(customer: TypedCustomer, bankIconUrl: String): File { val bankIconsDir = File(dataFolder, "bank_icons") bankIconsDir.mkdirs() @@ -239,7 +242,7 @@ open class BankingPresenter( } - open fun deleteAccount(customer: Customer) { + open fun deleteAccount(customer: TypedCustomer) { val wasSelected = isSingleSelectedAccount(customer) or // either account or one of its bank accounts is currently selected (customer.accounts.firstOrNull { isSingleSelectedBankAccount(it) } != null) @@ -264,7 +267,7 @@ open class BankingPresenter( } - open fun fetchAllAccountTransactionsAsync(customer: Customer, + open fun fetchAllAccountTransactionsAsync(customer: TypedCustomer, callback: (GetTransactionsResponse) -> Unit) { customer.accounts.forEach { bankAccount -> @@ -274,13 +277,13 @@ open class BankingPresenter( } } - open fun fetchAllAccountTransactionsAsync(bankAccount: BankAccount, + open fun fetchAllAccountTransactionsAsync(bankAccount: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) { fetchAccountTransactionsAsync(bankAccount, null, false, callback) } - open fun fetchAccountTransactionsAsync(bankAccount: BankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false, + open fun fetchAccountTransactionsAsync(bankAccount: TypedBankAccount, fromDate: Date?, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) { getBankingClientForAccount(bankAccount.customer)?.let { client -> @@ -319,7 +322,7 @@ open class BankingPresenter( } } - protected open fun updateBanksAccountsTransactionsAsync(accounts: List, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) { + protected open fun updateBanksAccountsTransactionsAsync(accounts: List, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) { accounts.forEach { bankAccount -> if (bankAccount.supportsRetrievingAccountTransactions) { updateBankAccountTransactionsAsync(bankAccount, abortIfTanIsRequired, callback) @@ -327,7 +330,7 @@ open class BankingPresenter( } } - protected open fun updateBankAccountTransactionsAsync(bankAccount: BankAccount, abortIfTanIsRequired: Boolean, callback: (GetTransactionsResponse) -> Unit) { + protected open fun updateBankAccountTransactionsAsync(bankAccount: TypedBankAccount, abortIfTanIsRequired: Boolean, callback: (GetTransactionsResponse) -> Unit) { val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback) @@ -347,7 +350,7 @@ open class BankingPresenter( callRetrievedAccountTransactionsResponseListener(response) } - protected open fun receivedAccountsTransactionChunk(bankAccount: BankAccount, accountTransactionsChunk: List) { + protected open fun receivedAccountsTransactionChunk(bankAccount: TypedBankAccount, accountTransactionsChunk: List) { if (accountTransactionsChunk.isNotEmpty()) { bankAccount.addBookedTransactions(accountTransactionsChunk) @@ -370,7 +373,7 @@ open class BankingPresenter( persistAccountTransactions(bankAccount, response.bookedTransactions, response.unbookedTransactions) } - protected open fun updateBalance(bankAccount: BankAccount, balance: BigDecimal) { + protected open fun updateBalance(bankAccount: TypedBankAccount, balance: BigDecimal) { bankAccount.balance = balance persistAccount(bankAccount.customer) @@ -388,25 +391,25 @@ open class BankingPresenter( } } - open fun accountDisplayIndexUpdated(account: Customer) { + open fun accountDisplayIndexUpdated(account: TypedCustomer) { persistAccount(account) } - open fun accountUpdated(bank: Customer) { + open fun accountUpdated(bank: TypedCustomer) { persistAccount(bank) getBankingClientForAccount(bank)?.dataChanged(bank) } - open fun accountUpdated(account: BankAccount) { + open fun accountUpdated(account: TypedBankAccount) { persistAccount(account.customer) } - protected open fun persistAccount(customer: Customer) { - persister.saveOrUpdateAccount(customer, customers) + protected open fun persistAccount(customer: ICustomer<*, *>) { + persister.saveOrUpdateAccount(customer as TypedCustomer, customers) } - protected open fun persistAccountTransactions(bankAccount: BankAccount, bookedTransactions: List, unbookedTransactions: List) { + protected open fun persistAccountTransactions(bankAccount: TypedBankAccount, bookedTransactions: List, unbookedTransactions: List) { persister.saveOrUpdateAccountTransactions(bankAccount, bookedTransactions) // TODO: someday also persist unbooked transactions @@ -513,11 +516,11 @@ open class BankingPresenter( } - open fun searchSelectedAccountTransactions(query: String): List { + open fun searchSelectedAccountTransactions(query: String): List { return searchAccountTransactions(query, selectedBankAccountsAccountTransactions) } - open fun searchAccountTransactions(query: String, transactions: List): List { + open fun searchAccountTransactions(query: String, transactions: List): List { val queryLowercase = query.trim().toLowerCase() if (queryLowercase.isEmpty()) { @@ -532,7 +535,7 @@ open class BankingPresenter( } - open fun getMessageLogForAccounts(customers: List): List { + open fun getMessageLogForAccounts(customers: List): List { val logEntries = customers.flatMap { getBankingClientForAccount(it)?.messageLogWithoutSensitiveData ?: listOf() } @@ -556,15 +559,15 @@ open class BankingPresenter( } - protected open fun getBankingClientForAccount(customer: Customer): IBankingClient? { - return bankingClientsForAccounts.get(customer) + protected open fun getBankingClientForAccount(customer: ICustomer<*, *>): IBankingClient? { + return bankingClientsForAccounts.get(customer as TypedCustomer) } - open val selectedBankAccounts: List + open val selectedBankAccounts: List get() = ArrayList(selectedBankAccountsField) - open val selectedBankAccountsAccountTransactions: List + open val selectedBankAccountsAccountTransactions: List get() = getAccountTransactionsForBankAccounts(selectedBankAccounts) open val balanceOfSelectedBankAccounts: BigDecimal @@ -574,12 +577,12 @@ open class BankingPresenter( open val areAllAccountSelected: Boolean get() = selectedAccountType == SelectedAccountType.AllAccounts - open fun isSingleSelectedAccount(customer: Customer): Boolean { + open fun isSingleSelectedAccount(customer: TypedCustomer): Boolean { return selectedAccountType == SelectedAccountType.SingleAccount && selectedBankAccountsField.map { it.customer }.toSet().containsExactly(customer) } - open fun isSingleSelectedBankAccount(bankAccount: BankAccount): Boolean { + open fun isSingleSelectedBankAccount(bankAccount: TypedBankAccount): Boolean { return selectedAccountType == SelectedAccountType.SingleBankAccount && selectedBankAccountsField.containsExactly(bankAccount) } @@ -590,39 +593,39 @@ open class BankingPresenter( setSelectedBankAccounts(bankAccounts) } - open fun selectedAccount(customer: Customer) { + open fun selectedAccount(customer: TypedCustomer) { selectedAccountType = SelectedAccountType.SingleAccount setSelectedBankAccounts(customer.accounts) } - open fun selectedBankAccount(bankAccount: BankAccount) { + open fun selectedBankAccount(bankAccount: TypedBankAccount) { selectedAccountType = SelectedAccountType.SingleBankAccount setSelectedBankAccounts(listOf(bankAccount)) } - protected open fun setSelectedBankAccounts(bankAccounts: List) { + protected open fun setSelectedBankAccounts(bankAccounts: List) { this.selectedBankAccountsField = ArrayList(bankAccounts) // make a copy callSelectedBankAccountsChangedListeners(selectedBankAccountsField) } - open val customers: List + open val customers: List get() = bankingClientsForAccounts.keys.toList() - open val bankAccounts: List + open val bankAccounts: List get() = customers.flatMap { it.accounts } - open val allTransactions: List + open val allTransactions: List get() = getAccountTransactionsForBankAccounts(bankAccounts) open val balanceOfAllAccounts: BigDecimal get() = getBalanceForAccounts(customers) - open val bankAccountsSupportingRetrievingAccountTransactions: List + open val bankAccountsSupportingRetrievingAccountTransactions: List get() = bankAccounts.filter { it.supportsRetrievingAccountTransactions } open val hasBankAccountsSupportingRetrievingAccountTransactions: Boolean @@ -631,12 +634,12 @@ open class BankingPresenter( open val doSelectedBankAccountsSupportRetrievingAccountTransactions: Boolean get() = doBankAccountsSupportRetrievingAccountTransactions(selectedBankAccounts) - open fun doBankAccountsSupportRetrievingAccountTransactions(bankAccounts: List): Boolean { + open fun doBankAccountsSupportRetrievingAccountTransactions(bankAccounts: List): Boolean { return bankAccounts.firstOrNull { it.supportsRetrievingAccountTransactions } != null } - open val bankAccountsSupportingRetrievingBalance: List + open val bankAccountsSupportingRetrievingBalance: List get() = bankAccounts.filter { it.supportsRetrievingBalance } open val hasBankAccountsSupportingRetrievingBalance: Boolean @@ -645,12 +648,12 @@ open class BankingPresenter( open val doSelectedBankAccountsSupportRetrievingBalance: Boolean get() = doBankAccountsSupportRetrievingBalance(selectedBankAccounts) - open fun doBankAccountsSupportRetrievingBalance(bankAccounts: List): Boolean { + open fun doBankAccountsSupportRetrievingBalance(bankAccounts: List): Boolean { return bankAccounts.firstOrNull { it.supportsRetrievingBalance } != null } - open val bankAccountsSupportingTransferringMoney: List + open val bankAccountsSupportingTransferringMoney: List get() = bankAccounts.filter { it.supportsTransferringMoney } open val hasBankAccountsSupportTransferringMoney: Boolean @@ -659,16 +662,16 @@ open class BankingPresenter( open val doSelectedBankAccountsSupportTransferringMoney: Boolean get() = doBankAccountsSupportTransferringMoney(selectedBankAccounts) - open fun doBankAccountsSupportTransferringMoney(bankAccounts: List): Boolean { + open fun doBankAccountsSupportTransferringMoney(bankAccounts: List): Boolean { return bankAccounts.firstOrNull { it.supportsTransferringMoney } != null } - protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection): List { + protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection): List { return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions } - protected open fun getBalanceForAccounts(customers: Collection): BigDecimal { + protected open fun getBalanceForAccounts(customers: Collection): BigDecimal { return customers.map { it.balance }.sum() } @@ -677,7 +680,7 @@ open class BankingPresenter( } - open fun getTanMediaForTanProcedure(bank: Customer, tanProcedure: TanProcedure): List { + open fun getTanMediaForTanProcedure(bank: TypedCustomer, tanProcedure: TanProcedure): List { if (ChipTanTanProcedures.contains(tanProcedure.type)) { return bank.tanMediaSorted.filterIsInstance() } @@ -719,11 +722,11 @@ open class BankingPresenter( } - open fun addAccountsChangedListener(listener: (List) -> Unit): Boolean { + open fun addAccountsChangedListener(listener: (List) -> Unit): Boolean { return accountsChangedListeners.add(listener) } - open fun removeAccountsChangedListener(listener: (List) -> Unit): Boolean { + open fun removeAccountsChangedListener(listener: (List) -> Unit): Boolean { return accountsChangedListeners.add(listener) } @@ -751,15 +754,15 @@ open class BankingPresenter( } - open fun addSelectedBankAccountsChangedListener(listener: (List) -> Unit): Boolean { + open fun addSelectedBankAccountsChangedListener(listener: (List) -> Unit): Boolean { return selectedBankAccountsChangedListeners.add(listener) } - open fun removeSelectedBankAccountsChangedListener(listener: (List) -> Unit): Boolean { + open fun removeSelectedBankAccountsChangedListener(listener: (List) -> Unit): Boolean { return selectedBankAccountsChangedListeners.add(listener) } - protected open fun callSelectedBankAccountsChangedListeners(selectedBankAccounts: List) { + protected open fun callSelectedBankAccountsChangedListeners(selectedBankAccounts: List) { val selectedBankAccounts = this.selectedBankAccounts ArrayList(selectedBankAccountsChangedListeners).forEach { diff --git a/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/BankingPresenterSwift.kt b/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/BankingPresenterSwift.kt index b3fb395d..add17df7 100644 --- a/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/BankingPresenterSwift.kt +++ b/ui/BankingUiNativeIntegration/src/iosMain/kotlin/net/dankito/banking/BankingPresenterSwift.kt @@ -5,6 +5,7 @@ import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.persistence.IBankingPersistence import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.ui.IRouter +import net.dankito.banking.ui.model.mapper.DefaultModelCreator import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.util.* import net.dankito.banking.util.extraction.NoOpInvoiceDataExtractor @@ -14,7 +15,7 @@ import net.dankito.utils.multiplatform.File class BankingPresenterSwift(dataFolder: File, router: IRouter, webClient: IWebClient, persistence: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder, serializer: ISerializer, asyncRunner: IAsyncRunner) - : BankingPresenter(fints4kBankingClientCreator(serializer, webClient), InMemoryBankFinder(), dataFolder, persistence, router, + : BankingPresenter(fints4kBankingClientCreator(DefaultModelCreator(), serializer, webClient), InMemoryBankFinder(), dataFolder, persistence, router, DefaultModelCreator(), remitteeSearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) { } \ No newline at end of file diff --git a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj index 53512726..557be7ec 100644 --- a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj +++ b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */; }; 366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.swift */; }; 3684EB8B2508F6F00001139E /* SearchBarWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */; }; + 3684EB8F250B7F3C0001139E /* BankingUiCommon.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3684EB90250B7F560001139E /* BankingUiCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */; }; 36B8A4482503D12100C15359 /* ProtectAppSettingsDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */; }; 36B8A44B2503D1E800C15359 /* BiometricAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */; }; 36B8A44D2503D96D00C15359 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A44C2503D96D00C15359 /* AuthenticationService.swift */; }; @@ -44,8 +46,6 @@ 36B8A4562503E9B200C15359 /* UIAlertBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4552503E9B200C15359 /* UIAlertBase.swift */; }; 36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4572503EEB600C15359 /* ActionSheet.swift */; }; 36BCF85424BA0C54005BEC29 /* BankList.json in Resources */ = {isa = PBXBuildFile; fileRef = 36BCF85324BA0C54005BEC29 /* BankList.json */; }; - 36BCF85824BA4274005BEC29 /* BankingUiCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36BCF85524BA41EE005BEC29 /* BankingUiCommon.framework */; }; - 36BCF85924BA4274005BEC29 /* BankingUiCommon.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 36BCF85524BA41EE005BEC29 /* BankingUiCommon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36BCF85E24BA4DA8005BEC29 /* MultiplatformUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36BCF85D24BA4DA8005BEC29 /* MultiplatformUtils.framework */; }; 36BCF85F24BA4DA8005BEC29 /* MultiplatformUtils.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 36BCF85D24BA4DA8005BEC29 /* MultiplatformUtils.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 36BCF86324BA5097005BEC29 /* SwiftUiRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36BCF86224BA5097005BEC29 /* SwiftUiRouter.swift */; }; @@ -138,8 +138,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 3684EB8F250B7F3C0001139E /* BankingUiCommon.framework in Embed Frameworks */, 36BCF86A24BA550D005BEC29 /* BankFinder.framework in Embed Frameworks */, - 36BCF85924BA4274005BEC29 /* BankingUiCommon.framework in Embed Frameworks */, 36BCF85F24BA4DA8005BEC29 /* MultiplatformUtils.framework in Embed Frameworks */, 36FC92D124B39C47002B12E9 /* fints4k.framework in Embed Frameworks */, 36BCF87124BB0F8A005BEC29 /* fints4kBankingClient.framework in Embed Frameworks */, @@ -179,6 +179,8 @@ 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanDialog.swift; sourceTree = ""; }; 366FA4E524C6EBF40094F009 /* EnterTanState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanState.swift; sourceTree = ""; }; 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarWithLabel.swift; sourceTree = ""; }; + 3684EB8C250B7F2B0001139E /* BankingUiCommon.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = BankingUiCommon.framework.dSYM; path = "../BankingUiCommon/build/xcode-frameworks/BankingUiCommon.framework.dSYM"; sourceTree = ""; }; + 3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BankingUiCommon.framework; path = "../BankingUiCommon/build/xcode-frameworks/BankingUiCommon.framework"; sourceTree = ""; }; 36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectAppSettingsDialog.swift; sourceTree = ""; }; 36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricAuthenticationService.swift; sourceTree = ""; }; 36B8A44C2503D96D00C15359 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = ""; }; @@ -264,8 +266,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3684EB90250B7F560001139E /* BankingUiCommon.framework in Frameworks */, 36BCF86924BA550D005BEC29 /* BankFinder.framework in Frameworks */, - 36BCF85824BA4274005BEC29 /* BankingUiCommon.framework in Frameworks */, 36BCF85E24BA4DA8005BEC29 /* MultiplatformUtils.framework in Frameworks */, 36FC92D024B39C47002B12E9 /* fints4k.framework in Frameworks */, 36BCF87024BB0F8A005BEC29 /* fints4kBankingClient.framework in Frameworks */, @@ -419,6 +421,8 @@ 36FC928F24B39A05002B12E9 = { isa = PBXGroup; children = ( + 3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */, + 3684EB8C250B7F2B0001139E /* BankingUiCommon.framework.dSYM */, 36FC929A24B39A05002B12E9 /* BankingiOSApp */, 36FC92B424B39A08002B12E9 /* BankingiOSAppTests */, 36FC92BF24B39A08002B12E9 /* BankingiOSAppUITests */, diff --git a/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift b/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift index cdd65dd4..4346273f 100644 --- a/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift +++ b/ui/BankingiOSApp/BankingiOSApp/Preview Content/PreviewData.swift @@ -15,7 +15,7 @@ let previewImageTanChallenge = ImageTanChallenge(image: TanImage(mimeType: "imag let previewFlickerCodeTanChallenge = FlickerCodeTanChallenge(flickerCode: FlickerCode(challengeHHD_UC: "", parsedDataSet: "", decodingError: nil), messageToShowToUser: "", tanProcedure: previewTanProcedures[0]) -func createPreviewBanks() -> [Customer] { +func createPreviewBanks() -> [ICustomer] { let bank1 = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: []) bank1.accounts = [ diff --git a/ui/BankingiOSApp/BankingiOSApp/extensions/Extensions.swift b/ui/BankingiOSApp/BankingiOSApp/extensions/Extensions.swift index 690b7147..a0d1c90e 100644 --- a/ui/BankingiOSApp/BankingiOSApp/extensions/Extensions.swift +++ b/ui/BankingiOSApp/BankingiOSApp/extensions/Extensions.swift @@ -89,7 +89,32 @@ extension AccountTransaction : Identifiable { } -extension Array where Element == Customer { +func ==(lhs: ICustomer, rhs: ICustomer) -> Bool { + return lhs.technicalId == rhs.technicalId +} + +func !=(lhs: ICustomer, rhs: ICustomer) -> Bool { + return lhs.technicalId != rhs.technicalId +} + +func ==(lhs: IBankAccount, rhs: IBankAccount) -> Bool { + return lhs.technicalId == rhs.technicalId +} + +func !=(lhs: IBankAccount, rhs: IBankAccount) -> Bool { + return lhs.technicalId != rhs.technicalId +} + +func ==(lhs: IAccountTransaction, rhs: IAccountTransaction) -> Bool { + return lhs.technicalId == rhs.technicalId +} + +func !=(lhs: IAccountTransaction, rhs: IAccountTransaction) -> Bool { + return lhs.technicalId != rhs.technicalId +} + + +extension Array where Element == ICustomer { func sumBalances() -> CommonBigDecimal { return CommonBigDecimal(decimal_: self.map { $0.balance.decimal }.sum()) @@ -97,7 +122,7 @@ extension Array where Element == Customer { } -extension Array where Element == AccountTransaction { +extension Array where Element == IAccountTransaction { func sumAmounts() -> CommonBigDecimal { return CommonBigDecimal(decimal_: self.map { $0.amount.decimal }.sum()) diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift index f4d568f5..98fb3c7d 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift @@ -6,8 +6,8 @@ class AppData : ObservableObject { @Inject private var presenter: BankingPresenterSwift - @Published var banks: [Customer] = [] - @Published var banksSorted: [Customer] = [] + @Published var banks: [ICustomer] = [] + @Published var banksSorted: [ICustomer] = [] @Published var hasAtLeastOneAccountBeenAdded: Bool = false @@ -23,7 +23,7 @@ class AppData : ObservableObject { } - private func setFieldsForBanks(_ banks: [Customer]) { + private func setFieldsForBanks(_ banks: [ICustomer]) { self.banks = presenter.customers self.banksSorted = banks.sortedByDisplayIndex() diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift index 5450aa7c..707798a2 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/CoreDataBankingPersistence.swift @@ -19,7 +19,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { } - func saveOrUpdateAccount(customer: Customer, allCustomers: [Customer]) { + func saveOrUpdateAccount(customer: ICustomer, allCustomers: [ICustomer]) { do { let mapped = mapper.map(customer, context) @@ -35,7 +35,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { } } - private func setIds(_ customer: Customer, _ mappedCustomer: PersistedCustomer) { + private func setIds(_ customer: ICustomer, _ mappedCustomer: PersistedCustomer) { customer.technicalId = mappedCustomer.objectIDAsString for account in customer.accounts { @@ -58,7 +58,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { } - func readPersistedAccounts_() -> [Customer] { + func readPersistedAccounts_() -> [ICustomer] { var customers: [PersistedCustomer] = [] do { @@ -73,7 +73,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { return customers.map( { mapper.map($0) } ) } - func deleteAccount(customer: Customer, allCustomers: [Customer]) { + func deleteAccount(customer: ICustomer, allCustomers: [ICustomer]) { do { let mapped = mapper.map(customer, context) @@ -85,7 +85,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { } } - func saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: [AccountTransaction]) { + func saveOrUpdateAccountTransactions(bankAccount: IBankAccount, transactions: [IAccountTransaction]) { if let persistedAccount = context.objectByID(bankAccount.technicalId) as? PersistedBankAccount { for transaction in transactions { if transaction.technicalId.isCoreDataId == false { // TODO: or also update already persisted transactions? @@ -96,7 +96,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher { transaction.technicalId = mappedTransaction.objectIDAsString } catch { - NSLog("Could not save transaction \(transaction.transactionIdentifier) of account \(bankAccount.displayName): \(error)") + NSLog("Could not save transaction \(transaction.buildTransactionIdentifier()) of account \(bankAccount.displayName): \(error)") } } } diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/EnterTanState.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/EnterTanState.swift index ce00f270..db576d11 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/EnterTanState.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/EnterTanState.swift @@ -6,14 +6,14 @@ class EnterTanState : Identifiable { let id: Foundation.UUID = UUID() - let customer: Customer + let customer: ICustomer let tanChallenge: TanChallenge let callback: (EnterTanResult) -> Void - init(_ customer: Customer, _ tanChallenge: TanChallenge, _ callback: @escaping (EnterTanResult) -> Void) { + init(_ customer: ICustomer, _ tanChallenge: TanChallenge, _ callback: @escaping (EnterTanResult) -> Void) { self.customer = customer self.tanChallenge = tanChallenge self.callback = callback diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift index dc3454ce..c5c8cfec 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift @@ -5,7 +5,7 @@ import BankingUiSwift class Mapper { - func map(_ customer: PersistedCustomer) -> Customer { + func map(_ customer: PersistedCustomer) -> ICustomer { let mapped = Customer(bankCode: map(customer.bankCode), customerId: map(customer.customerId), password: map(customer.password), finTsServerAddress: map(customer.finTsServerAddress), bankName: map(customer.bankName), bic: map(customer.bic), customerName: map(customer.customerName), userId: map(customer.userId), iconUrl: customer.iconUrl, accounts: []) mapped.userSetDisplayName = customer.userSetDisplayName @@ -23,7 +23,7 @@ class Mapper { return mapped } - func map(_ customer: Customer, _ context: NSManagedObjectContext) -> PersistedCustomer { + func map(_ customer: ICustomer, _ context: NSManagedObjectContext) -> PersistedCustomer { let mapped = context.objectByID(customer.technicalId) ?? PersistedCustomer(context: context) mapped.bankCode = customer.bankCode @@ -50,11 +50,11 @@ class Mapper { } - func map(_ customer: Customer, _ accounts: [PersistedBankAccount]?) -> [BankAccount] { + func map(_ customer: ICustomer, _ accounts: [PersistedBankAccount]?) -> [IBankAccount] { return accounts?.map( { map(customer, $0) } ) ?? [] } - func map(_ customer: Customer, _ account: PersistedBankAccount) -> BankAccount { + func map(_ customer: ICustomer, _ account: PersistedBankAccount) -> IBankAccount { let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, lastRetrievedTransactionsTimestamp: map(account.lastRetrievedTransactionsTimestamp), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: []) mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched @@ -69,11 +69,11 @@ class Mapper { return mapped } - func map(_ customer: PersistedCustomer, _ accounts: [BankAccount], _ context: NSManagedObjectContext) -> [PersistedBankAccount] { + func map(_ customer: PersistedCustomer, _ accounts: [IBankAccount], _ context: NSManagedObjectContext) -> [PersistedBankAccount] { return accounts.map( { map(customer, $0, context) } ) } - func map(_ customer: PersistedCustomer, _ account: BankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount { + func map(_ customer: PersistedCustomer, _ account: IBankAccount, _ context: NSManagedObjectContext) -> PersistedBankAccount { let mapped = context.objectByID(account.technicalId) ?? PersistedBankAccount(context: context) mapped.customer = customer @@ -136,11 +136,11 @@ class Mapper { } - func map(_ account: BankAccount, _ transactions: Set?) -> [AccountTransaction] { + func map(_ account: IBankAccount, _ transactions: Set?) -> [IAccountTransaction] { return transactions?.map( {map(account, $0) } ) ?? [] } - func map(_ account: BankAccount, _ transaction: PersistedAccountTransaction) -> AccountTransaction { + func map(_ account: IBankAccount, _ transaction: PersistedAccountTransaction) -> IAccountTransaction { let mapped = AccountTransaction(bankAccount: account, amount: map(transaction.amount), currency: map(transaction.currency), unparsedUsage: map(transaction.unparsedUsage), bookingDate: map(transaction.bookingDate), otherPartyName: transaction.otherPartyName, otherPartyBankCode: transaction.otherPartyBankCode, otherPartyAccountId: transaction.otherPartyAccountId, bookingText: transaction.bookingText, valueDate: map(transaction.valueDate), statementNumber: Int32(transaction.statementNumber), sequenceNumber: map(transaction.sequenceNumber), openingBalance: map(transaction.openingBalance), closingBalance: map(transaction.closingBalance), endToEndReference: transaction.endToEndReference, customerReference: transaction.customerReference, mandateReference: transaction.mandateReference, creditorIdentifier: transaction.creditorIdentifier, originatorsIdentificationCode: transaction.originatorsIdentificationCode, compensationAmount: transaction.compensationAmount, originalAmount: transaction.originalAmount, sepaUsage: transaction.sepaUsage, deviantOriginator: transaction.deviantOriginator, deviantRecipient: transaction.deviantRecipient, usageWithNoSpecialType: transaction.usageWithNoSpecialType, primaNotaNumber: transaction.primaNotaNumber, textKeySupplement: transaction.textKeySupplement, currencyType: transaction.currencyType, bookingKey: map(transaction.bookingKey), referenceForTheAccountOwner: map(transaction.referenceForTheAccountOwner), referenceOfTheAccountServicingInstitution: transaction.referenceOfTheAccountServicingInstitution, supplementaryDetails: transaction.supplementaryDetails, transactionReferenceNumber: map(transaction.transactionReferenceNumber), relatedReferenceNumber: transaction.relatedReferenceNumber) mapped.technicalId = transaction.objectIDAsString @@ -149,11 +149,11 @@ class Mapper { } - func map(_ account: PersistedBankAccount, _ transactions: [AccountTransaction], _ context: NSManagedObjectContext) -> [PersistedAccountTransaction] { + func map(_ account: PersistedBankAccount, _ transactions: [IAccountTransaction], _ context: NSManagedObjectContext) -> [PersistedAccountTransaction] { return transactions.map( {map(account, $0, context) } ) } - func map(_ account: PersistedBankAccount, _ transaction: AccountTransaction, _ context: NSManagedObjectContext) -> PersistedAccountTransaction { + func map(_ account: PersistedBankAccount, _ transaction: IAccountTransaction, _ context: NSManagedObjectContext) -> PersistedAccountTransaction { let mapped = context.objectByID(transaction.technicalId) ?? PersistedAccountTransaction(context: context) mapped.account = account diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/Messages.swift b/ui/BankingiOSApp/BankingiOSApp/ui/Messages.swift index dc077755..59918658 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/Messages.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/Messages.swift @@ -11,7 +11,7 @@ extension Message { secondaryButton: .cancel()) } - static func createAskUserToDeleteAccountMessage(_ bank: Customer, _ deleteAccount: @escaping (Customer) -> Void) -> Message { + static func createAskUserToDeleteAccountMessage(_ bank: ICustomer, _ deleteAccount: @escaping (ICustomer) -> Void) -> Message { return Message(title: Text("Really delete account '\(bank.displayName)'?"), message: Text("All data for this account will be permanently deleted locally."), primaryButton: .destructive(Text("Delete"), action: { deleteAccount(bank) } ), diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/SwiftUiRouter.swift b/ui/BankingiOSApp/BankingiOSApp/ui/SwiftUiRouter.swift index 0c16df94..842e8aa3 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/SwiftUiRouter.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/SwiftUiRouter.swift @@ -9,7 +9,7 @@ class SwiftUiRouter : IRouter { } - func getTanFromUserFromNonUiThread(customer: Customer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: @escaping (EnterTanResult) -> Void) { + func getTanFromUserFromNonUiThread(customer: ICustomer, tanChallenge: TanChallenge, presenter: BankingPresenter, callback: @escaping (EnterTanResult) -> Void) { let enterTanState = EnterTanState(customer, tanChallenge, callback) SceneDelegate.navigateToView(EnterTanDialog(enterTanState)) diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountTransactionsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountTransactionsDialog.swift index 4b079fd9..c31f15af 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountTransactionsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountTransactionsDialog.swift @@ -9,7 +9,7 @@ struct AccountTransactionsDialog: View { private let title: String - private let allTransactions: [AccountTransaction] + private let allTransactions: [IAccountTransaction] private let balanceOfAllTransactions: CommonBigDecimal @@ -20,10 +20,10 @@ struct AccountTransactionsDialog: View { @State private var showFetchAllTransactionsOverlay: Bool - @State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [BankAccount] + @State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] - @State private var filteredTransactions: [AccountTransaction] + @State private var filteredTransactions: [IAccountTransaction] @State private var balanceOfFilteredTransactions: CommonBigDecimal @@ -45,7 +45,7 @@ struct AccountTransactionsDialog: View { @Inject private var presenter: BankingPresenterSwift - init(allBanks: [Customer]) { + init(allBanks: [ICustomer]) { let allAccounts = allBanks.flatMap { $0.accounts } self.init("All accounts", allAccounts.flatMap { $0.bookedTransactions }, allBanks.sumBalances(), allAccounts.filter { $0.haveAllTransactionsBeenFetched == false }) @@ -53,19 +53,19 @@ struct AccountTransactionsDialog: View { presenter.selectedAllBankAccounts() } - init(bank: Customer) { + init(bank: ICustomer) { self.init(bank.displayName, bank.accounts.flatMap { $0.bookedTransactions }, bank.balance, bank.accounts.filter { $0.haveAllTransactionsBeenFetched == false }) presenter.selectedAccount(customer: bank) } - init(account: BankAccount) { + init(account: IBankAccount) { self.init(account.displayName, account.bookedTransactions, account.balance, account.haveAllTransactionsBeenFetched ? [] : [account]) presenter.selectedBankAccount(bankAccount: account) } - fileprivate init(_ title: String, _ transactions: [AccountTransaction], _ balance: CommonBigDecimal, _ accountsForWhichNotAllTransactionsHaveBeenFetched: [BankAccount] = []) { + fileprivate init(_ title: String, _ transactions: [IAccountTransaction], _ balance: CommonBigDecimal, _ accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = []) { self.title = title self.allTransactions = transactions @@ -74,7 +74,7 @@ struct AccountTransactionsDialog: View { self.balanceOfAllTransactions = balance self._balanceOfFilteredTransactions = State(initialValue: balance) - self.areMoreThanOneBanksTransactionsDisplayed = Set(allTransactions.compactMap { $0.bankAccount }.compactMap { $0.customer }).count > 1 + self.areMoreThanOneBanksTransactionsDisplayed = Set(allTransactions.compactMap { $0.bankAccount }.compactMap { $0.customer as! Customer }).count > 1 _accountsForWhichNotAllTransactionsHaveBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched) _haveAllTransactionsBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty) @@ -173,7 +173,7 @@ struct AccountTransactionsDialog: View { } } - private func fetchAllTransactions(_ accounts: [BankAccount]) { + private func fetchAllTransactions(_ accounts: [IBankAccount]) { accounts.forEach { account in presenter.fetchAllAccountTransactionsAsync(bankAccount: account, callback: self.handleGetAllTransactionsResult) } @@ -224,7 +224,7 @@ struct AccountTransactionsDialog: View { struct AccountTransactionsDialog_Previews: PreviewProvider { static var previews: some View { AccountTransactionsDialog(previewBanks[0].displayName, [ - AccountTransaction(bankAccount: previewBanks[0].accounts[0], amount: CommonBigDecimal(double: 1234.56), currency: "€", unparsedUsage: "Usage", bookingDate: CommonDate(year: 2020, month: 5, day: 7), otherPartyName: "Marieke Musterfrau", otherPartyBankCode: nil, otherPartyAccountId: nil, bookingText: "SEPA Ueberweisung", valueDate: CommonDate(year: 2020, month: 5, day: 7)) + AccountTransaction(bankAccount: previewBanks[0].accounts[0] as! BankAccount, amount: CommonBigDecimal(double: 1234.56), currency: "€", unparsedUsage: "Usage", bookingDate: CommonDate(year: 2020, month: 5, day: 7), otherPartyName: "Marieke Musterfrau", otherPartyBankCode: nil, otherPartyAccountId: nil, bookingText: "SEPA Ueberweisung", valueDate: CommonDate(year: 2020, month: 5, day: 7)) ], CommonBigDecimal(double: 84.12)) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountsDialog.swift index 49a89d2f..97ea8122 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AccountsDialog.swift @@ -22,7 +22,7 @@ struct AccountsDialog: View { Form { AllBanksListItem(banks: data.banks) - ForEach(data.banks.sortedByDisplayIndex()) { bank in + ForEach(data.banks.sortedByDisplayIndex(), id: \.technicalId) { bank in BankListItem(bank: bank) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AddAccountDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AddAccountDialog.swift index abb71bdf..336f396d 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AddAccountDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/AddAccountDialog.swift @@ -85,7 +85,7 @@ struct AddAccountDialog: View { isTryingToAddAccount = true UIApplication.hideKeyboard() - presenter.addAccountAsync(bankInfo: bank, customerId: customerId, pin: password) { (response) in + presenter.addAccountAsync(bankInfo: bank, customerId: customerId, password: password) { (response) in self.handleAddAccountResponse(response) } } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankAccountSettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankAccountSettingsDialog.swift index d78aaf5a..8d269cd6 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankAccountSettingsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankAccountSettingsDialog.swift @@ -9,7 +9,7 @@ struct BankAccountSettingsDialog: View { @Inject private var presenter: BankingPresenterSwift - private let account: BankAccount + private let account: IBankAccount @State private var displayName: String @@ -21,7 +21,7 @@ struct BankAccountSettingsDialog: View { } - init(_ account: BankAccount) { + init(_ account: IBankAccount) { self.account = account _displayName = State(initialValue: account.displayName) @@ -79,8 +79,8 @@ struct BankAccountSettingsDialog: View { private func donePressed() { if hasUnsavedData { account.userSetDisplayName = displayName - - presenter.accountUpdated(account: account.customer) + + presenter.accountUpdated(account: account) } closeDialog() diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankSettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankSettingsDialog.swift index e19a1046..95ab0195 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankSettingsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/BankSettingsDialog.swift @@ -10,7 +10,7 @@ struct BankSettingsDialog: View { @Inject private var presenter: BankingPresenterSwift - private let bank: Customer + private let bank: ICustomer @State private var displayName: String @@ -19,7 +19,7 @@ struct BankSettingsDialog: View { @State private var selectedTanProcedure: TanProcedure? - @State private var accountsSorted: [BankAccount] + @State private var accountsSorted: [IBankAccount] @State private var askUserToDeleteAccountOrSaveChangesMessage: Message? = nil @@ -32,7 +32,7 @@ struct BankSettingsDialog: View { } - init(_ bank: Customer) { + init(_ bank: ICustomer) { self.bank = bank _displayName = State(initialValue: bank.displayName) @@ -75,7 +75,7 @@ struct BankSettingsDialog: View { } Section(header: SectionHeaderWithRightAlignedEditButton("Accounts")) { - ForEach(accountsSorted) { account in + ForEach(accountsSorted, id: \.technicalId) { account in NavigationLink(destination: LazyView(BankAccountSettingsDialog(account))) { Text(account.displayName) } @@ -102,7 +102,7 @@ struct BankSettingsDialog: View { func reorderAccounts(from source: IndexSet, to destination: Int) { accountsSorted = accountsSorted.reorder(from: source, to: destination) - presenter.accountUpdated(account: bank) + presenter.accountDisplayIndexUpdated(account: bank) } @@ -110,7 +110,7 @@ struct BankSettingsDialog: View { self.askUserToDeleteAccountOrSaveChangesMessage = Message.createAskUserToDeleteAccountMessage(bank, self.deleteAccount) } - func deleteAccount(bank: Customer) { + func deleteAccount(bank: ICustomer) { presenter.deleteAccount(customer: bank) closeDialog() @@ -135,7 +135,7 @@ struct BankSettingsDialog: View { bank.selectedTanProcedure = selectedTanProcedure - presenter.accountUpdated(account: bank) + presenter.accountUpdated(bank: bank) } closeDialog() diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/EnterTanDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/EnterTanDialog.swift index a464e6ee..5d276e14 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/EnterTanDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/EnterTanDialog.swift @@ -11,7 +11,7 @@ struct EnterTanDialog: View { private var tanChallenge: TanChallenge - private var customer: Customer + private var customer: ICustomer private var customersTanMedia: [TanMedium] = [] diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/SettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/SettingsDialog.swift index 2efbf256..b94cd013 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/SettingsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/SettingsDialog.swift @@ -18,7 +18,7 @@ struct SettingsDialog: View { Form { Section(header: SectionHeaderWithRightAlignedEditButton("Bank Credentials", isEditButtonEnabled: data.hasAtLeastOneAccountBeenAdded), footer: footer) { - ForEach(data.banksSorted) { bank in + ForEach(data.banksSorted, id: \.technicalId) { bank in NavigationLink(destination: LazyView(BankSettingsDialog(bank))) { IconedTitleView(bank) } @@ -68,11 +68,11 @@ struct SettingsDialog: View { } } - func askUserToDeleteAccount(_ bankToDelete: Customer) { + func askUserToDeleteAccount(_ bankToDelete: ICustomer) { self.askToDeleteAccountMessage = Message.createAskUserToDeleteAccountMessage(bankToDelete, self.deleteAccountWithSecurityChecks) } - func deleteAccountWithSecurityChecks(_ bankToDelete: Customer) { + func deleteAccountWithSecurityChecks(_ bankToDelete: ICustomer) { // don't know why but when deleting last bank application crashes if we don't delete bank async DispatchQueue.main.async { if self.presenter.customers.count == 1 { @@ -88,7 +88,7 @@ struct SettingsDialog: View { } } - private func deleteAccount(_ bankToDelete: Customer) { + private func deleteAccount(_ bankToDelete: ICustomer) { self.presenter.deleteAccount(customer: bankToDelete) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/TransferMoneyDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/TransferMoneyDialog.swift index 383dc60a..35e2eb07 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/TransferMoneyDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/dialogs/TransferMoneyDialog.swift @@ -10,7 +10,7 @@ struct TransferMoneyDialog: View { private var showAccounts = false - private var accountsSupportingTransferringMoney: [BankAccount] = [] + private var accountsSupportingTransferringMoney: [IBankAccount] = [] @State private var selectedAccountIndex = 0 @@ -52,7 +52,7 @@ struct TransferMoneyDialog: View { @State private var didJustCorrectEnteredValue = false - private var account: BankAccount? { + private var account: IBankAccount? { if (self.selectedAccountIndex < self.accountsSupportingTransferringMoney.count) { return self.accountsSupportingTransferringMoney[selectedAccountIndex] } @@ -411,6 +411,7 @@ struct TransferMoneyDialog: View { self.transferMoneyResponseMessage = Message(message: Text("Could not transfer \(data.amount) \("€") to \(data.creditorName). Error: \(response.errorToShowToUser ?? "").")) } } + } struct TransferMoneyDialog_Previews: PreviewProvider { diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift index 071144a7..d69f0751 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/IconedTitleView.swift @@ -13,11 +13,11 @@ struct IconedTitleView: View { private var titleFont: Font? - init(_ bank: Customer, titleFont: Font? = nil) { + init(_ bank: ICustomer, titleFont: Font? = nil) { self.init(accountTitle: bank.displayName, iconUrl: bank.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) } - init(_ account: BankAccount, titleFont: Font? = nil) { + init(_ account: IBankAccount, titleFont: Font? = nil) { self.init(accountTitle: account.displayName, iconUrl: account.customer.iconUrl, defaultIconName: Styles.AccountFallbackIcon, titleFont: titleFont) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift index 4249945f..6d34145c 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AccountTransactionListItem.swift @@ -13,7 +13,7 @@ struct AccountTransactionListItem: View { }() - private let transaction: AccountTransaction + private let transaction: IAccountTransaction private let areMoreThanOneBanksTransactionsDisplayed: Bool @@ -21,7 +21,7 @@ struct AccountTransactionListItem: View { @Inject private var presenter: BankingPresenterSwift - init(_ transaction: AccountTransaction, _ areMoreThanOneBanksTransactionsDisplayed: Bool) { + init(_ transaction: IAccountTransaction, _ areMoreThanOneBanksTransactionsDisplayed: Bool) { self.transaction = transaction self.areMoreThanOneBanksTransactionsDisplayed = areMoreThanOneBanksTransactionsDisplayed @@ -82,7 +82,7 @@ struct AccountTransactionListItem: View { } - private func getTransactionLabel(_ transaction: AccountTransaction) -> String { + private func getTransactionLabel(_ transaction: IAccountTransaction) -> String { if transaction.bookingText?.localizedCaseInsensitiveCompare("Bargeldauszahlung") == ComparisonResult.orderedSame { return transaction.bookingText ?? "" } @@ -98,6 +98,6 @@ struct AccountTransactionListItem: View { struct AccountTransactionListItem_Previews: PreviewProvider { static var previews: some View { - AccountTransactionListItem(AccountTransaction(bankAccount: previewBanks[0].accounts[0], otherPartyName: "Marieke Musterfrau", unparsedUsage: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false) + AccountTransactionListItem(AccountTransaction(bankAccount: previewBanks[0].accounts[0] as! BankAccount, otherPartyName: "Marieke Musterfrau", unparsedUsage: "Vielen Dank für Ihre Mühen", amount: CommonBigDecimal(double: 1234.56), valueDate: CommonDate(year: 2020, month: .march, day_: 27), bookingText: "SEPA Überweisung"), false) } } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift index 6b535ef0..da258eba 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/AllBanksListItem.swift @@ -4,7 +4,7 @@ import BankingUiSwift struct AllBanksListItem: View { - let banks: [Customer] + let banks: [ICustomer] @State private var navigateToAccountTransactionsDialog = false diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankAccountListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankAccountListItem.swift index 036c636f..38db6028 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankAccountListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankAccountListItem.swift @@ -4,7 +4,7 @@ import BankingUiSwift struct BankAccountListItem : View { - let account: BankAccount + let account: IBankAccount @State private var navigateToAccountTransactionsDialog = false diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankListItem.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankListItem.swift index d0943af0..1451d9a6 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankListItem.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/listitems/BankListItem.swift @@ -4,7 +4,7 @@ import BankingUiSwift struct BankListItem : View { - let bank: Customer + let bank: ICustomer @State private var navigateToAccountTransactionsDialog = false @@ -47,7 +47,7 @@ struct BankListItem : View { } - ForEach(bank.accountsSorted) { account in + ForEach(bank.accountsSorted, id: \.technicalId) { account in BankAccountListItem(account: account) } .padding(.leading, Styles.AccountsIconWidth + Styles.DefaultSpaceBetweenIconAndText) @@ -70,7 +70,7 @@ struct BankListItem : View { ).show() } - private func deleteAccount(_ bank: Customer) { + private func deleteAccount(_ bank: ICustomer) { presenter.deleteAccount(customer: bank) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/tan/TanProcedurePicker.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/tan/TanProcedurePicker.swift index c88b91bf..d9adcf06 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/tan/TanProcedurePicker.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/tan/TanProcedurePicker.swift @@ -4,7 +4,7 @@ import BankingUiSwift struct TanProcedurePicker: View { - private let bank: Customer + private let bank: ICustomer private let selectedTanProcedureChanged: (TanProcedure) -> Void @@ -27,7 +27,7 @@ struct TanProcedurePicker: View { } - init(_ bank: Customer, _ initiallySelectedTanProcedure: TanProcedure? = nil, selectedTanProcedureChanged: @escaping (TanProcedure) -> Void) { + init(_ bank: ICustomer, _ initiallySelectedTanProcedure: TanProcedure? = nil, selectedTanProcedureChanged: @escaping (TanProcedure) -> Void) { self.bank = bank self.selectedTanProcedureChanged = selectedTanProcedureChanged diff --git a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClient.kt b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClient.kt index 96d6e63b..2e99f5d7 100644 --- a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClient.kt +++ b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClient.kt @@ -2,9 +2,6 @@ package net.dankito.banking import net.dankito.banking.ui.BankingClientCallback import net.dankito.banking.ui.IBankingClient -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.MessageLogEntry import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.AddAccountResponse @@ -20,13 +17,17 @@ import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.fints.webclient.KtorWebClient import net.dankito.banking.extensions.toMoney import net.dankito.banking.fints.response.client.FinTsClientResponse +import net.dankito.banking.ui.model.* +import net.dankito.banking.ui.model.MessageLogEntry +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.util.ISerializer import net.dankito.utils.multiplatform.File import net.dankito.utils.multiplatform.log.LoggerFactory open class fints4kBankingClient( - protected val customer: Customer, + protected val customer: TypedCustomer, + protected val modelCreator: IModelCreator, protected val dataFolder: File, protected val serializer: ISerializer, webClient: IWebClient = KtorWebClient(), @@ -42,7 +43,7 @@ open class fints4kBankingClient( } - protected val mapper = net.dankito.banking.mapper.fints4kModelMapper() + protected val mapper = net.dankito.banking.mapper.fints4kModelMapper(modelCreator) protected var didTryToGetAccountDataFromBank = false @@ -74,7 +75,7 @@ open class fints4kBankingClient( } - override fun getTransactionsAsync(bankAccount: BankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { + override fun getTransactionsAsync(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { findAccountForBankAccount(bankAccount) { account, errorMessage -> if (account == null) { callback(GetTransactionsResponse(bankAccount, false, errorMessage)) @@ -90,13 +91,13 @@ open class fints4kBankingClient( } protected open fun doGetTransactionsAsync(parameter: net.dankito.banking.fints.model.GetTransactionsParameter, - account: AccountData, bankAccount: BankAccount, callback: (GetTransactionsResponse) -> Unit) { + account: AccountData, bankAccount: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) { client.getTransactionsAsync(parameter, account) { response -> handleGetTransactionsResponse(bankAccount, response, callback) } } - protected open fun handleGetTransactionsResponse(bankAccount: BankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse, + protected open fun handleGetTransactionsResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse, callback: (GetTransactionsResponse) -> Unit) { val mappedResponse = mapper.mapResponse(bankAccount, response) @@ -132,12 +133,12 @@ open class fints4kBankingClient( } - override fun dataChanged(customer: Customer) { + override fun dataChanged(customer: TypedCustomer) { mapper.mapChangesFromUiToClientModel(customer, bank) } - protected open fun findAccountForBankAccount(bankAccount: BankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) { + protected open fun findAccountForBankAccount(bankAccount: TypedBankAccount, findAccountResult: (AccountData?, error: String?) -> Unit) { val mappedAccount = mapper.findAccountForBankAccount(bank, bankAccount) if (mappedAccount != null) { @@ -157,12 +158,12 @@ open class fints4kBankingClient( } - protected open fun restoreDataOrMapFromUiModel(customer: Customer): BankData { + protected open fun restoreDataOrMapFromUiModel(customer: TypedCustomer): BankData { return restoreData(customer) ?: BankData(customer.bankCode, customer.customerId, customer.password, customer.finTsServerAddress, customer.bic, customer.bankName) } - protected open fun restoreData(customer: Customer): BankData? { + protected open fun restoreData(customer: TypedCustomer): BankData? { try { return serializer.deserializeObject(getFints4kClientDataFile(customer.bankCode, customer.customerId), BankData::class) } catch (e: Exception) { diff --git a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClientCreator.kt b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClientCreator.kt index 1fa31c0b..87f061e0 100644 --- a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClientCreator.kt +++ b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/fints4kBankingClientCreator.kt @@ -5,25 +5,27 @@ import net.dankito.banking.ui.IBankingClient import net.dankito.banking.ui.IBankingClientCreator import net.dankito.banking.fints.webclient.IWebClient import net.dankito.banking.fints.webclient.KtorWebClient -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.util.IAsyncRunner import net.dankito.banking.util.ISerializer import net.dankito.utils.multiplatform.File open class fints4kBankingClientCreator( + protected val modelCreator: IModelCreator, protected val serializer: ISerializer, protected val webClient: IWebClient = KtorWebClient() ) : IBankingClientCreator { override fun createClient( - customer: Customer, + customer: TypedCustomer, dataFolder: File, asyncRunner: IAsyncRunner, callback: BankingClientCallback ): IBankingClient { - return fints4kBankingClient(customer, dataFolder, serializer, webClient, callback = callback) + return fints4kBankingClient(customer, modelCreator, dataFolder, serializer, webClient, callback = callback) } } \ No newline at end of file diff --git a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt index 157bf45b..75cec669 100644 --- a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt +++ b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt @@ -11,24 +11,24 @@ import net.dankito.banking.fints.messages.datenelemente.implementierte.signatur. import net.dankito.banking.fints.model.AccountData import net.dankito.banking.fints.model.AccountFeature import net.dankito.banking.fints.model.BankData -import net.dankito.banking.fints.model.Money import net.dankito.banking.fints.response.client.FinTsClientResponse import net.dankito.banking.fints.response.segments.AccountType +import net.dankito.banking.ui.model.mapper.IModelCreator -open class fints4kModelMapper { +open class fints4kModelMapper(protected val modelCreator: IModelCreator) { open fun mapResponse(response: FinTsClientResponse): BankingClientResponse { return BankingClientResponse(response.isSuccessful, mapErrorToShowToUser(response), response.userCancelledAction) } - open fun mapResponse(customer: Customer, response: net.dankito.banking.fints.response.client.AddAccountResponse): AddAccountResponse { + open fun mapResponse(customer: TypedCustomer, response: net.dankito.banking.fints.response.client.AddAccountResponse): AddAccountResponse { val balances = response.balances.mapKeys { findMatchingBankAccount(customer, it.key) }.filter { it.key != null } - .mapValues { (it.value as Money).toBigDecimal() } as Map + .mapValues { it.value.toBigDecimal() } as Map val bookedTransactions = response.bookedTransactions.associateBy { it.account } - val mappedBookedTransactions = mutableMapOf>() + val mappedBookedTransactions = mutableMapOf>() bookedTransactions.keys.forEach { accountData -> findMatchingBankAccount(customer, accountData)?.let { bankAccount -> @@ -44,7 +44,7 @@ open class fints4kModelMapper { response.userCancelledAction) } - open fun mapResponse(bankAccount: BankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { + open fun mapResponse(bankAccount: TypedBankAccount, response: net.dankito.banking.fints.response.client.GetTransactionsResponse): GetTransactionsResponse { return GetTransactionsResponse(bankAccount, response.isSuccessful, mapErrorToShowToUser(response), mapTransactions(bankAccount, response.bookedTransactions), @@ -60,7 +60,7 @@ open class fints4kModelMapper { } - open fun mapBank(customer: Customer, bank: BankData) { + open fun mapBank(customer: TypedCustomer, bank: BankData) { customer.bankCode = bank.bankCode customer.customerId = bank.customerId customer.password = bank.pin @@ -78,7 +78,7 @@ open class fints4kModelMapper { /** * In UI only customerId, password, (bankCode,) and selected TAN procedure can be set */ - open fun mapChangesFromUiToClientModel(customer: Customer, bank: BankData) { + open fun mapChangesFromUiToClientModel(customer: TypedCustomer, bank: BankData) { bank.customerId = customer.customerId bank.pin = customer.password @@ -105,9 +105,10 @@ open class fints4kModelMapper { } - open fun mapBankAccounts(customer: Customer, accountData: List): List { + open fun mapBankAccounts(customer: TypedCustomer, accountData: List): List { return accountData.mapIndexed { index, account -> - val mappedAccount = customer.accounts.firstOrNull { it.identifier == account.accountIdentifier } ?: BankAccount(customer, account.productName, account.accountIdentifier) + val mappedAccount = customer.accounts.firstOrNull { it.identifier == account.accountIdentifier } + ?: modelCreator.createBankAccount(customer, account.productName, account.accountIdentifier) mapBankAccount(mappedAccount, account) @@ -117,7 +118,7 @@ open class fints4kModelMapper { } } - open fun mapBankAccount(account: BankAccount, accountData: AccountData) { + open fun mapBankAccount(account: TypedBankAccount, accountData: AccountData) { account.identifier = accountData.accountIdentifier account.accountHolderName = accountData.accountHolderName account.iban = accountData.iban @@ -182,11 +183,11 @@ open class fints4kModelMapper { } } - open fun findAccountForBankAccount(bank: BankData, bankAccount: BankAccount): AccountData? { + open fun findAccountForBankAccount(bank: BankData, bankAccount: TypedBankAccount): AccountData? { return bank.accounts.firstOrNull { bankAccount.identifier == it.accountIdentifier } } - open fun findMatchingBankAccount(customer: Customer, accountData: AccountData): BankAccount? { + open fun findMatchingBankAccount(customer: TypedCustomer, accountData: AccountData): TypedBankAccount? { return customer.accounts.firstOrNull { it.identifier == accountData.accountIdentifier } } @@ -195,12 +196,12 @@ open class fints4kModelMapper { } - open fun mapTransactions(bankAccount: BankAccount, transactions: List): List { + open fun mapTransactions(bankAccount: TypedBankAccount, transactions: List): List { return transactions.map { mapTransaction(bankAccount, it) } } - open fun mapTransaction(bankAccount: BankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): AccountTransaction { - return AccountTransaction( + open fun mapTransaction(bankAccount: TypedBankAccount, transaction: net.dankito.banking.fints.model.AccountTransaction): IAccountTransaction { + return modelCreator.createTransaction( bankAccount, transaction.amount.toBigDecimal(), transaction.amount.currency.code, @@ -242,7 +243,7 @@ open class fints4kModelMapper { } - open fun updateTanMediaAndProcedures(account: Customer, bank: BankData) { + open fun updateTanMediaAndProcedures(account: TypedCustomer, bank: BankData) { account.supportedTanProcedures = mapTanProcedures(bank.tanProceduresAvailableForUser) if (bank.isTanProcedureSelected) { @@ -292,7 +293,7 @@ open class fints4kModelMapper { } } - protected open fun findMappedTanProcedure(customer: Customer, tanProcedure: net.dankito.banking.fints.model.TanProcedure): TanProcedure? { + protected open fun findMappedTanProcedure(customer: TypedCustomer, tanProcedure: net.dankito.banking.fints.model.TanProcedure): TanProcedure? { return customer.supportedTanProcedures.firstOrNull { it.bankInternalProcedureCode == tanProcedure.securityFunction.code } } diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/HbciCallback.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/HbciCallback.kt index 60e7bde3..3260b618 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/HbciCallback.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/HbciCallback.kt @@ -2,13 +2,12 @@ package net.dankito.banking import net.dankito.banking.model.AccountCredentials import net.dankito.banking.ui.BankingClientCallback -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer import net.dankito.banking.ui.model.tan.FlickerCodeTanChallenge import net.dankito.banking.ui.model.tan.ImageTanChallenge import net.dankito.banking.ui.model.tan.TanChallenge import net.dankito.banking.ui.model.tan.TanImage import net.dankito.banking.util.hbci4jModelMapper -import net.dankito.utils.multiplatform.Date import org.kapott.hbci.callback.AbstractHBCICallback import org.kapott.hbci.callback.HBCICallback import org.kapott.hbci.manager.HBCIUtils @@ -24,7 +23,7 @@ import org.slf4j.LoggerFactory */ open class HbciCallback( protected val credentials: AccountCredentials, - protected val customer: Customer, + protected val customer: TypedCustomer, protected val mapper: hbci4jModelMapper, protected val callback: BankingClientCallback ) : AbstractHBCICallback() { @@ -37,7 +36,7 @@ open class HbciCallback( /** * @see org.kapott.hbci.callback.HBCICallback.log */ - override fun log(msg: String, level: Int, date: Date, trace: StackTraceElement) { + override fun log(msg: String?, level: Int, date: java.util.Date?, trace: StackTraceElement?) { // Ausgabe von Log-Meldungen bei Bedarf when (level) { HBCIUtils.LOG_ERR -> log.error(msg) @@ -172,7 +171,7 @@ open class HbciCallback( } - open fun getTanFromUser(customer: Customer, messageToShowToUser: String, challengeHHD_UC: String): String? { + open fun getTanFromUser(customer: TypedCustomer, messageToShowToUser: String, challengeHHD_UC: String): String? { // Wenn per "retData" Daten uebergeben wurden, dann enthalten diese // den fuer chipTAN optisch zu verwendenden Flickercode. // Falls nicht, ist es eine TAN-Abfrage, fuer die keine weiteren diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClient.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClient.kt index 4ce97fbc..419b4640 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClient.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClient.kt @@ -5,6 +5,7 @@ import net.dankito.banking.model.ConnectResult import net.dankito.banking.ui.BankingClientCallback import net.dankito.banking.ui.IBankingClient import net.dankito.banking.ui.model.* +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.responses.AddAccountResponse @@ -32,7 +33,8 @@ import java.util.* open class hbci4jBankingClient( - protected val customer: Customer, + protected val customer: TypedCustomer, + modelCreator: IModelCreator, protected val dataFolder: File, protected val asyncRunner: IAsyncRunner = ThreadPoolAsyncRunner(ThreadPool()), protected val callback: BankingClientCallback @@ -51,9 +53,9 @@ open class hbci4jBankingClient( protected val credentials = AccountCredentials(customer) - protected val mapper = hbci4jModelMapper() + protected val mapper = hbci4jModelMapper(modelCreator) - protected val accountTransactionMapper = AccountTransactionMapper() + protected val accountTransactionMapper = AccountTransactionMapper(modelCreator) override val messageLogWithoutSensitiveData: List = listOf() // TODO: implement @@ -86,11 +88,11 @@ open class hbci4jBankingClient( return AddAccountResponse(false, connection.error.getInnerExceptionMessage(), customer) } - protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(customer: Customer): AddAccountResponse { + protected open fun tryToRetrieveAccountTransactionsForAddedAccounts(customer: TypedCustomer): AddAccountResponse { val transactionsOfLast90DaysResponses = mutableListOf() - val balances = mutableMapOf() - val bookedTransactions = mutableMapOf>() - val unbookedTransactions = mutableMapOf>() + val balances = mutableMapOf() + val bookedTransactions = mutableMapOf>() + val unbookedTransactions = mutableMapOf>() customer.accounts.forEach { bankAccount -> if (bankAccount.supportsRetrievingAccountTransactions) { @@ -117,7 +119,7 @@ open class hbci4jBankingClient( * So we simply try to retrieve at accounting entries of the last 90 days and see if a second factor is required * or not. */ - open fun getTransactionsOfLast90DaysAsync(bankAccount: BankAccount, callback: (GetTransactionsResponse) -> Unit) { + open fun getTransactionsOfLast90DaysAsync(bankAccount: TypedBankAccount, callback: (GetTransactionsResponse) -> Unit) { asyncRunner.runAsync { callback(getTransactionsOfLast90Days(bankAccount)) } @@ -130,19 +132,19 @@ open class hbci4jBankingClient( * So we simply try to retrieve at accounting entries of the last 90 days and see if a second factor is required * or not. */ - open fun getTransactionsOfLast90Days(bankAccount: BankAccount): GetTransactionsResponse { + open fun getTransactionsOfLast90Days(bankAccount: TypedBankAccount): GetTransactionsResponse { val ninetyDaysAgo = Date(Date().time - NinetyDaysInMilliseconds) return getTransactions(bankAccount, GetTransactionsParameter(bankAccount.supportsRetrievingBalance, ninetyDaysAgo)) // TODO: implement abortIfTanIsRequired } - override fun getTransactionsAsync(bankAccount: BankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { + override fun getTransactionsAsync(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter, callback: (GetTransactionsResponse) -> Unit) { asyncRunner.runAsync { callback(getTransactions(bankAccount, parameter)) } } - protected open fun getTransactions(bankAccount: BankAccount, parameter: GetTransactionsParameter): GetTransactionsResponse { + protected open fun getTransactions(bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): GetTransactionsResponse { val connection = connect() connection.handle?.let { handle -> @@ -195,7 +197,7 @@ open class hbci4jBankingClient( return GetTransactionsResponse(bankAccount, false, connection.error.getInnerExceptionMessage()) } - protected open fun executeJobsForGetAccountingEntries(handle: HBCIHandler, bankAccount: BankAccount, parameter: GetTransactionsParameter): Triple { + protected open fun executeJobsForGetAccountingEntries(handle: HBCIHandler, bankAccount: TypedBankAccount, parameter: GetTransactionsParameter): Triple { val konto = mapper.mapToKonto(bankAccount) // 1. Auftrag fuer das Abrufen des Saldos erzeugen @@ -271,7 +273,7 @@ open class hbci4jBankingClient( } - override fun dataChanged(customer: Customer) { + override fun dataChanged(customer: TypedCustomer) { if (customer.bankCode != credentials.bankCode || customer.customerId != credentials.customerId || customer.password != credentials.password) { getPassportFile(credentials).delete() } diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClientCreator.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClientCreator.kt index 43872618..42a9d8e6 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClientCreator.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/hbci4jBankingClientCreator.kt @@ -3,21 +3,24 @@ package net.dankito.banking import net.dankito.banking.ui.BankingClientCallback import net.dankito.banking.ui.IBankingClient import net.dankito.banking.ui.IBankingClientCreator -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.banking.util.IAsyncRunner import net.dankito.utils.multiplatform.File -open class hbci4jBankingClientCreator : IBankingClientCreator { +open class hbci4jBankingClientCreator( + protected val modelCreator: IModelCreator +) : IBankingClientCreator { override fun createClient( - customer: Customer, + customer: TypedCustomer, dataFolder: File, asyncRunner: IAsyncRunner, callback: BankingClientCallback ): IBankingClient { - return hbci4jBankingClient(customer, dataFolder, asyncRunner, callback) + return hbci4jBankingClient(customer, modelCreator, dataFolder, asyncRunner, callback) } } \ No newline at end of file diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/model/AccountCredentials.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/model/AccountCredentials.kt index 88b21eb6..af697e7b 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/model/AccountCredentials.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/model/AccountCredentials.kt @@ -1,6 +1,6 @@ package net.dankito.banking.model -import net.dankito.banking.ui.model.Customer +import net.dankito.banking.ui.model.TypedCustomer open class AccountCredentials( @@ -9,6 +9,6 @@ open class AccountCredentials( var password: String ) { - constructor(bank: Customer) : this(bank.bankCode, bank.customerId, bank.password) + constructor(bank: TypedCustomer) : this(bank.bankCode, bank.customerId, bank.password) } \ No newline at end of file diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/AccountTransactionMapper.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/AccountTransactionMapper.kt index 95fe4e8a..1827eea8 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/AccountTransactionMapper.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/AccountTransactionMapper.kt @@ -1,8 +1,10 @@ package net.dankito.banking.util import net.dankito.banking.fints.transactions.mt940.Mt940Parser -import net.dankito.banking.ui.model.AccountTransaction -import net.dankito.banking.ui.model.BankAccount +import net.dankito.banking.ui.model.TypedBankAccount +import net.dankito.banking.ui.model.IAccountTransaction +import net.dankito.banking.ui.model.mapper.IModelCreator +import net.dankito.utils.multiplatform.toDate import org.kapott.hbci.GV_Result.GVRKUms import org.kapott.hbci.structures.Value import org.slf4j.LoggerFactory @@ -10,7 +12,9 @@ import java.math.BigDecimal import java.text.SimpleDateFormat -open class AccountTransactionMapper { +open class AccountTransactionMapper( + protected val modelCreator: IModelCreator +) { companion object { protected val DateStartString = "DATUM " @@ -24,8 +28,8 @@ open class AccountTransactionMapper { } - open fun mapAccountTransactions(bankAccount: BankAccount, result: GVRKUms): List { - val entries = mutableListOf() + open fun mapAccountTransactions(bankAccount: TypedBankAccount, result: GVRKUms): List { + val entries = mutableListOf() result.dataPerDay.forEach { btag -> btag.lines.forEach { transaction -> @@ -38,17 +42,17 @@ open class AccountTransactionMapper { return entries } - protected open fun mapAccountingEntry(bankAccount: BankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): AccountTransaction { + protected open fun mapAccountingEntry(bankAccount: TypedBankAccount, btag: GVRKUms.BTag, transaction: GVRKUms.UmsLine): IAccountTransaction { val unparsedUsage = transaction.usage.joinToString("") val parsedUsage = Mt940Parser().getUsageParts(unparsedUsage) val statementAndMaySequenceNumber = btag.counter.split('/') - val result = AccountTransaction(bankAccount, - mapValue(transaction.value), transaction.value.curr, unparsedUsage, transaction.bdate, + return modelCreator.createTransaction(bankAccount, + mapValue(transaction.value), transaction.value.curr, unparsedUsage, transaction.bdate.toDate(), transaction.other.name + (transaction.other.name2 ?: ""), transaction.other.bic ?: transaction.other.blz, transaction.other.iban ?: transaction.other.number, - transaction.text, transaction.valuta, + transaction.text, transaction.valuta.toDate(), statementAndMaySequenceNumber[0].toInt(), if (statementAndMaySequenceNumber.size > 1) statementAndMaySequenceNumber[1].toInt() else null, mapValue(btag.start.value), mapValue(btag.end.value), @@ -76,12 +80,10 @@ open class AccountTransactionMapper { "", null ) - - return result } - protected open fun mapValue(value: Value): BigDecimal { - return BigDecimal.valueOf(value.longValue).divide(BigDecimal.valueOf(100)) + protected open fun mapValue(value: Value): net.dankito.utils.multiplatform.BigDecimal { + return net.dankito.utils.multiplatform.BigDecimal(BigDecimal.valueOf(value.longValue).divide(BigDecimal.valueOf(100)).toPlainString()) } } \ No newline at end of file diff --git a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt index bc5557cc..314177aa 100644 --- a/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt +++ b/ui/hbci4jBankingClient/src/main/kotlin/net/dankito/banking/util/hbci4jModelMapper.kt @@ -1,10 +1,9 @@ package net.dankito.banking.util +import net.dankito.banking.ui.model.* +import net.dankito.banking.ui.model.mapper.IModelCreator import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.toBigDecimal -import net.dankito.banking.ui.model.Customer -import net.dankito.banking.ui.model.BankAccount -import net.dankito.banking.ui.model.BankAccountType import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.tan.TanProcedureType import org.kapott.hbci.passport.HBCIPassport @@ -12,9 +11,11 @@ import org.kapott.hbci.structures.Konto import org.kapott.hbci.structures.Value -open class hbci4jModelMapper { +open class hbci4jModelMapper( + protected val modelCreator: IModelCreator +) { - open fun mapToKonto(bankAccount: BankAccount): Konto { + open fun mapToKonto(bankAccount: TypedBankAccount): Konto { val customer = bankAccount.customer val konto = Konto("DE", customer.bankCode, bankAccount.identifier, bankAccount.subAccountNumber) @@ -41,15 +42,26 @@ open class hbci4jModelMapper { } - open fun mapBankAccounts(customer: Customer, bankAccounts: Array, passport: HBCIPassport): List { + open fun mapBankAccounts(customer: TypedCustomer, bankAccounts: Array, passport: HBCIPassport): List { return bankAccounts.map { bankAccount -> val iban = if (bankAccount.iban.isNullOrBlank() == false) bankAccount.iban else passport.upd.getProperty("KInfo.iban") ?: "" - BankAccount(customer, bankAccount.number, - if (bankAccount.name2.isNullOrBlank() == false) bankAccount.name + " " + bankAccount.name2 else bankAccount.name, - iban, bankAccount.subnumber, bankAccount.customerid, BigDecimal.Zero, bankAccount.curr, mapBankAccountType(bankAccount), - null, bankAccount.limit?.value?.let { mapValue(it).toString() }, null, - bankAccount.allowedGVs.contains("HKKAZ"), bankAccount.allowedGVs.contains("HKSAL"), bankAccount.allowedGVs.contains("HKCCS")) + val result = modelCreator.createBankAccount(customer, bankAccount.number, + if (bankAccount.name2.isNullOrBlank() == false) bankAccount.name + " " + bankAccount.name2 else bankAccount.name) + + result.iban = iban + result.subAccountNumber = bankAccount.subnumber + result.customerId = bankAccount.customerid + + result.currency = bankAccount.curr + result.type = mapBankAccountType(bankAccount) + result.accountLimit = bankAccount.limit?.value?.let { mapValue(it).toString() } + + result.supportsRetrievingBalance = bankAccount.allowedGVs.contains("HKSAL") + result.supportsRetrievingAccountTransactions = bankAccount.allowedGVs.contains("HKKAZ") + result.supportsRetrievingBalance = bankAccount.allowedGVs.contains("HKCCS") + + result } }