Extracted interfaces for UI model classes Customer, BankAccount and AccountTransaction. So entities can implement these interfaces directly, there's no need for mapping anymore

This commit is contained in:
dankito 2020-09-11 12:25:05 +02:00
parent ddf2336ed5
commit 07941380ec
116 changed files with 1631 additions and 868 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,21 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<rect width="60" height="60" fill="white"/>
<rect width="60" height="60" fill="white"/>
<circle cx="29.9999" cy="30" r="25.9091" fill="black" fill-opacity="0.09"/>
<circle cx="29.9999" cy="30" r="25.6591" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<circle cx="30.0001" cy="30" r="16.3636" fill="black" fill-opacity="0.09"/>
<circle cx="30.0001" cy="30" r="16.1136" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.25 0H29.75V29.75H0V30.25H29.75V60H30.25V30.25H60V29.75H30.25V0Z" fill="#6277A1" fill-opacity="0.1"/>
<path d="M0 0L60 0L60 60L0 60L0 0Z" fill="#003832"/>
<path d="M16.2969 18.9254C16.2969 17.9348 17.022 17.0935 18.0018 16.9473L29.5243 15.2286C29.7173 15.1998 29.9135 15.1994 30.1067 15.2275L41.9916 16.953C42.9749 17.0957 43.7043 17.9387 43.7043 18.9322V20.963C43.7043 22.0675 42.8089 22.963 41.7043 22.963L18.2969 22.963C17.1923 22.963 16.2969 22.0675 16.2969 20.963V18.9254Z" fill="#77A83F"/>
<rect x="16.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#079326"/>
<rect x="26.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#68A93D"/>
<rect x="36.2961" y="25.5555" width="7.40741" height="19.2593" rx="2" fill="#CCFFB4"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="60" height="60" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,22 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="60" height="60" fill="white"/>
<rect width="60" height="60" fill="white"/>
<circle cx="29.9999" cy="30" r="25.9091" fill="black" fill-opacity="0.09"/>
<circle cx="29.9999" cy="30" r="25.6591" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<circle cx="30.0001" cy="30" r="16.3636" fill="black" fill-opacity="0.09"/>
<circle cx="30.0001" cy="30" r="16.1136" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.25 0H29.75V29.75H0V30.25H29.75V60H30.25V30.25H60V29.75H30.25V0Z" fill="#6277A1" fill-opacity="0.1"/>
<rect width="60" height="60" fill="white"/>
<rect width="60" height="60" fill="white"/>
<circle cx="29.9999" cy="30" r="25.9091" fill="black" fill-opacity="0.09"/>
<circle cx="29.9999" cy="30" r="25.6591" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<circle cx="30.0001" cy="30" r="16.3636" fill="black" fill-opacity="0.09"/>
<circle cx="30.0001" cy="30" r="16.1136" stroke="#2869BF" stroke-opacity="0.04" stroke-width="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.25 0H29.75V29.75H0V30.25H29.75V60H30.25V30.25H60V29.75H30.25V0Z" fill="#6277A1" fill-opacity="0.1"/>
<rect width="60" height="60" fill="#003832"/>
<path d="M30.0012 22.5524C30.0012 21.8764 30.4961 21.3022 31.1647 21.2025L39.0278 20.0296C39.1595 20.01 39.2934 20.0097 39.4252 20.0289L47.5357 21.2064C48.2067 21.3038 48.7044 21.879 48.7044 22.557V23.9428C48.7044 24.6966 48.0934 25.3077 47.3396 25.3077L31.366 25.3077C30.6123 25.3077 30.0012 24.6966 30.0012 23.9428V22.5524Z" fill="#77A83F"/>
<rect x="30" y="27.0769" width="5.05492" height="13.1428" rx="1.36483" fill="#079326"/>
<rect x="36.824" y="27.0769" width="5.05492" height="13.1428" rx="1.36483" fill="#68A93D"/>
<rect x="43.6482" y="27.0769" width="5.05492" height="13.1428" rx="1.36483" fill="#CCFFB4"/>
<path d="M11.317 39V21.5677H17.5787C19.8216 21.5677 21.5258 21.9788 22.6911 22.8009C23.8644 23.623 24.4511 24.8163 24.4511 26.3807C24.4511 27.2827 24.2436 28.0529 23.8285 28.6915C23.4135 29.33 22.8028 29.8009 21.9967 30.1043C22.9066 30.3437 23.605 30.7907 24.0919 31.4452C24.5788 32.0997 24.8222 32.8979 24.8222 33.8398C24.8222 35.5479 24.2795 36.8329 23.194 37.695C22.1164 38.549 20.5161 38.984 18.3929 39H11.317ZM15.5194 31.5889V35.7674H18.2732C19.0314 35.7674 19.6141 35.5958 20.0212 35.2525C20.4283 34.9013 20.6318 34.4105 20.6318 33.7799C20.6318 32.3272 19.9094 31.5969 18.4647 31.5889H15.5194ZM15.5194 28.8351H17.7104C18.6284 28.8272 19.2829 28.6595 19.674 28.3323C20.0651 28.005 20.2606 27.5221 20.2606 26.8836C20.2606 26.1493 20.0491 25.6225 19.6261 25.3032C19.203 24.9759 18.5206 24.8123 17.5787 24.8123H15.5194V28.8351Z" fill="#77A83F"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -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

View File

@ -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

88
docs/res/icons/new.pdf Normal file
View File

@ -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

View File

