def send_certificate_email(request): ''' Send email to a member with a link so the member can get her membership certificate. ''' member = request.validated_matchdict['member'] member.certificate_token = make_random_token() email_subject, email_body = make_membership_certificate_email( request, member) the_message = Message( subject=email_subject, sender=request.registry.settings['c3smembership.notification_sender'], recipients=[member.email], body=email_body ) send_message(request, the_message) member.certificate_email = True member.certificate_email_date = datetime.now() if hasattr(request, 'referer') and request.referer is not None and \ 'detail' in request.referer: return HTTPFound( request.route_url( 'detail', member_id=member.id, _anchor='certificate' ) ) else: return get_memberhip_listing_redirect(request, member.id)
def send_certificate_email(request): """ Send email to a member with a link so the member can get her membership certificate. """ _special_condition = False # for redirects to referrer mid = request.matchdict["id"] member = C3sMember.get_by_id(mid) if isinstance(member, NoneType) or not member.is_member(): return Response("that id does not exist or is not an accepted member. go back", status="404 Not Found") # create a token for the certificate member.certificate_token = make_random_token() email_subject, email_body = make_membership_certificate_email(request, member) the_message = Message(subject=email_subject, sender="*****@*****.**", recipients=[member.email], body=email_body) send_message(request, the_message) member.certificate_email = True member.certificate_email_date = datetime.now() try: # pragma: no cover if "detail" in request.referrer: _special_condition = True except TypeError: # pragma: no cover pass if _special_condition: # pragma: no cover return HTTPFound(location=request.referrer + "#certificate") else: return get_memberhip_listing_redirect(request, member.id)
def invite_member_bcgv(request): """ Send email to member with link to ticketing. === ==================================== URL http://app:port/invite_member/{m_id} === ==================================== """ member_id = request.matchdict['m_id'] member = C3sMember.get_by_id(member_id) if isinstance(member, NoneType): request.session.flash('id not found. no mail sent.', 'messages') return get_memberhip_listing_redirect(request) if not member.is_member(): request.session.flash('Invitations can only be sent to members.', 'messages') return get_memberhip_listing_redirect(request, member_id) # prepare a random token iff none is set if member.email_invite_token_bcgv17 is None: member.email_invite_token_bcgv17 = make_random_token() url = URL_PATTERN.format( ticketing_url=request.registry.settings['ticketing.url'], token=member.email_invite_token_bcgv17, email=member.email) LOG.info("mailing event invitation to to member id %s", member.id) email_subject, email_body = make_bcga17_invitation_email(member, url) message = Message(subject=email_subject, sender='*****@*****.**', recipients=[member.email], body=email_body, extra_headers={ 'Reply-To': '*****@*****.**', }) send_message(request, message) # member._token = _looong_token member.email_invite_flag_bcgv17 = True member.email_invite_date_bcgv17 = datetime.now() return get_memberhip_listing_redirect(request, member.id)
def send_certificate_email(request): ''' Send email to a member with a link so the member can get her membership certificate. ''' _special_condition = False # for redirects to referrer mid = request.matchdict['id'] member = C3sMember.get_by_id(mid) if isinstance(member, NoneType) or not member.is_member(): return Response( 'that id does not exist or is not an accepted member. go back', status='404 Not Found', ) # create a token for the certificate member.certificate_token = make_random_token() email_subject, email_body = make_membership_certificate_email( request, member) the_message = Message(subject=email_subject, sender='*****@*****.**', recipients=[member.email], body=email_body) send_message(request, the_message) member.certificate_email = True member.certificate_email_date = datetime.now() try: # pragma: no cover if 'detail' in request.referrer: _special_condition = True except TypeError: # pragma: no cover pass if _special_condition: # pragma: no cover return HTTPFound(location=request.referrer + '#certificate') else: return get_memberhip_listing_redirect(request, member.id)
def shares_edit(request): ''' Edit details of a package of shares. ''' # load info from DB -- if possible share = request.registry.share_information.get(request.matchdict['id']) if isinstance(share, NoneType): # entry was not found in database return get_memberhip_listing_redirect(request) else: appstruct = {} appstruct = { 'number': share.number, 'date_of_acquisition': share.date_of_acquisition, } # construct a form class SharesSchema(colander.Schema): """ Defines the colander schema for shares. """ number = colander.SchemaNode( colander.Integer(), title=_('Number of Shares'), ) date_of_acquisition = colander.SchemaNode( colander.Date(), title=_('Date of Acquisition')) schema = SharesSchema() form = deform.Form( schema, buttons=[deform.Button('submit', _(u'Submit'))], ) # form generation complete # 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, validation_failure: # pragma: no cover request.session.flash(_(u'Please note: There were errors, ' 'please check the form below.'), 'message_above_form', allow_duplicate=False) return {'form': validation_failure.render()} # if no error occurred, persist the changed values info in database test1 = ( # changed value through form (different from db)? appstruct['number'] == share.number) if not test1: LOG.info('info about number of shares of %s changed by %s to %s', share.id, request.user.login, appstruct['number']) share.number = appstruct['number'] test2 = ( # changed value through form (different from db)? appstruct['date_of_acquisition'] == share.date_of_acquisition) if not test2: LOG.info( 'info about date_of_acquisition of %s changed by %s to %s', share.id, request.user.login, appstruct['date_of_acquisition']) share.date_of_acquisition = appstruct['date_of_acquisition']
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 shares_edit(request): """ Edit details of a package of shares. """ # print(request.matchdict['id']) from c3smembership.models import Shares # load info from DB -- if possible _s = Shares.get_by_id(request.matchdict["id"]) if isinstance(_s, NoneType): # entry was not found in database return get_memberhip_listing_redirect(request) else: appstruct = {} appstruct = {"number": _s.number, "date_of_acquisition": _s.date_of_acquisition} # construct a form class Shares(colander.Schema): number = colander.SchemaNode(colander.Integer(), title=_("Number of Shares")) date_of_acquisition = colander.SchemaNode(colander.Date(), title=_("Date of Acquisition")) schema = Shares() form = deform.Form(schema, buttons=[deform.Button("submit", _(u"Submit"))]) # form generation complete # 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) except ValidationFailure, e: # pragma: no cover print(e) request.session.flash( _(u"Please note: There were errors, " "please check the form below."), "message_above_form", allow_duplicate=False, ) return {"form": e.render()} # if no error occurred, persist the changed values info in database test1 = appstruct["number"] == _s.number # changed value through form (different from db)? if not test1: log.info( "info about number of shares of %s changed by %s to %s" % (_s.id, request.user.login, appstruct["number"]) ) _s.number = appstruct["number"] test2 = ( # changed value through form (different from db)? appstruct["date_of_acquisition"] == _s.date_of_acquisition ) if not test2: log.info( "info about date_of_acquisition of %s changed by %s to %s" % (_s.id, request.user.login, appstruct["date_of_acquisition"]) ) _s.date_of_acquisition = appstruct["date_of_acquisition"]
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)
def shares_edit(request): ''' Edit details of a package of shares. ''' # load info from DB -- if possible share = request.registry.share_information.get(request.matchdict['id']) if isinstance(share, NoneType): # entry was not found in database return get_memberhip_listing_redirect(request) else: appstruct = {} appstruct = { 'number': share.number, 'date_of_acquisition': share.date_of_acquisition, } # construct a form class SharesSchema(colander.Schema): """ Defines the colander schema for shares. """ number = colander.SchemaNode( colander.Integer(), title=_('Number of Shares'), ) date_of_acquisition = colander.SchemaNode( colander.Date(), title=_('Date of Acquisition') ) schema = SharesSchema() form = deform.Form( schema, buttons=[deform.Button('submit', _(u'Submit'))], ) # form generation complete # 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, validation_failure: # pragma: no cover request.session.flash( _(u'Please note: There were errors, ' 'please check the form below.'), 'danger', allow_duplicate=False) return{'form': validation_failure.render()} # if no error occurred, persist the changed values info in database test1 = ( # changed value through form (different from db)? appstruct['number'] == share.number) if not test1: LOG.info( 'info about number of shares of %s changed by %s to %s', share.id, request.user.login, appstruct['number']) share.number = appstruct['number'] test2 = ( # changed value through form (different from db)? appstruct['date_of_acquisition'] == share.date_of_acquisition) if not test2: LOG.info( 'info about date_of_acquisition of %s changed by %s to %s', share.id, request.user.login, appstruct['date_of_acquisition']) share.date_of_acquisition = appstruct['date_of_acquisition']