Browse Source

Merge branch 'release/1.20.5'

Markus Lorenz 1 year ago
parent
commit
39af8e7005

+ 11
- 1
CHANGES.rst View File

@@ -2,13 +2,23 @@ Next Release
2 2
 ============
3 3
 
4 4
 
5
+- Fix double entry when applicant edits details
6
+
7
+
8
+
9
+1.20.5
10
+======
11
+
12
+
5 13
 - Remove editing of number of shares hold by a member.
6 14
 
7 15
 - Remove old import and export functionality.
8 16
 
9 17
 - Show error message if applicant is younger than 18 years old.
10 18
 
11
-- Fix double entry when applicant edits details
19
+- Invitations for general assembly and bar camp 2018.
20
+
21
+- Hide invoice 2017 sending in membership list and toolbox.
12 22
 
13 23
 
14 24
 

+ 1
- 1
VERSION View File

@@ -1 +1 @@
1
-1.20.4
1
+1.20.5

+ 27
- 0
alembic/versions/34c421bb0d0c_invitations_ga_bc_2018.py View File

@@ -0,0 +1,27 @@
1
+"""Invitations for general assembly and bar camp 2018
2
+
3
+Revision ID: 34c421bb0d0c
4
+Revises: 2fbe1bde5df8
5
+Create Date: 2018-04-23 20:03:17.014936
6
+
7
+"""
8
+
9
+# revision identifiers, used by Alembic.
10
+revision = '34c421bb0d0c'
11
+down_revision = '2fbe1bde5df8'
12
+
13
+from alembic import op
14
+import sqlalchemy as sa
15
+
16
+
17
+def upgrade():
18
+    op.add_column('members', sa.Column('email_invite_date_bcgv18', sa.DateTime(), nullable=True))
19
+    op.add_column('members', sa.Column('email_invite_flag_bcgv18', sa.Boolean(), nullable=True))
20
+    op.add_column('members', sa.Column('email_invite_token_bcgv18', sa.Unicode(length=255), nullable=True))
21
+
22
+
23
+def downgrade():
24
+    with op.batch_alter_table('members') as batch_op:
25
+        batch_op.drop_column('email_invite_token_bcgv18')
26
+        batch_op.drop_column('email_invite_flag_bcgv18')
27
+        batch_op.drop_column('email_invite_date_bcgv18')

+ 3
- 0
c3smembership/__init__.py View File

@@ -86,6 +86,9 @@ def main(global_config, **settings):
86 86
     share_acquisition = ShareAcquisition(ShareRepository)
87 87
     config.registry.share_acquisition = share_acquisition
88 88
 
89
+    from pyramid_mailer import get_mailer
90
+    config.registry.get_mailer = get_mailer
91
+
89 92
     # Membership application process
90 93
     # Step 1 (join.pt): home is /, the membership application form
91 94
     config.add_route('join', '/')

+ 1
- 1
c3smembership/accountants_views.py View File

@@ -383,7 +383,7 @@ def mail_payment_confirmation(request):
383 383
     if 'detail' in request.referrer:
384 384
         return HTTPFound(request.route_url(
385 385
             'detail',
386
-            memberid=request.matchdict['memberid']))
386
+            memberid=member.id))
387 387
     else:
388 388
         return get_dashboard_redirect(request, member.id)
389 389
 

+ 6
- 6
c3smembership/business/membership_application.py View File