@ -14,9 +14,7 @@ import net.dankito.banking.LuceneConfig.Companion.OtherPartyAccountIdFieldName
import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyBankCodeFieldName
import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName import net.dankito.banking.LuceneConfig.Companion.OtherPartyNameFieldName
import net.dankito.banking.LuceneConfig.Companion.UsageFieldName import net.dankito.banking.LuceneConfig.Companion.UsageFieldName
import net.dankito.banking.ui.model.Customer import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.util.ISerializer import net.dankito.banking.util.ISerializer
import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.utils.lucene.index.DocumentsWriter import net.dankito.utils.lucene.index.DocumentsWriter
@ -47,7 +45,7 @@ open class LuceneBankingPersistence(
protected val fields = FieldBuilder() protected val fields = FieldBuilder()
override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) { override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
val writer = getWriter() val writer = getWriter()
transactions.forEach { transaction -> transactions.forEach { transaction ->
@ -60,7 +58,7 @@ open class LuceneBankingPersistence(
writer.flushChangesToDisk() writer.flushChangesToDisk()
} }
protected open fun createFieldsForAccountTransaction(bankAccount: BankAccount, transaction: AccountTransaction): List<IndexableField?> { protected open fun createFieldsForAccountTransaction(bankAccount: TypedBankAccount, transaction: IAccountTransaction): List<IndexableField?> {
return listOf( return listOf(
fields.keywordField(BankAccountIdFieldName, bankAccount.technicalId), fields.keywordField(BankAccountIdFieldName, bankAccount.technicalId),
fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true), fields.nullableFullTextSearchField(OtherPartyNameFieldName, transaction.otherPartyName, true),
@ -79,7 +77,7 @@ open class LuceneBankingPersistence(
} }
override fun deleteAccount(customer: Customer, allCustomers: List<Customer>) { override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
try { try {
deleteAccountTransactions(customer.accounts) deleteAccountTransactions(customer.accounts)
} catch (e: Exception) { } catch (e: Exception) {
@ -89,7 +87,7 @@ open class LuceneBankingPersistence(
super.deleteAccount(customer, allCustomers) super.deleteAccount(customer, allCustomers)
} }
protected open fun deleteAccountTransactions(bankAccounts: List<BankAccount>) { protected open fun deleteAccountTransactions(bankAccounts: List<TypedBankAccount>) {
val writer = getWriter() val writer = getWriter()
val bankAccountIds = bankAccounts.map { it.technicalId } val bankAccountIds = bankAccounts.map { it.technicalId }

View File

@ -5,12 +5,14 @@ import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.BankAccount
import net.dankito.utils.io.FileUtils 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.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mockito.mock import org.mockito.Mockito.mock
import java.io.File
import java.math.BigDecimal import java.math.BigDecimal
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -211,7 +213,7 @@ class LuceneRemitteeSearcherTest {
otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(), otherPartyName: String = randomString(), otherPartyBankCode: String = randomString(),
otherPartyAccountId: String = randomString(), usage: String = randomString()): AccountTransaction { 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 { private fun randomString(): String {

View File

@ -17,10 +17,6 @@ compileTestKotlin {
dependencies { dependencies {
implementation project(':BankingUiCommon') implementation project(':BankingUiCommon')
implementation("org.mapstruct:mapstruct:$mapStructVersion")
kapt("org.mapstruct:mapstruct-processor:$mapStructVersion")
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"

View File

@ -1,13 +1,9 @@
package net.dankito.banking.persistence package net.dankito.banking.persistence
import net.dankito.banking.persistence.mapper.CustomerConverter
import net.dankito.banking.persistence.model.CustomerEntity import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.File 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 net.dankito.banking.util.ISerializer
import org.mapstruct.factory.Mappers
import java.io.FileOutputStream import java.io.FileOutputStream
import java.net.URL import java.net.URL
@ -17,39 +13,32 @@ open class BankingPersistenceJson(
protected val serializer: ISerializer protected val serializer: ISerializer
) : IBankingPersistence { ) : IBankingPersistence {
protected val mapper = Mappers.getMapper(CustomerConverter::class.java)
init { init {
jsonFile.absoluteFile.parentFile.mkdirs() jsonFile.absoluteFile.parentFile.mkdirs()
} }
override fun saveOrUpdateAccount(customer: Customer, allCustomers: List<Customer>) { override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
saveAllCustomers(allCustomers) saveAllCustomers(allCustomers)
} }
override fun deleteAccount(customer: Customer, allCustomers: List<Customer>) { override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
saveAllCustomers(allCustomers) saveAllCustomers(allCustomers)
} }
override fun readPersistedAccounts(): List<Customer> { override fun readPersistedAccounts(): List<TypedCustomer> {
val deserializedCustomers = serializer.deserializeListOr(jsonFile, CustomerEntity::class) return serializer.deserializeListOr(jsonFile, CustomerEntity::class).map { it as TypedCustomer }
return mapper.mapCustomerEntities(deserializedCustomers)
} }
override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) { override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
// done when called saveOrUpdateAccount() // done when called saveOrUpdateAccount()
// TODO: or also call saveAllCustomers()? // TODO: or also call saveAllCustomers()?
} }
protected open fun saveAllCustomers(allCustomers: List<Customer>) { protected open fun saveAllCustomers(allCustomers: List<TypedCustomer>) {
val mappedCustomers = mapper.mapCustomers(allCustomers) serializer.serializeObject(allCustomers, jsonFile)
serializer.serializeObject(mappedCustomers, jsonFile)
} }

View File

@ -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<Customer>, @Context context: CycleAvoidingMappingContext): List<CustomerEntity>
open fun mapCustomers(customers: List<Customer>): List<CustomerEntity> {
// 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<CustomerEntity>, @Context context: CycleAvoidingMappingContext): List<Customer>
open fun mapCustomerEntities(customers: List<CustomerEntity>): List<Customer> {
// 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<BankAccount>, @Context context: CycleAvoidingMappingContext): List<BankAccountEntity>
abstract fun mapBankAccountEntities(accounts: List<BankAccountEntity>, @Context context: CycleAvoidingMappingContext): List<BankAccount>
@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<AccountTransaction>, @Context context: CycleAvoidingMappingContext): List<AccountTransactionEntity>
abstract fun mapTransactionEntities(transactions: List<AccountTransactionEntity>, @Context context: CycleAvoidingMappingContext): List<AccountTransaction>
}

View File

@ -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<Any, Any> = IdentityHashMap()
/**
* Gets an instance out of this context if it is already mapped.
*/
@BeforeMapping
open fun <T> getMappedInstance(source: Any, @TargetType targetType: Class<T>): 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
}
}

View File

@ -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<Customer>): List<CustomerEntity> {
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<CustomerEntity>): List<Customer> {
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<BankAccount>, customer: CustomerEntity): List<BankAccountEntity> {
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<BankAccountEntity>, customer: Customer): List<BankAccount> {
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<AccountTransaction>, account: BankAccountEntity): List<AccountTransactionEntity> {
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<AccountTransactionEntity>, account: BankAccount): List<AccountTransaction> {
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
}
}

View File

@ -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)
}
}

View File

@ -2,57 +2,63 @@ package net.dankito.banking.persistence.model
import com.fasterxml.jackson.annotation.JsonIdentityInfo import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators import com.fasterxml.jackson.annotation.ObjectIdGenerators
import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.Date
import net.dankito.utils.multiplatform.UUID 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 // had to define all properties as 'var' 'cause MapStruct cannot handle vals
open class AccountTransactionEntity( open class AccountTransactionEntity(
open var bankAccount: BankAccountEntity, override var bankAccount: BankAccountEntity,
open var amount: BigDecimal, override var amount: BigDecimal,
open var currency: String, override var currency: String,
open var unparsedUsage: String, override var unparsedUsage: String,
open var bookingDate: Date, override var bookingDate: Date,
open var otherPartyName: String?, override var otherPartyName: String?,
open var otherPartyBankCode: String?, override var otherPartyBankCode: String?,
open var otherPartyAccountId: String?, override var otherPartyAccountId: String?,
open var bookingText: String?, override var bookingText: String?,
open var valueDate: Date, override var valueDate: Date,
open var statementNumber: Int, override var statementNumber: Int,
open var sequenceNumber: Int?, override var sequenceNumber: Int?,
open var openingBalance: BigDecimal?, override var openingBalance: BigDecimal?,
open var closingBalance: BigDecimal?, override var closingBalance: BigDecimal?,
open var endToEndReference: String?, override var endToEndReference: String?,
open var customerReference: String?, override var customerReference: String?,
open var mandateReference: String?, override var mandateReference: String?,
open var creditorIdentifier: String?, override var creditorIdentifier: String?,
open var originatorsIdentificationCode: String?, override var originatorsIdentificationCode: String?,
open var compensationAmount: String?, override var compensationAmount: String?,
open var originalAmount: String?, override var originalAmount: String?,
open var sepaUsage: String?, override var sepaUsage: String?,
open var deviantOriginator: String?, override var deviantOriginator: String?,
open var deviantRecipient: String?, override var deviantRecipient: String?,
open var usageWithNoSpecialType: String?, override var usageWithNoSpecialType: String?,
open var primaNotaNumber: String?, override var primaNotaNumber: String?,
open var textKeySupplement: String?, override var textKeySupplement: String?,
open var currencyType: String?, override var currencyType: String?,
open var bookingKey: String, override var bookingKey: String,
open var referenceForTheAccountOwner: String, override var referenceForTheAccountOwner: String,
open var referenceOfTheAccountServicingInstitution: String?, override var referenceOfTheAccountServicingInstitution: String?,
open var supplementaryDetails: String?, override var supplementaryDetails: String?,
open var transactionReferenceNumber: String, override var transactionReferenceNumber: String,
open var relatedReferenceNumber: String?, override var relatedReferenceNumber: String?,
var id: String = UUID.random().toString() override var technicalId: String = UUID.random()
) { ) : IAccountTransaction {
// for object deserializers // for object deserializers
internal constructor() : this(BankAccountEntity(), BigDecimal.Zero, "", "", Date(), null, null, null, null, Date(), 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, -1, null, null, 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)
} }

View File

@ -2,37 +2,39 @@ package net.dankito.banking.persistence.model
import com.fasterxml.jackson.annotation.JsonIdentityInfo import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators 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.BigDecimal
import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.Date
import net.dankito.utils.multiplatform.UUID 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) // 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 class BankAccountEntity(
open var customer: CustomerEntity, override var customer: CustomerEntity,
open var identifier: String, override var identifier: String,
open var accountHolderName: String, override var accountHolderName: String,
open var iban: String?, override var iban: String?,
open var subAccountNumber: String?, override var subAccountNumber: String?,
open var customerId: String, override var customerId: String,
open var balance: BigDecimal = BigDecimal.Zero, override var balance: BigDecimal = BigDecimal.Zero,
open var currency: String = "EUR", override var currency: String = "EUR",
open var type: BankAccountType = BankAccountType.Girokonto, override var type: BankAccountType = BankAccountType.Girokonto,
open var productName: String? = null, override var productName: String? = null,
open var accountLimit: String? = null, override var accountLimit: String? = null,
open var lastRetrievedTransactionsTimestamp: Date? = null, override var lastRetrievedTransactionsTimestamp: Date? = null,
open var supportsRetrievingAccountTransactions: Boolean = false, override var supportsRetrievingAccountTransactions: Boolean = false,
open var supportsRetrievingBalance: Boolean = false, override var supportsRetrievingBalance: Boolean = false,
open var supportsTransferringMoney: Boolean = false, override var supportsTransferringMoney: Boolean = false,
open var supportsInstantPaymentMoneyTransfer: Boolean = false, override var supportsInstantPaymentMoneyTransfer: Boolean = false,
open var bookedTransactions: List<AccountTransactionEntity> = listOf(), override var bookedTransactions: List<AccountTransactionEntity> = listOf(),
open var unbookedTransactions: List<Any> = listOf(), override var unbookedTransactions: List<Any> = listOf(),
open var id: String = UUID.random().toString(), override var technicalId: String = UUID.random(),
open var userSetDisplayName: String? = null override var userSetDisplayName: String? = null,
override var haveAllTransactionsBeenFetched: Boolean = false,
override var displayIndex: Int = 0
) { ) : IBankAccount<AccountTransactionEntity> {
internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers internal constructor() : this(CustomerEntity(), "", "", null, null, "") // for object deserializers

View File

@ -1,30 +1,32 @@
package net.dankito.banking.persistence.model package net.dankito.banking.persistence.model
import com.fasterxml.jackson.annotation.* 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.TanMedium
import net.dankito.banking.ui.model.tan.TanProcedure import net.dankito.banking.ui.model.tan.TanProcedure
import java.util.* 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) // 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( open class CustomerEntity(
var bankCode: String, override var bankCode: String,
var customerId: String, override var customerId: String,
var password: String, override var password: String,
var finTsServerAddress: String, override var finTsServerAddress: String,
var bankName: String, override var bankName: String,
var bic: String, override var bic: String,
var customerName: String, override var customerName: String,
var userId: String = customerId, override var userId: String = customerId,
var iconUrl: String? = null, override var iconUrl: String? = null,
var accounts: List<BankAccountEntity> = listOf(), override var accounts: List<BankAccountEntity> = listOf(),
var supportedTanProcedures: List<TanProcedure> = listOf(), override var supportedTanProcedures: List<TanProcedure> = listOf(),
var selectedTanProcedure: TanProcedure? = null, override var selectedTanProcedure: TanProcedure? = null,
var tanMedia: List<TanMedium> = listOf(), override var tanMedia: List<TanMedium> = listOf(),
var id: String = UUID.randomUUID().toString(), override var technicalId: String = UUID.randomUUID().toString(),
var userSetDisplayName: String? = null override var userSetDisplayName: String? = null,
) { override var displayIndex: Int = 0
) : ICustomer<BankAccountEntity, AccountTransactionEntity> {
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers internal constructor() : this("", "", "", "", "", "", "") // for object deserializers

View File

@ -1,13 +1,9 @@
package net.dankito.banking.persistence 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.AccountTransactionEntity
import net.dankito.banking.persistence.model.BankAccountEntity import net.dankito.banking.persistence.model.BankAccountEntity
import net.dankito.banking.persistence.model.CustomerEntity import net.dankito.banking.persistence.model.CustomerEntity
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.Customer
import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.utils.multiplatform.Date 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.assertj.core.api.Assertions.assertThat
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import org.mapstruct.factory.Mappers
import kotlin.random.Random import kotlin.random.Random
@ -72,7 +67,7 @@ class BankingPersistenceJsonTest {
// when // when
underTest.saveOrUpdateAccount(customers.first(), customers) underTest.saveOrUpdateAccount(customers.first() as TypedCustomer, customers.map { it as TypedCustomer })
// then // then
@ -89,13 +84,13 @@ class BankingPersistenceJsonTest {
// when // when
underTest.saveOrUpdateAccount(customer, listOf(customer)) underTest.saveOrUpdateAccount(customer as TypedCustomer, listOf(customer).map { it as TypedCustomer })
// then // then
val result = serializer.deserializeListOr(file, CustomerEntity::class) val result = serializer.deserializeListOr(file, CustomerEntity::class)
assertCustomersEqual(result, listOf(customer)) assertCustomersEqual(result, listOf(customer) as List<CustomerEntity>)
} }
@ -107,9 +102,8 @@ class BankingPersistenceJsonTest {
createCustomer(2), createCustomer(2),
createCustomer(3) createCustomer(3)
) )
val serializableCustomers = Mappers.getMapper(CustomerConverter::class.java).mapCustomers(customers, CycleAvoidingMappingContext())
serializer.serializeObject(serializableCustomers, file) serializer.serializeObject(customers, file)
// when // when
@ -117,19 +111,19 @@ class BankingPersistenceJsonTest {
// then // then
assertCustomersEqual(serializableCustomers, result) assertCustomersEqual(customers, result as List<CustomerEntity>)
} }
private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): Customer { private fun createCustomer(countBankAccounts: Int = 0, customerId: String = CustomerId): CustomerEntity {
val result = Customer(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl) val result = CustomerEntity(BankCode, customerId, Password, FinTsServerAddress, BankName, Bic, CustomerName, UserId, IconUrl)
result.accounts = createBankAccounts(countBankAccounts, result) result.accounts = createBankAccounts(countBankAccounts, result)
return result return result
} }
private fun createBankAccounts(count: Int, customer: Customer): List<BankAccount> { private fun createBankAccounts(count: Int, customer: CustomerEntity): List<BankAccountEntity> {
val random = Random(System.nanoTime()) val random = Random(System.nanoTime())
return IntRange(1, count).map { accountIndex -> return IntRange(1, count).map { accountIndex ->
@ -137,8 +131,8 @@ class BankingPersistenceJsonTest {
} }
} }
private fun createBankAccount(productName: String, customer: Customer, countTransactions: Int = 0): BankAccount { private fun createBankAccount(productName: String, customer: CustomerEntity, countTransactions: Int = 0): BankAccountEntity {
val result = BankAccount(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null, val result = BankAccountEntity(customer, customer.customerId, "AccountHolder", "DE00" + customer.bankCode + customer.customerId, null,
customer.customerId, BigDecimal(84.25), productName = productName) customer.customerId, BigDecimal(84.25), productName = productName)
result.bookedTransactions = createAccountTransactions(countTransactions, result) result.bookedTransactions = createAccountTransactions(countTransactions, result)
@ -146,14 +140,14 @@ class BankingPersistenceJsonTest {
return result return result
} }
private fun createAccountTransactions(countTransactions: Int, account: BankAccount): List<AccountTransaction> { private fun createAccountTransactions(countTransactions: Int, account: BankAccountEntity): List<AccountTransactionEntity> {
return IntRange(1, countTransactions).map { transactionIndex -> return IntRange(1, countTransactions).map { transactionIndex ->
createAccountTransaction(transactionIndex, account) createAccountTransaction(transactionIndex, account)
} }
} }
private fun createAccountTransaction(transactionIndex: Int, account: BankAccount): AccountTransaction { private fun createAccountTransaction(transactionIndex: Int, account: BankAccountEntity): AccountTransactionEntity {
return AccountTransaction(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null) return AccountTransactionEntity(account, "OtherParty_$transactionIndex", "Usage_$transactionIndex", BigDecimal(transactionIndex.toDouble()), createDate(), null)
} }
private fun createDate(): Date { private fun createDate(): Date {
@ -161,11 +155,11 @@ class BankingPersistenceJsonTest {
} }
private fun assertCustomersEqual(deserializedCustomers: List<CustomerEntity>, customers: List<Customer>) { private fun assertCustomersEqual(deserializedCustomers: List<CustomerEntity>, customers: List<CustomerEntity>) {
assertThat(deserializedCustomers.size).isEqualTo(customers.size) assertThat(deserializedCustomers.size).isEqualTo(customers.size)
deserializedCustomers.forEach { deserializedCustomer -> deserializedCustomers.forEach { deserializedCustomer ->
val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.id } val customer = customers.firstOrNull { it.technicalId == deserializedCustomer.technicalId }
if (customer == null) { if (customer == null) {
Assert.fail("Could not find matching customer for deserialized customer $deserializedCustomer. customers = $customers") 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.bankCode).isEqualTo(customer.bankCode)
assertThat(deserializedCustomer.customerId).isEqualTo(customer.customerId) assertThat(deserializedCustomer.customerId).isEqualTo(customer.customerId)
assertThat(deserializedCustomer.password).isEqualTo(customer.password) assertThat(deserializedCustomer.password).isEqualTo(customer.password)
@ -191,11 +185,11 @@ class BankingPersistenceJsonTest {
assertBankAccountsEqual(deserializedCustomer.accounts, customer.accounts) assertBankAccountsEqual(deserializedCustomer.accounts, customer.accounts)
} }
private fun assertBankAccountsEqual(deserializedAccounts: List<BankAccountEntity>, accounts: List<BankAccount>) { private fun assertBankAccountsEqual(deserializedAccounts: List<BankAccountEntity>, accounts: List<BankAccountEntity>) {
assertThat(deserializedAccounts.size).isEqualTo(accounts.size) assertThat(deserializedAccounts.size).isEqualTo(accounts.size)
deserializedAccounts.forEach { deserializedAccount -> deserializedAccounts.forEach { deserializedAccount ->
val account = accounts.firstOrNull { it.technicalId == deserializedAccount.id } val account = accounts.firstOrNull { it.technicalId == deserializedAccount.technicalId }
if (account == null) { if (account == null) {
Assert.fail("Could not find matching account for deserialized account $deserializedAccount. accounts = $accounts") 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 // 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.identifier).isEqualTo(account.identifier)
assertThat(deserializedAccount.iban).isEqualTo(account.iban) assertThat(deserializedAccount.iban).isEqualTo(account.iban)
@ -219,11 +213,11 @@ class BankingPersistenceJsonTest {
assertAccountTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions) assertAccountTransactionsEqual(deserializedAccount.bookedTransactions, account.bookedTransactions)
} }
private fun assertAccountTransactionsEqual(deserializedTransactions: List<AccountTransactionEntity>, transactions: List<AccountTransaction>) { private fun assertAccountTransactionsEqual(deserializedTransactions: List<AccountTransactionEntity>, transactions: List<AccountTransactionEntity>) {
assertThat(deserializedTransactions.size).isEqualTo(transactions.size) assertThat(deserializedTransactions.size).isEqualTo(transactions.size)
deserializedTransactions.forEach { deserializedTransaction -> deserializedTransactions.forEach { deserializedTransaction ->
val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.id } val transaction = transactions.firstOrNull { it.technicalId == deserializedTransaction.technicalId }
if (transaction == null) { if (transaction == null) {
Assert.fail("Could not find matching transaction for deserialized transaction $deserializedTransaction. transactions = $transactions") 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 // 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.otherPartyName).isEqualTo(transaction.otherPartyName)
assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage) assertThat(deserializedTransaction.unparsedUsage).isEqualTo(transaction.unparsedUsage)

View File

@ -3,7 +3,7 @@ package net.dankito.banking.ui.android
import net.dankito.banking.ui.android.util.CurrentActivityTracker import net.dankito.banking.ui.android.util.CurrentActivityTracker
import net.dankito.banking.ui.IRouter import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.android.dialogs.* 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.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult 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 -> activityTracker.currentOrNextActivity { activity ->
activity.runOnUiThread { activity.runOnUiThread {
EnterTanDialog().show(customer, tanChallenge, activity, false) { result -> EnterTanDialog().show(customer, tanChallenge, activity, false) { result ->

View File

@ -6,7 +6,7 @@ import android.view.View
import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.adapter.viewholder.AccountTransactionViewHolder import net.dankito.banking.ui.android.adapter.viewholder.AccountTransactionViewHolder
import net.dankito.banking.ui.android.extensions.showAmount 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.banking.ui.presenter.BankingPresenter
import net.dankito.utils.android.extensions.asActivity import net.dankito.utils.android.extensions.asActivity
import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter import net.dankito.utils.android.ui.adapter.ListRecyclerAdapter
@ -14,14 +14,14 @@ import java.text.DateFormat
open class AccountTransactionAdapter(protected val presenter: BankingPresenter) open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
: ListRecyclerAdapter<AccountTransaction, AccountTransactionViewHolder>() { : ListRecyclerAdapter<IAccountTransaction, AccountTransactionViewHolder>() {
companion object { companion object {
val ValueDateFormat = DateFormat.getDateInstance(DateFormat.SHORT) val ValueDateFormat = DateFormat.getDateInstance(DateFormat.SHORT)
} }
var selectedTransaction: AccountTransaction? = null var selectedTransaction: IAccountTransaction? = null
override fun getListItemLayoutId() = R.layout.list_item_account_transaction override fun getListItemLayoutId() = R.layout.list_item_account_transaction
@ -34,7 +34,7 @@ open class AccountTransactionAdapter(protected val presenter: BankingPresenter)
return viewHolder return viewHolder
} }
override fun bindItemToView(viewHolder: AccountTransactionViewHolder, item: AccountTransaction) { override fun bindItemToView(viewHolder: AccountTransactionViewHolder, item: IAccountTransaction) {
viewHolder.txtvwDate.text = ValueDateFormat.format(item.valueDate) viewHolder.txtvwDate.text = ValueDateFormat.format(item.valueDate)
val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText val label = if (item.showOtherPartyName) item.otherPartyName else item.bookingText

View File

@ -8,11 +8,11 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import kotlinx.android.synthetic.main.list_item_bank_account.view.* import kotlinx.android.synthetic.main.list_item_bank_account.view.*
import net.dankito.banking.ui.android.R 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 import net.dankito.utils.android.ui.adapter.ListAdapter
open class BankAccountsAdapter(bankAccounts: List<BankAccount>) : ListAdapter<BankAccount>(bankAccounts) { open class BankAccountsAdapter(bankAccounts: List<TypedBankAccount>) : ListAdapter<TypedBankAccount>(bankAccounts) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? { override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
@ -30,7 +30,7 @@ open class BankAccountsAdapter(bankAccounts: List<BankAccount>) : ListAdapter<Ba
return view return view
} }
protected open fun setIcon(bankAccount: BankAccount, imgBankIcon: ImageView) { protected open fun setIcon(bankAccount: TypedBankAccount, imgBankIcon: ImageView) {
try { try {
val iconUrl = bankAccount.customer.iconUrl val iconUrl = bankAccount.customer.iconUrl
imgBankIcon.visibility = if (iconUrl == null) View.GONE else View.VISIBLE imgBankIcon.visibility = if (iconUrl == null) View.GONE else View.VISIBLE

View File

@ -17,12 +17,11 @@ import net.dankito.banking.ui.IRouter
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.bankfinder.IBankFinder import net.dankito.banking.bankfinder.IBankFinder
import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.banking.persistence.mapper.EntitiesModelCreator
import net.dankito.banking.ui.model.mapper.IModelCreator
import net.dankito.utils.multiplatform.toFile import net.dankito.utils.multiplatform.toFile
import net.dankito.banking.util.* import net.dankito.banking.util.*
import net.dankito.banking.util.extraction.IInvoiceDataExtractor import net.dankito.banking.util.extraction.*
import net.dankito.banking.util.extraction.ITextExtractorRegistry
import net.dankito.banking.util.extraction.JavaInvoiceDataExtractor
import net.dankito.banking.util.extraction.JavaTextExtractorRegistry
import net.dankito.text.extraction.TextExtractorRegistry import net.dankito.text.extraction.TextExtractorRegistry
import net.dankito.text.extraction.pdf.PdfBoxAndroidPdfTextExtractor import net.dankito.text.extraction.pdf.PdfBoxAndroidPdfTextExtractor
import net.dankito.text.extraction.pdf.iText2PdfTextExtractor import net.dankito.text.extraction.pdf.iText2PdfTextExtractor
@ -91,9 +90,9 @@ class BankingModule(private val applicationContext: Context) {
@Named(DataFolderKey) dataFolder: File, @Named(DataFolderKey) dataFolder: File,
persister: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder, persister: IBankingPersistence, remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder,
textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor, textExtractorRegistry: ITextExtractorRegistry, router: IRouter, invoiceDataExtractor: IInvoiceDataExtractor,
serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter { modelCreator: IModelCreator, serializer: ISerializer, asyncRunner: IAsyncRunner) : BankingPresenter {
return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, return BankingPresenter(bankingClientCreator, bankFinder, dataFolder, persister, router, modelCreator,
router, remitteeSearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner) remitteeSearcher, bankIconFinder, textExtractorRegistry, invoiceDataExtractor, serializer, asyncRunner)
} }
@Provides @Provides
@ -110,8 +109,8 @@ class BankingModule(private val applicationContext: Context) {
@Provides @Provides
@Singleton @Singleton
fun provideBankingClientCreator(serializer: ISerializer) : IBankingClientCreator { fun provideBankingClientCreator(modelCreator: IModelCreator, serializer: ISerializer) : IBankingClientCreator {
return fints4kBankingClientCreator(serializer) return fints4kBankingClientCreator(modelCreator, serializer)
} }
@Provides @Provides
@ -151,7 +150,7 @@ class BankingModule(private val applicationContext: Context) {
@Provides @Provides
@Singleton @Singleton
fun provideInvoiceDataExtractor() : IInvoiceDataExtractor { fun provideInvoiceDataExtractor() : IInvoiceDataExtractor {
return JavaInvoiceDataExtractor() return NoOpInvoiceDataExtractor()
} }
@ -167,6 +166,12 @@ class BankingModule(private val applicationContext: Context) {
return JacksonJsonSerializer() return JacksonJsonSerializer()
} }
@Provides
@Singleton
fun provideModelCreator() : IModelCreator {
return EntitiesModelCreator()
}
@Provides @Provides
@Singleton @Singleton
fun provideAsyncRunner() : IAsyncRunner { fun provideAsyncRunner() : IAsyncRunner {

View File

@ -21,7 +21,7 @@ import net.dankito.banking.ui.android.adapter.TanMediumAdapter
import net.dankito.banking.ui.android.adapter.TanProceduresAdapter import net.dankito.banking.ui.android.adapter.TanProceduresAdapter
import net.dankito.banking.ui.android.di.BankingComponent import net.dankito.banking.ui.android.di.BankingComponent
import net.dankito.banking.ui.android.listener.ListItemSelectedListener import net.dankito.banking.ui.android.listener.ListItemSelectedListener
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.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.* import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
@ -41,7 +41,7 @@ open class EnterTanDialog : DialogFragment() {
} }
protected lateinit var customer: Customer protected lateinit var customer: TypedCustomer
protected lateinit var tanChallenge: TanChallenge protected lateinit var tanChallenge: TanChallenge
@ -59,7 +59,7 @@ open class EnterTanDialog : DialogFragment() {
} }
open fun show(customer: Customer, tanChallenge: TanChallenge, activity: AppCompatActivity, open fun show(customer: TypedCustomer, tanChallenge: TanChallenge, activity: AppCompatActivity,
fullscreen: Boolean = false, tanEnteredCallback: (EnterTanResult) -> Unit) { fullscreen: Boolean = false, tanEnteredCallback: (EnterTanResult) -> Unit) {
this.customer = customer this.customer = customer

View File

@ -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.StandardAutocompleteCallback
import net.dankito.banking.ui.android.util.StandardTextWatcher import net.dankito.banking.ui.android.util.StandardTextWatcher
import net.dankito.banking.search.Remittee 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.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.presenter.BankingPresenter 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 protected var preselectedValues: TransferMoneyData? = null

View File

@ -20,7 +20,7 @@ import com.mikepenz.materialdrawer.util.removeItemByPosition
import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView import com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
import net.dankito.banking.ui.android.R import net.dankito.banking.ui.android.R
import net.dankito.banking.ui.android.extensions.withIcon 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 net.dankito.banking.ui.presenter.BankingPresenter
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -139,7 +139,7 @@ open class DrawerView(
}.flatten() }.flatten()
} }
private fun createAccountDrawerItem(customer: Customer): IDrawerItem<*> { private fun createAccountDrawerItem(customer: TypedCustomer): IDrawerItem<*> {
val accountItem = AccountDrawerItem() val accountItem = AccountDrawerItem()
.withName(customer.displayName) .withName(customer.displayName)
@ -160,7 +160,7 @@ open class DrawerView(
return accountItem return accountItem
} }
private fun createBankAccountsDrawerItems(customer: Customer): List<IDrawerItem<*>> { private fun createBankAccountsDrawerItems(customer: TypedCustomer): List<IDrawerItem<*>> {
return customer.accounts.map { bankAccount -> return customer.accounts.map { bankAccount ->
SecondaryDrawerItem() SecondaryDrawerItem()
.withName(bankAccount.displayName) .withName(bankAccount.displayName)
@ -176,13 +176,13 @@ open class DrawerView(
return false return false
} }
private fun closeDrawerAndEditAccount(customer: Customer) { private fun closeDrawerAndEditAccount(customer: TypedCustomer) {
closeDrawer() closeDrawer()
editAccount(customer) editAccount(customer)
} }
private fun editAccount(customer: Customer) { private fun editAccount(customer: TypedCustomer) {
// TODO: implement editing account (e.g. displayed name etc.) // TODO: implement editing account (e.g. displayed name etc.)
AlertDialog.Builder(activity) AlertDialog.Builder(activity)

View File

@ -11,6 +11,7 @@ import net.dankito.banking.ui.presenter.BankingPresenter
import net.dankito.banking.util.BankIconFinder import net.dankito.banking.util.BankIconFinder
import net.dankito.banking.bankfinder.LuceneBankFinder import net.dankito.banking.bankfinder.LuceneBankFinder
import net.dankito.banking.persistence.LuceneBankingPersistence import net.dankito.banking.persistence.LuceneBankingPersistence
import net.dankito.banking.persistence.mapper.EntitiesModelCreator
import net.dankito.banking.search.LuceneRemitteeSearcher import net.dankito.banking.search.LuceneRemitteeSearcher
import net.dankito.banking.util.JacksonJsonSerializer import net.dankito.banking.util.JacksonJsonSerializer
import net.dankito.banking.util.extraction.JavaTextExtractorRegistry 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 indexFolder = ensureFolderExists(dataFolder, "index")
private val modelCreator = EntitiesModelCreator()
private val serializer = JacksonJsonSerializer() private val serializer = JacksonJsonSerializer()
private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German))) private val tesseractTextExtractor = Tesseract4CommandlineImageTextExtractor(TesseractConfig(listOf(OcrLanguage.English, OcrLanguage.German)))
@ -42,11 +45,11 @@ class MainWindow : View(messages["application.title"]) {
tesseractTextExtractor, TikaTextExtractor() tesseractTextExtractor, TikaTextExtractor()
))) )))
private val presenter = BankingPresenter(fints4kBankingClientCreator(serializer), private val presenter = BankingPresenter(fints4kBankingClientCreator(modelCreator, serializer),
LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), LuceneBankFinder(indexFolder), dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder),
RouterJavaFx(), LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry) RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder), BankIconFinder(), textExtractorRegistry)
// private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder), // private val presenter = BankingPresenter(hbci4jBankingClientCreator(), LuceneBankFinder(indexFolder),
// dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), LuceneRemitteeSearcher(indexFolder), // dataFolder, LuceneBankingPersistence(indexFolder, databaseFolder), RouterJavaFx(), modelCreator, LuceneRemitteeSearcher(indexFolder),
// BankIconFinder(), textExtractorRegistry) // BankIconFinder(), textExtractorRegistry)

View File

@ -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.AddAccountDialog
import net.dankito.banking.ui.javafx.dialogs.cashtransfer.TransferMoneyDialog import net.dankito.banking.ui.javafx.dialogs.cashtransfer.TransferMoneyDialog
import net.dankito.banking.ui.javafx.dialogs.tan.EnterTanDialog 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.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.tan.EnterTanResult
@ -22,7 +22,7 @@ open class RouterJavaFx : IRouter {
AddAccountDialog(presenter).show(messages["add.account.dialog.title"]) 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 { FX.runAndWait {
EnterTanDialog(customer, tanChallenge, presenter) { result -> EnterTanDialog(customer, tanChallenge, presenter) { result ->
callback(result) callback(result)

View File

@ -141,7 +141,7 @@ open class AccountTransactionsControlView(
protected open fun updateAccountTransactions(processingIndicatorButton: ProcessingIndicatorButton) { protected open fun updateAccountTransactions(processingIndicatorButton: ProcessingIndicatorButton) {
// TODO: or only update transactions of selected accounts? // TODO: or only update transactions of selected accounts?
presenter.updateAccountsTransactionsAsync { transactions -> presenter.updateAccountsTransactionsAsync {
runLater { runLater {
processingIndicatorButton.resetIsProcessing() processingIndicatorButton.resetIsProcessing()
} }

View File

@ -11,7 +11,7 @@ import javafx.scene.control.TableView
import javafx.scene.layout.Priority import javafx.scene.layout.Priority
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.util.Callback 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.banking.ui.presenter.BankingPresenter
import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible import net.dankito.utils.javafx.ui.extensions.ensureOnlyUsesSpaceIfVisible
import tornadofx.* import tornadofx.*
@ -21,8 +21,8 @@ import java.text.DateFormat
open class AccountTransactionsTable @JvmOverloads constructor( open class AccountTransactionsTable @JvmOverloads constructor(
protected val presenter: BankingPresenter, protected val presenter: BankingPresenter,
transactions: ObservableList<AccountTransaction> = FXCollections.emptyObservableList<AccountTransaction>() transactions: ObservableList<IAccountTransaction> = FXCollections.emptyObservableList()
) : TableView<AccountTransaction>(transactions) { ) : TableView<IAccountTransaction>(transactions) {
companion object { companion object {
@ -38,7 +38,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
protected open fun initUi() { 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 prefWidth = 115.0
cellFormat { cellFormat {
@ -48,7 +48,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
} }
} }
columns.add(TableColumn<AccountTransaction, AccountTransaction>(messages["account.transactions.table.column.header.usage"]).apply { columns.add(TableColumn<IAccountTransaction, IAccountTransaction>(messages["account.transactions.table.column.header.usage"]).apply {
this.cellFormat { this.cellFormat {
contentDisplay = ContentDisplay.GRAPHIC_ONLY contentDisplay = ContentDisplay.GRAPHIC_ONLY
@ -80,8 +80,8 @@ open class AccountTransactionsTable @JvmOverloads constructor(
} }
} }
cellValueFactory = Callback { object : ObjectBinding<AccountTransaction>() { cellValueFactory = Callback { object : ObjectBinding<IAccountTransaction>() {
override fun computeValue(): AccountTransaction { override fun computeValue(): IAccountTransaction {
return it.value return it.value
} }
@ -90,7 +90,7 @@ open class AccountTransactionsTable @JvmOverloads constructor(
weightedWidth(4.0) weightedWidth(4.0)
}) })
columns.add(TableColumn<AccountTransaction, String>(messages["account.transactions.table.column.header.amount"]).apply { columns.add(TableColumn<IAccountTransaction, String>(messages["account.transactions.table.column.header.amount"]).apply {
prefWidth = 85.0 prefWidth = 85.0
this.cellFormat { this.cellFormat {

View File

@ -8,8 +8,8 @@ import javafx.scene.input.ContextMenuEvent
import javafx.scene.input.MouseButton import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent import javafx.scene.input.MouseEvent
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.IAccountTransaction
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.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.GetTransactionsResponse import net.dankito.banking.ui.model.responses.GetTransactionsResponse
import net.dankito.banking.ui.presenter.BankingPresenter 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 balance = SimpleStringProperty("")
protected val transactionsToDisplay = FXCollections.observableArrayList<AccountTransaction>(listOf()) protected val transactionsToDisplay = FXCollections.observableArrayList<IAccountTransaction>(listOf())
protected var currentMenu: ContextMenu? = null 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) { if (event.button == MouseButton.PRIMARY || event.button == MouseButton.MIDDLE) {
currentMenu?.hide() 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() val contextMenu = ContextMenu()
contextMenu.apply { contextMenu.apply {
@ -105,21 +105,21 @@ open class AccountTransactionsView(private val presenter: BankingPresenter) : Vi
return contextMenu return contextMenu
} }
protected open fun showTransactionDetailsDialog(transaction: AccountTransaction) { protected open fun showTransactionDetailsDialog(transaction: IAccountTransaction) {
// TODO: // TODO:
// presenter.showTransactionDetailsWindow(transaction.item) // presenter.showTransactionDetailsWindow(transaction.item)
} }
protected open fun newTransferToSameRemittee(transaction: AccountTransaction) { protected open fun newTransferToSameRemittee(transaction: IAccountTransaction) {
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(transaction)) presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransactionWithoutAmountAndUsage(transaction))
} }
protected open fun newTransferWithSameData(transaction: AccountTransaction) { protected open fun newTransferWithSameData(transaction: IAccountTransaction) {
presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransaction(transaction)) presenter.showTransferMoneyDialog(TransferMoneyData.fromAccountTransaction(transaction))
} }
protected open fun handleSelectedBankAccountsChanged(selectedBankAccounts: List<BankAccount>) { protected open fun handleSelectedBankAccountsChanged(selectedBankAccounts: List<TypedBankAccount>) {
runLater { runLater {
isAccountSelected.value = selectedBankAccounts.isNotEmpty() isAccountSelected.value = selectedBankAccounts.isNotEmpty()

View File

@ -10,13 +10,13 @@ import javafx.scene.input.KeyCode
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.javafx.model.AccountsAccountTreeItem import net.dankito.banking.ui.javafx.model.AccountsAccountTreeItem
import net.dankito.banking.ui.javafx.model.AccountsRootTreeItem 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 net.dankito.banking.ui.presenter.BankingPresenter
import tornadofx.* import tornadofx.*
import tornadofx.FX.Companion.messages import tornadofx.FX.Companion.messages
open class AccountsTreeView(customers: ObservableList<Customer>, protected val presenter: BankingPresenter) open class AccountsTreeView(customers: ObservableList<TypedCustomer>, protected val presenter: BankingPresenter)
: TreeView<String>(AccountsRootTreeItem(customers)) { : TreeView<String>(AccountsRootTreeItem(customers)) {
protected var currentMenu: ContextMenu? = null protected var currentMenu: ContextMenu? = null

View File

@ -12,7 +12,7 @@ import javafx.scene.image.ImageView
import javafx.scene.layout.Priority import javafx.scene.layout.Priority
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService 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.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.BankingClientResponse import net.dankito.banking.ui.model.responses.BankingClientResponse
import net.dankito.banking.ui.presenter.BankingPresenter 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 bankAccountsSupportingTransferringMoney = FXCollections.observableArrayList(presenter.bankAccounts.filter { it.supportsTransferringMoney })
protected val selectedBankAccount = SimpleObjectProperty<BankAccount>(preselectedValues?.account ?: bankAccountsSupportingTransferringMoney.firstOrNull()) protected val selectedBankAccount = SimpleObjectProperty<TypedBankAccount>(preselectedValues?.account ?: bankAccountsSupportingTransferringMoney.firstOrNull())
protected val showBankAccounts = SimpleBooleanProperty(bankAccountsSupportingTransferringMoney.size > 1) 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 supportsInstantPayment.value = newValue?.supportsInstantPaymentMoneyTransfer ?: false
if (supportsInstantPayment.value == false) { if (supportsInstantPayment.value == false) {
@ -345,7 +345,7 @@ open class TransferMoneyDialog @JvmOverloads constructor(
} }
protected open fun transferMoney() { protected open fun transferMoney() {
remitteeBank.value?.let { remitteeBank -> remitteeBank.value?.let {
val bankAccount = selectedBankAccount.value val bankAccount = selectedBankAccount.value
val data = TransferMoneyData( 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 val currency = bankAccount.currency
if (response.isSuccessful) { if (response.isSuccessful) {

View File

@ -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.tan.controls.ChipTanFlickerCodeView
import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService import net.dankito.banking.ui.javafx.dialogs.JavaFxDialogService
import net.dankito.banking.ui.javafx.dialogs.tan.controls.TanImageView 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.responses.BankingClientResponse
import net.dankito.banking.ui.model.tan.* import net.dankito.banking.ui.model.tan.*
import net.dankito.banking.ui.presenter.BankingPresenter import net.dankito.banking.ui.presenter.BankingPresenter
@ -20,7 +20,7 @@ import tornadofx.*
open class EnterTanDialog( open class EnterTanDialog(
protected val customer: Customer, protected val customer: TypedCustomer,
protected val challenge: TanChallenge, protected val challenge: TanChallenge,
protected val presenter: BankingPresenter, protected val presenter: BankingPresenter,
protected val tanEnteredCallback: (EnterTanResult) -> Unit protected val tanEnteredCallback: (EnterTanResult) -> Unit

View File

@ -2,10 +2,10 @@ package net.dankito.banking.ui.javafx.model
import javafx.scene.Node import javafx.scene.Node
import javafx.scene.image.ImageView 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 { companion object {
private const val IconSize = 16.0 private const val IconSize = 16.0

View File

@ -1,6 +1,6 @@
package net.dankito.banking.ui.javafx.model 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) open class AccountsBankAccountTreeItem(val bankAccount: TypedBankAccount) : AccountsTreeItemBase(bankAccount.displayName)

View File

@ -2,13 +2,13 @@ package net.dankito.banking.ui.javafx.model
import javafx.collections.ListChangeListener import javafx.collections.ListChangeListener
import javafx.collections.ObservableList 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.FX.Companion.messages
import tornadofx.get import tornadofx.get
import tornadofx.runLater import tornadofx.runLater
open class AccountsRootTreeItem(customers: ObservableList<Customer>) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) { open class AccountsRootTreeItem(customers: ObservableList<TypedCustomer>) : AccountsTreeItemBase(messages["accounts.view.all.accounts"]) {
init { init {
setAccounts(customers) setAccounts(customers)
@ -18,7 +18,7 @@ open class AccountsRootTreeItem(customers: ObservableList<Customer>) : AccountsT
}) })
} }
protected open fun setAccounts(customers: List<Customer>) { protected open fun setAccounts(customers: List<TypedCustomer>) {
isExpanded = customers.isNotEmpty() isExpanded = customers.isNotEmpty()
children.setAll(customers.map { AccountsAccountTreeItem(it) }) children.setAll(customers.map { AccountsAccountTreeItem(it) })

View File

@ -1,21 +1,19 @@
package net.dankito.banking.persistence package net.dankito.banking.persistence
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.File 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 { interface IBankingPersistence {
fun saveOrUpdateAccount(customer: Customer, allCustomers: List<Customer>) fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>)
fun deleteAccount(customer: Customer, allCustomers: List<Customer>) fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>)
fun readPersistedAccounts(): List<Customer> fun readPersistedAccounts(): List<TypedCustomer>
fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>)
fun saveUrlToFile(url: String, file: File) fun saveUrlToFile(url: String, file: File)

View File

@ -1,27 +1,25 @@
package net.dankito.banking.persistence package net.dankito.banking.persistence
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.BankAccount
import net.dankito.banking.ui.model.Customer
import net.dankito.utils.multiplatform.File import net.dankito.utils.multiplatform.File
open class NoOpBankingPersistence : IBankingPersistence { open class NoOpBankingPersistence : IBankingPersistence {
override fun saveOrUpdateAccount(customer: Customer, allCustomers: List<Customer>) { override fun saveOrUpdateAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
} }
override fun deleteAccount(customer: Customer, allCustomers: List<Customer>) { override fun deleteAccount(customer: TypedCustomer, allCustomers: List<TypedCustomer>) {
} }
override fun readPersistedAccounts(): List<Customer> { override fun readPersistedAccounts(): List<TypedCustomer> {
return listOf() return listOf()
} }
override fun saveOrUpdateAccountTransactions(bankAccount: BankAccount, transactions: List<AccountTransaction>) { override fun saveOrUpdateAccountTransactions(bankAccount: TypedBankAccount, transactions: List<IAccountTransaction>) {
} }

View File

@ -1,6 +1,6 @@
package net.dankito.banking.ui 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.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.tan.EnterTanResult
import net.dankito.banking.ui.model.tan.TanChallenge import net.dankito.banking.ui.model.tan.TanChallenge
@ -9,7 +9,7 @@ import net.dankito.banking.ui.model.tan.TanGeneratorTanMedium
interface BankingClientCallback { 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. * This method gets called for chipTan TAN generators when the bank asks the customer to synchronize her/his TAN generator.

View File

@ -1,8 +1,6 @@
package net.dankito.banking.ui package net.dankito.banking.ui
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.*
import net.dankito.banking.ui.model.Customer
import net.dankito.banking.ui.model.MessageLogEntry
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.responses.AddAccountResponse import net.dankito.banking.ui.model.responses.AddAccountResponse
@ -18,7 +16,7 @@ interface IBankingClient {
fun addAccountAsync(callback: (AddAccountResponse) -> Unit) fun addAccountAsync(callback: (AddAccountResponse) -> Unit)
fun getTransactionsAsync( fun getTransactionsAsync(
bankAccount: BankAccount, bankAccount: TypedBankAccount,
parameter: GetTransactionsParameter, parameter: GetTransactionsParameter,
callback: (GetTransactionsResponse) -> Unit callback: (GetTransactionsResponse) -> Unit
) )
@ -26,6 +24,6 @@ interface IBankingClient {
fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit) fun transferMoneyAsync(data: TransferMoneyData, callback: (BankingClientResponse) -> Unit)
fun dataChanged(customer: Customer) fun dataChanged(customer: TypedCustomer)
} }

View File

@ -1,14 +1,14 @@
package net.dankito.banking.ui package net.dankito.banking.ui
import net.dankito.utils.multiplatform.File 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 import net.dankito.banking.util.IAsyncRunner
interface IBankingClientCreator { interface IBankingClientCreator {
fun createClient( fun createClient(
customer: Customer, customer: TypedCustomer,
dataFolder: File, dataFolder: File,
asyncRunner: IAsyncRunner, asyncRunner: IAsyncRunner,
callback: BankingClientCallback callback: BankingClientCallback

View File

@ -1,7 +1,6 @@
package net.dankito.banking.ui 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.BankAccount
import net.dankito.banking.ui.model.parameters.TransferMoneyData import net.dankito.banking.ui.model.parameters.TransferMoneyData
import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult import net.dankito.banking.ui.model.tan.EnterTanGeneratorAtcResult
import net.dankito.banking.ui.model.tan.EnterTanResult import net.dankito.banking.ui.model.tan.EnterTanResult
@ -14,7 +13,7 @@ interface IRouter {
fun showAddAccountDialog(presenter: BankingPresenter) 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) fun getAtcFromUserFromNonUiThread(tanMedium: TanGeneratorTanMedium, callback: (EnterTanGeneratorAtcResult) -> Unit)

View File

@ -7,49 +7,44 @@ import net.dankito.utils.multiplatform.DateFormatter
open class AccountTransaction( open class AccountTransaction(
open val bankAccount: BankAccount, override val bankAccount: TypedBankAccount,
open val amount: BigDecimal, override val amount: BigDecimal,
open val currency: String, override val currency: String,
open val unparsedUsage: String, override val unparsedUsage: String,
open val bookingDate: Date, override val bookingDate: Date,
open val otherPartyName: String?, override val otherPartyName: String?,
open val otherPartyBankCode: String?, override val otherPartyBankCode: String?,
open val otherPartyAccountId: String?, override val otherPartyAccountId: String?,
open val bookingText: String?, override val bookingText: String?,
open val valueDate: Date, override val valueDate: Date,
open val statementNumber: Int, override val statementNumber: Int,
open val sequenceNumber: Int?, override val sequenceNumber: Int?,
open val openingBalance: BigDecimal?, override val openingBalance: BigDecimal?,
open val closingBalance: BigDecimal?, override val closingBalance: BigDecimal?,
open val endToEndReference: String?, override val endToEndReference: String?,
open val customerReference: String?, override val customerReference: String?,
open val mandateReference: String?, override val mandateReference: String?,
open val creditorIdentifier: String?, override val creditorIdentifier: String?,
open val originatorsIdentificationCode: String?, override val originatorsIdentificationCode: String?,
open val compensationAmount: String?, override val compensationAmount: String?,
open val originalAmount: String?, override val originalAmount: String?,
open val sepaUsage: String?, override val sepaUsage: String?,
open val deviantOriginator: String?, override val deviantOriginator: String?,
open val deviantRecipient: String?, override val deviantRecipient: String?,
open val usageWithNoSpecialType: String?, override val usageWithNoSpecialType: String?,
open val primaNotaNumber: String?, override val primaNotaNumber: String?,
open val textKeySupplement: String?, override val textKeySupplement: String?,
open val currencyType: String?, override val currencyType: String?,
open val bookingKey: String, override val bookingKey: String,
open val referenceForTheAccountOwner: String, override val referenceForTheAccountOwner: String,
open val referenceOfTheAccountServicingInstitution: String?, override val referenceOfTheAccountServicingInstitution: String?,
open val supplementaryDetails: String?, override val supplementaryDetails: String?,
open val transactionReferenceNumber: String,
open val relatedReferenceNumber: String?
) {
companion object {
val IdDateFormat = DateFormatter("yyyy.MM.dd")
}
override val transactionReferenceNumber: String,
override val relatedReferenceNumber: String?
) : IAccountTransaction {
// for object deserializers // for object deserializers
internal constructor() : this(BankAccount(), null, "", BigDecimal.Zero, Date(), null) 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) 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() override 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 "<uninitialized_bank_acccount> ${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 fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -7,69 +7,47 @@ import kotlin.jvm.JvmOverloads
open class BankAccount @JvmOverloads constructor( open class BankAccount @JvmOverloads constructor(
open val customer: Customer, override val customer: TypedCustomer,
open var identifier: String, override var identifier: String,
open var accountHolderName: String, override var accountHolderName: String,
open var iban: String?, override var iban: String?,
open var subAccountNumber: String?, override var subAccountNumber: String?,
open var customerId: String, override var customerId: String,
open var balance: BigDecimal = BigDecimal.Zero, override var balance: BigDecimal = BigDecimal.Zero,
open var currency: String = "EUR", override var currency: String = "EUR",
open var type: BankAccountType = BankAccountType.Girokonto, override var type: BankAccountType = BankAccountType.Girokonto,
open var productName: String? = null, override var productName: String? = null,
open var accountLimit: String? = null, override var accountLimit: String? = null,
open var lastRetrievedTransactionsTimestamp: Date? = null, override var lastRetrievedTransactionsTimestamp: Date? = null,
open var supportsRetrievingAccountTransactions: Boolean = false, override var supportsRetrievingAccountTransactions: Boolean = false,
open var supportsRetrievingBalance: Boolean = false, override var supportsRetrievingBalance: Boolean = false,
open var supportsTransferringMoney: Boolean = false, override var supportsTransferringMoney: Boolean = false,
open var supportsInstantPaymentMoneyTransfer: Boolean = false, override var supportsInstantPaymentMoneyTransfer: Boolean = false,
open var bookedTransactions: List<AccountTransaction> = listOf(), override var bookedTransactions: List<IAccountTransaction> = listOf(),
open var unbookedTransactions: List<Any> = listOf() override var unbookedTransactions: List<Any> = listOf()
) : OrderedDisplayable { ) : TypedBankAccount {
internal constructor() : this(Customer(), null, "") // for object deserializers internal constructor() : this(Customer(), null, "") // for object deserializers
/* convenience constructors for languages not supporting default values */ /* 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) : 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 var userSetDisplayName: String? = null
override val displayName: String
get() {
return userSetDisplayName ?: productName ?: subAccountNumber ?: identifier
}
override var displayIndex: Int = 0 override var displayIndex: Int = 0
open fun addBookedTransactions(retrievedBookedTransactions: List<AccountTransaction>) {
val uniqueTransactions = this.bookedTransactions.toMutableSet()
uniqueTransactions.addAll(retrievedBookedTransactions)
this.bookedTransactions = uniqueTransactions.toList()
}
open fun addUnbookedTransactions(retrievedUnbookedTransactions: List<Any>) {
val uniqueUnbookedTransactions = this.unbookedTransactions.toMutableSet()
uniqueUnbookedTransactions.addAll(retrievedUnbookedTransactions)
this.unbookedTransactions = uniqueUnbookedTransactions.toList()
}
override fun toString(): String { override fun toString(): String {
return "$accountHolderName ($identifier)" return "$accountHolderName ($identifier)"
} }

View File

@ -1,26 +1,22 @@
package net.dankito.banking.ui.model 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.TanMedium
import net.dankito.banking.ui.model.tan.TanMediumStatus
import net.dankito.banking.ui.model.tan.TanProcedure import net.dankito.banking.ui.model.tan.TanProcedure
import net.dankito.banking.util.sortedByDisplayIndex
import net.dankito.utils.multiplatform.UUID import net.dankito.utils.multiplatform.UUID
open class Customer( open class Customer(
open var bankCode: String, override var bankCode: String,
open var customerId: String, override var customerId: String,
open var password: String, override var password: String,
open var finTsServerAddress: String, override var finTsServerAddress: String,
open var bankName: String, override var bankName: String,
open var bic: String, override var bic: String,
open var customerName: String, override var customerName: String,
open var userId: String = customerId, override var userId: String = customerId,
open var iconUrl: String? = null, override var iconUrl: String? = null,
open var accounts: List<BankAccount> = listOf() override var accounts: List<TypedBankAccount> = listOf()
) : OrderedDisplayable { ) : TypedCustomer {
internal constructor() : this("", "", "", "", "", "", "") // for object deserializers internal constructor() : this("", "", "", "", "", "", "") // for object deserializers
@ -31,40 +27,23 @@ open class Customer(
: this(bankCode, customerId, password, finTsServerAddress, "", "", "") : this(bankCode, customerId, password, finTsServerAddress, "", "", "")
open var technicalId: String = UUID.random() override var technicalId: String = UUID.random()
open var supportedTanProcedures: List<TanProcedure> = listOf() override var supportedTanProcedures: List<TanProcedure> = listOf()
open var selectedTanProcedure: TanProcedure? = null override var selectedTanProcedure: TanProcedure? = null
open var tanMedia: List<TanMedium> = listOf() override var tanMedia: List<TanMedium> = listOf()
open val tanMediaSorted: List<TanMedium>
get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used }
open var userSetDisplayName: String? = null override var userSetDisplayName: String? = null
override val displayName: String
get() = userSetDisplayName ?: bankName
override var displayIndex: Int = 0 override var displayIndex: Int = 0
open val accountsSorted: List<BankAccount>
get() = accounts.sortedByDisplayIndex()
open val balance: BigDecimal
get() = accounts.map { it.balance }.sum()
open val transactions: List<AccountTransaction>
get() = accounts.flatMap { it.bookedTransactions }
override fun toString(): String { override fun toString(): String {
return "$customerName ($customerId)" return "$bankName $customerId"
} }
} }

View File

@ -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 "<uninitialized_bank_acccount> ${IdDateFormat.format(bookingDate)} ${IdDateFormat.format(valueDate)} $amount $currency $unparsedUsage $otherPartyName $otherPartyBankCode $otherPartyAccountId"
}
}
}

View File

@ -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<IAccountTransaction>
interface IBankAccount<TTransaction: IAccountTransaction> : 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<TTransaction>
var unbookedTransactions: List<Any>
var technicalId: String
var haveAllTransactionsBeenFetched: Boolean
var userSetDisplayName: String?
override val displayName: String
get() {
return userSetDisplayName ?: productName ?: subAccountNumber ?: identifier
}
fun addBookedTransactions(retrievedBookedTransactions: List<TTransaction>) {
val uniqueTransactions = this.bookedTransactions.toMutableSet()
uniqueTransactions.addAll(retrievedBookedTransactions)
this.bookedTransactions = uniqueTransactions.toList()
}
fun addUnbookedTransactions(retrievedUnbookedTransactions: List<Any>) {
val uniqueUnbookedTransactions = this.unbookedTransactions.toMutableSet()
uniqueUnbookedTransactions.addAll(retrievedUnbookedTransactions)
this.unbookedTransactions = uniqueUnbookedTransactions.toList()
}
}

View File

@ -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<IBankAccount<IAccountTransaction>, IAccountTransaction>
interface ICustomer<TAccount: IBankAccount<TAccountTransaction>, 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<TAccount>
var supportedTanProcedures: List<TanProcedure>
var selectedTanProcedure: TanProcedure?
var tanMedia: List<TanMedium>
var userSetDisplayName: String?
var technicalId: String
override val displayName: String
get() = userSetDisplayName ?: bankName
val accountsSorted: List<TAccount>
get() = accounts.sortedByDisplayIndex()
val balance: BigDecimal
get() = accounts.map { it.balance }.sum()
val transactions: List<IAccountTransaction>
get() = accounts.flatMap { it.bookedTransactions }
val tanMediaSorted: List<TanMedium>
get() = tanMedia.sortedByDescending { it.status == TanMediumStatus.Used }
}

View File

@ -6,7 +6,7 @@ import net.dankito.utils.multiplatform.Date
open class MessageLogEntry( open class MessageLogEntry(
val message: String, val message: String,
val time: Date, val time: Date,
val customer: Customer val customer: TypedCustomer
) { ) {
override fun toString(): String { override fun toString(): String {

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -1,7 +1,7 @@
package net.dankito.banking.ui.model.parameters package net.dankito.banking.ui.model.parameters
import net.dankito.utils.multiplatform.Date import net.dankito.utils.multiplatform.Date
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.IAccountTransaction
open class GetTransactionsParameter( open class GetTransactionsParameter(
@ -9,7 +9,7 @@ open class GetTransactionsParameter(
val fromDate: Date? = null, val fromDate: Date? = null,
val toDate: Date? = null, val toDate: Date? = null,
val abortIfTanIsRequired: Boolean = false, val abortIfTanIsRequired: Boolean = false,
val retrievedChunkListener: ((List<AccountTransaction>) -> Unit)? = null val retrievedChunkListener: ((List<IAccountTransaction>) -> Unit)? = null
) { ) {
constructor() : this(true, null, null) // for Java constructor() : this(true, null, null) // for Java

View File

@ -1,12 +1,11 @@
package net.dankito.banking.ui.model.parameters package net.dankito.banking.ui.model.parameters
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.banking.ui.model.AccountTransaction
import net.dankito.banking.ui.model.BankAccount
open class TransferMoneyData( open class TransferMoneyData(
val account: BankAccount, val account: TypedBankAccount,
val creditorName: String, val creditorName: String,
val creditorIban: String, val creditorIban: String,
val creditorBic: String, val creditorBic: String,
@ -17,9 +16,9 @@ open class TransferMoneyData(
companion object { companion object {
fun fromAccountTransactionWithoutAmountAndUsage(transaction: AccountTransaction): TransferMoneyData { fun fromAccountTransactionWithoutAmountAndUsage(transaction: IAccountTransaction): TransferMoneyData {
return TransferMoneyData( return TransferMoneyData(
transaction.bankAccount, transaction.bankAccount as TypedBankAccount,
transaction.otherPartyName ?: "", transaction.otherPartyName ?: "",
transaction.otherPartyAccountId ?: "", transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "", transaction.otherPartyBankCode ?: "",
@ -28,9 +27,9 @@ open class TransferMoneyData(
) )
} }
fun fromAccountTransaction(transaction: AccountTransaction): TransferMoneyData { fun fromAccountTransaction(transaction: IAccountTransaction): TransferMoneyData {
return TransferMoneyData( return TransferMoneyData(
transaction.bankAccount, transaction.bankAccount as TypedBankAccount,
transaction.otherPartyName ?: "", transaction.otherPartyName ?: "",
transaction.otherPartyAccountId ?: "", transaction.otherPartyAccountId ?: "",
transaction.otherPartyBankCode ?: "", transaction.otherPartyBankCode ?: "",

View File

@ -1,19 +1,17 @@
package net.dankito.banking.ui.model.responses package net.dankito.banking.ui.model.responses
import net.dankito.banking.ui.model.*
import net.dankito.utils.multiplatform.BigDecimal 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( open class AddAccountResponse(
isSuccessful: Boolean, isSuccessful: Boolean,
errorToShowToUser: String?, errorToShowToUser: String?,
val customer: Customer, val customer: TypedCustomer,
val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false, val supportsRetrievingTransactionsOfLast90DaysWithoutTan: Boolean = false,
val bookedTransactionsOfLast90Days: Map<BankAccount, List<AccountTransaction>> = mapOf(), val bookedTransactionsOfLast90Days: Map<TypedBankAccount, List<IAccountTransaction>> = mapOf(),
val unbookedTransactionsOfLast90Days: Map<BankAccount, List<Any>> = mapOf(), val unbookedTransactionsOfLast90Days: Map<TypedBankAccount, List<Any>> = mapOf(),
val balances: Map<BankAccount, BigDecimal> = mapOf(), val balances: Map<TypedBankAccount, BigDecimal> = mapOf(),
userCancelledAction: Boolean = false userCancelledAction: Boolean = false
) )
: BankingClientResponse(isSuccessful, errorToShowToUser, userCancelledAction) { : BankingClientResponse(isSuccessful, errorToShowToUser, userCancelledAction) {

View File

@ -1,15 +1,15 @@
package net.dankito.banking.ui.model.responses package net.dankito.banking.ui.model.responses
import net.dankito.utils.multiplatform.BigDecimal import net.dankito.utils.multiplatform.BigDecimal
import net.dankito.banking.ui.model.AccountTransaction import net.dankito.banking.ui.model.IAccountTransaction
import net.dankito.banking.ui.model.BankAccount import net.dankito.banking.ui.model.TypedBankAccount
open class GetTransactionsResponse( open class GetTransactionsResponse(
val bankAccount: BankAccount, val bankAccount: TypedBankAccount,
isSuccessful: Boolean, isSuccessful: Boolean,
errorToShowToUser: String?, errorToShowToUser: String?,
val bookedTransactions: List<AccountTransaction> = listOf(), val bookedTransactions: List<IAccountTransaction> = listOf(),
val unbookedTransactions: List<Any> = listOf(), val unbookedTransactions: List<Any> = listOf(),
val balance: BigDecimal? = null, val balance: BigDecimal? = null,
userCancelledAction: Boolean = false, userCancelledAction: Boolean = false,

View File

@ -15,6 +15,8 @@ import net.dankito.banking.bankfinder.BankInfo
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.search.NoOpRemitteeSearcher import net.dankito.banking.search.NoOpRemitteeSearcher
import net.dankito.banking.search.Remittee 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.ExtractTransferMoneyDataFromPdfResult
import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType import net.dankito.banking.ui.model.moneytransfer.ExtractTransferMoneyDataFromPdfResultType
import net.dankito.banking.ui.model.parameters.GetTransactionsParameter import net.dankito.banking.ui.model.parameters.GetTransactionsParameter
@ -30,12 +32,13 @@ import net.dankito.utils.multiplatform.log.LoggerFactory
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class BankingPresenter( open class BankingPresenter constructor(
protected val bankingClientCreator: IBankingClientCreator, protected val bankingClientCreator: IBankingClientCreator,
protected val bankFinder: IBankFinder, protected val bankFinder: IBankFinder,
protected val dataFolder: File, protected val dataFolder: File,
protected val persister: IBankingPersistence, protected val persister: IBankingPersistence,
protected val router: IRouter, protected val router: IRouter,
protected val modelCreator: IModelCreator = DefaultModelCreator(),
protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(), protected val remitteeSearcher: IRemitteeSearcher = NoOpRemitteeSearcher(),
protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(), protected val bankIconFinder: IBankIconFinder = NoOpBankIconFinder(),
protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(), protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(),
@ -56,25 +59,25 @@ open class BankingPresenter(
} }
protected val bankingClientsForAccounts = mutableMapOf<Customer, IBankingClient>() protected val bankingClientsForAccounts = mutableMapOf<TypedCustomer, IBankingClient>()
protected var selectedBankAccountsField = mutableListOf<BankAccount>() protected var selectedBankAccountsField = mutableListOf<TypedBankAccount>()
protected var selectedAccountType = SelectedAccountType.AllAccounts protected var selectedAccountType = SelectedAccountType.AllAccounts
protected var saveAccountOnNextEnterTanInvocation = false protected var saveAccountOnNextEnterTanInvocation = false
protected val accountsChangedListeners = mutableListOf<(List<Customer>) -> Unit>() protected val accountsChangedListeners = mutableListOf<(List<TypedCustomer>) -> Unit>()
protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(GetTransactionsResponse) -> Unit>() protected val retrievedAccountTransactionsResponseListeners = mutableListOf<(GetTransactionsResponse) -> Unit>()
protected val selectedBankAccountsChangedListeners = mutableListOf<(List<BankAccount>) -> Unit>() protected val selectedBankAccountsChangedListeners = mutableListOf<(List<TypedBankAccount>) -> Unit>()
protected val callback: BankingClientCallback = object : BankingClientCallback { 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) { if (saveAccountOnNextEnterTanInvocation) {
persistAccount(customer) persistAccount(customer)
saveAccountOnNextEnterTanInvocation = false 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) bankingClientsForAccounts.put(customer, client)
} }
// TODO: move BankInfo out of fints4k // TODO: move BankInfo out of fints4k
open fun addAccountAsync(bankInfo: BankInfo, customerId: String, pin: String, callback: (AddAccountResponse) -> Unit) { open fun addAccountAsync(bankInfo: BankInfo, customerId: String, password: String, callback: (AddAccountResponse) -> Unit) {
val customer = Customer(bankInfo.bankCode, customerId, pin, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "") val customer = modelCreator.createCustomer(bankInfo.bankCode, customerId, password, bankInfo.pinTanAddress ?: "", bankInfo.name, bankInfo.bic, "")
val newClient = bankingClientCreator.createClient(customer, dataFolder, asyncRunner, this.callback) 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 -> bankIconFinder.findIconForBankAsync(customer.bankName) { bankIconUrl ->
bankIconUrl?.let { bankIconUrl?.let {
try { 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) val bankIconFile = saveBankIconToDisk(customer, bankIconUrl)
var iconFilePath = bankIconFile.getAbsolutePath() var iconFilePath = bankIconFile.getAbsolutePath()
@ -208,7 +211,7 @@ open class BankingPresenter(
callAccountsChangedListeners() 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") val bankIconsDir = File(dataFolder, "bank_icons")
bankIconsDir.mkdirs() 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 val wasSelected = isSingleSelectedAccount(customer) or // either account or one of its bank accounts is currently selected
(customer.accounts.firstOrNull { isSingleSelectedBankAccount(it) } != null) (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) { callback: (GetTransactionsResponse) -> Unit) {
customer.accounts.forEach { bankAccount -> customer.accounts.forEach { bankAccount ->
@ -274,13 +277,13 @@ open class BankingPresenter(
} }
} }
open fun fetchAllAccountTransactionsAsync(bankAccount: BankAccount, open fun fetchAllAccountTransactionsAsync(bankAccount: TypedBankAccount,
callback: (GetTransactionsResponse) -> Unit) { callback: (GetTransactionsResponse) -> Unit) {
fetchAccountTransactionsAsync(bankAccount, null, false, callback) 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) { callback: (GetTransactionsResponse) -> Unit) {
getBankingClientForAccount(bankAccount.customer)?.let { client -> getBankingClientForAccount(bankAccount.customer)?.let { client ->
@ -319,7 +322,7 @@ open class BankingPresenter(
} }
} }
protected open fun updateBanksAccountsTransactionsAsync(accounts: List<BankAccount>, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) { protected open fun updateBanksAccountsTransactionsAsync(accounts: List<TypedBankAccount>, abortIfTanIsRequired: Boolean = false, callback: (GetTransactionsResponse) -> Unit) {
accounts.forEach { bankAccount -> accounts.forEach { bankAccount ->
if (bankAccount.supportsRetrievingAccountTransactions) { if (bankAccount.supportsRetrievingAccountTransactions) {
updateBankAccountTransactionsAsync(bankAccount, abortIfTanIsRequired, callback) 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 val fromDate = bankAccount.lastRetrievedTransactionsTimestamp?.let { Date(it.millisSinceEpoch - OneDayMillis) } // one day before last received transactions
fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback) fetchAccountTransactionsAsync(bankAccount, fromDate, abortIfTanIsRequired, callback)
@ -347,7 +350,7 @@ open class BankingPresenter(
callRetrievedAccountTransactionsResponseListener(response) callRetrievedAccountTransactionsResponseListener(response)
} }
protected open fun receivedAccountsTransactionChunk(bankAccount: BankAccount, accountTransactionsChunk: List<AccountTransaction>) { protected open fun receivedAccountsTransactionChunk(bankAccount: TypedBankAccount, accountTransactionsChunk: List<IAccountTransaction>) {
if (accountTransactionsChunk.isNotEmpty()) { if (accountTransactionsChunk.isNotEmpty()) {
bankAccount.addBookedTransactions(accountTransactionsChunk) bankAccount.addBookedTransactions(accountTransactionsChunk)
@ -370,7 +373,7 @@ open class BankingPresenter(
persistAccountTransactions(bankAccount, response.bookedTransactions, response.unbookedTransactions) 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 bankAccount.balance = balance
persistAccount(bankAccount.customer) persistAccount(bankAccount.customer)
@ -388,25 +391,25 @@ open class BankingPresenter(
} }
} }
open fun accountDisplayIndexUpdated(account: Customer) { open fun accountDisplayIndexUpdated(account: TypedCustomer) {
persistAccount(account) persistAccount(account)
} }
open fun accountUpdated(bank: Customer) { open fun accountUpdated(bank: TypedCustomer) {
persistAccount(bank) persistAccount(bank)
getBankingClientForAccount(bank)?.dataChanged(bank) getBankingClientForAccount(bank)?.dataChanged(bank)
} }
open fun accountUpdated(account: BankAccount) { open fun accountUpdated(account: TypedBankAccount) {
persistAccount(account.customer) persistAccount(account.customer)
} }
protected open fun persistAccount(customer: Customer) { protected open fun persistAccount(customer: ICustomer<*, *>) {
persister.saveOrUpdateAccount(customer, customers) persister.saveOrUpdateAccount(customer as TypedCustomer, customers)
} }
protected open fun persistAccountTransactions(bankAccount: BankAccount, bookedTransactions: List<AccountTransaction>, unbookedTransactions: List<Any>) { protected open fun persistAccountTransactions(bankAccount: TypedBankAccount, bookedTransactions: List<IAccountTransaction>, unbookedTransactions: List<Any>) {
persister.saveOrUpdateAccountTransactions(bankAccount, bookedTransactions) persister.saveOrUpdateAccountTransactions(bankAccount, bookedTransactions)
// TODO: someday also persist unbooked transactions // TODO: someday also persist unbooked transactions
@ -513,11 +516,11 @@ open class BankingPresenter(
} }
open fun searchSelectedAccountTransactions(query: String): List<AccountTransaction> { open fun searchSelectedAccountTransactions(query: String): List<IAccountTransaction> {
return searchAccountTransactions(query, selectedBankAccountsAccountTransactions) return searchAccountTransactions(query, selectedBankAccountsAccountTransactions)
} }
open fun searchAccountTransactions(query: String, transactions: List<AccountTransaction>): List<AccountTransaction> { open fun searchAccountTransactions(query: String, transactions: List<IAccountTransaction>): List<IAccountTransaction> {
val queryLowercase = query.trim().toLowerCase() val queryLowercase = query.trim().toLowerCase()
if (queryLowercase.isEmpty()) { if (queryLowercase.isEmpty()) {
@ -532,7 +535,7 @@ open class BankingPresenter(
} }
open fun getMessageLogForAccounts(customers: List<Customer>): List<String> { open fun getMessageLogForAccounts(customers: List<TypedCustomer>): List<String> {
val logEntries = customers.flatMap { val logEntries = customers.flatMap {
getBankingClientForAccount(it)?.messageLogWithoutSensitiveData ?: listOf() getBankingClientForAccount(it)?.messageLogWithoutSensitiveData ?: listOf()
} }
@ -556,15 +559,15 @@ open class BankingPresenter(
} }
protected open fun getBankingClientForAccount(customer: Customer): IBankingClient? { protected open fun getBankingClientForAccount(customer: ICustomer<*, *>): IBankingClient? {
return bankingClientsForAccounts.get(customer) return bankingClientsForAccounts.get(customer as TypedCustomer)
} }
open val selectedBankAccounts: List<BankAccount> open val selectedBankAccounts: List<TypedBankAccount>
get() = ArrayList(selectedBankAccountsField) get() = ArrayList(selectedBankAccountsField)
open val selectedBankAccountsAccountTransactions: List<AccountTransaction> open val selectedBankAccountsAccountTransactions: List<IAccountTransaction>
get() = getAccountTransactionsForBankAccounts(selectedBankAccounts) get() = getAccountTransactionsForBankAccounts(selectedBankAccounts)
open val balanceOfSelectedBankAccounts: BigDecimal open val balanceOfSelectedBankAccounts: BigDecimal
@ -574,12 +577,12 @@ open class BankingPresenter(
open val areAllAccountSelected: Boolean open val areAllAccountSelected: Boolean
get() = selectedAccountType == SelectedAccountType.AllAccounts get() = selectedAccountType == SelectedAccountType.AllAccounts
open fun isSingleSelectedAccount(customer: Customer): Boolean { open fun isSingleSelectedAccount(customer: TypedCustomer): Boolean {
return selectedAccountType == SelectedAccountType.SingleAccount return selectedAccountType == SelectedAccountType.SingleAccount
&& selectedBankAccountsField.map { it.customer }.toSet().containsExactly(customer) && selectedBankAccountsField.map { it.customer }.toSet().containsExactly(customer)
} }
open fun isSingleSelectedBankAccount(bankAccount: BankAccount): Boolean { open fun isSingleSelectedBankAccount(bankAccount: TypedBankAccount): Boolean {
return selectedAccountType == SelectedAccountType.SingleBankAccount return selectedAccountType == SelectedAccountType.SingleBankAccount
&& selectedBankAccountsField.containsExactly(bankAccount) && selectedBankAccountsField.containsExactly(bankAccount)
} }
@ -590,39 +593,39 @@ open class BankingPresenter(
setSelectedBankAccounts(bankAccounts) setSelectedBankAccounts(bankAccounts)
} }
open fun selectedAccount(customer: Customer) { open fun selectedAccount(customer: TypedCustomer) {
selectedAccountType = SelectedAccountType.SingleAccount selectedAccountType = SelectedAccountType.SingleAccount
setSelectedBankAccounts(customer.accounts) setSelectedBankAccounts(customer.accounts)
} }
open fun selectedBankAccount(bankAccount: BankAccount) { open fun selectedBankAccount(bankAccount: TypedBankAccount) {
selectedAccountType = SelectedAccountType.SingleBankAccount selectedAccountType = SelectedAccountType.SingleBankAccount
setSelectedBankAccounts(listOf(bankAccount)) setSelectedBankAccounts(listOf(bankAccount))
} }
protected open fun setSelectedBankAccounts(bankAccounts: List<BankAccount>) { protected open fun setSelectedBankAccounts(bankAccounts: List<TypedBankAccount>) {
this.selectedBankAccountsField = ArrayList(bankAccounts) // make a copy this.selectedBankAccountsField = ArrayList(bankAccounts) // make a copy
callSelectedBankAccountsChangedListeners(selectedBankAccountsField) callSelectedBankAccountsChangedListeners(selectedBankAccountsField)
} }
open val customers: List<Customer> open val customers: List<TypedCustomer>
get() = bankingClientsForAccounts.keys.toList() get() = bankingClientsForAccounts.keys.toList()
open val bankAccounts: List<BankAccount> open val bankAccounts: List<TypedBankAccount>
get() = customers.flatMap { it.accounts } get() = customers.flatMap { it.accounts }
open val allTransactions: List<AccountTransaction> open val allTransactions: List<IAccountTransaction>
get() = getAccountTransactionsForBankAccounts(bankAccounts) get() = getAccountTransactionsForBankAccounts(bankAccounts)
open val balanceOfAllAccounts: BigDecimal open val balanceOfAllAccounts: BigDecimal
get() = getBalanceForAccounts(customers) get() = getBalanceForAccounts(customers)
open val bankAccountsSupportingRetrievingAccountTransactions: List<BankAccount> open val bankAccountsSupportingRetrievingAccountTransactions: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsRetrievingAccountTransactions } get() = bankAccounts.filter { it.supportsRetrievingAccountTransactions }
open val hasBankAccountsSupportingRetrievingAccountTransactions: Boolean open val hasBankAccountsSupportingRetrievingAccountTransactions: Boolean
@ -631,12 +634,12 @@ open class BankingPresenter(
open val doSelectedBankAccountsSupportRetrievingAccountTransactions: Boolean open val doSelectedBankAccountsSupportRetrievingAccountTransactions: Boolean
get() = doBankAccountsSupportRetrievingAccountTransactions(selectedBankAccounts) get() = doBankAccountsSupportRetrievingAccountTransactions(selectedBankAccounts)
open fun doBankAccountsSupportRetrievingAccountTransactions(bankAccounts: List<BankAccount>): Boolean { open fun doBankAccountsSupportRetrievingAccountTransactions(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsRetrievingAccountTransactions } != null return bankAccounts.firstOrNull { it.supportsRetrievingAccountTransactions } != null
} }
open val bankAccountsSupportingRetrievingBalance: List<BankAccount> open val bankAccountsSupportingRetrievingBalance: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsRetrievingBalance } get() = bankAccounts.filter { it.supportsRetrievingBalance }
open val hasBankAccountsSupportingRetrievingBalance: Boolean open val hasBankAccountsSupportingRetrievingBalance: Boolean
@ -645,12 +648,12 @@ open class BankingPresenter(
open val doSelectedBankAccountsSupportRetrievingBalance: Boolean open val doSelectedBankAccountsSupportRetrievingBalance: Boolean
get() = doBankAccountsSupportRetrievingBalance(selectedBankAccounts) get() = doBankAccountsSupportRetrievingBalance(selectedBankAccounts)
open fun doBankAccountsSupportRetrievingBalance(bankAccounts: List<BankAccount>): Boolean { open fun doBankAccountsSupportRetrievingBalance(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsRetrievingBalance } != null return bankAccounts.firstOrNull { it.supportsRetrievingBalance } != null
} }
open val bankAccountsSupportingTransferringMoney: List<BankAccount> open val bankAccountsSupportingTransferringMoney: List<TypedBankAccount>
get() = bankAccounts.filter { it.supportsTransferringMoney } get() = bankAccounts.filter { it.supportsTransferringMoney }
open val hasBankAccountsSupportTransferringMoney: Boolean open val hasBankAccountsSupportTransferringMoney: Boolean
@ -659,16 +662,16 @@ open class BankingPresenter(
open val doSelectedBankAccountsSupportTransferringMoney: Boolean open val doSelectedBankAccountsSupportTransferringMoney: Boolean
get() = doBankAccountsSupportTransferringMoney(selectedBankAccounts) get() = doBankAccountsSupportTransferringMoney(selectedBankAccounts)
open fun doBankAccountsSupportTransferringMoney(bankAccounts: List<BankAccount>): Boolean { open fun doBankAccountsSupportTransferringMoney(bankAccounts: List<TypedBankAccount>): Boolean {
return bankAccounts.firstOrNull { it.supportsTransferringMoney } != null return bankAccounts.firstOrNull { it.supportsTransferringMoney } != null
} }
protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection<BankAccount>): List<AccountTransaction> { protected open fun getAccountTransactionsForBankAccounts(bankAccounts: Collection<TypedBankAccount>): List<IAccountTransaction> {
return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions return bankAccounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate.millisSinceEpoch } // TODO: someday add unbooked transactions
} }
protected open fun getBalanceForAccounts(customers: Collection<Customer>): BigDecimal { protected open fun getBalanceForAccounts(customers: Collection<TypedCustomer>): BigDecimal {
return customers.map { it.balance }.sum() return customers.map { it.balance }.sum()
} }
@ -677,7 +680,7 @@ open class BankingPresenter(
} }
open fun getTanMediaForTanProcedure(bank: Customer, tanProcedure: TanProcedure): List<TanMedium> { open fun getTanMediaForTanProcedure(bank: TypedCustomer, tanProcedure: TanProcedure): List<TanMedium> {
if (ChipTanTanProcedures.contains(tanProcedure.type)) { if (ChipTanTanProcedures.contains(tanProcedure.type)) {
return bank.tanMediaSorted.filterIsInstance<TanGeneratorTanMedium>() return bank.tanMediaSorted.filterIsInstance<TanGeneratorTanMedium>()
} }
@ -719,11 +722,11 @@ open class BankingPresenter(
} }
open fun addAccountsChangedListener(listener: (List<Customer>) -> Unit): Boolean { open fun addAccountsChangedListener(listener: (List<TypedCustomer>) -> Unit): Boolean {
return accountsChangedListeners.add(listener) return accountsChangedListeners.add(listener)
} }
open fun removeAccountsChangedListener(listener: (List<Customer>) -> Unit): Boolean { open fun removeAccountsChangedListener(listener: (List<TypedCustomer>) -> Unit): Boolean {
return accountsChangedListeners.add(listener) return accountsChangedListeners.add(listener)
} }
@ -751,15 +754,15 @@ open class BankingPresenter(
} }
open fun addSelectedBankAccountsChangedListener(listener: (List<BankAccount>) -> Unit): Boolean { open fun addSelectedBankAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedBankAccountsChangedListeners.add(listener) return selectedBankAccountsChangedListeners.add(listener)
} }
open fun removeSelectedBankAccountsChangedListener(listener: (List<BankAccount>) -> Unit): Boolean { open fun removeSelectedBankAccountsChangedListener(listener: (List<TypedBankAccount>) -> Unit): Boolean {
return selectedBankAccountsChangedListeners.add(listener) return selectedBankAccountsChangedListeners.add(listener)
} }
protected open fun callSelectedBankAccountsChangedListeners(selectedBankAccounts: List<BankAccount>) { protected open fun callSelectedBankAccountsChangedListeners(selectedBankAccounts: List<TypedBankAccount>) {
val selectedBankAccounts = this.selectedBankAccounts val selectedBankAccounts = this.selectedBankAccounts
ArrayList(selectedBankAccountsChangedListeners).forEach { ArrayList(selectedBankAccountsChangedListeners).forEach {

View File

@ -5,6 +5,7 @@ import net.dankito.banking.fints.webclient.IWebClient
import net.dankito.banking.persistence.IBankingPersistence import net.dankito.banking.persistence.IBankingPersistence
import net.dankito.banking.search.IRemitteeSearcher import net.dankito.banking.search.IRemitteeSearcher
import net.dankito.banking.ui.IRouter 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.ui.presenter.BankingPresenter
import net.dankito.banking.util.* import net.dankito.banking.util.*
import net.dankito.banking.util.extraction.NoOpInvoiceDataExtractor 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, class BankingPresenterSwift(dataFolder: File, router: IRouter, webClient: IWebClient, persistence: IBankingPersistence,
remitteeSearcher: IRemitteeSearcher, bankIconFinder: IBankIconFinder, serializer: ISerializer, asyncRunner: IAsyncRunner) 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) { remitteeSearcher, bankIconFinder, NoOpTextExtractorRegistry(), NoOpInvoiceDataExtractor(), serializer, asyncRunner) {
} }

View File

@ -35,6 +35,8 @@
366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */; }; 366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */; };
366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.swift */; }; 366FA4E624C6EBF40094F009 /* EnterTanState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4E524C6EBF40094F009 /* EnterTanState.swift */; };
3684EB8B2508F6F00001139E /* SearchBarWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3684EB8A2508F6F00001139E /* SearchBarWithLabel.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 */; }; 36B8A4482503D12100C15359 /* ProtectAppSettingsDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */; };
36B8A44B2503D1E800C15359 /* BiometricAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */; }; 36B8A44B2503D1E800C15359 /* BiometricAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */; };
36B8A44D2503D96D00C15359 /* AuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A44C2503D96D00C15359 /* AuthenticationService.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 */; }; 36B8A4562503E9B200C15359 /* UIAlertBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4552503E9B200C15359 /* UIAlertBase.swift */; };
36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4572503EEB600C15359 /* ActionSheet.swift */; }; 36B8A4582503EEB600C15359 /* ActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B8A4572503EEB600C15359 /* ActionSheet.swift */; };
36BCF85424BA0C54005BEC29 /* BankList.json in Resources */ = {isa = PBXBuildFile; fileRef = 36BCF85324BA0C54005BEC29 /* BankList.json */; }; 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 */; }; 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, ); }; }; 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 */; }; 36BCF86324BA5097005BEC29 /* SwiftUiRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36BCF86224BA5097005BEC29 /* SwiftUiRouter.swift */; };
@ -138,8 +138,8 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
3684EB8F250B7F3C0001139E /* BankingUiCommon.framework in Embed Frameworks */,
36BCF86A24BA550D005BEC29 /* BankFinder.framework in Embed Frameworks */, 36BCF86A24BA550D005BEC29 /* BankFinder.framework in Embed Frameworks */,
36BCF85924BA4274005BEC29 /* BankingUiCommon.framework in Embed Frameworks */,
36BCF85F24BA4DA8005BEC29 /* MultiplatformUtils.framework in Embed Frameworks */, 36BCF85F24BA4DA8005BEC29 /* MultiplatformUtils.framework in Embed Frameworks */,
36FC92D124B39C47002B12E9 /* fints4k.framework in Embed Frameworks */, 36FC92D124B39C47002B12E9 /* fints4k.framework in Embed Frameworks */,
36BCF87124BB0F8A005BEC29 /* fints4kBankingClient.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 = "<group>"; }; 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanDialog.swift; sourceTree = "<group>"; };
366FA4E524C6EBF40094F009 /* EnterTanState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanState.swift; sourceTree = "<group>"; }; 366FA4E524C6EBF40094F009 /* EnterTanState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterTanState.swift; sourceTree = "<group>"; };
3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarWithLabel.swift; sourceTree = "<group>"; }; 3684EB8A2508F6F00001139E /* SearchBarWithLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarWithLabel.swift; sourceTree = "<group>"; };
3684EB8C250B7F2B0001139E /* BankingUiCommon.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = BankingUiCommon.framework.dSYM; path = "../BankingUiCommon/build/xcode-frameworks/BankingUiCommon.framework.dSYM"; sourceTree = "<group>"; };
3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BankingUiCommon.framework; path = "../BankingUiCommon/build/xcode-frameworks/BankingUiCommon.framework"; sourceTree = "<group>"; };
36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectAppSettingsDialog.swift; sourceTree = "<group>"; }; 36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectAppSettingsDialog.swift; sourceTree = "<group>"; };
36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricAuthenticationService.swift; sourceTree = "<group>"; }; 36B8A44A2503D1E800C15359 /* BiometricAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricAuthenticationService.swift; sourceTree = "<group>"; };
36B8A44C2503D96D00C15359 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; }; 36B8A44C2503D96D00C15359 /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; };
@ -264,8 +266,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3684EB90250B7F560001139E /* BankingUiCommon.framework in Frameworks */,
36BCF86924BA550D005BEC29 /* BankFinder.framework in Frameworks */, 36BCF86924BA550D005BEC29 /* BankFinder.framework in Frameworks */,
36BCF85824BA4274005BEC29 /* BankingUiCommon.framework in Frameworks */,
36BCF85E24BA4DA8005BEC29 /* MultiplatformUtils.framework in Frameworks */, 36BCF85E24BA4DA8005BEC29 /* MultiplatformUtils.framework in Frameworks */,
36FC92D024B39C47002B12E9 /* fints4k.framework in Frameworks */, 36FC92D024B39C47002B12E9 /* fints4k.framework in Frameworks */,
36BCF87024BB0F8A005BEC29 /* fints4kBankingClient.framework in Frameworks */, 36BCF87024BB0F8A005BEC29 /* fints4kBankingClient.framework in Frameworks */,
@ -419,6 +421,8 @@
36FC928F24B39A05002B12E9 = { 36FC928F24B39A05002B12E9 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */,
3684EB8C250B7F2B0001139E /* BankingUiCommon.framework.dSYM */,
36FC929A24B39A05002B12E9 /* BankingiOSApp */, 36FC929A24B39A05002B12E9 /* BankingiOSApp */,
36FC92B424B39A08002B12E9 /* BankingiOSAppTests */, 36FC92B424B39A08002B12E9 /* BankingiOSAppTests */,
36FC92BF24B39A08002B12E9 /* BankingiOSAppUITests */, 36FC92BF24B39A08002B12E9 /* BankingiOSAppUITests */,

View File

@ -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]) 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: []) let bank1 = Customer(bankCode: "", customerId: "", password: "", finTsServerAddress: "", bankName: "Abzockbank", bic: "", customerName: "Marieke Musterfrau", userId: "", iconUrl: "", accounts: [])
bank1.accounts = [ bank1.accounts = [

View File

@ -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 { func sumBalances() -> CommonBigDecimal {
return CommonBigDecimal(decimal_: self.map { $0.balance.decimal }.sum()) 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 { func sumAmounts() -> CommonBigDecimal {
return CommonBigDecimal(decimal_: self.map { $0.amount.decimal }.sum()) return CommonBigDecimal(decimal_: self.map { $0.amount.decimal }.sum())

View File

@ -6,8 +6,8 @@ class AppData : ObservableObject {
@Inject private var presenter: BankingPresenterSwift @Inject private var presenter: BankingPresenterSwift
@Published var banks: [Customer] = [] @Published var banks: [ICustomer] = []
@Published var banksSorted: [Customer] = [] @Published var banksSorted: [ICustomer] = []
@Published var hasAtLeastOneAccountBeenAdded: Bool = false @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.banks = presenter.customers
self.banksSorted = banks.sortedByDisplayIndex() self.banksSorted = banks.sortedByDisplayIndex()

View File

@ -19,7 +19,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
} }
func saveOrUpdateAccount(customer: Customer, allCustomers: [Customer]) { func saveOrUpdateAccount(customer: ICustomer, allCustomers: [ICustomer]) {
do { do {
let mapped = mapper.map(customer, context) 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 customer.technicalId = mappedCustomer.objectIDAsString
for account in customer.accounts { for account in customer.accounts {
@ -58,7 +58,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
} }
func readPersistedAccounts_() -> [Customer] { func readPersistedAccounts_() -> [ICustomer] {
var customers: [PersistedCustomer] = [] var customers: [PersistedCustomer] = []
do { do {
@ -73,7 +73,7 @@ class CoreDataBankingPersistence: IBankingPersistence, IRemitteeSearcher {
return customers.map( { mapper.map($0) } ) return customers.map( { mapper.map($0) } )
} }
func deleteAccount(customer: Customer, allCustomers: [Customer]) { func deleteAccount(customer: ICustomer, allCustomers: [ICustomer]) {
do { do {
let mapped = mapper.map(customer, context) 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 { if let persistedAccount = context.objectByID(bankAccount.technicalId) as? PersistedBankAccount {
for transaction in transactions { for transaction in transactions {
if transaction.technicalId.isCoreDataId == false { // TODO: or also update already persisted 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 transaction.technicalId = mappedTransaction.objectIDAsString
} catch { } 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)")
} }
} }
} }

View File

@ -6,14 +6,14 @@ class EnterTanState : Identifiable {
let id: Foundation.UUID = UUID() let id: Foundation.UUID = UUID()
let customer: Customer let customer: ICustomer
let tanChallenge: TanChallenge let tanChallenge: TanChallenge
let callback: (EnterTanResult) -> Void 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.customer = customer
self.tanChallenge = tanChallenge self.tanChallenge = tanChallenge
self.callback = callback self.callback = callback

View File

@ -5,7 +5,7 @@ import BankingUiSwift
class Mapper { 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: []) 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 mapped.userSetDisplayName = customer.userSetDisplayName
@ -23,7 +23,7 @@ class Mapper {
return mapped 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) let mapped = context.objectByID(customer.technicalId) ?? PersistedCustomer(context: context)
mapped.bankCode = customer.bankCode 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) } ) ?? [] 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: []) 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 mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched
@ -69,11 +69,11 @@ class Mapper {
return mapped 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) } ) 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) let mapped = context.objectByID(account.technicalId) ?? PersistedBankAccount(context: context)
mapped.customer = customer mapped.customer = customer
@ -136,11 +136,11 @@ class Mapper {
} }
func map(_ account: BankAccount, _ transactions: Set<PersistedAccountTransaction>?) -> [AccountTransaction] { func map(_ account: IBankAccount, _ transactions: Set<PersistedAccountTransaction>?) -> [IAccountTransaction] {
return transactions?.map( {map(account, $0) } ) ?? [] 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) 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 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) } ) 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) let mapped = context.objectByID(transaction.technicalId) ?? PersistedAccountTransaction(context: context)
mapped.account = account mapped.account = account

View File

@ -11,7 +11,7 @@ extension Message {
secondaryButton: .cancel()) 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)'?"), return Message(title: Text("Really delete account '\(bank.displayName)'?"),
message: Text("All data for this account will be permanently deleted locally."), message: Text("All data for this account will be permanently deleted locally."),
primaryButton: .destructive(Text("Delete"), action: { deleteAccount(bank) } ), primaryButton: .destructive(Text("Delete"), action: { deleteAccount(bank) } ),

View File

@ -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) let enterTanState = EnterTanState(customer, tanChallenge, callback)
SceneDelegate.navigateToView(EnterTanDialog(enterTanState)) SceneDelegate.navigateToView(EnterTanDialog(enterTanState))

View File

@ -9,7 +9,7 @@ struct AccountTransactionsDialog: View {
private let title: String private let title: String
private let allTransactions: [AccountTransaction] private let allTransactions: [IAccountTransaction]
private let balanceOfAllTransactions: CommonBigDecimal private let balanceOfAllTransactions: CommonBigDecimal
@ -20,10 +20,10 @@ struct AccountTransactionsDialog: View {
@State private var showFetchAllTransactionsOverlay: Bool @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 @State private var balanceOfFilteredTransactions: CommonBigDecimal
@ -45,7 +45,7 @@ struct AccountTransactionsDialog: View {
@Inject private var presenter: BankingPresenterSwift @Inject private var presenter: BankingPresenterSwift
init(allBanks: [Customer]) { init(allBanks: [ICustomer]) {
let allAccounts = allBanks.flatMap { $0.accounts } let allAccounts = allBanks.flatMap { $0.accounts }
self.init("All accounts", allAccounts.flatMap { $0.bookedTransactions }, allBanks.sumBalances(), allAccounts.filter { $0.haveAllTransactionsBeenFetched == false }) self.init("All accounts", allAccounts.flatMap { $0.bookedTransactions }, allBanks.sumBalances(), allAccounts.filter { $0.haveAllTransactionsBeenFetched == false })
@ -53,19 +53,19 @@ struct AccountTransactionsDialog: View {
presenter.selectedAllBankAccounts() 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 }) self.init(bank.displayName, bank.accounts.flatMap { $0.bookedTransactions }, bank.balance, bank.accounts.filter { $0.haveAllTransactionsBeenFetched == false })
presenter.selectedAccount(customer: bank) presenter.selectedAccount(customer: bank)
} }
init(account: BankAccount) { init(account: IBankAccount) {
self.init(account.displayName, account.bookedTransactions, account.balance, account.haveAllTransactionsBeenFetched ? [] : [account]) self.init(account.displayName, account.bookedTransactions, account.balance, account.haveAllTransactionsBeenFetched ? [] : [account])
presenter.selectedBankAccount(bankAccount: 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.title = title
self.allTransactions = transactions self.allTransactions = transactions
@ -74,7 +74,7 @@ struct AccountTransactionsDialog: View {
self.balanceOfAllTransactions = balance self.balanceOfAllTransactions = balance
self._balanceOfFilteredTransactions = State(initialValue: 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) _accountsForWhichNotAllTransactionsHaveBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched)
_haveAllTransactionsBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty) _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 accounts.forEach { account in
presenter.fetchAllAccountTransactionsAsync(bankAccount: account, callback: self.handleGetAllTransactionsResult) presenter.fetchAllAccountTransactionsAsync(bankAccount: account, callback: self.handleGetAllTransactionsResult)
} }
@ -224,7 +224,7 @@ struct AccountTransactionsDialog: View {
struct AccountTransactionsDialog_Previews: PreviewProvider { struct AccountTransactionsDialog_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AccountTransactionsDialog(previewBanks[0].displayName, [ 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)) CommonBigDecimal(double: 84.12))
} }

View File

@ -22,7 +22,7 @@ struct AccountsDialog: View {
Form { Form {
AllBanksListItem(banks: data.banks) AllBanksListItem(banks: data.banks)
ForEach(data.banks.sortedByDisplayIndex()) { bank in ForEach(data.banks.sortedByDisplayIndex(), id: \.technicalId) { bank in
BankListItem(bank: bank) BankListItem(bank: bank)
} }

View File

@ -85,7 +85,7 @@ struct AddAccountDialog: View {
isTryingToAddAccount = true isTryingToAddAccount = true
UIApplication.hideKeyboard() 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) self.handleAddAccountResponse(response)
} }
} }

View File

@ -9,7 +9,7 @@ struct BankAccountSettingsDialog: View {
@Inject private var presenter: BankingPresenterSwift @Inject private var presenter: BankingPresenterSwift
private let account: BankAccount private let account: IBankAccount
@State private var displayName: String @State private var displayName: String
@ -21,7 +21,7 @@ struct BankAccountSettingsDialog: View {
} }
init(_ account: BankAccount) { init(_ account: IBankAccount) {
self.account = account self.account = account
_displayName = State(initialValue: account.displayName) _displayName = State(initialValue: account.displayName)
@ -80,7 +80,7 @@ struct BankAccountSettingsDialog: View {
if hasUnsavedData { if hasUnsavedData {
account.userSetDisplayName = displayName account.userSetDisplayName = displayName
presenter.accountUpdated(account: account.customer) presenter.accountUpdated(account: account)
} }
closeDialog() closeDialog()

View File

@ -10,7 +10,7 @@ struct BankSettingsDialog: View {
@Inject private var presenter: BankingPresenterSwift @Inject private var presenter: BankingPresenterSwift
private let bank: Customer private let bank: ICustomer
@State private var displayName: String @State private var displayName: String
@ -19,7 +19,7 @@ struct BankSettingsDialog: View {
@State private var selectedTanProcedure: TanProcedure? @State private var selectedTanProcedure: TanProcedure?
@State private var accountsSorted: [BankAccount] @State private var accountsSorted: [IBankAccount]
@State private var askUserToDeleteAccountOrSaveChangesMessage: Message? = nil @State private var askUserToDeleteAccountOrSaveChangesMessage: Message? = nil
@ -32,7 +32,7 @@ struct BankSettingsDialog: View {
} }
init(_ bank: Customer) { init(_ bank: ICustomer) {
self.bank = bank self.bank = bank
_displayName = State(initialValue: bank.displayName) _displayName = State(initialValue: bank.displayName)
@ -75,7 +75,7 @@ struct BankSettingsDialog: View {
} }
Section(header: SectionHeaderWithRightAlignedEditButton("Accounts")) { Section(header: SectionHeaderWithRightAlignedEditButton("Accounts")) {
ForEach(accountsSorted) { account in ForEach(accountsSorted, id: \.technicalId) { account in
NavigationLink(destination: LazyView(BankAccountSettingsDialog(account))) { NavigationLink(destination: LazyView(BankAccountSettingsDialog(account))) {
Text(account.displayName) Text(account.displayName)
} }
@ -102,7 +102,7 @@ struct BankSettingsDialog: View {
func reorderAccounts(from source: IndexSet, to destination: Int) { func reorderAccounts(from source: IndexSet, to destination: Int) {
accountsSorted = accountsSorted.reorder(from: source, to: destination) 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) self.askUserToDeleteAccountOrSaveChangesMessage = Message.createAskUserToDeleteAccountMessage(bank, self.deleteAccount)
} }
func deleteAccount(bank: Customer) { func deleteAccount(bank: ICustomer) {
presenter.deleteAccount(customer: bank) presenter.deleteAccount(customer: bank)
closeDialog() closeDialog()
@ -135,7 +135,7 @@ struct BankSettingsDialog: View {
bank.selectedTanProcedure = selectedTanProcedure bank.selectedTanProcedure = selectedTanProcedure
presenter.accountUpdated(account: bank) presenter.accountUpdated(bank: bank)
} }
closeDialog() closeDialog()

View File

@ -11,7 +11,7 @@ struct EnterTanDialog: View {
private var tanChallenge: TanChallenge private var tanChallenge: TanChallenge
private var customer: Customer private var customer: ICustomer
private var customersTanMedia: [TanMedium] = [] private var customersTanMedia: [TanMedium] = []

View File

@ -18,7 +18,7 @@ struct SettingsDialog: View {
Form { Form {
Section(header: SectionHeaderWithRightAlignedEditButton("Bank Credentials", isEditButtonEnabled: data.hasAtLeastOneAccountBeenAdded), Section(header: SectionHeaderWithRightAlignedEditButton("Bank Credentials", isEditButtonEnabled: data.hasAtLeastOneAccountBeenAdded),
footer: footer) { footer: footer) {
ForEach(data.banksSorted) { bank in ForEach(data.banksSorted, id: \.technicalId) { bank in
NavigationLink(destination: LazyView(BankSettingsDialog(bank))) { NavigationLink(destination: LazyView(BankSettingsDialog(bank))) {
IconedTitleView(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) 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 // don't know why but when deleting last bank application crashes if we don't delete bank async
DispatchQueue.main.async { DispatchQueue.main.async {
if self.presenter.customers.count == 1 { 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) self.presenter.deleteAccount(customer: bankToDelete)
} }

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