def create(cls, membership_number, shares_quantity, board_confirmation=None): """ Create a shares package. Args: membership_number: The membership number of the member for which the shares package is created. shares_quantity: The number of shares of the package to be created. board_confirmation: Optional. The date on which the board of directors confirmed the acquisition of the shares package. Returns: The technical primary key of the created shares package. """ shares = Shares( number=shares_quantity, date_of_acquisition=board_confirmation, ) # pylint: disable=no-member member = DBSession.query(C3sMember).filter( C3sMember.membership_number == membership_number).first() # pylint: disable=no-member DBSession.add(shares) member.shares.append(shares) member.num_shares += shares_quantity DBSession.flush() return shares.id
def setUp(self): my_settings = {"sqlalchemy.url": "sqlite:///:memory:", "api_auth_token": u"SECRETAUTHTOKEN"} self.config = testing.setUp() app = main({}, **my_settings) # set up the database engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( # german firstname=u"SomeFirstnäme", lastname=u"SomeLastnäme", email=u"*****@*****.**", address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u"ABCDEFGFOO", password=u"arandompassword", date_of_submission=date.today(), membership_type=u"normal", member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u"23", ) DBSession.add(member1) member1.email_invite_token_bcgv16 = u"MEMBERS_TOKEN" DBSession.flush() self.testapp = TestApp(app)
def setUp(self): my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'api_auth_token': u"SECRETAUTHTOKEN", 'c3smembership.url': u'localhost', 'testing.mail_to_console': u'false', } self.config = testing.setUp() app = main({}, **my_settings) self.get_mailer = GetMailerDummy() app.registry.get_mailer = self.get_mailer app.registry.membership_application.datetime = DateTimeDummy( datetime.datetime(2018, 4, 26, 12, 23, 34)) engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() self.testapp = TestApp(app)
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print("removed old DBSession ==============================") except: # print("no DBSession to remove =============================") pass # try: # os.remove('test_webtest_functional.db') # #print "deleted old test database" # except: # pass # #print "never mind" my_settings = { # 'sqlalchemy.url': 'sqlite:///test_webtest_functional.db', 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.mailaddr': '*****@*****.**', 'testing.mail_to_console': 'false' } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) self.session = DBSession # () Base.metadata.create_all(engine) # dummy database entries for testing with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) DBSession.add(member1) DBSession.flush() from c3smembership import main app = main({}, **my_settings) app.registry.get_mailer = get_mailer from webtest import TestApp self.testapp = TestApp(app)
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print("removed old DBSession ==============================") except: # print("no DBSession to remove =============================") pass # try: # os.remove('test_webtest_functional.db') # #print "deleted old test database" # except: # pass # #print "never mind" my_settings = { # 'sqlalchemy.url': 'sqlite:///test_webtest_functional.db', 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.notification_sender': '*****@*****.**', 'testing.mail_to_console': 'false'} engine = engine_from_config(my_settings) DBSession.configure(bind=engine) self.session = DBSession # () Base.metadata.create_all(engine) # dummy database entries for testing with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) DBSession.add(member1) DBSession.flush() from c3smembership import main app = main({}, **my_settings) app.registry.get_mailer = get_mailer from webtest import TestApp self.testapp = TestApp(app)
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print "closed and removed DBSession" except: pass # print "no session to close" # try: # os.remove('test_webtest_accountants.db') # #print "deleted old test database" # except: # pass # #print "never mind" # self.session = DBSession() my_settings = { # 'sqlalchemy.url': 'sqlite:///test_webtest_accountants.db', 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30' } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: print("could not add group staff.") # pass # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() except: print("it borked! (rut)") # pass from c3smembership import main app = main({}, **my_settings) from webtest import TestApp self.testapp = TestApp(app)
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print "closed and removed DBSession" except: pass # print "no session to close" # try: # os.remove('test_webtest_accountants.db') # #print "deleted old test database" # except: # pass # #print "never mind" # self.session = DBSession() my_settings = { # 'sqlalchemy.url': 'sqlite:///test_webtest_accountants.db', 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30'} engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: print("could not add group staff.") # pass # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() except: print("it borked! (rut)") # pass from c3smembership import main app = main({}, **my_settings) from webtest import TestApp self.testapp = TestApp(app)
def create_general_assembly(cls, number, name, date): """ Create a general assembly Args: number: Integer. The number of the general assembly as a unique identifier. name: String. The name of the general assembly. date: `datetime.date`. The date at which the general assembly takes place. """ assembly = GeneralAssembly(number, name, date) # pylint: disable=no-member DBSession.add(assembly) DBSession.flush()
def invite_member(cls, membership_number, general_assembly_number, token): """ Store the member invitation for the general assembly Args: membership_number: Integer. The membership number of the member for which the general assembly invitation is stored. general_assembly_number: Integer. The number of the general assembly for which the invitation is stored for the member. token: String. The token set to verify the member for API access by the ticketing application. """ # pylint: disable=no-member member = MemberRepository.get_member(membership_number) assembly = cls.get_general_assembly(general_assembly_number) invitation = GeneralAssemblyInvitation( assembly, member, cls.datetime.now(), token) DBSession.add(invitation) DBSession.flush()
def setUp(self): """ set up everything for a test case """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print("removing old DBSession ==============================") except: # print("no DBSession to remove ==============================") pass from sqlalchemy import create_engine # engine = create_engine('sqlite:///test_utils.db') engine = create_engine('sqlite:///:memory:') DBSession.configure(bind=engine) self.session = DBSession # () Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) DBSession.add(member1) DBSession.flush()
def setUp(self): my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'api_auth_token': u"SECRETAUTHTOKEN", } self.config = testing.setUp() app = main({}, **my_settings) # set up the database engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member1.membership_number = 'M1' # pylint: disable=no-member DBSession.add(member1) GeneralAssemblyRepository.invite_member( member1.membership_number, GENERAL_ASSEMBLY, u'MEMBERS_TOKEN') # pylint: disable=no-member DBSession.flush() self.testapp = TestApp(app)
def setUp(self): """ Set up database and engine """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession().close() DBSession.remove() from sqlalchemy import create_engine engine = create_engine('sqlite:///:memory:') DBSession.configure(bind=engine) self.session = DBSession Base.metadata.create_all(engine) with transaction.manager: # German member member1 = C3sMember( firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u'addr one', address2=u'addr two', postcode=u'12345', city=u'Footown Mäh', country=u'Foocountry', locale=u'DE', date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u'GEMA', num_shares=u'23', ) db_session = DBSession() db_session.add(member1) db_session.flush()
def edit_general_assembly(cls, number, name, date): """ Edit a general assembly Args: number: Integer. The number of the general assembly to be edited. name: String. The edited name of the general assembly. date: `datetime.date`. The edited date at which the general assembly takes place. Raises: ValueError: In case the a general assembly with the number given does not exist. """ assembly = cls.get_general_assembly(number) if assembly is None: raise ValueError( 'A general assembly with this number does not exist.') assembly.name = name assembly.date = date # pylint: disable=no-member DBSession.flush()
def setUp(self): my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'api_auth_token': u"SECRETAUTHTOKEN", } self.config = testing.setUp() app = main({}, **my_settings) # set up the database engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) # pylint: disable=no-member DBSession.add(member1) member1.email_invite_token_bcgv18 = u'MEMBERS_TOKEN' # pylint: disable=no-member DBSession.flush() self.testapp = TestApp(app)
def setUp(self): my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'api_auth_token': u"SECRETAUTHTOKEN", 'c3smembership.url': u'localhost', 'c3smembership.notification_sender': u'*****@*****.**', 'c3smembership.status_receiver': u'*****@*****.**', 'testing.mail_to_console': u'false', } self.config = testing.setUp() app = main({}, **my_settings) self.get_mailer = GetMailerDummy() app.registry.get_mailer = self.get_mailer app.registry.membership_application.datetime = DateTimeDummy( datetime.datetime(2018, 4, 26, 12, 23, 34)) engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = Staff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() self.testapp = TestApp(app)
def setUp(self): """ Setup test cases """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession.close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30' } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) # self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() app = main({}, **my_settings) self.testapp = TestApp(app)
def setUp(self): """ Setup test cases """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession.close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30'} engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) # self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = Staff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() app = main({}, **my_settings) self.testapp = TestApp(app)
def main(argv=sys.argv): """ initialize the database """ if len(argv) != 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.create_all(engine) # Setup alembic database migration information. # This creates the alembic_version table in the database # which is the basis for migrations and the "alembic current" # command. alembic_cfg = Config('alembic.ini') command.stamp(alembic_cfg, 'head') # add some content with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() except: # pragma: no cover print("could not add group staff.") # pass with transaction.manager: # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(staffer1) DBSession.flush() except: # pragma: no cover print("it borked! (rut)") # one more staffer with transaction.manager: staffer2 = C3sStaff( login=u"reel", password=u"boo", email=u"*****@*****.**", ) staffer2.groups = [accountants_group] try: DBSession.add(staffer2) DBSession.flush() except: # pragma: no cover print("it borked! (reel)") # a member, actually a membership form submission with transaction.manager: member1 = C3sMember( firstname=u"Firstnäme", # includes umlaut lastname=u"Lastname", email=u"*****@*****.**", password=u"berries", address1=u"address one", address2=u"address two", postcode=u"12345 foo", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date(1971, 02, 03), email_is_confirmed=False, email_confirm_code=u"ABCDEFGHIJ", num_shares=u'10', date_of_submission=datetime.now(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", ) try: DBSession.add(member1) except: # pragma: no cover pass with transaction.manager: normal_de = C3sMember( # german normal firstname=u'Ada Traumhaft', lastname=u'Musiziert', email=u'*****@*****.**', address1=u"Musikergasse 34", address2=u"Hinterhaus", postcode=u"12345", city=u"Foostadt Ada", country=u"Germany", locale=u"de", date_of_birth=date(1971, 3, 4), email_is_confirmed=False, email_confirm_code=u'NORMAL_DE1', password=u'adasrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) normal_en = C3sMember( # english normal firstname=u'James', lastname=u'Musician', email=u'*****@*****.**', address1=u"james addr 1", address2=u"james appartment 2", postcode=u"12345", city=u"Jamestown", country=u"Jamescountry", locale=u"en", date_of_birth=date(1972, 4, 5), email_is_confirmed=False, email_confirm_code=u'NORMAL_DE', password=u'jamesrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"", num_shares=u'2', ) investing_de = C3sMember( # german investing firstname=u'Herman', lastname=u'Investor', email=u'*****@*****.**', address1=u"c/o Mutti", address2=u"addr two4", postcode=u"12344", city=u"Footown M44", country=u"Austria", locale=u"de", date_of_birth=date(1974, 9, 8), email_is_confirmed=False, email_confirm_code=u'INVESTING_DE', password=u'arandompasswor4', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'6', ) investing_en = C3sMember( # english investing firstname=u'Britany', lastname=u'Investing', email=u'*****@*****.**', address1=u"aone5", address2=u"atwo5", postcode=u"12355", city=u"London", country=u"United Kingdom", locale=u"en", date_of_birth=date(1978, 4, 1), email_is_confirmed=False, email_confirm_code=u'INVESTING_EN', password=u'arandompasswor5', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=True, name_of_colsoc=u"", num_shares=u'60', ) legal_entity_de = C3sMember( # german investing legal entity firstname=u'Günther Vorstand', lastname=u'Deutscher Musikverlag', email=u'*****@*****.**', address1=u"Ährenweg 1", address2=u"", postcode=u"98765", city=u"Teststadt", country=u"Germany", locale=u"de", date_of_birth=date(1987, 3, 6), email_is_confirmed=False, email_confirm_code=u'VERLAG_DE', password=u'arandompasswor6', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'60', ) legal_entity_en = C3sMember( # english investing legal entity firstname=u'John BigBoss', lastname=u'Some Company', email=u'*****@*****.**', address1=u"foo boulevard", address2=u"123-345", postcode=u"98765", city=u"London", country=u"United Kingdom", locale=u"en", date_of_birth=date(1982, 4, 2), email_is_confirmed=False, email_confirm_code=u'COMPANY_EN', password=u'arandompasswor6', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'60', ) DBSession.add(normal_de) DBSession.add(normal_en) DBSession.add(investing_de) DBSession.add(investing_en) legal_entity_de.is_legalentity = True DBSession.add(legal_entity_de) legal_entity_en.is_legalentity = True DBSession.add(legal_entity_en)
def setUp(self): """ Setup test cases """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession.close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30' } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) # self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) shares1_m1 = Shares( number=2, date_of_acquisition=date.today(), reference_code=u'ABCDEFGH', signature_received=True, signature_received_date=date(2014, 6, 7), payment_received=True, payment_received_date=date(2014, 6, 8), signature_confirmed=True, signature_confirmed_date=date(2014, 6, 8), payment_confirmed=True, payment_confirmed_date=date(2014, 6, 9), accountant_comment=u'no comment', ) member1.shares = [shares1_m1] shares2_m1 = Shares( number=23, date_of_acquisition=date.today(), reference_code=u'IJKLMNO', signature_received=True, signature_received_date=date(2014, 1, 7), payment_received=True, payment_received_date=date(2014, 1, 8), signature_confirmed=True, signature_confirmed_date=date(2014, 1, 8), payment_confirmed=True, payment_confirmed_date=date(2014, 1, 9), accountant_comment=u'not connected', ) member1.shares.append(shares2_m1) member1.membership_accepted = True member2 = C3sMember( # english firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) founding_member3 = C3sMember( # english firstname=u'BBBSomeFirstnäme', lastname=u'YYYSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCBARdungHH_', password=u'anotherrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) member4_lost = C3sMember( firstname=u'Resigned', lastname=u'Smith', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date(1980, 1, 2), email_is_confirmed=False, email_confirm_code=u'RESIGNEDSMITH', password=u'arandompassword', date_of_submission=date.today() - timedelta(days=370), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) DBSession.add(shares1_m1) DBSession.add(shares2_m1) DBSession.add(member1) DBSession.add(member2) DBSession.add(founding_member3) DBSession.add(member4_lost) app = main({}, **my_settings) self.testapp = TestApp(app)
def test_membership_loss(self): ''' Test the loss of membership. Test cases for: 1 Editing non members 1.1 Loss inputs must be hidden 1.1 Hidden loss inputs should not make any problem and therefore submit without changes should work 1.2 Try setting hidden values -> error 2 Editing members 2.1 Loss inputs must not be hidden 2.2 Loss date and loss type must both be either set or unset 2.2.1 Set only loss date -> error, set both 2.2.2 Set only loss type -> error, set both 2.2.3 Set neither loss date nor type -> success 2.2.4 Set loss date and type to valid values -> success 2.3 Loss date must be larger than acceptance date 2.3.1 Set loss date prior to membership acceptance date -> error, set date larger membership acceptance 2.3.2 Set loss date after membership acceptance date -> success 2.4 Loss date for resignation must be 31st of December 2.4.1 Set loss type to resignation and loss date other than 31st of December -> fail 2.4.2 Set loss type to resignation and loss date to 31st but not December -> fail 2.4.3 Set loss type to resignation and loss date to December but not 31st -> fail 2.4.4 Set loss type to resignation and loss date to 31st of December succeed 2.5 Only natural persons can be set to loss type death 2.5.1 Set loss type to death and entity type to legal entity -> error 2.5.2 Set loss type to death and entity type to natural person -> success 2.6 Only legal entites can be set to loss type winding-up 2.6.1 Set loss type to winding-up and entity type to natural person error 2.6.2 Set loss type to winding-up and entity type to legal entity -> success ''' # setup res = self.testapp.reset() self.__login() member = EditMemberTests.__create_membership_applicant() DBSession.add(member) DBSession.flush() # 1 Editing non members res = self.__get_edit_member(member.id) self.assertFalse(res.form['membership_accepted'].checked) # 1.1 Loss inputs must be hidden res = self.__get_edit_member(member.id) self.assertTrue( type(res.form['membership_loss_date']) == webtest.forms.Hidden) self.assertTrue(res.form['membership_loss_date'].value == '') self.assertTrue( type(res.form['membership_loss_type']) == webtest.forms.Hidden) self.assertTrue(res.form['membership_loss_type'].value == '') # 1.2 Hidden loss inputs should not make any problem and therefore # submit without changes should work res = self.__get_edit_member(member.id) self.__validate_successful_submit(res) # 1.3 Try setting hidden values -> error self.__validate_abortive_edit( member.id, { 'membership_loss_date': date.today(), 'membership_loss_type': u'resignation', }, [u'Please note: There were errors, please check the form below.']) # 2 Editing members member = EditMemberTests.__create_accepted_member_full() DBSession.add(member) DBSession.flush() res = self.__get_edit_member(member.id) # make sure default values are valid self.__validate_successful_submit(res) # 2.1 Loss inputs must not be hidden res = self.__get_edit_member(member.id) self.assertTrue(res.form['membership_accepted'].checked) self.assertTrue( type(res.form['membership_loss_date']) == webtest.forms.Field) self.assertTrue(res.form['membership_loss_date'].value == '') self.assertTrue( type(res.form['membership_loss_type']) == webtest.forms.Select) self.assertTrue(res.form['membership_loss_type'].value == '') # 2.2.1 Set only loss date -> error, set both self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date and type of membership loss must be set both or none.', ]) # 2.2.2 Set only loss type -> error, set both self.__validate_abortive_edit( member.id, { 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date and type of membership loss must be set both or none.', ]) # 2.2.3 Set neither loss date nor type -> success self.__validate_successful_edit( member.id, { 'membership_loss_type': '', 'membership_loss_date': '', }) # 2.2.4 Set loss date and type to valid values -> success self.__validate_successful_edit( member.id, { 'membership_loss_type': 'resignation', 'membership_loss_date': date(2016, 12, 31), }) # 2.3 Loss date must be larger than acceptance date # 2.3.1 Set loss date prior to membership acceptance date -> error, # set date larger membership acceptance self.__validate_abortive_edit( member.id, { 'membership_loss_date': ( member.membership_date - timedelta(days=1)), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date membership loss must be larger than membership ' 'acceptance date.', ]) # 2.3.2 Set loss date after membership acceptance date -> success self.__validate_successful_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), 'membership_loss_type': 'resignation', }) # 2.4 Loss date for resignation must be 31st of December # 2.4.1 Set loss type to resignation and loss date other than 31st # of December -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 5, 28), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Resignations are only allowed to the 31st of December of a ' 'year.', ]) # 2.4.2 Set loss type to resignation and loss date to 31st but not # December -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 10, 31), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Resignations are only allowed to the 31st of December of a ' 'year.', ]) # 2.4.3 Set loss type to resignation and loss date to December but # not 31st -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 12, 30), 'membership_loss_type': 'resignation', }, [u'Resignations are only allowed to the 31st of December of a ' 'year.']) # 2.4.4 Set loss type to resignation and loss date to 31st of # December succeed self.__validate_successful_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), 'membership_loss_type': 'resignation', }) # 2.5 Only natural persons can be set to loss type death # 2.5.1 Set loss type to death and entity type to legal entity -> # error self.__validate_abortive_edit( member.id, { 'entity_type': 'legalentity', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'death', }, [u'The membership loss type \'death\' is only allowed for natural ' u'person members and not for legal entity members.']) # 2.5.2 Set loss type to death and entity type to natural person -> # success self.__validate_successful_edit( member.id, { 'entity_type': 'person', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'death', }) # 2.6 Only legal entites can be set to loss type winding-up # 2.6.1 Set loss type to winding-up and entity type to natural # person error self.__validate_abortive_edit( member.id, { 'entity_type': 'person', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'winding-up', }, [u'The membership loss type \'winding-up\' is only allowed for ' u'legal entity members and not for natural person members.']) # 2.6.2 Set loss type to winding-up and entity type to legal entity # -> success self.__validate_successful_edit( member.id, { 'entity_type': 'legalentity', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'winding-up', })
def _initTestingDB(): my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # There is a side effect of test_initialization.py after which there are # still records in the database although it is setup from scratch. # Therefore, remove all members to have an empty table. members = C3sMember.get_all() for member in members: DBSession.delete(member) DBSession.flush() member1 = C3sMember( # german person firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG1', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) member2 = C3sMember( # english person firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG2', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) member3 = C3sMember( # german legalentity firstname=u'Cooles PlattenLabel', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG3', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'42', ) member4 = C3sMember( # english legalentity firstname=u'Incredible Records', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG4', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'2', ) member1.membership_accepted = True DBSession.add(member1) member2.membership_accepted = True DBSession.add(member2) member3.membership_accepted = True DBSession.add(member3) member4.membership_accepted = True DBSession.add(member4) DBSession.flush() return DBSession
def test_edit_members(self): ''' tests for the edit_member view ''' # unauthorized access must be prevented res = self.testapp.reset() # delete cookie res = self.testapp.get('/edit/1', status=403) self.failUnless('Access was denied to this resource' in res.body) self.__login() # no member in DB, so redirecting to dashboard res = self.testapp.get('/edit/1', status=302) self.__validate_dashboard_redirect(res) member = EditMemberTests.__create_membership_applicant() DBSession.add(member) DBSession.flush() # now there is a member in the DB # let's try invalid input res = self.testapp.get('/edit/foo', status=302) self.__validate_dashboard_redirect(res) # now try valid id res = self.__get_edit_member(member.id) # set the date correctly self.__validate_successful_edit( member.id, { 'firstname': u'EinVörname', 'lastname': u'EinNachname', 'email': u'*****@*****.**', 'address1': u'adressteil 1', 'address2': u'adressteil 2', 'postcode': u'12346', 'city': u'die city', 'country': u'FI', 'membership_type': u'investing', 'entity_type': u'legalentity', 'other_colsoc': u'no', 'name_of_colsoc': u'', 'date_of_birth': '1999-12-30', 'membership_date': '2013-09-24', 'signature_received_date': '2013-09-24', 'payment_received_date': '2013-09-24', }, [ u'EinNachname', u'*****@*****.**', u'adressteil 1', u'adressteil 2', u'12346', u'die city', u'FI', u'investing', ]) # edit again ... changing membership acceptance status self.__validate_successful_edit(member.id, { 'membership_accepted': True, })
def test_join_c3s(self): # setup self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession.close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30' } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() app = main({}, **my_settings) self.testapp = TestApp(app) # sucess for valid entry res = self.testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) res = form.submit(u'submit', status=302) res = res.follow() self.assertTrue('information below to be correct' in res.body) # success for 18th birthday res = self.testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['year'] = unicode(date.today().year - 18) form['month'] = unicode(date.today().month) form['day'] = unicode(date.today().day) res = form.submit(u'submit', status=302) res = res.follow() self.assertTrue('information below to be correct' in res.body) # failure on test one day before 18th birthday res = self.testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['year'] = unicode(date.today().year - 18) form['month'] = unicode(date.today().month) form['day'] = unicode(date.today().day + 1) res = form.submit(u'submit', status=200) self.assertTrue('underaged person is currently not' in res.body) # failure for statute not checked res = self.testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['got_dues_regulations'].value__set(False) res = form.submit(u'submit', status=200) # failure for dues regulations not checked res = self.testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['got_dues_regulations'].value__set(False) res = form.submit(u'submit', status=200) # teardown DBSession.close() DBSession.remove() testing.tearDown()
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') engine = create_engine(u'sqlite://') DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( firstname=u'firsie', lastname=u'lastie', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member2 = C3sMember( # german firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member3 = C3sMember( # german firstname=u'BBBSomeFirstnäme', lastname=u'AAASomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAZ', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=23, ) DBSession.add(member1) DBSession.add(member2) DBSession.add(member3) accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: print("could not add group staff.") # pass # staff personnel staffer1 = Staff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() except: print("it borked! (rut)")
def dues18_reduction(request): """ reduce a members dues upon valid request to do so. * change payable amount for member * cancel old invoice by issuing a cancellation * issue a new invoice with the new amount (if new amount != 0) """ member_id = request.matchdict.get('member_id') member = C3sMember.get_by_id(member_id) # is in database if (member is None or not member.membership_accepted or not member.dues18_invoice): request.session.flash( u"Member not found or not a member or no invoice to reduce", 'dues18_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') # sanity check: the given amount is a positive decimal try: reduced_amount = D(request.POST['amount']) assert not reduced_amount.is_signed() if DEBUG: print("DEBUG: reduction to {}".format(reduced_amount)) except (KeyError, AssertionError): # pragma: no cover request.session.flash( (u"Invalid amount to reduce to: '{}' " u"Use the dot ('.') as decimal mark, e.g. '23.42'".format( request.POST['amount'])), 'dues18_message_to_staff' # message queue for user ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') if DEBUG: print("DEBUG: member.dues18_amount: {}".format( member.dues18_amount)) print("DEBUG: type(member.dues18_amount): {}".format( type(member.dues18_amount))) print("DEBUG: member.dues18_reduced: {}".format( member.dues18_reduced)) print("DEBUG: member.dues18_amount_reduced: {}".format( member.dues18_amount_reduced)) print("DEBUG: type(member.dues18_amount_reduced): {}".format( type(member.dues18_amount_reduced))) # The hidden input 'confirmed' must have the value 'yes' which is set by # the confirmation dialog. reduction_confirmed = request.POST['confirmed'] if reduction_confirmed != 'yes': request.session.flash( u'Die Reduktion wurde nicht bestätigt.', 'dues18_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') # check the reduction amount: same as default calculated amount? if (not member.dues18_reduced and member.dues18_amount == reduced_amount): request.session.flash( u"Dieser Beitrag ist der default-Beitrag!", 'dues18_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') if (member.dues18_reduced and reduced_amount == member.dues18_amount_reduced): request.session.flash( u"Auf diesen Beitrag wurde schon reduziert!", 'dues18_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') if (member.dues18_reduced and reduced_amount > member.dues18_amount_reduced or reduced_amount > member.dues18_amount): request.session.flash( u'Beitrag darf nicht über den berechneten oder bereits' u'reduzierten Wert gesetzt werden.', 'dues18_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', member_id=member.id) + '#dues18') # prepare: get highest invoice no from db max_invoice_no = DuesInvoiceRepository.get_max_invoice_number(2018) # things to be done: # * change dues amount for that member # * cancel old invoice by issuing a reversal invoice # * issue a new invoice with the new amount member.set_dues18_reduced_amount(reduced_amount) request.session.flash('reduction to {}'.format(reduced_amount), 'dues18_message_to_staff') old_invoice = DuesInvoiceRepository.get_by_number( member.dues18_invoice_no, 2018) old_invoice.is_cancelled = True reversal_invoice_amount = -D(old_invoice.invoice_amount) # prepare reversal invoice number new_invoice_no = max_invoice_no + 1 # create reversal invoice reversal_invoice = Dues18Invoice( invoice_no=new_invoice_no, invoice_no_string=( u'C3S-dues2018-' + str(new_invoice_no).zfill(4)) + '-S', invoice_date=datetime.today(), invoice_amount=reversal_invoice_amount.to_eng_string(), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues18_token, ) reversal_invoice.preceding_invoice_no = old_invoice.invoice_no reversal_invoice.is_reversal = True DBSession.add(reversal_invoice) DBSession.flush() old_invoice.succeeding_invoice_no = new_invoice_no # check if this is an exemption (reduction to zero) is_exemption = False # sane default # check if reduction to zero if reduced_amount.is_zero(): is_exemption = True if DEBUG: print("this is an exemption: reduction to zero") else: if DEBUG: print("this is a reduction to {}".format(reduced_amount)) if not is_exemption: # create new invoice new_invoice = Dues18Invoice( invoice_no=new_invoice_no + 1, invoice_no_string=( u'C3S-dues2018-' + str(new_invoice_no + 1).zfill(4)), invoice_date=datetime.today(), invoice_amount=u'' + str(reduced_amount), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues18_token, ) new_invoice.is_altered = True new_invoice.preceding_invoice_no = reversal_invoice.invoice_no reversal_invoice.succeeding_invoice_no = new_invoice_no + 1 DBSession.add(new_invoice) # in the members record, store the current invoice no member.dues18_invoice_no = new_invoice_no + 1 DBSession.flush() # persist newer invoices reversal_url = ( request.route_url( 'make_dues18_reversal_invoice_pdf', email=member.email, code=member.dues18_token, no=str(reversal_invoice.invoice_no).zfill(4) ) ) if is_exemption: email_subject, email_body = make_dues_exemption_email( member, reversal_url) else: invoice_url = ( request.route_url( 'make_dues18_invoice_no_pdf', email=member.email, code=member.dues18_token, i=str(new_invoice_no + 1).zfill(4) ) ) email_subject, email_body = make_dues18_reduction_email( member, new_invoice, invoice_url, reversal_url) message = Message( subject=email_subject, sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[member.email], body=email_body, ) if is_exemption: request.session.flash('exemption email was sent to user!', 'dues18_message_to_staff') else: request.session.flash('update email was sent to user!', 'dues18_message_to_staff') send_message(request, message) return HTTPFound( request.route_url( 'detail', member_id=member_id) + '#dues18')
def init_testing_db(): """ Initializes the memory database with test samples. """ my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # There is a side effect of test_initialization.py after which there # are still records in the database although it is setup from scratch. # Therefore, remove all members to have an empty table. # pylint: disable=no-member members = C3sMember.get_all() for member in members: DBSession.delete(member) DBSession.flush() DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2014, u'1. ordentliche Generalversammlung', date(2014, 8, 23))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2015, u'2. ordentliche Generalversammlung', date(2015, 6, 13))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2015_2, u'Außerordentliche Generalversammlung', date(2015, 7, 16))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2016, u'3. ordentliche Generalversammlung', date(2016, 4, 17))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2017, u'4. ordentliche Generalversammlung', date(2017, 4, 2))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2018, u'5. ordentliche Generalversammlung', date(2018, 6, 3))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2018_2, u'Außerordentliche Generalversammlung', date(2018, 12, 1))) # German person member1 = C3sMember( firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG1', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) # English person member2 = C3sMember( firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG2', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) # German legal entity member3 = C3sMember( firstname=u'Cooles PlattenLabel', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG3', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'42', ) # English legal entity member4 = C3sMember( firstname=u'Incredible Records', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG4', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'2', ) member1.membership_accepted = True member1.membership_number = u'11' DBSession.add(member1) member2.membership_accepted = True member2.membership_number = u'22' DBSession.add(member2) member3.membership_accepted = True member3.membership_number = u'33' DBSession.add(member3) member4.membership_accepted = True member4.membership_number = u'44' DBSession.add(member4) DBSession.flush() return DBSession
def verify_mailaddress_conf(request): ''' Let a prospective member confirm her email address by clicking a link. Needed for applications that came in solely on paper and were digitalized/entered into DB by staff. ''' user_email = request.matchdict['email'] refcode = request.matchdict['refcode'] token = request.matchdict['token'] # try to get entry from DB afm = C3sMember.get_by_code(refcode) if isinstance(afm, NoneType): # no entry? return { 'confirmed': False, 'firstname': 'foo', 'lastname': 'bar', 'result_msg': 'bad URL / bad codes. please contact [email protected]!', } # check token if ('_used' in afm.email_confirm_token): # token was invalidated already return { 'confirmed': False, 'firstname': afm.firstname, 'lastname': afm.lastname, 'result_msg': ('your token is invalid. ' 'please contact [email protected]!'), } try: assert(afm.email_confirm_token in token) assert(token in afm.email_confirm_token) assert(afm.email in user_email) assert(user_email in afm.email) except: return { 'confirmed': False, 'firstname': 'foo', 'lastname': 'bar', 'result_msg': 'bad token/email. please contact [email protected]!', } afm.email_is_confirmed = True afm.email_confirm_token += u'_used' DBSession.flush() # notify staff message = Message( subject='[C3S Yes!] afm email confirmed', sender='*****@*****.**', recipients=[request.registry.settings['c3smembership.mailaddr'], ], body=u'see {}/detail/{}'.format( request.registry.settings['c3smembership.url'], afm.id) ) mailer = get_mailer(request) mailer.send(message) return { 'confirmed': True, 'firstname': afm.firstname, 'lastname': afm.lastname, 'result_msg': u'', }
def test_membership_loss(self): ''' Test the loss of membership. Test cases for: 1 Editing non members 1.1 Loss inputs must be hidden 1.1 Hidden loss inputs should not make any problem and therefore submit without changes should work 1.2 Try setting hidden values -> error 2 Editing members 2.1 Loss inputs must not be hidden 2.2 Loss date and loss type must both be either set or unset 2.2.1 Set only loss date -> error, set both 2.2.2 Set only loss type -> error, set both 2.2.3 Set neither loss date nor type -> success 2.2.4 Set loss date and type to valid values -> success 2.3 Loss date must be larger than acceptance date 2.3.1 Set loss date prior to membership acceptance date -> error, set date larger membership acceptance 2.3.2 Set loss date after membership acceptance date -> success 2.4 Loss date for resignation must be 31st of December 2.4.1 Set loss type to resignation and loss date other than 31st of December -> fail 2.4.2 Set loss type to resignation and loss date to 31st but not December -> fail 2.4.3 Set loss type to resignation and loss date to December but not 31st -> fail 2.4.4 Set loss type to resignation and loss date to 31st of December succeed 2.5 Only natural persons can be set to loss type death 2.5.1 Set loss type to death and entity type to legal entity -> error 2.5.2 Set loss type to death and entity type to natural person -> success 2.6 Only legal entites can be set to loss type winding-up 2.6.1 Set loss type to winding-up and entity type to natural person error 2.6.2 Set loss type to winding-up and entity type to legal entity -> success ''' # setup res = self.testapp.reset() self.__login() member = EditMemberTests.__create_membership_applicant() DBSession.add(member) DBSession.flush() # 1 Editing non members res = self.__get_edit_member(member.id) self.assertFalse(res.form['membership_accepted'].checked) # 1.1 Loss inputs must be hidden res = self.__get_edit_member(member.id) self.assertTrue( type(res.form['membership_loss_date']) == webtest.forms.Hidden) self.assertTrue(res.form['membership_loss_date'].value == '') self.assertTrue( type(res.form['membership_loss_type']) == webtest.forms.Hidden) self.assertTrue(res.form['membership_loss_type'].value == '') # 1.2 Hidden loss inputs should not make any problem and therefore # submit without changes should work res = self.__get_edit_member(member.id) self.__validate_successful_submit(res) # 1.3 Try setting hidden values -> error self.__validate_abortive_edit( member.id, { 'membership_loss_date': date.today(), 'membership_loss_type': u'resignation', }, [u'Please note: There were errors, please check the form below.']) # 2 Editing members member = EditMemberTests.__create_accepted_member_full() DBSession.add(member) DBSession.flush() res = self.__get_edit_member(member.id) # make sure default values are valid self.__validate_successful_submit(res) # 2.1 Loss inputs must not be hidden res = self.__get_edit_member(member.id) self.assertTrue(res.form['membership_accepted'].checked) self.assertTrue( type(res.form['membership_loss_date']) == webtest.forms.Text) self.assertTrue(res.form['membership_loss_date'].value == '') self.assertTrue( type(res.form['membership_loss_type']) == webtest.forms.Select) self.assertTrue(res.form['membership_loss_type'].value == '') # 2.2.1 Set only loss date -> error, set both self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date and type of membership loss must be set both or none.', ]) # 2.2.2 Set only loss type -> error, set both self.__validate_abortive_edit(member.id, { 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date and type of membership loss must be set both or none.', ]) # 2.2.3 Set neither loss date nor type -> success self.__validate_successful_edit(member.id, { 'membership_loss_type': '', 'membership_loss_date': '', }) # 2.2.4 Set loss date and type to valid values -> success self.__validate_successful_edit( member.id, { 'membership_loss_type': 'resignation', 'membership_loss_date': date(2016, 12, 31), }) # 2.3 Loss date must be larger than acceptance date # 2.3.1 Set loss date prior to membership acceptance date -> error, # set date larger membership acceptance self.__validate_abortive_edit( member.id, { 'membership_loss_date': (member.membership_date - timedelta(days=1)), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Date membership loss must be larger than membership ' 'acceptance date.', ]) # 2.3.2 Set loss date after membership acceptance date -> success self.__validate_successful_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), 'membership_loss_type': 'resignation', }) # 2.4 Loss date for resignation must be 31st of December # 2.4.1 Set loss type to resignation and loss date other than 31st # of December -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 5, 28), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Resignations are only allowed to the 31st of December of a ' 'year.', ]) # 2.4.2 Set loss type to resignation and loss date to 31st but not # December -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 10, 31), 'membership_loss_type': 'resignation', }, [ 'Please note: There were errors, please check the form ' 'below.', 'Resignations are only allowed to the 31st of December of a ' 'year.', ]) # 2.4.3 Set loss type to resignation and loss date to December but # not 31st -> fail self.__validate_abortive_edit( member.id, { 'membership_loss_date': date(2016, 12, 30), 'membership_loss_type': 'resignation', }, [ u'Resignations are only allowed to the 31st of December of a ' 'year.' ]) # 2.4.4 Set loss type to resignation and loss date to 31st of # December succeed self.__validate_successful_edit( member.id, { 'membership_loss_date': date(2016, 12, 31), 'membership_loss_type': 'resignation', }) # 2.5 Only natural persons can be set to loss type death # 2.5.1 Set loss type to death and entity type to legal entity -> # error self.__validate_abortive_edit( member.id, { 'entity_type': 'legalentity', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'death', }, [ u'The membership loss type \'death\' is only allowed for natural ' u'person members and not for legal entity members.' ]) # 2.5.2 Set loss type to death and entity type to natural person -> # success self.__validate_successful_edit( member.id, { 'entity_type': 'person', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'death', }) # 2.6 Only legal entites can be set to loss type winding-up # 2.6.1 Set loss type to winding-up and entity type to natural # person error self.__validate_abortive_edit( member.id, { 'entity_type': 'person', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'winding-up', }, [ u'The membership loss type \'winding-up\' is only allowed for ' u'legal entity members and not for natural person members.' ]) # 2.6.2 Set loss type to winding-up and entity type to legal entity # -> success self.__validate_successful_edit( member.id, { 'entity_type': 'legalentity', 'membership_loss_date': date(2016, 3, 25), 'membership_loss_type': 'winding-up', })
def new_member(request): ''' let staff create a new member entry, when receiving input via dead wood ''' # XXX check if submitted, etc... class PersonalData(colander.MappingSchema): """ colander schema for membership application form """ firstname = colander.SchemaNode( colander.String(), title=u'Vorname (b. Körpersch.: Ansprechpartner)', oid="firstname", ) lastname = colander.SchemaNode( colander.String(), title=u'Nachname (b. Körpersch.: Name der Körperschaft)', oid="lastname", ) email = colander.SchemaNode( colander.String(), title=_(u'E-Mail'), validator=colander.Email(), oid="email", ) passwort = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='NoneSet', missing='NoneSetPurposefully' ) address1 = colander.SchemaNode( colander.String(), title='Adresse Zeile 1' ) address2 = colander.SchemaNode( colander.String(), missing=unicode(''), title='Adresse Zeile 2' ) postcode = colander.SchemaNode( colander.String(), title='Postleitzahl', oid="postcode" ) city = colander.SchemaNode( colander.String(), title='Ort', oid="city", ) country = colander.SchemaNode( colander.String(), title='Land', default=country_default, widget=deform.widget.SelectWidget( values=country_codes), oid="country", ) date_of_birth = colander.SchemaNode( colander.Date(), title='Geburtsdatum', # widget=deform.widget.DatePartsWidget( # inline=True), default=date(1970, 1, 1), oid="date_of_birth", ) locale = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='de', missing='de', ) class MembershipInfo(colander.Schema): yes_no = ((u'yes', _(u'Yes')), (u'no', _(u'No')), (u'dontknow', _(u'Unknown')),) entity_type = colander.SchemaNode( colander.String(), title=(u'Person oder Körperschaft?'), description=u'Bitte die Kategorie des Mitglied auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'person', (u'Person')), (u'legalentity', u'Körperschaft'), ), ), missing=unicode(''), oid='entity_type', ) membership_type = colander.SchemaNode( colander.String(), title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'), description=u'Bitte die Art der Mitgliedschaft auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'normal', (u'Normales Mitglied')), (u'investing', u'Investierendes Mitglied'), (u'unknown', u'Unbekannt.'), ), ), missing=unicode(''), oid='membership_type', ) member_of_colsoc = colander.SchemaNode( colander.String(), title='Mitglied einer Verwertungsgesellschaft?', validator=colander.OneOf([x[0] for x in yes_no]), widget=deform.widget.RadioChoiceWidget(values=yes_no), missing=unicode(''), oid="other_colsoc", # validator=colsoc_validator ) name_of_colsoc = colander.SchemaNode( colander.String(), title=(u'Falls ja, welche? (Kommasepariert)'), missing=unicode(''), oid="colsoc_name", # validator=colander.All( # colsoc_validator, # ) ) class Shares(colander.Schema): """ the number of shares a member wants to hold """ num_shares = colander.SchemaNode( colander.Integer(), title='Anzahl Anteile (1-60)', default="1", validator=colander.Range( min=1, max=60, min_err=u'mindestens 1', max_err=u'höchstens 60', ), oid="num_shares") class MembershipForm(colander.Schema): """ The Form consists of - Personal Data - Membership Information - Shares """ person = PersonalData( title=_(u"Personal Data"), # description=_(u"this is a test"), # css_class="thisisjustatest" ) membership_info = MembershipInfo( title=_(u"Membership Requirements") ) shares = Shares( title=_(u"Shares") ) schema = MembershipForm() form = deform.Form( schema, buttons=[ deform.Button('submit', _(u'Submit')), deform.Button('reset', _(u'Reset')) ], use_ajax=True, # renderer=zpt_renderer ) # if the form has NOT been used and submitted, remove error messages if any if 'submit' not in request.POST: request.session.pop_flash() # print('ping!') # if the form has been used and SUBMITTED, check contents if 'submit' in request.POST: controls = request.POST.items() try: appstruct = form.validate(controls) # print("the appstruct from the form: %s \n") % appstruct # for thing in appstruct: # print("the thing: %s") % thing # print("type: %s") % type(thing) # data sanity: if not in collecting society, don't save # collsoc name even if it was supplied through form # if 'no' in appstruct['membership_info']['member_of_colsoc']: # appstruct['membership_info']['name_of_colsoc'] = '' # print appstruct['membership_info']['name_of_colsoc'] # print '-'*80 except ValidationFailure as e: # print("Validation Failure!") # print("the request.POST: %s \n" % request.POST) # for thing in request.POST: # print("the thing: %s") % thing # print("type: %s") % type(thing) # print(e.args) # print(e.error) # print(e.message) request.session.flash( _(u"Please note: There were errors, " "please check the form below."), 'message_above_form', allow_duplicate=False) return{'form': e.render()} def make_random_string(): """ used as email confirmation code """ import random import string return u''.join( random.choice( string.ascii_uppercase + string.digits ) for x in range(10)) # make confirmation code and randomstring = make_random_string() # check if confirmation code is already used while (C3sMember.check_for_existing_confirm_code(randomstring)): # create a new one, if the new one already exists in the database randomstring = make_random_string() # pragma: no cover # to store the data in the DB, an objet is created member = C3sMember( firstname=appstruct['person']['firstname'], lastname=appstruct['person']['lastname'], email=appstruct['person']['email'], password='******', address1=appstruct['person']['address1'], address2=appstruct['person']['address2'], postcode=appstruct['person']['postcode'], city=appstruct['person']['city'], country=appstruct['person']['country'], locale=appstruct['person']['locale'], date_of_birth=appstruct['person']['date_of_birth'], email_is_confirmed=False, email_confirm_code=randomstring, # is_composer=('composer' in appstruct['activity']), # is_lyricist=('lyricist' in appstruct['activity']), # is_producer=('music producer' in appstruct['activity']), # is_remixer=('remixer' in appstruct['activity']), # is_dj=('dj' in appstruct['activity']), date_of_submission=datetime.now(), # invest_member=( # appstruct['membership_info']['invest_member'] == u'yes'), membership_type=appstruct['membership_info']['membership_type'], member_of_colsoc=( appstruct['membership_info']['member_of_colsoc'] == u'yes'), name_of_colsoc=appstruct['membership_info']['name_of_colsoc'], # opt_band=appstruct['opt_band'], # opt_URL=appstruct['opt_URL'], num_shares=appstruct['shares']['num_shares'], ) if 'legalentity' in appstruct['membership_info']['entity_type']: # print "this is a legal entity" member.membership_type = u'investing' member.is_legalentity = True dbsession = DBSession() try: _temp = request.url.split('?')[1].split('=') if 'id' in _temp[0]: _id = _temp[1] # print("the id we want to recreate: %s" % _id) # add a member with a DB id that had seen its entry deleted before _mem = C3sMember.get_by_id(_id) # load from id if isinstance(_mem, NoneType): # check deletion status member.id = _id # set id as specified except: # print "no splitable url params found, creating new entry" pass # add member at next free DB id (default if member.id not set) try: dbsession.add(member) dbsession.flush() # print(member.id) the_new_id = member.id # appstruct['email_confirm_code'] = randomstring # ??? except InvalidRequestError, e: # pragma: no cover print("InvalidRequestError! %s") % e except IntegrityError, ie: # pragma: no cover print("IntegrityError! %s") % ie
def _insert_members(self): with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member2 = C3sMember( # german firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member3 = C3sMember( # german firstname=u'BBBSomeFirstnäme', lastname=u'AAASomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAZ', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) DBSession.add(member1) DBSession.add(member2) DBSession.add(member3) DBSession.flush()
def dues17_reduction(request): """ reduce a members dues upon valid request to do so. * change payable amount for member * cancel old invoice by issuing a cancellation * issue a new invoice with the new amount (if new amount != 0) this will only work for *normal* members. """ # member: sanity checks try: member_id = request.matchdict['member_id'] member = C3sMember.get_by_id(member_id) # is in database assert member.membership_accepted # is a member assert 'investing' not in member.membership_type # is normal member except (KeyError, AssertionError): # pragma: no cover request.session.flash( u"No member OR not accepted OR not normal member", 'dues17_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') # sanity check: the given amount is a positive decimal try: reduced_amount = D(request.POST['amount']) assert not reduced_amount.is_signed() if DEBUG: print("DEBUG: reduction to {}".format(reduced_amount)) except (KeyError, AssertionError): # pragma: no cover request.session.flash( (u"Invalid amount to reduce to: '{}' " u"Use the dot ('.') as decimal mark, e.g. '23.42'".format( request.POST['amount'])), 'dues17_message_to_staff' # message queue for user ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') if DEBUG: print("DEBUG: member.dues17_amount: {}".format(member.dues17_amount)) print("DEBUG: type(member.dues17_amount): {}".format( type(member.dues17_amount))) print("DEBUG: member.dues17_reduced: {}".format(member.dues17_reduced)) print("DEBUG: member.dues17_amount_reduced: {}".format( member.dues17_amount_reduced)) print("DEBUG: type(member.dues17_amount_reduced): {}".format( type(member.dues17_amount_reduced))) # The hidden input 'confirmed' must have the value 'yes' which is set by # the confirmation dialog. reduction_confirmed = request.POST['confirmed'] if reduction_confirmed != 'yes': request.session.flash( u'Die Reduktion wurde nicht bestätigt.', 'dues17_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') # check the reduction amount: same as default calculated amount? if ((member.dues17_reduced is False) and (member.dues17_amount == reduced_amount)): request.session.flash( u"Dieser Beitrag ist der default-Beitrag!", 'dues17_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') if reduced_amount == member.dues17_amount_reduced: request.session.flash( u"Auf diesen Beitrag wurde schon reduziert!", 'dues17_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') if member.dues17_reduced \ and reduced_amount > member.dues17_amount_reduced \ or reduced_amount > member.dues17_amount: request.session.flash( u'Beitrag darf nicht über den berechneten oder bereits' u'reduzierten Wert gesetzt werden.', 'dues17_message_to_staff' # message queue for staff ) return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') # prepare: get highest invoice no from db max_invoice_no = Dues17Invoice.get_max_invoice_no() # things to be done: # * change dues amount for that member # * cancel old invoice by issuing a reversal invoice # * issue a new invoice with the new amount member.set_dues17_reduced_amount(reduced_amount) request.session.flash('reduction to {}'.format(reduced_amount), 'dues17_message_to_staff') old_invoice = Dues17Invoice.get_by_invoice_no(member.dues17_invoice_no) old_invoice.is_cancelled = True reversal_invoice_amount = -D(old_invoice.invoice_amount) # prepare reversal invoice number new_invoice_no = max_invoice_no + 1 # create reversal invoice reversal_invoice = Dues17Invoice( invoice_no=new_invoice_no, invoice_no_string=(u'C3S-dues2017-' + str(new_invoice_no).zfill(4)) + '-S', invoice_date=datetime.today(), invoice_amount=reversal_invoice_amount.to_eng_string(), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues17_token, ) reversal_invoice.preceding_invoice_no = old_invoice.invoice_no reversal_invoice.is_reversal = True DBSession.add(reversal_invoice) DBSession.flush() old_invoice.succeeding_invoice_no = new_invoice_no # check if this is an exemption (reduction to zero) is_exemption = False # sane default # check if reduction to zero if reduced_amount.is_zero(): is_exemption = True if DEBUG: print("this is an exemption: reduction to zero") else: if DEBUG: print("this is a reduction to {}".format(reduced_amount)) if not is_exemption: # create new invoice new_invoice = Dues17Invoice( invoice_no=new_invoice_no + 1, invoice_no_string=(u'C3S-dues2017-' + str(new_invoice_no + 1).zfill(4)), invoice_date=datetime.today(), invoice_amount=u'' + str(reduced_amount), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues17_token, ) new_invoice.is_altered = True new_invoice.preceding_invoice_no = reversal_invoice.invoice_no reversal_invoice.succeeding_invoice_no = new_invoice_no + 1 DBSession.add(new_invoice) # in the members record, store the current invoice no member.dues17_invoice_no = new_invoice_no + 1 DBSession.flush() # persist newer invoices reversal_url = (request.route_url( 'make_dues17_reversal_invoice_pdf', email=member.email, code=member.dues17_token, no=str(reversal_invoice.invoice_no).zfill(4))) if is_exemption: email_subject, email_body = make_dues_exemption_email( member, reversal_url) else: invoice_url = (request.route_url('make_dues17_invoice_no_pdf', email=member.email, code=member.dues17_token, i=str(new_invoice_no + 1).zfill(4))) email_subject, email_body = make_dues17_reduction_email( member, new_invoice, invoice_url, reversal_url) message = Message(subject=email_subject, sender='*****@*****.**', recipients=[member.email], body=email_body, extra_headers={ 'Reply-To': '*****@*****.**', }) if is_exemption: request.session.flash('exemption email was sent to user!', 'dues17_message_to_staff') else: request.session.flash('update email was sent to user!', 'dues17_message_to_staff') send_message(request, message) return HTTPFound( request.route_url('detail', memberid=member_id) + '#dues17')
def new_member(request): ''' Let staff create a new member entry, when receiving input via dead wood ''' class PersonalData(colander.MappingSchema): """ Schema for personal data """ firstname = colander.SchemaNode( colander.String(), title=u'Vorname (b. Körpersch.: Ansprechpartner)', oid="firstname", ) lastname = colander.SchemaNode( colander.String(), title=u'Nachname (b. Körpersch.: Name der Körperschaft)', oid="lastname", ) email = colander.SchemaNode( colander.String(), title=_(u'E-Mail'), validator=colander.Email(), oid="email", ) passwort = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='NoneSet', missing='NoneSetPurposefully' ) address1 = colander.SchemaNode( colander.String(), title='Adresse Zeile 1' ) address2 = colander.SchemaNode( colander.String(), missing=unicode(''), title='Adresse Zeile 2' ) postcode = colander.SchemaNode( colander.String(), title='Postleitzahl', oid="postcode" ) city = colander.SchemaNode( colander.String(), title='Ort', oid="city", ) country = colander.SchemaNode( colander.String(), title='Land', default=COUNTRY_DEFAULT, widget=deform.widget.SelectWidget( values=country_codes), oid="country", ) date_of_birth = colander.SchemaNode( colander.Date(), title='Geburtsdatum', default=date(1970, 1, 1), oid="date_of_birth", ) locale = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='de', missing='de', ) class MembershipInfo(colander.Schema): """ Schema for membership specific information """ yes_no = ((u'yes', _(u'Yes')), (u'no', _(u'No')), (u'dontknow', _(u'Unknown')),) entity_type = colander.SchemaNode( colander.String(), title=(u'Person oder Körperschaft?'), description=u'Bitte die Kategorie des Mitglied auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'person', (u'Person')), (u'legalentity', u'Körperschaft'), ), ), missing=unicode(''), oid='entity_type', ) membership_type = colander.SchemaNode( colander.String(), title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'), description=u'Bitte die Art der Mitgliedschaft auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'normal', (u'Normales Mitglied')), (u'investing', u'Investierendes Mitglied'), (u'unknown', u'Unbekannt.'), ), ), missing=unicode(''), oid='membership_type', ) member_of_colsoc = colander.SchemaNode( colander.String(), title='Mitglied einer Verwertungsgesellschaft?', validator=colander.OneOf([x[0] for x in yes_no]), widget=deform.widget.RadioChoiceWidget(values=yes_no), missing=unicode(''), oid="other_colsoc", ) name_of_colsoc = colander.SchemaNode( colander.String(), title=(u'Falls ja, welche? (Kommasepariert)'), missing=unicode(''), oid="colsoc_name", ) class Shares(colander.Schema): """ the number of shares a member wants to hold """ num_shares = colander.SchemaNode( colander.Integer(), title='Anzahl Anteile (1-60)', default="1", validator=colander.Range( min=1, max=60, min_err=u'mindestens 1', max_err=u'höchstens 60', ), oid="num_shares") class MembershipForm(colander.Schema): """ The Form consists of - Personal Data - Membership Information - Shares """ person = PersonalData( title=_(u"Personal Data"), ) membership_info = MembershipInfo( title=_(u"Membership Requirements") ) shares = Shares( title=_(u"Shares") ) schema = MembershipForm() form = deform.Form( schema, buttons=[ deform.Button('submit', _(u'Submit')), deform.Button('reset', _(u'Reset')) ], use_ajax=True, renderer=ZPT_RENDERER, ) # if the form has NOT been used and submitted, remove error messages if any if 'submit' not in request.POST: request.session.pop_flash() # if the form has been used and SUBMITTED, check contents if 'submit' in request.POST: controls = request.POST.items() try: appstruct = form.validate(controls) except ValidationFailure as exception: request.session.flash( _(u"Please note: There were errors, " "please check the form below."), 'danger', allow_duplicate=False) return{'form': exception.render()} def make_random_string(): """ used as email confirmation code """ return u''.join( random.choice( string.ascii_uppercase + string.digits ) for x in range(10)) randomstring = make_random_string() while C3sMember.check_for_existing_confirm_code(randomstring): randomstring = make_random_string() # to store the data in the DB, an objet is created member = C3sMember( firstname=appstruct['person']['firstname'], lastname=appstruct['person']['lastname'], email=appstruct['person']['email'], password='******', address1=appstruct['person']['address1'], address2=appstruct['person']['address2'], postcode=appstruct['person']['postcode'], city=appstruct['person']['city'], country=appstruct['person']['country'], locale=appstruct['person']['locale'], date_of_birth=appstruct['person']['date_of_birth'], email_is_confirmed=False, email_confirm_code=randomstring, date_of_submission=datetime.now(), membership_type=appstruct['membership_info']['membership_type'], member_of_colsoc=( appstruct['membership_info']['member_of_colsoc'] == u'yes'), name_of_colsoc=appstruct['membership_info']['name_of_colsoc'], num_shares=appstruct['shares']['num_shares'], ) if 'legalentity' in appstruct['membership_info']['entity_type']: member.membership_type = u'investing' member.is_legalentity = True dbsession = DBSession() try: _temp = request.url.split('?')[1].split('=') if 'id' in _temp[0]: _id = _temp[1] # add a member with a DB id that had seen its entry deleted # before _mem = C3sMember.get_by_id(_id) if isinstance(_mem, NoneType): member.id = _id except: pass # add member at next free DB id (default if member.id not set) try: dbsession.add(member) dbsession.flush() the_new_id = member.id except InvalidRequestError as exception: print("InvalidRequestError! %s") % exception except IntegrityError as exception: print("IntegrityError! %s") % exception # redirect to success page, then return the PDF # first, store appstruct in session request.session['appstruct'] = appstruct request.session[ 'appstruct']['locale'] = appstruct['person']['locale'] # empty the messages queue (as validation worked anyways) request.session.pop_flash() return HTTPFound( # redirect to success page location=request.route_url( 'detail', member_id=the_new_id), ) # if the form was submitted and gathered info shown on the success page, # BUT the user wants to correct their information: else: # remove annoying message from other session request.session.pop_flash() if 'appstruct' in request.session: appstruct = request.session['appstruct'] # pre-fill the form with the values from last time form.set_appstruct(appstruct) html = form.render() return {'form': html}
def setUp(self): """ Setup test cases """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession.close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30'} engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) # self._insert_members() with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") DBSession.add(accountants_group) DBSession.flush() # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) shares1_m1 = Shares( number=2, date_of_acquisition=datetime.today(), reference_code=u'ABCDEFGH', signature_received=True, signature_received_date=datetime(2014, 6, 7), payment_received=True, payment_received_date=datetime(2014, 6, 8), signature_confirmed=True, signature_confirmed_date=datetime(2014, 6, 8), payment_confirmed=True, payment_confirmed_date=datetime(2014, 6, 9), accountant_comment=u'no comment', ) member1.shares = [shares1_m1] shares2_m1 = Shares( number=23, date_of_acquisition=datetime.today(), reference_code=u'IJKLMNO', signature_received=True, signature_received_date=datetime(2014, 1, 7), payment_received=True, payment_received_date=datetime(2014, 1, 8), signature_confirmed=True, signature_confirmed_date=datetime(2014, 1, 8), payment_confirmed=True, payment_confirmed_date=datetime(2014, 1, 9), accountant_comment=u'not connected', ) member1.shares.append(shares2_m1) member1.membership_accepted = True member2 = C3sMember( # english firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) founding_member3 = C3sMember( # english firstname=u'BBBSomeFirstnäme', lastname=u'YYYSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCBARdungHH_', password=u'anotherrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) member4_lost = C3sMember( firstname=u'Resigned', lastname=u'Smith', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date(1980, 1, 2), email_is_confirmed=False, email_confirm_code=u'RESIGNEDSMITH', password=u'arandompassword', date_of_submission=date.today() - timedelta(days=370), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'2', ) DBSession.add(shares1_m1) DBSession.add(shares2_m1) DBSession.add(member1) DBSession.add(member2) DBSession.add(founding_member3) DBSession.add(member4_lost) app = main({}, **my_settings) self.testapp = TestApp(app)
def test_join_c3s(self): """ Test the join form """ self.config = testing.setUp() self.config.include('pyramid_mailer.testing') DBSession().close() DBSession.remove() my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30'} engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: accountants_group = Group(name=u"staff") db_session = DBSession() db_session.add(accountants_group) db_session.flush() staffer1 = Staff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] db_session.add(accountants_group) db_session.add(staffer1) db_session.flush() app = main({}, **my_settings) testapp = TestApp(app) # sucess for valid entry res = testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) res = form.submit(u'submit', status=302) res = res.follow() self.assertTrue('information below to be correct' in res.body) # success for 18th birthday res = testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) join.date = DummyDate(date(2018, 4, 29)) form['year'] = u'2000' form['month'] = u'04' form['day'] = u'29' res = form.submit(u'submit', status=302) res = res.follow() self.assertTrue('information below to be correct' in res.body) # failure on test one day before 18th birthday res = testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) join.date = DummyDate(date(2018, 4, 29)) form['year'] = u'2000' form['month'] = u'04' form['day'] = u'30' res = form.submit(u'submit', status=200) self.assertTrue('underaged person is currently not' in res.body) # failure for statute not checked res = testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['got_dues_regulations'].value__set(False) res = form.submit(u'submit', status=200) # failure for dues regulations not checked res = testapp.get('/', status=200) form = self._fill_form_valid_natural(res.form) form['got_dues_regulations'].value__set(False) res = form.submit(u'submit', status=200) # teardown DBSession().close() DBSession.remove() testing.tearDown()
def init_testing_db(): """ Initializes the memory database with test samples. """ my_settings = { 'sqlalchemy.url': 'sqlite:///:memory:', } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: # There is a side effect of test_initialization.py after which there are # still records in the database although it is setup from scratch. # Therefore, remove all members to have an empty table. members = C3sMember.get_all() for member in members: DBSession.delete(member) DBSession.flush() member1 = C3sMember( # german person firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG1', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) member2 = C3sMember( # english person firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG2', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'23', ) member3 = C3sMember( # german legalentity firstname=u'Cooles PlattenLabel', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG3', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'42', ) member4 = C3sMember( # english legalentity firstname=u'Incredible Records', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFG4', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'2', ) member1.membership_accepted = True DBSession.add(member1) member2.membership_accepted = True DBSession.add(member2) member3.membership_accepted = True DBSession.add(member3) member4.membership_accepted = True DBSession.add(member4) DBSession.flush() return DBSession
def staff_view(request): """ This view lets admins edit staff personnel. - edit/change password - delete """ _staffers = Staff.get_all() class Staffer(colander.MappingSchema): """ Staff login schema """ login = colander.SchemaNode( colander.String(), title='login', ) password = colander.SchemaNode( colander.String(), title='passwort', ) schema = Staffer() stafferform = deform.Form( schema, buttons=[ deform.Button('new_staffer', 'save') ] ) if 'action' in request.POST: try: _staffer = Staff.get_by_id(int(request.POST['id'])) except (KeyError, ValueError): return HTTPFound(location=request.route_url('staff')) if request.POST['action'] == u'delete': Staff.delete_by_id(_staffer.id) encrypted = encrypt_with_gnupg('''hi, %s was deleted from the backend by %s. best, your membership tool''' % (_staffer.login, authenticated_userid(request))) message = Message( subject='[C3S Yes] staff was deleted.', sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[request.registry.settings[ 'c3smembership.status_receiver']], body=encrypted ) mailer = get_mailer(request) mailer.send(message) return HTTPFound(location=request.route_url('staff')) elif request.POST['action'] == 'edit': appstruct = { 'login': _staffer.login, 'password': '******', } stafferform.set_appstruct(appstruct) if 'new_staffer' in request.POST: controls = request.POST.items() try: appstruct = stafferform.validate(controls) except ValidationFailure, error: return { 'stafferform': error.render() } existing = Staff.get_by_login(appstruct['login']) if existing is not None: if u'_UNCHANGED_' in appstruct['password']: pass else: existing.password = appstruct['password'] existing.last_password_change = datetime.now() encrypted = encrypt_with_gnupg('''hi, the password of %s was changed by %s. best, your membership tool''' % (existing.login, authenticated_userid(request))) message = Message( subject='[C3S Yes] staff password changed.', sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[request.registry.settings[ 'c3smembership.status_receiver']], body=encrypted ) else: # create new entry staffer = Staff( login=appstruct['login'], password=appstruct['password'], email=u'', ) staffer.groups = [Group.get_staffers_group()] # pylint: disable=no-member DBSession.add(staffer) DBSession.flush() encrypted = encrypt_with_gnupg('''hi, %s was added to the backend by %s. best, your membership tool''' % (staffer.login, authenticated_userid(request))) message = Message( subject='[C3S Yes] staff was added.', sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[request.registry.settings[ 'c3smembership.status_receiver']], body=encrypted ) mailer = get_mailer(request) mailer.send(message) return HTTPFound( request.route_url('staff') )
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') engine = create_engine(u'sqlite://') from c3smembership.models import Base DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( firstname=u'firsie', lastname=u'lastie', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member2 = C3sMember( # german firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) member3 = C3sMember( # german firstname=u'BBBSomeFirstnäme', lastname=u'AAASomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"de", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAZ', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=23, ) DBSession.add(member1) DBSession.add(member2) DBSession.add(member3) accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: print("could not add group staff.") # pass # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() except: print("it borked! (rut)")
def setUp(self): self.config = testing.setUp() self.config.include('pyramid_mailer.testing') try: DBSession.close() DBSession.remove() # print "closed and removed DBSession" except: pass # print "no session to close" # try: # os.remove('test_import.db') # #print "deleted old test database" # except: # pass # #print "never mind" # self.session = DBSession() my_settings = { # 'sqlalchemy.url': 'sqlite:///test_import.db', 'sqlalchemy.url': 'sqlite:///:memory:', 'available_languages': 'da de en es fr', 'c3smembership.dashboard_number': '30', } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: member1 = C3sMember( # german firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) DBSession.add(member1) DBSession.flush() self.m1_last_pw_change = member1.last_password_change with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: # print("could not add group staff.") pass # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(accountants_group) DBSession.add(staffer1) DBSession.flush() except: # print("it borked! (rut)") pass from c3smembership import main app = main({}, **my_settings) app.registry.settings['c3smembership.runmode'] = 'dev' from webtest import TestApp self.testapp = TestApp(app)
def main(argv=sys.argv): """ initialize the database """ if len(argv) != 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.create_all(engine) # Setup alembic database migration information. # This creates the alembic_version table in the database # which is the basis for migrations and the "alembic current" # command. alembic_cfg = Config('alembic.ini') command.stamp(alembic_cfg, 'head') # add some content with transaction.manager: # a group for accountants/staff accountants_group = Group(name=u"staff") try: DBSession.add(accountants_group) DBSession.flush() # print("adding group staff") except: # pragma: no cover print("could not add group staff.") # pass with transaction.manager: # staff personnel staffer1 = C3sStaff( login=u"rut", password=u"berries", email=u"*****@*****.**", ) staffer1.groups = [accountants_group] try: DBSession.add(staffer1) # print("adding staff rut") DBSession.flush() except: # pragma: no cover print("it borked! (rut)") # pass # one more staffer with transaction.manager: staffer2 = C3sStaff( login=u"reel", password=u"boo", email=u"*****@*****.**", ) staffer2.groups = [accountants_group] try: DBSession.add(staffer2) # print("adding staff reel") DBSession.flush() except: # pragma: no cover print("it borked! (reel)") # pass # a member, actually a membership form submission with transaction.manager: member1 = C3sMember( firstname=u"Firstnäme", # includes umlaut lastname=u"Lastname", email=u"*****@*****.**", password=u"berries", address1=u"address one", address2=u"address two", postcode=u"12345 foo", city=u"Footown Mäh", country=u"Foocountry", locale=u"en", date_of_birth=date(1971, 02, 03), email_is_confirmed=False, email_confirm_code=u"ABCDEFGHIJ", num_shares=u'10', date_of_submission=datetime.now(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", ) try: DBSession.add(member1) # print("adding Firstnäme") except: # pragma: no cover pass with transaction.manager: normal_de = C3sMember( # german normal firstname=u'Ada Traumhaft', lastname=u'Musiziert', email=u'*****@*****.**', address1=u"Musikergasse 34", address2=u"Hinterhaus", postcode=u"12345", city=u"Foostadt Ada", country=u"Germany", locale=u"de", date_of_birth=date(1971, 3, 4), email_is_confirmed=False, email_confirm_code=u'NORMAL_DE1', password=u'adasrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=u'23', ) normal_en = C3sMember( # english normal firstname=u'James', lastname=u'Musician', email=u'*****@*****.**', address1=u"james addr 1", address2=u"james appartment 2", postcode=u"12345", city=u"Jamestown", country=u"Jamescountry", locale=u"en", date_of_birth=date(1972, 4, 5), email_is_confirmed=False, email_confirm_code=u'NORMAL_DE', password=u'jamesrandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"", num_shares=u'2', ) investing_de = C3sMember( # german investing firstname=u'Herman', lastname=u'Investor', email=u'*****@*****.**', address1=u"c/o Mutti", address2=u"addr two4", postcode=u"12344", city=u"Footown M44", country=u"Austria", locale=u"de", date_of_birth=date(1974, 9, 8), email_is_confirmed=False, email_confirm_code=u'INVESTING_DE', password=u'arandompasswor4', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'6', ) investing_en = C3sMember( # english investing firstname=u'Britany', lastname=u'Investing', email=u'*****@*****.**', address1=u"aone5", address2=u"atwo5", postcode=u"12355", city=u"London", country=u"United Kingdom", locale=u"en", date_of_birth=date(1978, 4, 1), email_is_confirmed=False, email_confirm_code=u'INVESTING_EN', password=u'arandompasswor5', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=True, name_of_colsoc=u"", num_shares=u'60', ) legal_entity_de = C3sMember( # german investing legal entity firstname=u'Günther Vorstand', lastname=u'Deutscher Musikverlag', email=u'*****@*****.**', address1=u"Ährenweg 1", address2=u"", postcode=u"98765", city=u"Teststadt", country=u"Germany", locale=u"de", date_of_birth=date(1987, 3, 6), email_is_confirmed=False, email_confirm_code=u'VERLAG_DE', password=u'arandompasswor6', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'60', ) legal_entity_en = C3sMember( # english investing legal entity firstname=u'John BigBoss', lastname=u'Some Company', email=u'*****@*****.**', address1=u"foo boulevard", address2=u"123-345", postcode=u"98765", city=u"London", country=u"United Kingdom", locale=u"en", date_of_birth=date(1982, 4, 2), email_is_confirmed=False, email_confirm_code=u'COMPANY_EN', password=u'arandompasswor6', date_of_submission=date.today(), membership_type=u'investing', member_of_colsoc=False, name_of_colsoc=u"", num_shares=u'60', ) DBSession.add(normal_de) DBSession.add(normal_en) DBSession.add(investing_de) DBSession.add(investing_en) legal_entity_de.is_legalentity = True DBSession.add(legal_entity_de) legal_entity_en.is_legalentity = True DBSession.add(legal_entity_en)
def test_edit_members(self): ''' tests for the edit_member view ''' # unauthorized access must be prevented res = self.testapp.reset() # delete cookie res = self.testapp.get('/edit/1', status=403) self.failUnless('Access was denied to this resource' in res.body) self.__login() # no member in DB, so redirecting to dashboard res = self.testapp.get('/edit/1', status=302) self.__validate_dashboard_redirect(res) member = EditMemberTests.__create_membership_applicant() DBSession.add(member) DBSession.flush() # now there is a member in the DB # let's try invalid input res = self.testapp.get('/edit/foo', status=302) self.__validate_dashboard_redirect(res) # now try valid id res = self.__get_edit_member(member.id) # set the date correctly self.__validate_successful_edit( member.id, { 'firstname': u'EinVörname', 'lastname': u'EinNachname', 'email': u'*****@*****.**', 'address1': u'adressteil 1', 'address2': u'adressteil 2', 'postcode': u'12346', 'city': u'die city', 'country': u'FI', 'membership_type': u'investing', 'entity_type': u'legalentity', 'other_colsoc': u'no', 'name_of_colsoc': u'', 'date_of_birth': '1999-12-30', 'membership_date': '2013-09-24', 'signature_received_date': '2013-09-24', 'payment_received_date': '2013-09-24', }, [ u'EinNachname', u'*****@*****.**', u'adressteil 1', u'adressteil 2', u'12346', u'die city', u'FI', u'investing', ]) # edit again ... changing membership acceptance status self.__validate_successful_edit( member.id, { 'membership_accepted': True, })
def send_dues18_invoice_email(request, m_id=None): """ Send email to a member to prompt her to pay the membership dues. - For normal members, also send link to invoice. - For investing members that are legal entities, ask for additional support depending on yearly turnover. This view function works both if called via URL, e.g. /dues_invoice/123 and if called as a function with a member id as parameter. The latter is useful for batch processing. When this function is used for the first time for one member, some database fields are filled: - Invoice number - Invoice amount (calculated from date of membership approval by the board) - Invoice token Also, the database table of invoices (and cancellations) is appended. If this function gets called the second time for a member, no new invoice is produced, but the same mail sent again. """ # either we are given a member id via url or function parameter try: # view was called via http/s member_id = request.matchdict['member_id'] batch = False except KeyError: # ...or was called as function with parameter (see batch) member_id = m_id batch = True try: # get member from DB member = C3sMember.get_by_id(member_id) assert(member is not None) except AssertionError: if not batch: request.session.flash( "member with id {} not found in DB!".format(member_id), 'warning') return HTTPFound(request.route_url('dues')) # sanity check:is this a member? try: assert(member.membership_accepted) # must be accepted member! except AssertionError: request.session.flash( "member {} not accepted by the board!".format(member_id), 'warning') return HTTPFound(request.route_url('dues')) if 'normal' not in member.membership_type and \ 'investing' not in member.membership_type: request.session.flash( 'The membership type of member {0} is not specified! The ' 'membership type must either be "normal" or "investing" in order ' 'to be able to send an invoice email.'.format(member.id), 'warning') return get_memberhip_listing_redirect(request) if member.membership_date >= date(2019, 1, 1) or ( member.membership_loss_date is not None and member.membership_loss_date < date(2018, 1, 1) ): request.session.flash( 'Member {0} was not a member in 2018. Therefore, you cannot send ' 'an invoice for 2018.'.format(member.id), 'warning') return get_memberhip_listing_redirect(request) # check if invoice no already exists. # if yes: just send that email again! # also: offer staffers to cancel this invoice if member.dues18_invoice is True: invoice = DuesInvoiceRepository.get_by_number( member.dues18_invoice_no, 2018) member.dues18_invoice_date = datetime.now() else: # if no invoice already exists: # make dues token and ... randomstring = make_random_string() # check if dues token is already used while DuesInvoiceRepository.token_exists(randomstring, 2018): # create a new one, if the new one already exists in the database randomstring = make_random_string() # pragma: no cover # prepare invoice number try: # either we already have an invoice number for that client... invoice_no = member.dues18_invoice_no assert invoice_no is not None except AssertionError: # ... or we create a new one and save it # get max invoice no from db max_invoice_no = DuesInvoiceRepository.get_max_invoice_number(2018) # use the next free number, save it to db new_invoice_no = int(max_invoice_no) + 1 DBSession.flush() # save dataset to DB # calculate dues amount (maybe partial, depending on quarter) dues_start, dues_amount = calculate_partial_dues18(member) # now we have enough info to update the member info # and persist invoice info for bookkeeping # store some info in DB/member table member.dues18_invoice = True member.dues18_invoice_no = new_invoice_no # irrelevant for investing member.dues18_invoice_date = datetime.now() member.dues18_token = randomstring member.dues18_start = dues_start if 'normal' in member.membership_type: # only for normal members member.set_dues18_amount(dues_amount) # store some more info about invoice in invoice table invoice = Dues18Invoice( invoice_no=member.dues18_invoice_no, invoice_no_string=( u'C3S-dues2018-' + str(member.dues18_invoice_no).zfill(4)), invoice_date=member.dues18_invoice_date, invoice_amount=u'' + str(member.dues18_amount), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues18_token, ) DBSession.add(invoice) DBSession.flush() # now: prepare that email # only normal (not investing) members *have to* pay the dues. # only the normal members get an invoice link and PDF produced for them. # only investing legalentities are asked for more support. if 'investing' not in member.membership_type: start_quarter = string_start_quarter_dues18(member) invoice_url = ( request.route_url( 'make_dues18_invoice_no_pdf', email=member.email, code=member.dues18_token, i=str(member.dues18_invoice_no).zfill(4) ) ) email_subject, email_body = make_dues18_invoice_email( member, invoice, invoice_url, start_quarter) message = Message( subject=email_subject, sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[member.email], body=email_body, ) elif 'investing' in member.membership_type: if member.is_legalentity: email_subject, email_body = \ make_dues_invoice_legalentity_email(member) else: email_subject, email_body = \ make_dues_invoice_investing_email(member) message = Message( subject=email_subject, sender=request.registry.settings[ 'c3smembership.notification_sender'], recipients=[member.email], body=email_body, ) # print to console or send mail if 'true' in request.registry.settings['testing.mail_to_console']: print(message.body.encode('utf-8')) # pragma: no cover else: send_message(request, message) # now choose where to redirect if 'detail' in request.referrer: return HTTPFound( request.route_url( 'detail', member_id=member.id) + '#dues18') if 'dues' in request.referrer: return HTTPFound(request.route_url('dues')) else: return get_memberhip_listing_redirect(request, member.id)
def new_member(request): ''' let staff create a new member entry, when receiving input via dead wood ''' # XXX check if submitted, etc... class PersonalData(colander.MappingSchema): """ colander schema for membership application form """ firstname = colander.SchemaNode( colander.String(), title=u'Vorname (b. Körpersch.: Ansprechpartner)', oid="firstname", ) lastname = colander.SchemaNode( colander.String(), title=u'Nachname (b. Körpersch.: Name der Körperschaft)', oid="lastname", ) email = colander.SchemaNode( colander.String(), title=_(u'E-Mail'), validator=colander.Email(), oid="email", ) passwort = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='NoneSet', missing='NoneSetPurposefully' ) address1 = colander.SchemaNode( colander.String(), title='Adresse Zeile 1' ) address2 = colander.SchemaNode( colander.String(), missing=unicode(''), title='Adresse Zeile 2' ) postcode = colander.SchemaNode( colander.String(), title='Postleitzahl', oid="postcode" ) city = colander.SchemaNode( colander.String(), title='Ort', oid="city", ) country = colander.SchemaNode( colander.String(), title='Land', default=country_default, widget=deform.widget.SelectWidget( values=country_codes), oid="country", ) date_of_birth = colander.SchemaNode( colander.Date(), title='Geburtsdatum', # widget=deform.widget.DatePartsWidget( # inline=True), default=date(1970, 1, 1), validator=Range( min=date(1913, 1, 1), max=date(2000, 1, 1), min_err=_(u'${val} is earlier than earliest date ${min}'), max_err=_(u'${val} is later than latest date ${max}') ), oid="date_of_birth", ) _LOCALE_ = colander.SchemaNode( colander.String(), widget=deform.widget.HiddenWidget(), default='de', missing='de', ) class MembershipInfo(colander.Schema): yes_no = ((u'yes', _(u'Yes')), (u'no', _(u'No')), (u'dontknow', _(u'Unknown')),) entity_type = colander.SchemaNode( colander.String(), title=(u'Person oder Körperschaft?'), description=u'Bitte die Kategorie des Mitglied auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'person', (u'Person')), (u'legalentity', u'Körperschaft'), ), ), missing=unicode(''), oid='entity_type', ) membership_type = colander.SchemaNode( colander.String(), title=(u'Art der Mitgliedschaft (lt. Satzung, §4)'), description=u'Bitte die Art der Mitgliedschaft auswählen.', widget=deform.widget.RadioChoiceWidget( values=( (u'normal', (u'Normales Mitglied')), (u'investing', u'Investierendes Mitglied'), (u'unknown', u'Unbekannt.'), ), ), missing=unicode(''), oid='membership_type', ) member_of_colsoc = colander.SchemaNode( colander.String(), title='Mitglied einer Verwertungsgesellschaft?', validator=colander.OneOf([x[0] for x in yes_no]), widget=deform.widget.RadioChoiceWidget(values=yes_no), missing=unicode(''), oid="other_colsoc", # validator=colsoc_validator ) name_of_colsoc = colander.SchemaNode( colander.String(), title=(u'Falls ja, welche? (Kommasepariert)'), missing=unicode(''), oid="colsoc_name", # validator=colander.All( # colsoc_validator, # ) ) class Shares(colander.Schema): """ the number of shares a member wants to hold """ num_shares = colander.SchemaNode( colander.Integer(), title='Anzahl Anteile (1-60)', default="1", validator=colander.Range( min=1, max=60, min_err=u'mindestens 1', max_err=u'höchstens 60', ), oid="num_shares") class MembershipForm(colander.Schema): """ The Form consists of - Personal Data - Membership Information - Shares """ person = PersonalData( title=_(u"Personal Data"), # description=_(u"this is a test"), # css_class="thisisjustatest" ) membership_info = MembershipInfo( title=_(u"Membership Requirements") ) shares = Shares( title=_(u"Shares") ) schema = MembershipForm() form = deform.Form( schema, buttons=[ deform.Button('submit', _(u'Submit')), deform.Button('reset', _(u'Reset')) ], use_ajax=True, # renderer=zpt_renderer ) # if the form has NOT been used and submitted, remove error messages if any if 'submit' not in request.POST: request.session.pop_flash() # print('ping!') # if the form has been used and SUBMITTED, check contents if 'submit' in request.POST: controls = request.POST.items() try: appstruct = form.validate(controls) # print("the appstruct from the form: %s \n") % appstruct # for thing in appstruct: # print("the thing: %s") % thing # print("type: %s") % type(thing) # data sanity: if not in collecting society, don't save # collsoc name even if it was supplied through form # if 'no' in appstruct['membership_info']['member_of_colsoc']: # appstruct['membership_info']['name_of_colsoc'] = '' # print appstruct['membership_info']['name_of_colsoc'] # print '-'*80 except ValidationFailure as e: # print("Validation Failure!") # print("the request.POST: %s \n" % request.POST) # for thing in request.POST: # print("the thing: %s") % thing # print("type: %s") % type(thing) # print(e.args) # print(e.error) # print(e.message) request.session.flash( _(u"Please note: There were errors, " "please check the form below."), 'message_above_form', allow_duplicate=False) return{'form': e.render()} def make_random_string(): """ used as email confirmation code """ import random import string return u''.join( random.choice( string.ascii_uppercase + string.digits ) for x in range(10)) # make confirmation code and randomstring = make_random_string() # check if confirmation code is already used while (C3sMember.check_for_existing_confirm_code(randomstring)): # create a new one, if the new one already exists in the database randomstring = make_random_string() # pragma: no cover # to store the data in the DB, an objet is created member = C3sMember( firstname=appstruct['person']['firstname'], lastname=appstruct['person']['lastname'], email=appstruct['person']['email'], password='******', address1=appstruct['person']['address1'], address2=appstruct['person']['address2'], postcode=appstruct['person']['postcode'], city=appstruct['person']['city'], country=appstruct['person']['country'], locale=appstruct['person']['_LOCALE_'], date_of_birth=appstruct['person']['date_of_birth'], email_is_confirmed=False, email_confirm_code=randomstring, # is_composer=('composer' in appstruct['activity']), # is_lyricist=('lyricist' in appstruct['activity']), # is_producer=('music producer' in appstruct['activity']), # is_remixer=('remixer' in appstruct['activity']), # is_dj=('dj' in appstruct['activity']), date_of_submission=datetime.now(), # invest_member=( # appstruct['membership_info']['invest_member'] == u'yes'), membership_type=appstruct['membership_info']['membership_type'], member_of_colsoc=( appstruct['membership_info']['member_of_colsoc'] == u'yes'), name_of_colsoc=appstruct['membership_info']['name_of_colsoc'], # opt_band=appstruct['opt_band'], # opt_URL=appstruct['opt_URL'], num_shares=appstruct['shares']['num_shares'], ) if 'legalentity' in appstruct['membership_info']['entity_type']: # print "this is a legal entity" member.membership_type = u'investing' member.is_legalentity = True dbsession = DBSession() try: _temp = request.url.split('?')[1].split('=') if 'id' in _temp[0]: _id = _temp[1] # print("the id we want to recreate: %s" % _id) # add a member with a DB id that had seen its entry deleted before _mem = C3sMember.get_by_id(_id) # load from id if isinstance(_mem, NoneType): # check deletion status member.id = _id # set id as specified except: # print "no splitable url params found, creating new entry" pass # add member at next free DB id (default if member.id not set) try: dbsession.add(member) dbsession.flush() # print(member.id) the_new_id = member.id # appstruct['email_confirm_code'] = randomstring # ??? except InvalidRequestError, e: # pragma: no cover print("InvalidRequestError! %s") % e except IntegrityError, ie: # pragma: no cover print("IntegrityError! %s") % ie
def setUp(self): """ Set up tests """ datetime_mock = mock.Mock() GeneralAssemblyRepository.datetime = datetime_mock # pylint: disable=no-member my_settings = {'sqlalchemy.url': 'sqlite:///:memory:', } engine = engine_from_config(my_settings) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2014, u'1. ordentliche Generalversammlung', date(2014, 8, 23))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2015, u'2. ordentliche Generalversammlung', date(2015, 6, 13))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2015_2, u'Außerordentliche Generalversammlung', date(2015, 7, 16))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2016, u'3. ordentliche Generalversammlung', date(2016, 4, 17))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2017, u'4. ordentliche Generalversammlung', date(2017, 4, 2))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2018, u'5. ordentliche Generalversammlung', date(2018, 6, 3))) DBSession.add(GeneralAssembly( GENERAL_ASSEMBLY_NUMBER_2018_2, u'Außerordentliche Generalversammlung', date(2018, 12, 1))) member1 = C3sMember( firstname=u'SomeFirstnäme', lastname=u'SomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGFOO', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=35, ) member2 = C3sMember( firstname=u'AAASomeFirstnäme', lastname=u'XXXSomeLastnäme', email=u'*****@*****.**', address1=u"addr one", address2=u"addr two", postcode=u"12345", city=u"Footown Mäh", country=u"Foocountry", locale=u"DE", date_of_birth=date.today(), email_is_confirmed=False, email_confirm_code=u'ABCDEFGBAR', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=45, ) member3 = C3sMember( firstname=u'Not invited', lastname=u'at all', email=u'*****@*****.**', address1=u'Some', address2=u'Address', postcode=u'45678', city=u'Hamburg', country=u'Germany', locale=u'DE', date_of_birth=date(1980, 1, 2), email_is_confirmed=False, email_confirm_code=u'member3', password=u'arandompassword', date_of_submission=date.today(), membership_type=u'normal', member_of_colsoc=True, name_of_colsoc=u"GEMA", num_shares=45, ) member1.membership_number = u'member_1' member1.membership_date = date(2018, 1, 1) member1.membership_accepted = True DBSession.add(member1) datetime_mock.now.side_effect = [datetime(2018, 9, 1, 23, 5, 15)] GeneralAssemblyRepository.invite_member( 'member_1', GENERAL_ASSEMBLY_NUMBER_2018_2, u'test_token_1') member2.membership_number = u'member_2' member2.membership_date = date(2017, 1, 1) member2.membership_accepted = True DBSession.add(member2) datetime_mock.now.side_effect = [datetime(2018, 9, 2, 22, 3, 10)] GeneralAssemblyRepository.invite_member( 'member_2', GENERAL_ASSEMBLY_NUMBER_2018_2, u'test_token_2') member3.membership_number = u'member_3' member3.membership_date = date(2016, 1, 1) member3.membership_accepted = True DBSession.add(member3) DBSession.flush()
def send_dues17_invoice_email(request, m_id=None): """ Send email to a member to prompt her to pay the membership dues. - For normal members, also send link to invoice. - For investing members that are legal entities, ask for additional support depending on yearly turnover. This view function works both if called via URL, e.g. /dues_invoice/123 and if called as a function with a member id as parameter. The latter is useful for batch processing. When this function is used for the first time for one member, some database fields are filled: - Invoice number - Invoice amount (calculated from date of membership approval by the board) - Invoice token Also, the database table of invoices (and cancellations) is appended. If this function gets called the second time for a member, no new invoice is produced, but the same mail sent again. """ # either we are given a member id via url or function parameter try: # view was called via http/s member_id = request.matchdict['member_id'] batch = False except KeyError: # ...or was called as function with parameter (see batch) member_id = m_id batch = True try: # get member from DB member = C3sMember.get_by_id(member_id) assert (member is not None) except AssertionError: if not batch: request.session.flash( "member with id {} not found in DB!".format(member_id), 'message_to_staff') return HTTPFound(request.route_url('toolbox')) # sanity check:is this a member? try: assert (member.membership_accepted) # must be accepted member! except AssertionError: request.session.flash( "member {} not accepted by the board!".format(member_id), 'message_to_staff') return HTTPFound(request.route_url('toolbox')) if 'normal' not in member.membership_type and \ 'investing' not in member.membership_type: request.session.flash( 'The membership type of member {0} is not specified! The ' 'membership type must either be "normal" or "investing" in order ' 'to be able to send an invoice email.'.format(member.id), 'message_to_staff') return get_memberhip_listing_redirect(request) if member.membership_date >= date(2018,1,1) or ( \ member.membership_loss_date is not None and member.membership_loss_date < date(2017,1,1) ): request.session.flash( 'Member {0} was not a member in 2017. Therefore, you cannot send ' 'an invoice for 2017.'.format(member.id), 'message_to_staff') return get_memberhip_listing_redirect(request) # check if invoice no already exists. # if yes: just send that email again! # also: offer staffers to cancel this invoice if member.dues17_invoice is True: invoice = Dues17Invoice.get_by_invoice_no(member.dues17_invoice_no) member.dues17_invoice_date = datetime.now() else: # if no invoice already exists: # make dues token and ... randomstring = make_random_string() # check if dues token is already used while (Dues17Invoice.check_for_existing_dues17_token(randomstring)): # create a new one, if the new one already exists in the database randomstring = make_random_string() # pragma: no cover # prepare invoice number try: # either we already have an invoice number for that client... invoice_no = member.dues17_invoice_no assert invoice_no is not None except AssertionError: # ... or we create a new one and save it # get max invoice no from db max_invoice_no = Dues17Invoice.get_max_invoice_no() # use the next free number, save it to db new_invoice_no = int(max_invoice_no) + 1 DBSession.flush() # save dataset to DB # calculate dues amount (maybe partial, depending on quarter) dues_start, dues_amount = calculate_partial_dues17(member) # now we have enough info to update the member info # and persist invoice info for bookkeeping # store some info in DB/member table member.dues17_invoice = True member.dues17_invoice_no = new_invoice_no # irrelevant for investing member.dues17_invoice_date = datetime.now() member.dues17_token = randomstring member.dues17_start = dues_start if 'normal' in member.membership_type: # only for normal members member.set_dues17_amount(dues_amount) # store some more info about invoice in invoice table invoice = Dues17Invoice( invoice_no=member.dues17_invoice_no, invoice_no_string=(u'C3S-dues2017-' + str(member.dues17_invoice_no).zfill(4)), invoice_date=member.dues17_invoice_date, invoice_amount=u'' + str(member.dues17_amount), member_id=member.id, membership_no=member.membership_number, email=member.email, token=member.dues17_token, ) DBSession.add(invoice) DBSession.flush() # now: prepare that email # only normal (not investing) members *have to* pay the dues. # only the normal members get an invoice link and PDF produced for them. # only investing legalentities are asked for more support. if 'investing' not in member.membership_type: start_quarter = string_start_quarter_dues17(member) invoice_url = (request.route_url( 'make_dues17_invoice_no_pdf', email=member.email, code=member.dues17_token, i=str(member.dues17_invoice_no).zfill(4))) email_subject, email_body = make_dues17_invoice_email( member, invoice, invoice_url, start_quarter) message = Message(subject=email_subject, sender='*****@*****.**', recipients=[member.email], body=email_body, extra_headers={ 'Reply-To': '*****@*****.**', }) elif 'investing' in member.membership_type: if member.is_legalentity: email_subject, email_body = \ make_dues_invoice_legalentity_email(member) else: email_subject, email_body = \ make_dues_invoice_investing_email(member) message = Message(subject=email_subject, sender='*****@*****.**', recipients=[member.email], body=email_body, extra_headers={ 'Reply-To': '*****@*****.**', }) # print to console or send mail if 'true' in request.registry.settings['testing.mail_to_console']: print(message.body.encode('utf-8')) # pragma: no cover else: send_message(request, message) # now choose where to redirect if 'detail' in request.referrer: return HTTPFound( request.route_url('detail', memberid=member.id) + '#dues17') if 'toolbox' in request.referrer: return HTTPFound(request.route_url('toolbox')) else: return get_memberhip_listing_redirect(request, member.id)