@@ -13,15 +13,16 @@ from c3smembership.mail_utils import (
13 13
 from pyramid_mailer.message import Message
14 14
 
15 15
 
16
+_make_signature_confirmation_email = make_signature_confirmation_email
17
+_send_message = send_message
18
+
19
+
16 20
 class MembershipApplication(object):
17 21
     """
18 22
     Provides functionality for the membership application process.
19 23
     """
20 24
 
21 25
     datetime = datetime
22
-    # pylint: disable=invalid-name
23
-    make_signature_confirmation_email = make_signature_confirmation_email
24
-    send_message = send_message
25 26
 
26 27
     def __init__(self, member_repository):
27 28
         """
@@ -138,8 +139,7 @@ class MembershipApplication(object):
138 139
         # - Remove dependency to pyramid_mail and move to separate service.
139 140
         member = self.member_repository.get_member_by_id(member_id)
140 141
         # pylint: disable=too-many-function-args
141
-        email_subject, email_body = self.make_signature_confirmation_email(
142
-            member)
142
+        email_subject, email_body = _make_signature_confirmation_email(member)
143 143
         message = Message(
144 144
             subject=email_subject,
145 145
             sender='yes@c3s.cc',
@@ -147,6 +147,6 @@ class MembershipApplication(object):
147 147
             body=email_body
148 148
         )
149 149
         # pylint: disable=too-many-function-args
150
-        self.send_message(request, message)
150
+        _send_message(request, message)
151 151
         member.signature_confirmed = True
152 152
         member.signature_confirmed_date = self.datetime.now()

+ 18
- 8
c3smembership/business/tests/test_membership_application.py View File

@@ -8,6 +8,8 @@ from datetime import (
8 8
 
9 9
 from unittest import TestCase
10 10
 
11
+import c3smembership.business.membership_application as \
12
+    membership_application_package
11 13
 from c3smembership.business.membership_application import (
12 14
     MembershipApplication,
13 15
 )
@@ -141,10 +143,16 @@ class MemberApplicationTest(TestCase):
141 143
             signature_confirmed=None)
142 144
         member_repository_mock.get_member_by_id.side_effect = [member_mock]
143 145
         membership_application = MembershipApplication(member_repository_mock)
144
-        membership_application.make_signature_confirmation_email = mock.Mock()
145
-        membership_application.make_signature_confirmation_email.side_effect = \
146
+
147
+        make_signature_confirmation_email = mock.Mock()
148
+        make_signature_confirmation_email.side_effect = \
146 149
             [('email subject', 'email body')]
147
-        membership_application.send_message = mock.Mock()
150
+        send_message = mock.Mock()
151
+
152
+        membership_application_package._make_signature_confirmation_email = \
153
+            make_signature_confirmation_email
154
+        membership_application_package._send_message = send_message
155
+
148 156
         membership_application.datetime = mock.Mock()
149 157
         membership_application.datetime.now.side_effect = ['now result']
150 158
 
@@ -152,21 +160,23 @@ class MemberApplicationTest(TestCase):
152 160
             'member id',
153 161
             'pyramid request')
154 162
 
163
+        make_signature_confirmation_email.assert_called_with(member_mock)
155 164
         member_repository_mock.get_member_by_id.assert_called_with('member id')
156 165
         self.assertEqual(
157
-            membership_application.send_message.call_args[0][0],
166
+            send_message.call_args[0][0],
158 167
             'pyramid request')
159 168
         self.assertEqual(
160
-            membership_application.send_message.call_args[0][1].subject,
169
+            send_message.call_args[0][1].subject,
161 170
             'email subject')
162 171
         self.assertEqual(
163
-            membership_application.send_message.call_args[0][1].sender,
172
+            send_message.call_args[0][1].sender,
164 173
             'yes@c3s.cc')
165 174
         self.assertEqual(
166
-            membership_application.send_message.call_args[0][1].recipients,
175
+            send_message.call_args[0][1].recipients,
167 176
             ['jane@example.com'])
168 177
         self.assertEqual(
169
-            membership_application.send_message.call_args[0][1].body,
178
+            send_message.call_args[0][1].body,
170 179
             'email body')
180
+
171 181
         self.assertTrue(member_mock.signature_confirmed)
172 182
         self.assertEqual(member_mock.signature_confirmed_date, 'now result')

+ 14
- 13
c3smembership/invite_members.py View File

@@ -16,6 +16,7 @@ It was then reused for:
16 16
 - BarCamp and General Assembly 2015
17 17
 - BarCamp and General Assembly 2016
18 18
 - BarCamp and General Assembly 2017
19
+- BarCamp and General Assembly 2018
19 20
 
20 21
 How it works
21 22
 ------------
@@ -43,7 +44,7 @@ from pyramid.httpexceptions import HTTPFound
43 44
 from pyramid.view import view_config
44 45
 from pyramid_mailer.message import Message
45 46
 
46
-from c3smembership.invite_members_texts import make_bcga17_invitation_email
47
+from c3smembership.invite_members_texts import make_bcga18_invitation_email
47 48
 from c3smembership.mail_utils import send_message
48 49
 from c3smembership.membership_certificate import make_random_token
49 50
 from c3smembership.models import C3sMember
@@ -82,16 +83,16 @@ def invite_member_bcgv(request):
82 83
         return get_memberhip_listing_redirect(request, member_id)
83 84
 
84 85
     # prepare a random token iff none is set
85
-    if member.email_invite_token_bcgv17 is None:
86
-        member.email_invite_token_bcgv17 = make_random_token()
86
+    if member.email_invite_token_bcgv18 is None:
87
+        member.email_invite_token_bcgv18 = make_random_token()
87 88
     url = URL_PATTERN.format(
88 89
         ticketing_url=request.registry.settings['ticketing.url'],
89
-        token=member.email_invite_token_bcgv17,
90
+        token=member.email_invite_token_bcgv18,
90 91
         email=member.email)
91 92
 
92 93
     LOG.info("mailing event invitation to to member id %s", member.id)
93 94
 
94
-    email_subject, email_body = make_bcga17_invitation_email(member, url)
95
+    email_subject, email_body = make_bcga18_invitation_email(member, url)
95 96
     message = Message(
96 97
         subject=email_subject,
97 98
         sender='yes@c3s.cc',
@@ -104,8 +105,8 @@ def invite_member_bcgv(request):
104 105
     send_message(request, message)
105 106
 
106 107
     # member._token = _looong_token
107
-    member.email_invite_flag_bcgv17 = True
108
-    member.email_invite_date_bcgv17 = datetime.now()
108
+    member.email_invite_flag_bcgv18 = True
109
+    member.email_invite_date_bcgv18 = datetime.now()
109 110
     return get_memberhip_listing_redirect(request, member.id)
110 111
 
111 112
 
@@ -146,16 +147,16 @@ def batch_invite(request):
146 147
 
147 148
     for member in invitees:
148 149
         # prepare a random token iff none is set
149
-        if member.email_invite_token_bcgv17 is None:
150
-            member.email_invite_token_bcgv17 = make_random_token()
150
+        if member.email_invite_token_bcgv18 is None:
151
+            member.email_invite_token_bcgv18 = make_random_token()
151 152
         url = URL_PATTERN.format(
152 153
             ticketing_url=request.registry.settings['ticketing.url'],
153
-            token=member.email_invite_token_bcgv17,
154
+            token=member.email_invite_token_bcgv18,
154 155
             email=member.email)
155 156
 
156 157
         LOG.info("mailing event invitation to to member id %s", member.id)
157 158
 
158
-        email_subject, email_body = make_bcga17_invitation_email(member, url)
159
+        email_subject, email_body = make_bcga18_invitation_email(member, url)
159 160
         message = Message(
160 161
             subject=email_subject,
161 162
             sender='yes@c3s.cc',
@@ -167,8 +168,8 @@ def batch_invite(request):
167 168
         )
168 169
         send_message(request, message)
169 170
 
170
-        member.email_invite_flag_bcgv17 = True
171
-        member.email_invite_date_bcgv17 = datetime.now()
171
+        member.email_invite_flag_bcgv18 = True
172
+        member.email_invite_date_bcgv18 = datetime.now()
172 173
         num_sent += 1
173 174
         ids_sent.append(member.id)
174 175
 

+ 5
- 5
c3smembership/invite_members_texts.py View File

@@ -11,7 +11,7 @@ from c3smembership.mail_utils import (
11 11
 DEBUG = False
12 12
 
13 13
 
14
-def make_bcga17_invitation_email(member, url):
14
+def make_bcga18_invitation_email(member, url):
15 15
     """
16 16
     Create email subject and body for an invitation email for members.
17 17
 
@@ -23,18 +23,18 @@ def make_bcga17_invitation_email(member, url):
23 23
         print(u"the member.locale: {}".format(member.locale))
24 24
         print(u"the url: {}".format(url))
25 25
         print(u"the subject: {}".format(
26
-            get_template_text('bcga2017_invite_subject', member.locale)))
26
+            get_template_text('bcga2018_invite_subject', member.locale)))
27 27
         print(u"the salutation: {}".format(get_salutation(member)))
28 28
         print(u"the footer: {}".format(get_email_footer(member.locale)))
29 29
         print(u"the body: {}".format(
30
-            get_template_text('bcga2017_invite_body', member.locale).format(
30
+            get_template_text('bcga2018_invite_body', member.locale).format(
31 31
                 salutation=get_salutation(member),
32 32
                 invitation_url=url,
33 33
                 footer=get_email_footer(member.locale))))
34 34
     return (
35
-        get_template_text('bcga2017_invite_subject', member.locale).rstrip(
35
+        get_template_text('bcga2018_invite_subject', member.locale).rstrip(
36 36
             '\n'),  # remove newline (\n) from mail subject
37
-        get_template_text('bcga2017_invite_body', member.locale).format(
37
+        get_template_text('bcga2018_invite_body', member.locale).format(
38 38
             salutation=get_salutation(member),
39 39
             invitation_url=url,
40 40
             footer=get_email_footer(member.locale)

+ 13
- 1
c3smembership/membership_list.py View File

@@ -407,9 +407,21 @@ def make_member_view(request):
407 407
             member.is_legalentity = False
408 408
         member.membership_number = C3sMember.get_next_free_membership_number()
409 409
 
410
+        # Currently, the inconsistent data model stores the amount of applied
411
+        # shares in member.num_shares which must be moved to a membership
412
+        # application process property. As the acquisition of shares increases
413
+        # the amount of shares and this is still read from member.num_shares,
414
+        # this value must first be reset to 0 so that it can be increased by
415
+        # the share acquisition. Once the new data model is complete the
416
+        # property num_shares will not exist anymore. Instead, a membership
417
+        # application process stores the number of applied shares and the
418
+        # shares store the number of actual shares.
419
+        num_shares = member.num_shares
420
+        member.num_shares = 0
421
+
410 422
         share_id = request.registry.share_acquisition.create(
411 423
             member.membership_number,
412
-            member.num_shares,
424
+            num_shares,
413 425
             member.membership_date)
414 426
         share_acquisition = request.registry.share_acquisition
415 427
         share_acquisition.set_signature_reception(

+ 7
- 4
c3smembership/models.py View File

@@ -597,6 +597,9 @@ class C3sMember(Base):
597 597
     email_invite_flag_bcgv17 = Column(Boolean, default=False)
598 598
     email_invite_date_bcgv17 = Column(DateTime(), default=datetime(1970, 1, 1))
599 599
     email_invite_token_bcgv17 = Column(Unicode(255))
600
+    email_invite_flag_bcgv18 = Column(Boolean, default=False)
601
+    email_invite_date_bcgv18 = Column(DateTime(), default=datetime(1970, 1, 1))
602
+    email_invite_token_bcgv18 = Column(Unicode(255))
600 603
     # legal entities
601 604
     is_legalentity = Column(Boolean, default=False)
602 605
     court_of_law = Column(Unicode(255))
@@ -854,7 +857,7 @@ class C3sMember(Base):
854 857
             object: C3sMember object
855 858
         """
856 859
         return DBSession.query(cls).filter(
857
-            cls.email_invite_token_bcgv17 == token).first()
860
+            cls.email_invite_token_bcgv18 == token).first()
858 861
 
859 862
     @classmethod
860 863
     def check_for_existing_confirm_code(cls, email_confirm_code):
@@ -922,10 +925,10 @@ class C3sMember(Base):
922 925
             and_(
923 926
                 cls.is_member_filter(),
924 927
                 or_(
925
-                    (cls.email_invite_flag_bcgv17 == 0),
926
-                    (cls.email_invite_flag_bcgv17 == ''),
928
+                    (cls.email_invite_flag_bcgv18 == 0),
929
+                    (cls.email_invite_flag_bcgv18 == ''),
927 930
                     # pylint: disable=singleton-comparison
928
-                    (cls.email_invite_flag_bcgv17 == None),
931
+                    (cls.email_invite_flag_bcgv18 == None),
929 932
                 )
930 933
             )
931 934
         ).slice(0, num).all()

+ 7
- 7
c3smembership/presentation/templates/memberships_list_backend.pt View File

@@ -96,16 +96,16 @@
96 96
                  title="Sort by id: descending"
97 97
                  class="glyphicon glyphicon-chevron-down"></a>
98 98
             </th>
99
-            <!--!
100 99
             <th>
101 100
                 bc &amp; ga<br />
102 101
                 invitation
103 102
             </th>
104
-            -->
103
+            <!--!
105 104
             <th>
106 105
                 dues17<br />
107 106
                 invoice
108 107
             </th>
108
+            -->
109 109
             <th>
110 110
                 certificate
111 111
             </th>
@@ -158,20 +158,19 @@
158 158
                     </a>
159 159
                 </div>
160 160
             </td>
161
-            <!--!
162 161
             <td>
163 162
                 <div tal:condition="member.is_member()" tal:omit-tag="">
164
-                  <a tal:condition="member.email_invite_flag_bcgv17 is not True"
163
+                  <a tal:condition="member.email_invite_flag_bcgv18 is not True"
165 164
                       href="${request.route_url('invite_member', m_id=member.id)}"
166 165
                       title="invitation not sent yet. Click to send!"
167 166
                       class="btn btn-danger"></a>
168
-                  <a tal:condition="member.email_invite_flag_bcgv17 is True"
167
+                  <a tal:condition="member.email_invite_flag_bcgv18 is True"
169 168
                       href="${request.route_url('invite_member', m_id=member.id)}"
170
-                      title="gesendet ${member.email_invite_date_bcgv17.strftime('am %d.%m.%Y um %H:%M')}"
169
+                      title="gesendet ${member.email_invite_date_bcgv18.strftime('am %d.%m.%Y um %H:%M')}"
171 170
                       class="btn btn-success"></a>
172 171
                 </div>
173 172
             </td>
174
-            -->
173
+            <!--!
175 174
             <td>
176 175
                 <div tal:omit-tag="" tal:condition="not member.membership_date > date(2017,12,31) and (member.membership_loss_date is None or member.membership_loss_date >= date(2017,1,1))">
177 176
                     <a tal:condition="member.dues17_invoice is False"
@@ -184,6 +183,7 @@
184 183
                         class="btn btn-success"></a>
185 184
                 </div>
186 185
             </td>
186
+            -->
187 187
             <td>
188 188
                 <div tal:condition="member.is_member()" tal:omit-tag="">
189 189
                   <a tal:condition="not member.certificate_email"

+ 3
- 1
c3smembership/presentation/templates/toolbox.pt View File

@@ -88,6 +88,7 @@
88 88
       </form>
89 89
     </p>
90 90
     -->
91
+    <!--!
91 92
     <h3>Mail Invoices for Membership Dues 2017</h3>
92 93
     <p>
93 94
       <a href="${request.route_url('send_dues17_invoice_batch', number=5)}"
@@ -100,6 +101,7 @@
100 101
         <input type='submit' name='submit'></input>
101 102
       </form>
102 103
     </p>
104
+    -->
103 105
 
104 106
     <h2>Search</h2>
105 107
     <p>
@@ -149,7 +151,7 @@
149 151
       </div>
150 152
     </p>
151 153
 
152
-    <h4>Mail Invitations for GA &amp; BC 2017</h4>
154
+    <h4>Mail Invitations for GA &amp; BC 2018</h4>
153 155
     <p>
154 156
       <a href="${request.route_url('invite_batch', number=5)}"
155 157
          title="Note: change number in URL as appropriate! Default is 5."

+ 192
- 0
c3smembership/templates/mail/bcga2018_invite_body_de.txt View File

@@ -0,0 +1,192 @@
1
+
2
+Hallo {salutation},
3
+
4
+der Verwaltungsrat der
5
+
6
+Cultural Commons Collecting Society SCE
7
+mit beschränkter Haftung
8
+- C3S SCE -
9
+Rochusstr. 44
10
+40479 Düsseldorf
11
+
12
+lädt Dich ein
13
+
14
+* zur 5. ordentlichen Generalversammlung nach § 13 der Satzung der
15
+ C3S SCE [1] am 03.06.2018 und
16
+* zum C3S-Barcamp 2018 am Vortag der Generalversammlung
17
+
18
+Bitte lies den gesamten Einladungstext, er enthält wichtige Hinweise.
19
+
20
+***************************  W I C H T I G  ***************************
21
+Dies ist Dein individueller Link zur Anmeldung:
22
+
23
+  {invitation_url}
24
+
25
+Bitte teile uns dort frühzeitig mit, ob Du teilnimmst oder nicht oder
26
+ob du dich vertreten lassen möchtest.
27
+Auch Absagen sind erwünscht. Auf der verlinkten Seite kannst Du separat
28
+die Teilnahme für die Generalversammlung und das Barcamp bestätigen.
29
+***************************
30
+
31
+Die Generalversammlung
32
+======================
33
+
34
+Die Generalversammlung 2018 der C3S SCE findet in Düsseldorf im
35
+selben Gebäude statt, in dem wir auch unser Büro haben:
36
+
37
+03.06.2018: 5. ordentliche Generalversammlung der C3S SCE
38
+            Rochusstr. 44
39
+            40479 Düsseldorf [2]
40
+            13 - 17 Uhr
41
+            Akkreditierung ab 12 Uhr
42
+            Eintritt frei
43
+
44
+Bitte komme zeitig, um Verzögerungen zu vermeiden, da die Akkreditierung
45
+Zeit benötigt. Die Teilnahme an der GV ist selbstverständlich kostenlos.
46
+
47
+Das Barcamp
48
+===========
49
+
50
+02.06.2018: Barcamp [3]
51
+            Rochusstr. 44
52
+            40479 Düsseldorf [2]
53
+            13 - 19 Uhr
54
+            Eintritt frei
55
+
56
+Wir gehen davon aus, dass es zu einigen Themen mehr Informations-
57
+und Diskussionsbedarf gibt, als wir in der Generalversammlung selbst
58
+unterbringen können. Um die Generalversammlung vorzubereiten und
59
+Diskussionen vorzuverlagern, bieten wir am Vortag ein Barcamp [3] an,
60
+das einen halben Tag lang in mehreren parallelen Slots Zeit für
61
+Diskussion und Austausch bietet. Ziel ist es am Ende des Barcamps kurze 
62
+und verständliche Zusammenfassungen des Besprochenen zu erstellen.
63
+
64
+Dies ist der Link zu unserer wiki-Seite, auf der Du die Themen für das
65
+Barcamp ergänzen und einsehen kannst:
66
+
67
+    https://wiki.c3s.cc/index.php/Themenvorschl%C3%A4ge_BarCamp2018
68
+
69
+Zugangsdaten:
70
+Name: schwarm
71
+Kennwort: letmein
72
+
73
+Nach einem anstrengend-produktiven Barcamp braucht man auch wieder neue
74
+Energiezufuhr. Das wollen wir gerne in der gemeinsamen Runde
75
+bewerkstelligen. Deshalb würde es uns sehr freuen, wenn Du nach dem
76
+Barcamp mit uns gemeinsam Essen gehst -- wahrscheinlich in einem
77
+italienischen Restaurant. In jedem Fall wird es aber eine Auswahl
78
+an vegetarischen Speisen geben. Bitte vermerke das in der Anmeldung,
79
+damit wir entsprechend reservieren können.
80
+
81
+
82
+Agenda der Generalversammlung 2018 der C3S SCE
83
+==============================================
84
+
85
+Begrüßung der Anwesenden
86
+
87
+# 1 Wahl der Versammlungsleitung und de(s/r) Protokollführer(s/in)
88
+
89
+# 2 Genehmigung der Tagesordnung
90
+
91
+# 3 Wiederkehrende Tagesordnungspunkte
92
+
93
+## 3.1 Entgegennahme der Tätigkeitsberichte der geschäftsführenden
94
+       Direktoren und des Verwaltungsrates mit anschließender Aussprache
95
+## 3.2 Feststellung des Jahresabschlusses
96
+## 3.3 Entscheidung über die Verwendung des Jahresüberschusses und die
97
+       Verrechnung des Jahresfehlbetrages
98
+## 3.4 Entlastung der geschäftsführenden Direktoren und des
99
+       Verwaltungsrates
100
+
101
+# 4 Nachwahl von Mitgliedern des Verwaltungsrates
102
+
103
+# 5 Bericht zum Stand des Zulassungsverfahrens beim Deutschen Patent- 
104
+    und Markenamt (DPMA) 
105
+
106
+# 6 Bericht vom C3S-Barcamp 2018
107
+
108
+# 7 Berichte aus den Beratungskommissionen
109
+
110
+## 7.1 Kommission Tarife
111
+## 7.2 Kommission Verteilung und Beitragsordnung 
112
+## 7.3 Kommission Wahrnehmungsverträge
113
+
114
+# 8 Diskussion zu und ggfs. Beschlussfassung über Festlegung einer
115
+    vorläufigen Beitragsordnung
116
+    https://url.c3s.cc/ga5bv8de
117
+
118
+# 9 Diskussionen / Sonstiges
119
+
120
+Beschlussanträge und Anträge zur Änderung der Tagesordnung kannst Du bis
121
+zum 23.05.2018 (Ausschlussfrist 24 Uhr MESZ/CEST, d.h. UTC +2) in Textform
122
+unter agenda@c3s.cc einreichen.
123
+
124
+
125
+Organisatorisches
126
+=================
127
+
128
+Teilnahme
129
+---------
130
+Teilnahmeberechtigt an der Generalversammlung sind nur Mitglieder der
131
+C3S SCE oder Bevollmächtigte nicht anwesender Mitglieder.
132
+
133
+Stellvertretende Bevollmächtigte
134
+--------------------------------
135
+Solltest Du nicht teilnehmen können und stimmberechtigt sein,
136
+also nutzendes Mitglied, kannst Du eine Vollmacht erteilen. Weitere
137
+Informationen hierzu findest du bei der Anmeldung.
138
+
139
+Audio-Protokoll
140
+---------------
141
+Während der Generalversammlung wird ein Audio-Mitschnitt aufgezeichnet, um
142
+ein fehlerfreies Protokoll zu gewährleisten. Der Mitschnitt wird nicht
143
+veröffentlicht, aber intern als Anhang zum Protokoll archiviert.
144
+Wer nicht möchte, dass sein Redebeitrag aufgezeichnet wird, kann dem vor
145
+Beginn seines/ihres Beitrags widersprechen.
146
+
147
+
148
+Warum ist Deine Teilnahme wichtig?
149
+==================================
150
+
151
+Auf dem Barcamp kannst Du ausführlich zu den aktuellen Themen mitdiskutieren
152
+und Dich mit dem Kernteam der C3S austauschen. Die Generalversammlung ist das
153
+Organ, das die grundlegenden Entscheidungen der C3S SCE trifft.
154
+(Gemeinsam mit den anderen Mitgliedern bist Du die Generalversammlung.)
155
+
156
+Das war's! Versorge uns mit Themenvorschlägen, plane Deine Fahrt - dann sehen
157
+wir uns Anfang Juni in Düsseldorf! Bei Fragen kannst Du Dich, wie immer, an
158
+info@c3s.cc wenden.
159
+
160
+Wir freuen uns auf Dich & Deine Ideen!
161
+
162
+Der Verwaltungsrat der C3S SCE
163
+Veit Winkler - Vorsitzender
164
+
165
+
166
+====================
167
+Der Verwaltungsrat der C3S SCE setzt sich zusammen aus:
168
+
169
+Vorsitz:
170
+Veit Winkler, Vorsitzender des VR
171
+Danny Bruder, stellv. Vorsitzender des VR
172
+
173
+Weitere Mitglieder:
174
+* Johanna Breuckmann
175
+* m.eik michalke
176
+* Thomas Mielke 
177
+* Christoph Scheid
178
+* Elmar Schuck
179
+* Holger Schwetter
180
+
181
+====================
182
+
183
+Links:
184
+
185
+[1] Satzung der C3S SCE: https://url.c3s.cc/satzung
186
+[2] Karte Veranstaltungsort:
187
+https://www.openstreetmap.org/node/2679106873#map=19/51.23274/6.78834
188
+[3] Was ist ein Barcamp? https://url.c3s.cc/bcerklaerung
189
+
190
+{footer}
191
+
192
+

+ 191
- 0
c3smembership/templates/mail/bcga2018_invite_body_en.txt View File

@@ -0,0 +1,191 @@
1
+
2
+Hello {salutation},
3
+
4
+the administrative board of the
5
+
6
+CulturalCommons Collecting Society SCE
7
+mit beschränkter Haftung
8
+- C3S SCE -
9
+Rochusstr. 44
10
+40479 Düsseldorf
11
+
12
+invites you
13
+
14
+* to the 5th statutory general assembly, according to § 13 of the
15
+  articles of association of the C3S SCE [1] on 3rd June, 2018 and
16
+* to the C3S barcamp 2018 on the day preceding the general assembly.
17
+
18
+Please read the whole text of the invitation. It contains important
19
+information.
20
+
21
+*************************** Important ***************************
22
+This is your individual registration link:
23
+
24
+  {invitation_url}
25
+
26
+Please use it to inform us early on about whether or not you would
27
+like to participate or if you want to be represented by somebody.
28
+Cancellations are requested as well. You can separately confirm your
29
+participation in the general assembly and the barcamp.
30
+***************************
31
+
32
+
33
+The general assembly
34
+====================
35
+
36
+The general assembly 2018 of the C3S SCE will be held in Düsseldorf
37
+in the building where we also have our office.
38
+
39
+June 3rd, 2018:  5th statutory general assembly of the C3S SCE
40
+                 Rochusstr. 44
41
+                 40479 Düsseldorf [2]
42
+                 1.00 pm - 5.00 pm
43
+                 Accreditation from 12.00 pm
44
+                 Admission free
45
+
46
+Please be punctual in order to avoid delays, because the accreditation
47
+will take some time. Of course, participation is free.
48
+
49
+The Barcamp
50
+===========
51
+
52
+June 2nd, 2018:  Barcamp [3]
53
+                 Rochusstr. 44
54
+                 40479 Düsseldorf [2]
55
+                 1.00 pm - 7.00 pm
56
+                 Admission free
57
+
58
+
59
+Again, we are of the opinion that on some topics there will be more
60
+need for information and discussion than we can deal with in the general
61
+assembly. In order to prepare the general assembly and to move up
62
+debates, we are offering a barcamp [3], where half a day is devoted to
63
+discussions and idea exchange in parallel time slots.
64
+The goal is to produce short and comprehensible summaries of what
65
+has been discussed at the barcamp.
66
+
67
+On the following wiki page you can add to and view the topics for the
68
+barcamp: https://wiki.c3s.cc/index.php/Themenvorschl%C3%A4ge_BarCamp2018
69
+
70
+Login details:
71
+Name: schwarm
72
+Password: letmein
73
+
74
+At the end of an exhausting and productive barcamp, everyone needs new
75
+energy. Therefore we would be happy to have dinner with you after the
76
+barcamp – likely at an Italian restaurant. In any case there will be
77
+vegetarian dishes as well. Please note on registration wether you
78
+want to attend so that we can make reservations accordingly.
79
+
80
+
81
+Agenda of the general assembly 2018 of the C3S SCE
82
+==================================================
83
+
84
+Welcoming address
85
+
86
+# 1 Appointment of the chairperson of the assembly
87
+    and the minute taker
88
+
89
+# 2 Approval of the agenda
90
+
91
+# 3 Recurring items on the agenda
92
+
93
+## 3.1 Acceptance of the progress report of the executive directors and
94
+       the administrative board, followed by debate
95
+## 3.2 Approval of the annual financial statement
96
+## 3.3 Resolution on the appropriation
97
+       of the net income and the offsetting ofthe annual net loss
98
+## 3.4 Discharge of the executive directors and the members of the
99
+       adminstrative board
100
+
101
+# 4 By-election of members of the administrative board
102
+
103
+# 5 Status report regarding the approval procedure by the German Patent 
104
+    and Trade Mark Office
105
+
106
+# 6 Report from the C3S barcamp 2018
107
+
108
+# 7 Reports from the consulting committees
109
+
110
+## 7.1 Tariffs commission
111
+## 7.2 Distribution and contributing rules commission
112
+## 7.3 Collection agreements commission
113
+
114
+# 8 Discussion and, if applicable, resolution on the determination of
115
+    temporary contribution rules
116
+    https://url.c3s.cc/ga5bv8en
117
+
118
+# 9 Discussion / Other issues
119
+
120
+You may contribute written proposals for resolutions and for alterations
121
+of the agenda until 23rd Mai, 2018 (midnight MESZ/CEST, i.e. UTC +2)
122
+to agenda@c3s.cc.
123
+
124
+
125
+Organizational issues
126
+=====================
127
+
128
+Participation
129
+-------------
130
+Participation in the general assembly is limited to members of the C3S
131
+SCE or authorized representatives of absent members.
132
+
133
+Authorized representatives
134
+--------------------------------
135
+If you are an active member and therefore entitled to vote, but unable
136
+to participate, you may grant a power of attorney. Further information
137
+on that will be included in the registration e-mail.
138
+
139
+Audio recording
140
+---------------
141
+During the general assembly, an audio recording will be made in order to
142
+ensure error-free minutes. The recording will not be published but
143
+archived internally as an appendix to the minutes.
144
+
145
+Those who do not wish their speech contributions to be recorded, may
146
+veto before commencing to speak.
147
+
148
+
149
+Why is it important that you participate?
150
+=========================================
151
+
152
+At the barcamp, you will be able to make detailed contributions to the
153
+discussions of the current topics and talk to the core team of the C3S.
154
+The general assembly is the body that takes the fundamental decisions
155
+for the C3S. (You are the general assembly, together with the other
156
+members.)
157
+
158
+That's all! Please let us know your proposals for topics, plan your
159
+trip - and we'll meet in Düsseldorf in June! If you have questions,
160
+you can get in touch, as always, via info@c3s.cc.
161
+
162
+We look forward to you and your ideas!
163
+
164
+For and on behalf of the administrative board of the C3S SCE
165
+Veit Winkler - Chairperson
166
+
167
+===============
168
+The administrative board of the C3S SCE consists of:
169
+
170
+Chairpersons:
171
+Veit Winkler, chairperson of the administrative board
172
+Danny Bruder, deputy chairperson of the administrative board
173
+
174
+Other members:
175
+* Johanna Breuckmann
176
+* m.eik michalke
177
+* Thomas Mielke 
178
+* Christoph Scheid
179
+* Elmar Schuck
180
+* Holger Schwetter
181
+
182
+======================
183
+
184
+Links:
185
+
186
+[1] Articles of association of the C3S SCE: https://url.c3s.cc/statutes
187
+[2] Map of location:
188
+https://www.openstreetmap.org/node/2679106873#map=19/51.23274/6.78834
189
+[3] What is a barcamp? https://url.c3s.cc/bcexplanation
190
+
191
+{footer}

+ 1
- 0
c3smembership/templates/mail/bcga2018_invite_subject_de.txt View File

@@ -0,0 +1 @@
1
+[C3S] Einladung zu Barcamp und Generalversammlung

+ 1
- 0
c3smembership/templates/mail/bcga2018_invite_subject_en.txt View File

@@ -0,0 +1 @@
1
+[C3S] Invitation to Barcamp and General Assembly

+ 4
- 4
c3smembership/tests/test_api_views.py View File

@@ -61,7 +61,7 @@ class TestApiViews(unittest.TestCase):
61 61
             )
62 62
         # pylint: disable=no-member
63 63
         DBSession.add(member1)
64
-        member1.email_invite_token_bcgv17 = u'MEMBERS_TOKEN'
64
+        member1.email_invite_token_bcgv18 = u'MEMBERS_TOKEN'
65 65
         # pylint: disable=no-member
66 66
         DBSession.flush()
67 67
 
@@ -112,7 +112,7 @@ class TestApiViews(unittest.TestCase):
112 112
         # now use the correct auth token
113 113
         _auth_info = {'X-messaging-token': 'SECRETAUTHTOKEN'}
114 114
 
115
-        # ..but a non-existing refcode (email_invite_token_bcgv17)
115
+        # ..but a non-existing refcode (email_invite_token_bcgv18)
116 116
         # returns no user (None)
117 117
         res = self.testapp.put_json(
118 118
             '/lm', dict(token='foo'), headers=_auth_info, status=200)
@@ -124,9 +124,9 @@ class TestApiViews(unittest.TestCase):
124 124
 
125 125
         member1 = C3sMember.get_by_id(1)  # load member from DB for crosscheck
126 126
 
127
-        # now try a valid refcode (email_invite_token_bcgv17)
127
+        # now try a valid refcode (email_invite_token_bcgv18)
128 128
         res2 = self.testapp.put_json(
129
-            '/lm', dict(token=member1.email_invite_token_bcgv17),
129
+            '/lm', dict(token=member1.email_invite_token_bcgv18),
130 130
             headers=_auth_info, status=200)
131 131
         self.assertTrue(json.loads(res2.body)['firstname'], member1.firstname)
132 132
         self.assertTrue(json.loads(res2.body)['lastname'], member1.lastname)

+ 17
- 17
c3smembership/tests/test_invite_member.py View File

@@ -160,15 +160,15 @@ class TestInvitation(unittest.TestCase):
160 160
         Test the invitation procedure for one single member at a time.
161 161
 
162 162
         Load this member from the DB,
163
-        assure the email_invite_flag_bcgv17 and token are not set,
163
+        assure the email_invite_flag_bcgv18 and token are not set,
164 164
         prepare cookies, invite this member,
165
-        assure the email_invite_flag_bcgv17 and token are now set,
165
+        assure the email_invite_flag_bcgv18 and token are now set,
166 166
         """
167 167
         from c3smembership.invite_members import invite_member_bcgv
168 168
 
169 169
         member1 = C3sMember.get_by_id(1)
170
-        self.assertEqual(member1.email_invite_flag_bcgv17, False)
171
-        self.assertTrue(member1.email_invite_token_bcgv17 is None)
170
+        self.assertEqual(member1.email_invite_flag_bcgv18, False)
171
+        self.assertTrue(member1.email_invite_token_bcgv18 is None)
172 172
 
173 173
         req = testing.DummyRequest()
174 174
         # have some cookies
@@ -184,8 +184,8 @@ class TestInvitation(unittest.TestCase):
184 184
         req.matchdict = {'m_id': member1.id}
185 185
         res = invite_member_bcgv(req)
186 186
 
187
-        self.assertEqual(member1.email_invite_flag_bcgv17, True)
188
-        self.assertTrue(member1.email_invite_token_bcgv17 is not None)
187
+        self.assertEqual(member1.email_invite_flag_bcgv18, True)
188
+        self.assertTrue(member1.email_invite_token_bcgv18 is not None)
189 189
 
190 190
         # now really send email
191 191
         self.config.registry.settings['testing.mail_to_console'] = 'false'
@@ -199,23 +199,23 @@ class TestInvitation(unittest.TestCase):
199 199
                         in mailer.outbox[1].subject)
200 200
         self.assertTrue(member1.firstname
201 201
                         in mailer.outbox[1].body)
202
-        self.assertTrue(member1.email_invite_token_bcgv17
202
+        self.assertTrue(member1.email_invite_token_bcgv18
203 203
                         in mailer.outbox[1].body)
204 204
 
205 205
         # now send invitation to english member
206 206
         member2 = C3sMember.get_by_id(2)
207
-        self.assertEqual(member2.email_invite_flag_bcgv17, False)
208
-        self.assertTrue(member2.email_invite_token_bcgv17 is None)
207
+        self.assertEqual(member2.email_invite_flag_bcgv18, False)
208
+        self.assertTrue(member2.email_invite_token_bcgv18 is None)
209 209
         req.matchdict = {'m_id': member2.id}
210 210
         res = invite_member_bcgv(req)
211
-        self.assertEqual(member2.email_invite_flag_bcgv17, True)
212
-        self.assertTrue(member2.email_invite_token_bcgv17 is not None)
211
+        self.assertEqual(member2.email_invite_flag_bcgv18, True)
212
+        self.assertTrue(member2.email_invite_token_bcgv18 is not None)
213 213
         self.assertEqual(len(mailer.outbox), 3)
214 214
         self.assertTrue(u'[C3S] Invitation to Barcamp and General Assembly'
215 215
                         in mailer.outbox[2].subject)
216 216
         self.assertTrue(member2.firstname
217 217
                         in mailer.outbox[2].body)
218
-        self.assertTrue(member2.email_invite_token_bcgv17
218
+        self.assertTrue(member2.email_invite_token_bcgv18
219 219
                         in mailer.outbox[2].body)
220 220
 
221 221
     def test_invitation_batch(self):
@@ -226,8 +226,8 @@ class TestInvitation(unittest.TestCase):
226 226
 
227 227
         members = C3sMember.get_all()
228 228
         for member in members:
229
-            self.assertEqual(member.email_invite_flag_bcgv17, False)
230
-            self.assertTrue(member.email_invite_token_bcgv17 is None)
229
+            self.assertEqual(member.email_invite_flag_bcgv18, False)
230
+            self.assertTrue(member.email_invite_token_bcgv18 is None)
231 231
             self.assertTrue(member.membership_accepted is True)
232 232
 
233 233
         req = testing.DummyRequest()
@@ -284,12 +284,12 @@ class TestInvitation(unittest.TestCase):
284 284
 
285 285
         for member in members:
286 286
             # has been invited
287
-            self.assertEqual(member.email_invite_flag_bcgv17, True)
287
+            self.assertEqual(member.email_invite_flag_bcgv18, True)
288 288
             # has a token
289
-            self.assertTrue(member.email_invite_token_bcgv17 is not None)
289
+            self.assertTrue(member.email_invite_token_bcgv18 is not None)
290 290
             # firstname and token are in email body
291 291
             self.assertTrue(
292 292
                 members[member.id - 1].firstname in mailer.outbox[member.id - 1].body)
293 293
             self.assertTrue(
294
-                members[member.id - 1].email_invite_token_bcgv17 in mailer.outbox[
294
+                members[member.id - 1].email_invite_token_bcgv18 in mailer.outbox[
295 295
                     member.id - 1].body)

+ 292
- 0
c3smembership/tests/test_membership_application.py View File

@@ -0,0 +1,292 @@
1
+# -*- coding: utf-8  -*-
2
+"""
3
+Tests the c3smembership.data.repository.share_repository package.
4
+"""
5
+
6
+import datetime
7
+import re
8
+import transaction
9
+import unittest
10
+
11
+from pyramid import testing
12
+from sqlalchemy import engine_from_config
13
+from sqlalchemy.sql import func
14
+from webtest import TestApp
15
+
16
+from c3smembership import main
17
+from c3smembership.data.model.base import (
18
+    DBSession,
19
+    Base,
20
+)
21
+from c3smembership.models import (
22
+    C3sStaff,
23
+    Group,
24
+    C3sMember,
25
+)
26
+
27
+
28
+class MailerDummy(object):
29
+
30
+    def __init__(self):
31
+        self._email = None
32
+
33
+    def send(self, email):
34
+        self._email = email
35
+
36
+    def get_email(self):
37
+        return self._email
38
+
39
+
40
+class GetMailerDummy(object):
41
+
42
+    def __init__(self):
43
+        self._mailer = MailerDummy()
44
+
45
+    def __call__(self, request):
46
+        return self._mailer
47
+
48
+
49
+class DateTimeDummy(object):
50
+
51
+    def __init__(self, now):
52
+        self._now = now
53
+
54
+    def now(self):
55
+        return self._now
56
+
57
+
58
+class MembershipApplicationTest(unittest.TestCase):
59
+
60
+    def setUp(self):
61
+        my_settings = {
62
+            'sqlalchemy.url': 'sqlite:///:memory:',
63
+            'api_auth_token': u"SECRETAUTHTOKEN",
64
+            'c3smembership.url': u'localhost',
65
+            'testing.mail_to_console': u'false',
66
+        }
67
+        self.config = testing.setUp()
68
+        app = main({}, **my_settings)
69
+        self.get_mailer = GetMailerDummy()
70
+        app.registry.get_mailer = self.get_mailer
71
+        app.registry.membership_application.datetime = DateTimeDummy(
72
+            datetime.datetime(2018, 4, 26, 12, 23, 34))
73
+
74
+        engine = engine_from_config(my_settings)
75
+        DBSession.configure(bind=engine)
76
+        Base.metadata.create_all(engine)
77
+
78
+        with transaction.manager:
79
+            # a group for accountants/staff
80
+            accountants_group = Group(name=u"staff")
81
+            DBSession.add(accountants_group)
82
+            DBSession.flush()
83
+            # staff personnel
84
+            staffer1 = C3sStaff(
85
+                login=u"rut",
86
+                password=u"berries",
87
+                email=u"noreply@c3s.cc",
88
+            )
89
+            staffer1.groups = [accountants_group]
90
+            DBSession.add(accountants_group)
91
+            DBSession.add(staffer1)
92
+            DBSession.flush()
93
+
94
+        self.testapp = TestApp(app)
95
+
96
+    def tearDown(self):
97
+        testing.tearDown()
98
+        DBSession.close()
99
+        DBSession.remove()
100
+
101
+    def _login(self):
102
+        """
103
+        Log into the membership backend
104
+        """
105
+        res = self.testapp.get('/login', status=200)
106
+        self.failUnless('login' in res.body)
107
+        form = res.form
108
+        form['login'] = 'rut'
109
+        form['password'] = 'berries'
110
+        res = form.submit('submit', status=302)
111
+
112
+    def _validate_dashboard_redirect(self, res):
113
+        """
114
+        Validate that res is redirecting to the dashboard
115
+        """
116
+        res = res.follow()  # being redirected to dashboard with parameters
117
+        self.__validate_dashboard(res)
118
+
119
+    def _validate_dashboard(self, res):
120
+        """
121
+        Validate that res is the dashboard
122
+        """
123
+        self.failUnless('Dashboard' in res.body)
124
+
125
+    @classmethod
126
+    def _response_to_bare_text(cls, res):
127
+        html = res.normal_body
128
+        # remove JavaScript
129
+        html = re.sub(re.compile('<script.*</script>'), '', html)
130
+        # remove all tags
131
+        html = re.sub(re.compile('<.*?>'), '', html)
132
+        # remove html characters like &nbsp;
133
+        html = re.sub(re.compile('&[A-Za-z]+;'), '', html)
134
+        return html
135
+
136
+    def test_membership_application(self):
137
+        """
138
+        Test the membership application process.
139
+
140
+         1. Enter applicant data to application form
141
+         2. Verify entered data and confirm
142
+         3. Verify sent confirmation email
143
+         4. Confirm email address via confirmation link
144
+         5. Login to backend
145
+         6. Verify applicant's detail page
146
+         7. Set payment received
147
+         8. Set signature received
148
+         9. Make member
149
+        10. Verify member details
150
+        """
151
+        self.testapp.reset()
152
+
153
+        # 1. Enter applicant data to application form
154
+        res = self.testapp.get('/', status=200)
155
+        properties = {
156
+            'firstname': u'Sönke',
157
+            'lastname': u'Blømqvist',
158
+            'email': u'soenke@example.com',
159
+            'address1': u'℅ Big Boss',
160
+            'address2': u'Håkanvägen 12',
161
+            'postcode': u'ABC1234',
162
+            'city': u'Stockholm',
163
+            'year': u'1980',
164
+            'month': u'01',
165
+            'day': u'02',
166
+            'name_of_colsoc': u'Svenska Tonsättares Internationella Musikbyrå',
167
+            'num_shares': u'15',
168
+            'password': u'worst password ever chosen',
169
+            'password-confirm': u'worst password ever chosen',
170
+        }
171
+        for key, value in properties.iteritems():
172
+            res.form[key] = value
173
+        res.form['country'].select(text=u'Sweden')
174
+        res.form['membership_type'].value__set(u'normal')
175
+        res.form['other_colsoc'].value__set(u'yes')
176
+        res.form['got_statute'].checked = True
177
+        res.form['got_dues_regulations'].checked = True
178
+        res = res.form.submit(u'submit', status=302)
179
+        res = res.follow()
180
+
181
+        # 2. Verify entered data and confirm
182
+        body = self._response_to_bare_text(res)
183
+        self.assertTrue('First Name: Sönke' in body)
184
+        self.assertTrue('Last Name: Blømqvist' in body)
185
+        self.assertTrue('Email Address: soenke@example.com' in body)
186
+        self.assertTrue('Address Line 1: ℅ Big Boss' in body)
187
+        self.assertTrue('Address Line 2: Håkanvägen 12' in body)
188
+        self.assertTrue('Postal Code: ABC1234' in body)
189
+        self.assertTrue('City: Stockholm' in body)
190
+        self.assertTrue('Country: SE' in body)
191
+        self.assertTrue('Date of Birth: 1980-01-02' in body)
192
+        self.assertTrue('Type of Membership:normal' in body)
193
+        self.assertTrue('Member of other Collecting Society: yes' in body)
194
+        self.assertTrue('Membership(s): Svenska Tonsättares Internationella Musikbyrå' in body)
195
+        self.assertTrue('Number of Shares: 15' in body)
196
+        self.assertTrue('Cost of Shares (50 € each): 750 €' in body)
197
+        res = res.forms[1].submit(status=200)
198
+
199
+        # 3. Verify sent confirmation email
200
+        mailer = self.get_mailer(None)
201
+        email = mailer.get_email()
202
+        self.assertEqual(email.recipients, ['soenke@example.com'])
203
+        self.assertEqual(email.subject, 'C3S: confirm your email address and load your PDF')
204
+
205
+        # 4. Confirm email address via confirmation link
206
+        match = re.search(
207
+            'localhost(?P<url>[^\s]+)',
208
+            email.body)
209
+
210
+        self.assertTrue(match is not None)
211
+        res = self.testapp.get(
212
+            match.group('url'),
213
+            status=200)
214
+
215
+        self.assertTrue(u'password in order to verify your email' in res.body)
216
+        res.form['password'] = 'worst password ever chosen'
217
+        res = res.form.submit(u'submit', status=200)
218
+
219
+        # 5. Login to backend
220
+        self.testapp.reset()
221
+        self._login()
222
+
223
+        # 6. Verify applicant's detail page
224
+        member_id = DBSession.query(func.max(C3sMember.id)).scalar()
225
+        res = self.testapp.get('/detail/{0}'.format(member_id), status=200)
226
+
227
+        body = self._response_to_bare_text(res)
228
+        self.assertTrue('firstname Sönke' in body)
229
+        self.assertTrue('lastname Blømqvist' in body)
230
+        self.assertTrue('email soenke@example.com' in body)
231
+        self.assertTrue('email confirmed? Yes' in body)
232
+        self.assertTrue('address1 ℅ Big Boss' in body)
233
+        self.assertTrue('address2 Håkanvägen 12' in body)
234
+        self.assertTrue('postcode ABC1234' in body)
235
+        self.assertTrue('city Stockholm' in body)
236
+        self.assertTrue('country SE' in body)
237
+        self.assertTrue('date_of_birth 1980-01-02' in body)
238
+        self.assertTrue('membership_accepted  No' in body)
239
+        self.assertTrue('entity type Person' in body)
240
+        self.assertTrue('membership type normal' in body)
241
+        self.assertTrue('member_of_colsoc Yes' in body)
242
+        self.assertTrue('name_of_colsoc Svenska Tonsättares Internationella Musikbyrå' in body)
243
+        self.assertTrue('date_of_submission ' in body)
244
+        self.assertTrue('signature received?   Nein' in body)
245
+        self.assertTrue('signature confirmed (mail sent)?No' in body)
246
+        self.assertTrue('payment received?   Nein' in body)
247
+        self.assertTrue('payment confirmed?No' in body)
248
+        self.assertTrue('# shares  total: 15' in body)
249
+        # TODO:
250
+        # - code
251
+        # - locale, set explicitly and test both German and English
252
+        # - date of submission
253
+
254
+        # 7. Set payment received
255
+        res = self.testapp.get(
256
+            '/switch_pay/{0}'.format(member_id),
257
+            headers={'Referer': 'asdf'},
258
+            status=302)
259
+        res = res.follow()
260
+        body = self._response_to_bare_text(res)
261
+        self.assertTrue('payment received?    Ja' in body)
262
+        self.assertTrue('payment reception date 2018-04-26 12:23:34' in body)
263
+
264
+        # 8. Set signature received
265
+        res = self.testapp.get(
266
+            '/switch_sig/{0}'.format(member_id),
267
+            headers={'Referer': 'asdf'},
268
+            status=302)
269
+        res = res.follow()
270
+        body = self._response_to_bare_text(res)
271
+        self.assertTrue('signature received?    Ja' in body)
272
+        self.assertTrue('signature reception date2018-04-26 12:23:34' in body)
273
+
274
+        # 9. Make member
275
+        res = self.testapp.get(
276
+            '/make_member/{0}'.format(member_id),
277
+            headers={'Referer': 'asdf'},
278
+            status=200)
279
+        res.form['membership_date'] = '2018-04-27'
280
+        res = res.form.submit('submit', status=302)
281
+        res = res.follow()
282
+
283
+        # 10. Verify member details
284
+        membership_number = C3sMember.get_next_free_membership_number() - 1
285
+        body = self._response_to_bare_text(res)
286
+        self.assertTrue('membership_accepted  Yes' in body)
287
+        self.assertTrue(
288
+            'membership_number  {0}'.format(membership_number) in body)
289
+        self.assertTrue('membership_date 2018-04-27' in body)
290
+        self.assertTrue('# shares  total: 15' in body)
291
+        self.assertTrue('1 package(s)' in body)
292
+        self.assertTrue('15 shares   (2018-04-27)' in body)

+ 3
- 3
c3smembership/tests/test_models.py View File

@@ -191,17 +191,17 @@ class C3sMembershipModelTests(C3sMembershipModelTestBase):
191 191
 
192 192
     def test_get_by_bcgvtoken(self):
193 193
         """
194
-        test: get one entry by bcgv17 token
194
+        test: get one entry by bcgv18 token
195 195
         """
196 196
         instance = self._make_one()
197 197
         self.session.add(instance)
198
-        instance.email_invite_token_bcgv17 = u'SHINY_TOKEN'
198
+        instance.email_invite_token_bcgv18 = u'SHINY_TOKEN'
199 199
         instance_from_db = self._get_target_class().get_by_bcgvtoken(
200 200
             u'SHINY_TOKEN')
201 201
         self.assertEqual(instance_from_db.firstname, u'SomeFirstnäme')
202 202
         self.assertEqual(instance_from_db.email, u'some@shri.de')
203 203
         self.assertEqual(
204
-            instance_from_db.email_invite_token_bcgv17, u'SHINY_TOKEN')
204
+            instance_from_db.email_invite_token_bcgv18, u'SHINY_TOKEN')
205 205
 
206 206
     def test_get_by_dues15_token(self):
207 207
         """

+ 2
- 0
c3smembership/tests/test_webtest.py View File

@@ -14,6 +14,7 @@ There are two 'areas' covered:
14 14
 # import os
15 15
 import unittest
16 16
 from pyramid import testing
17
+from pyramid_mailer import get_mailer
17 18
 from c3smembership.data.model.base import (
18 19
     DBSession,
19 20
     Base,
@@ -665,6 +666,7 @@ class FunctionalTests(unittest.TestCase):
665 666
 
666 667
         from c3smembership import main
667 668
         app = main({}, **my_settings)
669
+        app.registry.get_mailer = get_mailer
668 670
 
669 671
         from webtest import TestApp
670 672
         self.testapp = TestApp(app)

+ 2
- 3
c3smembership/views/afm.py View File

@@ -36,7 +36,6 @@ from pyramid.i18n import (
36 36
 )
37 37
 from pyramid.httpexceptions import HTTPFound
38 38
 from pyramid.view import view_config
39
-from pyramid_mailer import get_mailer
40 39
 from pyramid_mailer.message import Message
41 40
 
42 41
 from c3smembership.deform_text_input_slider_widget import (
@@ -500,7 +499,7 @@ def success_check_email(request):
500 499
         # we do have valid info from the form in the session (good)
501 500
         appstruct = request.session['appstruct']
502 501
         try:
503
-            mailer = get_mailer(request)
502
+            mailer = request.registry.get_mailer(request)
504 503
         except:
505 504
             return HTTPFound(location=request.route_url('join'))
506 505
 
@@ -696,7 +695,7 @@ def show_success_pdf(request):
696 695
     if 'appstruct' in request.session:
697 696
         # we do have valid info from the form in the session
698 697
         # send mail to accountants // prepare a mailer
699
-        mailer = get_mailer(request)
698
+        mailer = request.registry.get_mailer(request)
700 699
         # prepare mail
701 700
         appstruct = request.session['appstruct']
702 701
         message_recipient = request.registry.settings['c3smembership.mailaddr']

+ 2
- 1
c3smembership/views/tests/test_afm.py View File

@@ -4,6 +4,7 @@ from datetime import date
4 4
 import unittest
5 5
 # from pyramid.config import Configurator
6 6
 from pyramid import testing
7
+from pyramid_mailer import get_mailer
7 8
 from sqlalchemy import engine_from_config
8 9
 from c3smembership.data.model.base import (
9 10
     DBSession,
@@ -53,6 +54,7 @@ class TestViews(unittest.TestCase):
53 54
             'c3smembership.url'] = 'https://yes.c3s.cc'
54 55
         self.config.registry.settings['c3smembership.mailaddr'] = 'c@c3s.cc'
55 56
         self.config.registry.settings['testing.mail_to_console'] = 'false'
57
+        self.config.registry.get_mailer = get_mailer
56 58
 
57 59
         DBSession.remove()
58 60
         self.session = _initTestingDB()
@@ -92,7 +94,6 @@ class TestViews(unittest.TestCase):
92 94
         """
93 95
         from c3smembership.views.afm import success_check_email
94 96
         self.config.add_route('join', '/')
95
-        from pyramid_mailer import get_mailer
96 97
         request = testing.DummyRequest(
97 98
             params={
98 99
                 'appstruct': {

Loading…
Cancel
Save