コード例 #1
0
def make_dues15_reversal_invoice_pdf(request):
    """
    This view checks supplied information (in URL) against info in database
    -- especially the invoice number --
    and conditionally returns
    - an error message or
    - a PDF
    """
    token = request.matchdict['code']
    invoice_number = request.matchdict['no']

    try:
        member = C3sMember.get_by_dues15_token(token)
        assert member is not None
        assert member.dues15_token == token

    except AssertionError:
        request.session.flash(
            u"This member and token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    try:
        invoice = Dues15Invoice.get_by_invoice_no(invoice_number)
        assert invoice is not None
    except AssertionError:
        request.session.flash(
            u"No invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice token must match with token
    try:
        assert (invoice.token == token)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: reversal invoice token must be reversal
    try:
        assert (invoice.is_reversal)
    except AssertionError:
        request.session.flash(
            u"No reversal invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # return a pdf file
    pdf_file = make_reversal_pdf_pdflatex(member, invoice)
    response = Response(content_type='application/pdf')
    pdf_file.seek(0)  # rewind to beginning
    response.app_iter = open(pdf_file.name, "r")
    return response
コード例 #2
0
def stats_view(request):
    """
    This view lets accountants view statistics:
    how many membership applications, real members, shares, etc.
    """
    # countries_dict = C3sMember.get_countries_list()
    _cl = C3sMember.get_countries_list()
    _cl_sorted = _cl.items()
    # print "die liste: {}".format(_cl_sorted)
    import operator
    _cl_sorted.sort(key=operator.itemgetter(1), reverse=True)
    # print "sortiert: {}".format(_cl_sorted)
    share_information = request.registry.share_information
    return {
        # form submissions
        '_number_of_datasets': C3sMember.get_number(),
        'afm_shares_unpaid': C3sMember.afm_num_shares_unpaid(),
        'afm_shares_paid': C3sMember.afm_num_shares_paid(),
        # shares
        'num_shares_members': share_information.get_share_count(),
        # 'num_shares_mem_norm': Shares.get_sum_norm(),
        # 'num_shares_mem_inv': Shares.get_sum_inv(),

        # memberships
        'num_members_accepted': C3sMember.get_num_members_accepted(),
        'num_non_accepted': C3sMember.get_num_non_accepted(),
        'num_nonmember_listing': C3sMember.nonmember_listing_count(),
        'num_duplicates': len(C3sMember.get_duplicates()),
        # 'num_empty_slots': C3sMember.get_num_empty_slots(),
        # normal persons vs. legal entities
        'num_ms_nat_acc': C3sMember.get_num_mem_nat_acc(),
        'num_ms_jur_acc': C3sMember.get_num_mem_jur_acc(),
        # normal vs. investing memberships
        'num_ms_norm': C3sMember.get_num_mem_norm(),
        'num_ms_inves': C3sMember.get_num_mem_invest(),
        'num_ms_features': C3sMember.get_num_mem_other_features(),
        'num_membership_lost': C3sMember.get_num_membership_lost(),
        # membership_numbers
        'num_memnums': C3sMember.get_num_membership_numbers(),
        'max_memnum': C3sMember.get_highest_membership_number(),
        'next_memnum': C3sMember.get_next_free_membership_number(),

        # countries
        'num_countries': C3sMember.get_num_countries(),
        'countries_list': _cl_sorted,
        #    key=lambda x: x[1]
        # ),  # XXX TODO: sorte

        # dues stats
        'dues15_stats': Dues15Invoice.get_monthly_stats(),
        'dues16_stats': Dues16Invoice.get_monthly_stats(),
        'dues17_stats': Dues17Invoice.get_monthly_stats(),

        # staff figures
        'num_staff': len(C3sStaff.get_all())
    }
コード例 #3
0
def stats_view(request):
    """
    This view lets accountants view statistics:
    how many membership applications, real members, shares, etc.
    """
    # countries_dict = C3sMember.get_countries_list()
    _cl = C3sMember.get_countries_list()
    _cl_sorted = _cl.items()
    # print "die liste: {}".format(_cl_sorted)
    import operator
    _cl_sorted.sort(key=operator.itemgetter(1), reverse=True)
    # print "sortiert: {}".format(_cl_sorted)
    return {
        # form submissions
        '_number_of_datasets': C3sMember.get_number(),
        'afm_shares_unpaid': C3sMember.afm_num_shares_unpaid(),
        'afm_shares_paid': C3sMember.afm_num_shares_paid(),
        # shares
        # 'num_shares': C3sMember.get_total_shares(),
        'num_shares_members': Shares.get_total_shares(),
        # 'num_shares_mem_norm': Shares.get_sum_norm(),
        # 'num_shares_mem_inv': Shares.get_sum_inv(),

        # memberships
        'num_members_accepted': C3sMember.get_num_members_accepted(),
        'num_non_accepted': C3sMember.get_num_non_accepted(),
        'num_nonmember_listing': C3sMember.nonmember_listing_count(),
        'num_duplicates': len(C3sMember.get_duplicates()),
        # 'num_empty_slots': C3sMember.get_num_empty_slots(),
        # normal persons vs. legal entities
        'num_ms_nat_acc': C3sMember.get_num_mem_nat_acc(),
        'num_ms_jur_acc': C3sMember.get_num_mem_jur_acc(),
        # normal vs. investing memberships
        'num_ms_norm': C3sMember.get_num_mem_norm(),
        'num_ms_inves': C3sMember.get_num_mem_invest(),
        'num_ms_features': C3sMember.get_num_mem_other_features(),
        # membership_numbers
        'num_memnums': C3sMember.get_num_membership_numbers(),
        'max_memnum': C3sMember.get_highest_membership_number(),
        'next_memnum': C3sMember.get_next_free_membership_number(),

        # countries
        'num_countries': C3sMember.get_num_countries(),
        'countries_list': _cl_sorted,
        #    key=lambda x: x[1]
        # ),  # XXX TODO: sorte

        # dues stats
        'dues15_stats': Dues15Invoice.get_monthly_stats(),
        'dues16_stats': Dues16Invoice.get_monthly_stats(),

        # staff figures
        'num_staff': len(C3sStaff.get_all())
    }
コード例 #4
0
def dues15_listing(request):
    """
    a listing of all invoices for the 2015 dues run.
    shall show both active/valid and cancelled/invalid invoices.
    """
    # get them all from the DB
    dues15_invoices = Dues15Invoice.get_all()

    return {
        'count': len(dues15_invoices),
        '_today': date.today(),
        'invoices': dues15_invoices,
    }
コード例 #5
0
def dues15_listing(request):
    """
    a listing of all invoices for the 2015 dues run.
    shall show both active/valid and cancelled/invalid invoices.
    """
    # get them all from the DB
    dues15_invoices = Dues15Invoice.get_all()

    return {
        'count': len(dues15_invoices),
        '_today': date.today(),
        'invoices': dues15_invoices,
    }
コード例 #6
0
def member_detail(request):
    """
    This view lets accountants view member details:

    - has their signature arrived?
    - how about the payment?

    Mostly all the info about an application or membership
    in the database can be seen here.
    """
    from decimal import Decimal as D
    logged_in = authenticated_userid(request)
    memberid = request.matchdict['memberid']
    LOG.info("member details of id %s checked by %s", memberid, logged_in)

    member = C3sMember.get_by_id(memberid)

    if member is None:  # that memberid did not produce good results
        request.session.flash(
            "A Member with id "
            "{} could not be found in the DB. run for the backups!".format(
                memberid), 'message_to_staff')
        return HTTPFound(  # back to base
            request.route_url('toolbox'))

    # get the members invoices from the DB
    invoices15 = Dues15Invoice.get_by_membership_no(member.membership_number)
    invoices16 = Dues16Invoice.get_by_membership_no(member.membership_number)
    invoices17 = Dues17Invoice.get_by_membership_no(member.membership_number)
    shares = request.registry.share_information.get_member_shares(
        member.membership_number)

    return {
        'today': date.today().strftime('%Y-%m-%d'),
        'D': D,
        'member': member,
        'shares': shares,
        'invoices15': invoices15,
        'invoices16': invoices16,
        'invoices17': invoices17,
        # 'form': html
    }
コード例 #7
0
def member_detail(request):
    """
    This view lets accountants view member details:

    - has their signature arrived?
    - how about the payment?

    Mostly all the info about an application or membership
    in the database can be seen here.
    """
    from decimal import Decimal as D
    logged_in = authenticated_userid(request)
    memberid = request.matchdict['memberid']
    LOG.info("member details of id %s checked by %s", memberid, logged_in)

    member = C3sMember.get_by_id(memberid)

    if member is None:  # that memberid did not produce good results
        request.session.flash(
            "A Member with id "
            "{} could not be found in the DB. run for the backups!".format(
                memberid),
            'message_to_staff'
        )
        return HTTPFound(  # back to base
            request.route_url('toolbox'))

    # get the members invoices from the DB
    invoices15 = Dues15Invoice.get_by_membership_no(member.membership_number)
    invoices16 = Dues16Invoice.get_by_membership_no(member.membership_number)

    return {
        'today': date.today().strftime('%Y-%m-%d'),
        'D': D,
        'member': member,
        'invoices15': invoices15,
        'invoices16': invoices16,
        # 'form': html
    }
コード例 #8
0
    def test_dues15_reduction(self):
        """
        test the dues15_reduction functionality
        """
        # have to accept their membersip first
        m1 = C3sMember.get_by_id(1)  # german normal member
        m1.membership_accepted = True
        m2 = C3sMember.get_by_id(2)  # english normal member
        m2.membership_accepted = True

        self.config.add_route('make_dues15_invoice_no_pdf', '/')
        self.config.add_route('make_dues15_reversal_invoice_pdf', '/')
        self.config.add_route('detail', '/detail/')
        self.config.add_route('error_page', '/error_page')
        self.config.add_route('toolbox', '/toolbox')
        req = testing.DummyRequest()
        req.referrer = 'toolbox'
        from c3smembership.views.membership_dues import send_dues15_invoice_batch
        # send out invoices. this is a prerequisite for reductions
        res = send_dues15_invoice_batch(req)
        res
        """
        test reduction of dues
        """
        # pre-check
        self.assertFalse(m1.dues15_reduced)  # not reduced yet!
        _m1_amount_reduced = m1.dues15_amount_reduced  # is Decimal('0')
        _number_of_invoices_before_reduction = len(Dues15Invoice.get_all())
        # print("_number_of_invoices_before_reduction: {}".format(
        #    _number_of_invoices_before_reduction))
        # we have 2 invoices as of now
        self.assertEqual(len(Dues15Invoice.get_all()), 2)
        # import the function under test
        from c3smembership.views.membership_dues import dues15_reduction

        #############################################################
        # try to reduce to the given calculated amount (edge case coverage)
        # this will not work, produce no new invoices
        req_reduce = testing.DummyRequest(  # prepare request
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 50,
            },
        )
        req_reduce.matchdict['member_id'] = 1  # do it for member with id 1

        res_reduce = dues15_reduction(req_reduce)  # call reduce on her

        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # try to reduce above the given calculated amount
        # this will not work, produce no new invoices
        req_reduce = testing.DummyRequest(  # prepare request
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 500,
            },
        )
        req_reduce.matchdict['member_id'] = 1  # do it for member with id 1

        res_reduce = dues15_reduction(req_reduce)  # call reduce on her

        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # valid reduction but without confirmation
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'no',
                'submit': True,
                'amount': 42,
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # valid reduction
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 42,
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        _number_of_invoices_after_reduction = len(Dues15Invoice.get_all())

        assert(  # two new invoices must have been issued
            (_number_of_invoices_before_reduction + 2) ==
            _number_of_invoices_after_reduction)
        assert(_number_of_invoices_after_reduction == 4)
        assert('detail' in res_reduce.headers['Location'])  # 302 to detail p.
        assert(_m1_amount_reduced != m1.dues15_amount_reduced)  # changed!
        assert(m1.dues15_amount_reduced == 42)  # changed to 42!

        # check the invoice created
        _rev_inv = Dues15Invoice.get_by_invoice_no(
            _number_of_invoices_before_reduction + 1)
        _new_inv = Dues15Invoice.get_by_invoice_no(
            _number_of_invoices_before_reduction + 2)
        # print(_rev_inv.invoice_amount)
        # print(type(_rev_inv.invoice_amount))
        assert(_rev_inv.invoice_amount == D('-50'))
        assert(_new_inv.invoice_amount == D('42'))

        # we have 4 invoices as of now
        self.assertEqual(len(Dues15Invoice.get_all()), 4)

        #############################################################
        # now try to raise above the previous reduction
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 50,
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        _number_of_invoices_after_reduction = len(Dues15Invoice.get_all())

        # no new invoices were created, we still have 4 invoices
        self.assertEqual(len(Dues15Invoice.get_all()), 4)

        #############################################################
        # try to reduce to the same amount again (edge case coverage)
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 42,
                # lots of values missing
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        # try to reduce to zero (edge case coverage)
        # print("------------- reduction to zero ahead!!!")
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            },
        )
        req_reduce.matchdict['member_id'] = 2
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        # try to reduce to zero with english member (edge case coverage)
        # how to do this if you already reduced to zero? reduce to more first!
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 1,
                # lots of values missing
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        m1.locale = u'en'
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            },
        )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        """
        test reversal invoice PDF generation
        """

        from c3smembership.views.membership_dues import (
            make_dues15_reversal_invoice_pdf)
        req2 = testing.DummyRequest()

        # wrong token: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token + 'false!!!',  # must fail
            'no': u'0006',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        # wrong invoice number: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'no': u'1234',  # must fail
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        # wrong invoice token: must fail!
        i2 = Dues15Invoice.get_by_invoice_no('2')
        i2.token = u'not_matching'
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'no': u'2',  # must fail
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        ######################################################################
        # wrong invoice type (not a reversal): must fail! (edge case coverage)
        assert(not i2.is_reversal)  # i2 is not a reversal
        i2.token = m2.dues15_token  # we give it a valid token
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'no': u'0002',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error
        ######################################################################

        # retry with valid token:
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'no': u'0003',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        # print("length of the result: {}".format(len(res.body)))
        # print("headers of the result: {}".format((res.headers)))
        assert(60000 < len(res.body) < 80000)
        assert('application/pdf' in res.headers['Content-Type'])
コード例 #9
0
    def test_send_dues15_invoice_email_via_BATCH(self):
        """
        test the send_dues15_invoice_batch function
        for batch processing
        """
        # from pyramid_mailer import get_mailer
        from c3smembership.views.membership_dues import send_dues15_invoice_batch
        self.config.add_route('make_dues15_invoice_no_pdf', '/')
        self.config.add_route('make_dues15_reversal_invoice_pdf', '/')
        self.config.add_route('detail', '/detail/')
        self.config.add_route('error_page', '/error_page')
        self.config.add_route('toolbox', '/toolbox')

        # have to accept their membersip first
        m1 = C3sMember.get_by_id(1)  # german normal member
        m1.membership_accepted = True
        m2 = C3sMember.get_by_id(2)  # english normal member
        m2.membership_accepted = True
        m3 = C3sMember.get_by_id(3)  # german investing member
        m3.membership_accepted = True
        m4 = C3sMember.get_by_id(4)  # english investing member
        m4.membership_accepted = True
        m5 = C3sMember.get_by_id(5)  # german investing member
        m5.membership_accepted = True
        m5 = C3sMember.get_by_id(6)  # english investing member
        m5.membership_accepted = True

        # check number of invoices: should be 0
        _number_of_invoices_before_batch = len(Dues15Invoice.get_all())
        # print("_number_of_invoices_before_batch: {}".format(
        #    _number_of_invoices_before_batch))
        assert(_number_of_invoices_before_batch == 0)

        req = testing.DummyRequest()
        req.referrer = 'toolbox'
        res = send_dues15_invoice_batch(req)
        # print res

        # check number of invoices: should be 2
        _number_of_invoices_batch = len(Dues15Invoice.get_all())
        # print("number of invoices after batch: {}".format(
        #    _number_of_invoices_batch))
        assert(_number_of_invoices_batch == 2)

        # try to post a number for batch processing
        req_post = testing.DummyRequest(
            post={
                'submit': True,
                'number': 24
                # lots of values missing
            },
        )
        req.referrer = 'toolbox'
        res = send_dues15_invoice_batch(req_post)

        assert(
            'sent out 5 mails (to members with ids [1, 2, 3, 4, 5])' in
            req.session.pop_flash('message_to_staff'))

        # try to batch-send once more:
        # this will respond with a redirect and tell
        # that there are no invitees left
        res2 = send_dues15_invoice_batch(req)
        # print res2
        self.assertEquals(res2.status, '302 Found')
        self.assertEquals(res2.status_code, 302)
        assert(
            'no invoicees left. all done!' in
            req.session.pop_flash('message_to_staff'))

        """
        and now some tests for make_dues15_invoice_no_pdf
        """
        from c3smembership.views.membership_dues import (
            make_dues15_invoice_no_pdf)
        req2 = testing.DummyRequest()

        # wrong token: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token + 'false!!!',  # must fail
            'i': u'0001',
        }

        res = make_dues15_invoice_no_pdf(req2)

        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        # wrong invoice number: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'1234',  # must fail
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        # wrong invoice token: must fail!
        i2 = Dues15Invoice.get_by_invoice_no(2)
        i2.token = u'not_matching'
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'i': u'3',  # must fail
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error

        #######################################################################
        # one more edge case:
        # check _inv.token must match code, or else!!!
        # first, set inv_code to something wrong:
        i1 = Dues15Invoice.get_by_invoice_no(1)
        _old_i1_token = i1.token
        i1.token = u'not_right'
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error
        # reset it to what was there before
        i1.token = _old_i1_token
        #######################################################################
        # one more edge case:
        # check this invoice is not a reversal, or else no PDF!!!
        # first, set is_reversal to something wrong:
        i1 = Dues15Invoice.get_by_invoice_no(1)
        _old_i1_reversal_status = i1.is_reversal  # False
        i1.is_reversal = True
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert('error_page' in res.headers['Location'])  # but error
        # reset it to what was there before
        i1.is_reversal = _old_i1_reversal_status
        #######################################################################

        # retry with valid token:
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        # m1.
        # print("length of the result: {}".format(len(res.body)))
        # print("headers of the result: {}".format((res.headers)))
        assert(60000 < len(res.body) < 80000)
        assert('application/pdf' in res.headers['Content-Type'])

        """
        test dues listing
        """
        from c3smembership.views.membership_dues import dues15_listing
        req_list = testing.DummyRequest()
        resp_list = dues15_listing(req_list)
        # print resp_list
        # {'count': 5,
        #   'invoices': [
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761a50>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761690>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df815a50>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761c90>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761c10>],
        #   '_today': datetime.date(2015, 9, 1)}
        assert(resp_list['count'] == 2)
コード例 #10
0
    def test_send_dues15_invoice_email_single(self):
        """
        test the send_dues15_invoice_email view

        * calculate invoice amount and send invoice email
        ** to not accepted member
        ** to accepted member
        ** to non-existing member (wrong id)
        ** to same member (just send email, don't create new invoice)

        ... and also check email texts for
        * german normal member
        * english normal member
        * german investing member
        * english investing member
        * german investing legal entity
        * english investing legal entity

        """
        from pyramid_mailer import get_mailer
        from c3smembership.views.membership_dues import send_dues15_invoice_email
        from c3smembership.models import Dues15Invoice

        _number_of_invoices = len(Dues15Invoice.get_all())

        self.config.add_route('toolbox', '/')
        self.config.add_route('detail', '/')
        self.config.add_route('make_dues15_invoice_no_pdf', '/')

        req = testing.DummyRequest()
        req.matchdict = {
            'member_id': '1',
        }
        req.referrer = 'detail'
        res = send_dues15_invoice_email(req)
        self.assertTrue(res.status_code == 302)
        self.assertTrue('http://example.com/' in res.headers['Location'])
        # member 1 not accepted by the board. problem!

        _number_of_invoices_2 = len(Dues15Invoice.get_all())
        assert(_number_of_invoices == _number_of_invoices_2 == 0)
        # print("_number_of_invoices: {}".format(_number_of_invoices))

        m1 = C3sMember.get_by_id(1)
        m1.membership_accepted = True

        res = send_dues15_invoice_email(req)
        # print('#'*60)
        # print res
        # print('#'*60)

        _number_of_invoices_3 = len(Dues15Invoice.get_all())
        # print("_number_of_invoices 3: {}".format(_number_of_invoices_3))
        assert(_number_of_invoices_3 == 1)

        # check for outgoing email
        mailer = get_mailer(req)
        # print mailer
        # print mailer.outbox
        self.assertEqual(len(mailer.outbox), 1)
        # print mailer.outbox[0].body
        self.assertTrue(
            'Verwendungszweck: C3S-dues2015-0001' in mailer.outbox[0].body)

        """
        now we try to get an id that does not exist
        """
        req2 = testing.DummyRequest()
        req2.matchdict = {
            'member_id': '1234',
        }
        req2.referrer = 'detail'
        res2 = send_dues15_invoice_email(req2)
        self.assertTrue(res2.status_code == 302)
        self.assertTrue('http://example.com/' in res2.headers['Location'])

        """
        what if we call that function (and send email) twice?
        test that no second invoice is created in DB.
        """
        req3 = testing.DummyRequest()
        req3.matchdict = {
            'member_id': '1',
        }
        req3.referrer = 'detail'
        res3 = send_dues15_invoice_email(req3)
        self.assertTrue(res3.status_code == 302)
        self.assertTrue('http://example.com/' in res3.headers['Location'])
        _number_of_invoices_4 = len(Dues15Invoice.get_all())
        self.assertEqual(_number_of_invoices_3, _number_of_invoices_4)
        """
        check for email texts
        """
        self.assertEqual(len(mailer.outbox), 2)
        self.assertTrue(
            (u'Dein Mitgliedsbeitrag ab Quartal 1 beträgt also 50 Euro.')
            in mailer.outbox[0].body)
        # print(mailer.outbox[0].body)
        self.assertTrue(
            (u'Dein Mitgliedsbeitrag ab Quartal 1 beträgt also 50 Euro.')
            in mailer.outbox[1].body)
        # print(mailer.outbox[1].body)

        """
        send email to
        * english member,
        * investing members (de/en),
        * legal entities (de/en)
        """
        # english normal #####################################################
        m2 = C3sMember.get_by_id(2)
        m2.membership_accepted = True
        req_en_normal = testing.DummyRequest()
        req_en_normal.matchdict = {
            'member_id': '2',
        }
        req_en_normal.referrer = 'detail'
        res_en_normal = send_dues15_invoice_email(req_en_normal)
        self.assertTrue(res_en_normal.status_code == 302)
        self.assertEqual(len(mailer.outbox), 3)
        # print(mailer.outbox[2].body)
        self.assertTrue(
            (u'Please transfer 50 Euro')
            in mailer.outbox[2].body)

        # german investing ###################################################
        m3 = C3sMember.get_by_id(3)
        m3.membership_accepted = True
        req_de_investing = testing.DummyRequest()
        req_de_investing.matchdict = {
            'member_id': '3',
        }
        req_de_investing.referrer = 'detail'
        res_de_investing = send_dues15_invoice_email(req_de_investing)
        self.assertTrue(res_de_investing.status_code == 302)
        self.assertEqual(len(mailer.outbox), 4)
        # print(mailer.outbox[3].body)
        self.assertTrue(
            (u'Da Du investierendes Mitglied bist')
            in mailer.outbox[3].body)

        # english investing ##################################################
        m4 = C3sMember.get_by_id(4)
        m4.membership_accepted = True
        req_en_investing = testing.DummyRequest()
        req_en_investing.matchdict = {
            'member_id': '4',
        }
        req_en_investing.referrer = 'detail'
        res_en_investing = send_dues15_invoice_email(req_en_investing)
        self.assertTrue(res_en_investing.status_code == 302)
        self.assertEqual(len(mailer.outbox), 5)
        # print(mailer.outbox[4].body)
        self.assertTrue(
            (u'Since you are an investing member')
            in mailer.outbox[4].body)

        # german legal entity ################################################
        m5 = C3sMember.get_by_id(5)
        m5.membership_accepted = True
        req_de_legalentity = testing.DummyRequest()
        req_de_legalentity.matchdict = {
            'member_id': '5',
        }
        req_de_legalentity.referrer = 'detail'
        res_de_legalentity = send_dues15_invoice_email(req_de_legalentity)
        self.assertTrue(res_de_legalentity.status_code == 302)
        self.assertEqual(len(mailer.outbox), 6)
        # print(mailer.outbox[5].body)
        self.assertTrue(
            (u'')
            in mailer.outbox[5].body)

        # english legal entity ###############################################
        m6 = C3sMember.get_by_id(6)
        m6.membership_accepted = True
        req_en_legalentity = testing.DummyRequest()
        req_en_legalentity.matchdict = {
            'member_id': '6',
        }
        req_en_legalentity.referrer = 'detail'
        res_en_legalentity = send_dues15_invoice_email(req_en_legalentity)
        self.assertTrue(res_en_legalentity.status_code == 302)
        self.assertEqual(len(mailer.outbox), 7)
        # print(mailer.outbox[6].body)
        self.assertTrue(
            (u'Da Musikverlag investierendes Mitglied ist')
            in mailer.outbox[6].body)
        self.assertTrue(
            (u'Für juristische Personen wird empfohlen')
            in mailer.outbox[6].body)
コード例 #11
0
    def test_dues15_reduction(self):
        """
        test the dues15_reduction functionality
        """
        # have to accept their membersip first
        m1 = C3sMember.get_by_id(1)  # german normal member
        m1.membership_accepted = True
        m2 = C3sMember.get_by_id(2)  # english normal member
        m2.membership_accepted = True

        self.config.add_route('make_dues15_invoice_no_pdf', '/')
        self.config.add_route('make_dues15_reversal_invoice_pdf', '/')
        self.config.add_route('detail', '/detail/')
        self.config.add_route('error_page', '/error_page')
        self.config.add_route('toolbox', '/toolbox')
        req = testing.DummyRequest()
        req.referrer = 'toolbox'
        from c3smembership.views.membership_dues import send_dues15_invoice_batch
        # send out invoices. this is a prerequisite for reductions
        res = send_dues15_invoice_batch(req)
        res
        """
        test reduction of dues
        """
        # pre-check
        self.assertFalse(m1.dues15_reduced)  # not reduced yet!
        _m1_amount_reduced = m1.dues15_amount_reduced  # is Decimal('0')
        _number_of_invoices_before_reduction = len(Dues15Invoice.get_all())
        # print("_number_of_invoices_before_reduction: {}".format(
        #    _number_of_invoices_before_reduction))
        # we have 2 invoices as of now
        self.assertEqual(len(Dues15Invoice.get_all()), 2)
        # import the function under test
        from c3smembership.views.membership_dues import dues15_reduction

        #############################################################
        # try to reduce to the given calculated amount (edge case coverage)
        # this will not work, produce no new invoices
        req_reduce = testing.DummyRequest(  # prepare request
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 50,
            }, )
        req_reduce.matchdict['member_id'] = 1  # do it for member with id 1

        res_reduce = dues15_reduction(req_reduce)  # call reduce on her

        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # try to reduce above the given calculated amount
        # this will not work, produce no new invoices
        req_reduce = testing.DummyRequest(  # prepare request
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 500,
            }, )
        req_reduce.matchdict['member_id'] = 1  # do it for member with id 1

        res_reduce = dues15_reduction(req_reduce)  # call reduce on her

        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # valid reduction but without confirmation
        req_reduce = testing.DummyRequest(post={
            'confirmed': 'no',
            'submit': True,
            'amount': 42,
        }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        self.assertEqual(len(Dues15Invoice.get_all()), 2)  # no new invoice

        #############################################################
        # valid reduction
        req_reduce = testing.DummyRequest(post={
            'confirmed': 'yes',
            'submit': True,
            'amount': 42,
        }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        _number_of_invoices_after_reduction = len(Dues15Invoice.get_all())

        assert (  # two new invoices must have been issued
            (_number_of_invoices_before_reduction +
             2) == _number_of_invoices_after_reduction)
        assert (_number_of_invoices_after_reduction == 4)
        assert ('detail' in res_reduce.headers['Location'])  # 302 to detail p.
        assert (_m1_amount_reduced != m1.dues15_amount_reduced)  # changed!
        assert (m1.dues15_amount_reduced == 42)  # changed to 42!

        # check the invoice created
        _rev_inv = Dues15Invoice.get_by_invoice_no(
            _number_of_invoices_before_reduction + 1)
        _new_inv = Dues15Invoice.get_by_invoice_no(
            _number_of_invoices_before_reduction + 2)
        # print(_rev_inv.invoice_amount)
        # print(type(_rev_inv.invoice_amount))
        assert (_rev_inv.invoice_amount == D('-50'))
        assert (_new_inv.invoice_amount == D('42'))

        # we have 4 invoices as of now
        self.assertEqual(len(Dues15Invoice.get_all()), 4)

        #############################################################
        # now try to raise above the previous reduction
        req_reduce = testing.DummyRequest(post={
            'confirmed': 'yes',
            'submit': True,
            'amount': 50,
        }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        _number_of_invoices_after_reduction = len(Dues15Invoice.get_all())

        # no new invoices were created, we still have 4 invoices
        self.assertEqual(len(Dues15Invoice.get_all()), 4)

        #############################################################
        # try to reduce to the same amount again (edge case coverage)
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 42,
                # lots of values missing
            }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        # try to reduce to zero (edge case coverage)
        # print("------------- reduction to zero ahead!!!")
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)

        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            }, )
        req_reduce.matchdict['member_id'] = 2
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        # try to reduce to zero with english member (edge case coverage)
        # how to do this if you already reduced to zero? reduce to more first!
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 1,
                # lots of values missing
            }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        m1.locale = u'en'
        req_reduce = testing.DummyRequest(
            post={
                'confirmed': 'yes',
                'submit': True,
                'amount': 0,
                # lots of values missing
            }, )
        req_reduce.matchdict['member_id'] = 1
        res_reduce = dues15_reduction(req_reduce)
        #############################################################
        """
        test reversal invoice PDF generation
        """

        from c3smembership.views.membership_dues import (
            make_dues15_reversal_invoice_pdf)
        req2 = testing.DummyRequest()

        # wrong token: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token + 'false!!!',  # must fail
            'no': u'0006',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        # wrong invoice number: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'no': u'1234',  # must fail
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        # wrong invoice token: must fail!
        i2 = Dues15Invoice.get_by_invoice_no('2')
        i2.token = u'not_matching'
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'no': u'2',  # must fail
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        ######################################################################
        # wrong invoice type (not a reversal): must fail! (edge case coverage)
        assert (not i2.is_reversal)  # i2 is not a reversal
        i2.token = m2.dues15_token  # we give it a valid token
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'no': u'0002',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error
        ######################################################################

        # retry with valid token:
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'no': u'0003',
        }
        res = make_dues15_reversal_invoice_pdf(req2)
        # print("length of the result: {}".format(len(res.body)))
        # print("headers of the result: {}".format((res.headers)))
        assert (60000 < len(res.body) < 80000)
        assert ('application/pdf' in res.headers['Content-Type'])
コード例 #12
0
    def test_send_dues15_invoice_email_via_BATCH(self):
        """
        test the send_dues15_invoice_batch function
        for batch processing
        """
        # from pyramid_mailer import get_mailer
        from c3smembership.views.membership_dues import send_dues15_invoice_batch
        self.config.add_route('make_dues15_invoice_no_pdf', '/')
        self.config.add_route('make_dues15_reversal_invoice_pdf', '/')
        self.config.add_route('detail', '/detail/')
        self.config.add_route('error_page', '/error_page')
        self.config.add_route('toolbox', '/toolbox')

        # have to accept their membersip first
        m1 = C3sMember.get_by_id(1)  # german normal member
        m1.membership_accepted = True
        m2 = C3sMember.get_by_id(2)  # english normal member
        m2.membership_accepted = True
        m3 = C3sMember.get_by_id(3)  # german investing member
        m3.membership_accepted = True
        m4 = C3sMember.get_by_id(4)  # english investing member
        m4.membership_accepted = True
        m5 = C3sMember.get_by_id(5)  # german investing member
        m5.membership_accepted = True
        m5 = C3sMember.get_by_id(6)  # english investing member
        m5.membership_accepted = True

        # check number of invoices: should be 0
        _number_of_invoices_before_batch = len(Dues15Invoice.get_all())
        # print("_number_of_invoices_before_batch: {}".format(
        #    _number_of_invoices_before_batch))
        assert (_number_of_invoices_before_batch == 0)

        req = testing.DummyRequest()
        req.referrer = 'toolbox'
        res = send_dues15_invoice_batch(req)
        # print res

        # check number of invoices: should be 2
        _number_of_invoices_batch = len(Dues15Invoice.get_all())
        # print("number of invoices after batch: {}".format(
        #    _number_of_invoices_batch))
        assert (_number_of_invoices_batch == 2)

        # try to post a number for batch processing
        req_post = testing.DummyRequest(
            post={
                'submit': True,
                'number': 24
                # lots of values missing
            }, )
        req.referrer = 'toolbox'
        res = send_dues15_invoice_batch(req_post)

        assert ('sent out 5 mails (to members with ids [1, 2, 3, 4, 5])'
                in req.session.pop_flash('message_to_staff'))

        # try to batch-send once more:
        # this will respond with a redirect and tell
        # that there are no invitees left
        res2 = send_dues15_invoice_batch(req)
        # print res2
        self.assertEquals(res2.status, '302 Found')
        self.assertEquals(res2.status_code, 302)
        assert ('no invoicees left. all done!'
                in req.session.pop_flash('message_to_staff'))
        """
        and now some tests for make_dues15_invoice_no_pdf
        """
        from c3smembership.views.membership_dues import (
            make_dues15_invoice_no_pdf)
        req2 = testing.DummyRequest()

        # wrong token: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token + 'false!!!',  # must fail
            'i': u'0001',
        }

        res = make_dues15_invoice_no_pdf(req2)

        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        # wrong invoice number: must fail!
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'1234',  # must fail
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        # wrong invoice token: must fail!
        i2 = Dues15Invoice.get_by_invoice_no(2)
        i2.token = u'not_matching'
        req2.matchdict = {
            'email': m2.email,
            'code': m2.dues15_token,
            'i': u'3',  # must fail
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error

        #######################################################################
        # one more edge case:
        # check _inv.token must match code, or else!!!
        # first, set inv_code to something wrong:
        i1 = Dues15Invoice.get_by_invoice_no(1)
        _old_i1_token = i1.token
        i1.token = u'not_right'
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error
        # reset it to what was there before
        i1.token = _old_i1_token
        #######################################################################
        # one more edge case:
        # check this invoice is not a reversal, or else no PDF!!!
        # first, set is_reversal to something wrong:
        i1 = Dues15Invoice.get_by_invoice_no(1)
        _old_i1_reversal_status = i1.is_reversal  # False
        i1.is_reversal = True
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        assert ('application/pdf' not in res.headers['Content-Type'])  # no PDF
        assert ('error_page' in res.headers['Location'])  # but error
        # reset it to what was there before
        i1.is_reversal = _old_i1_reversal_status
        #######################################################################

        # retry with valid token:
        req2.matchdict = {
            'email': m1.email,
            'code': m1.dues15_token,
            'i': u'0001',
        }
        res = make_dues15_invoice_no_pdf(req2)
        # m1.
        # print("length of the result: {}".format(len(res.body)))
        # print("headers of the result: {}".format((res.headers)))
        assert (60000 < len(res.body) < 80000)
        assert ('application/pdf' in res.headers['Content-Type'])
        """
        test dues listing
        """
        from c3smembership.views.membership_dues import dues15_listing
        req_list = testing.DummyRequest()
        resp_list = dues15_listing(req_list)
        # print resp_list
        # {'count': 5,
        #   'invoices': [
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761a50>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761690>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df815a50>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761c90>,
        #       <c3smembership.models.Dues15Invoice object at 0x7f95df761c10>],
        #   '_today': datetime.date(2015, 9, 1)}
        assert (resp_list['count'] == 2)
コード例 #13
0
    def test_send_dues15_invoice_email_single(self):
        """
        test the send_dues15_invoice_email view

        * calculate invoice amount and send invoice email
        ** to not accepted member
        ** to accepted member
        ** to non-existing member (wrong id)
        ** to same member (just send email, don't create new invoice)

        ... and also check email texts for
        * german normal member
        * english normal member
        * german investing member
        * english investing member
        * german investing legal entity
        * english investing legal entity

        """
        from pyramid_mailer import get_mailer
        from c3smembership.views.membership_dues import send_dues15_invoice_email
        from c3smembership.models import Dues15Invoice

        _number_of_invoices = len(Dues15Invoice.get_all())

        self.config.add_route('toolbox', '/')
        self.config.add_route('detail', '/')
        self.config.add_route('make_dues15_invoice_no_pdf', '/')

        req = testing.DummyRequest()
        req.matchdict = {
            'member_id': '1',
        }
        req.referrer = 'detail'
        res = send_dues15_invoice_email(req)
        self.assertTrue(res.status_code == 302)
        self.assertTrue('http://example.com/' in res.headers['Location'])
        # member 1 not accepted by the board. problem!

        _number_of_invoices_2 = len(Dues15Invoice.get_all())
        assert (_number_of_invoices == _number_of_invoices_2 == 0)
        # print("_number_of_invoices: {}".format(_number_of_invoices))

        m1 = C3sMember.get_by_id(1)
        m1.membership_accepted = True

        res = send_dues15_invoice_email(req)
        # print('#'*60)
        # print res
        # print('#'*60)

        _number_of_invoices_3 = len(Dues15Invoice.get_all())
        # print("_number_of_invoices 3: {}".format(_number_of_invoices_3))
        assert (_number_of_invoices_3 == 1)

        # check for outgoing email
        mailer = get_mailer(req)
        # print mailer
        # print mailer.outbox
        self.assertEqual(len(mailer.outbox), 1)
        # print mailer.outbox[0].body
        self.assertTrue(
            'Verwendungszweck: C3S-dues2015-0001' in mailer.outbox[0].body)
        """
        now we try to get an id that does not exist
        """
        req2 = testing.DummyRequest()
        req2.matchdict = {
            'member_id': '1234',
        }
        req2.referrer = 'detail'
        res2 = send_dues15_invoice_email(req2)
        self.assertTrue(res2.status_code == 302)
        self.assertTrue('http://example.com/' in res2.headers['Location'])
        """
        what if we call that function (and send email) twice?
        test that no second invoice is created in DB.
        """
        req3 = testing.DummyRequest()
        req3.matchdict = {
            'member_id': '1',
        }
        req3.referrer = 'detail'
        res3 = send_dues15_invoice_email(req3)
        self.assertTrue(res3.status_code == 302)
        self.assertTrue('http://example.com/' in res3.headers['Location'])
        _number_of_invoices_4 = len(Dues15Invoice.get_all())
        self.assertEqual(_number_of_invoices_3, _number_of_invoices_4)
        """
        check for email texts
        """
        self.assertEqual(len(mailer.outbox), 2)
        self.assertTrue(
            (u'Dein Mitgliedsbeitrag ab Quartal 1 beträgt also 50 Euro.'
             ) in mailer.outbox[0].body)
        # print(mailer.outbox[0].body)
        self.assertTrue(
            (u'Dein Mitgliedsbeitrag ab Quartal 1 beträgt also 50 Euro.'
             ) in mailer.outbox[1].body)
        # print(mailer.outbox[1].body)
        """
        send email to
        * english member,
        * investing members (de/en),
        * legal entities (de/en)
        """
        # english normal #####################################################
        m2 = C3sMember.get_by_id(2)
        m2.membership_accepted = True
        req_en_normal = testing.DummyRequest()
        req_en_normal.matchdict = {
            'member_id': '2',
        }
        req_en_normal.referrer = 'detail'
        res_en_normal = send_dues15_invoice_email(req_en_normal)
        self.assertTrue(res_en_normal.status_code == 302)
        self.assertEqual(len(mailer.outbox), 3)
        # print(mailer.outbox[2].body)
        self.assertTrue((u'Please transfer 50 Euro') in mailer.outbox[2].body)

        # german investing ###################################################
        m3 = C3sMember.get_by_id(3)
        m3.membership_accepted = True
        req_de_investing = testing.DummyRequest()
        req_de_investing.matchdict = {
            'member_id': '3',
        }
        req_de_investing.referrer = 'detail'
        res_de_investing = send_dues15_invoice_email(req_de_investing)
        self.assertTrue(res_de_investing.status_code == 302)
        self.assertEqual(len(mailer.outbox), 4)
        # print(mailer.outbox[3].body)
        self.assertTrue(
            (u'Da Du investierendes Mitglied bist') in mailer.outbox[3].body)

        # english investing ##################################################
        m4 = C3sMember.get_by_id(4)
        m4.membership_accepted = True
        req_en_investing = testing.DummyRequest()
        req_en_investing.matchdict = {
            'member_id': '4',
        }
        req_en_investing.referrer = 'detail'
        res_en_investing = send_dues15_invoice_email(req_en_investing)
        self.assertTrue(res_en_investing.status_code == 302)
        self.assertEqual(len(mailer.outbox), 5)
        # print(mailer.outbox[4].body)
        self.assertTrue(
            (u'Since you are an investing member') in mailer.outbox[4].body)

        # german legal entity ################################################
        m5 = C3sMember.get_by_id(5)
        m5.membership_accepted = True
        req_de_legalentity = testing.DummyRequest()
        req_de_legalentity.matchdict = {
            'member_id': '5',
        }
        req_de_legalentity.referrer = 'detail'
        res_de_legalentity = send_dues15_invoice_email(req_de_legalentity)
        self.assertTrue(res_de_legalentity.status_code == 302)
        self.assertEqual(len(mailer.outbox), 6)
        # print(mailer.outbox[5].body)
        self.assertTrue((u'') in mailer.outbox[5].body)

        # english legal entity ###############################################
        m6 = C3sMember.get_by_id(6)
        m6.membership_accepted = True
        req_en_legalentity = testing.DummyRequest()
        req_en_legalentity.matchdict = {
            'member_id': '6',
        }
        req_en_legalentity.referrer = 'detail'
        res_en_legalentity = send_dues15_invoice_email(req_en_legalentity)
        self.assertTrue(res_en_legalentity.status_code == 302)
        self.assertEqual(len(mailer.outbox), 7)
        # print(mailer.outbox[6].body)
        self.assertTrue((u'Da Musikverlag investierendes Mitglied ist'
                         ) in mailer.outbox[6].body)
        self.assertTrue((u'Für juristische Personen wird empfohlen'
                         ) in mailer.outbox[6].body)
コード例 #14
0
def make_dues15_reversal_invoice_pdf(request):
    """
    This view checks supplied information (in URL) against info in database
    -- especially the invoice number --
    and conditionally returns
    - an error message or
    - a PDF
    """
    email_address = request.matchdict['email']
    token = request.matchdict['code']
    invoice_number = request.matchdict['no']

    try:
        member = C3sMember.get_by_dues15_token(token)
        assert member is not None
        assert member.dues15_token == token
        assert member.email == email_address

    except AssertionError:
        request.session.flash(
            u"This member and token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    try:
        invoice = Dues15Invoice.get_by_invoice_no(invoice_number)
        assert invoice is not None
    except AssertionError:
        request.session.flash(
            u"No invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice token must match with token
    try:
        assert(invoice.token == token)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: reversal invoice token must be reversal
    try:
        assert(invoice.is_reversal)
    except AssertionError:
        request.session.flash(
            u"No reversal invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # return a pdf file
    pdf_file = make_reversal_pdf_pdflatex(member, invoice)
    response = Response(content_type='application/pdf')
    pdf_file.seek(0)  # rewind to beginning
    response.app_iter = open(pdf_file.name, "r")
    return response
コード例 #15
0
def dues15_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",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(

            request.route_url('detail', memberid=member.id) + '#dues15')

    # 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'])),
            'dues15_message_to_staff'  # message queue for user
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if DEBUG:
        print("DEBUG: member.dues15_amount: {}".format(
            member.dues15_amount))
        print("DEBUG: type(member.dues15_amount): {}".format(
            type(member.dues15_amount)))
        print("DEBUG: member.dues15_reduced: {}".format(
            member.dues15_reduced))
        print("DEBUG: member.dues15_amount_reduced: {}".format(
            member.dues15_amount_reduced))
        print("DEBUG: type(member.dues15_amount_reduced): {}".format(
            type(member.dues15_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.',
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    # check the reduction amount: same as default calculated amount?
    if ((member.dues15_reduced is False) and (
            member.dues15_amount == reduced_amount)):
        request.session.flash(
            u"Dieser Beitrag ist der default-Beitrag!",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if reduced_amount == member.dues15_amount_reduced:
        request.session.flash(
            u"Auf diesen Beitrag wurde schon reduziert!",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if member.dues15_reduced \
            and reduced_amount > member.dues15_amount_reduced \
            or reduced_amount > member.dues15_amount:
        request.session.flash(
            u'Beitrag darf nicht über den berechneten oder bereits'
            u'reduzierten Wert gesetzt werden.',
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    # prepare: get highest invoice no from db
    max_invoice_no = Dues15Invoice.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_dues15_reduced_amount(reduced_amount)
    request.session.flash('reduction to {}'.format(reduced_amount),
                          'dues15_message_to_staff')

    old_invoice = Dues15Invoice.get_by_invoice_no(member.dues15_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 = Dues15Invoice(
        invoice_no=new_invoice_no,
        invoice_no_string=(
            u'C3S-dues2015-' + 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.dues15_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 = Dues15Invoice(
            invoice_no=new_invoice_no + 1,
            invoice_no_string=(
                u'C3S-dues2015-' + 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.dues15_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.dues15_invoice_no = new_invoice_no + 1

        DBSession.flush()  # persist newer invoices

    reversal_url = (
        request.route_url(
            'make_dues15_reversal_invoice_pdf',
            email=member.email,
            code=member.dues15_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_dues15_invoice_no_pdf',
                email=member.email,
                code=member.dues15_token,
                i=str(new_invoice_no + 1).zfill(4)
            )
        )
        email_subject, email_body = make_dues_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!',
                              'dues15_message_to_staff')
    else:
        request.session.flash('update email was sent to user!',
                              'dues15_message_to_staff')
    send_message(request, message)
    return HTTPFound(
        request.route_url(
            'detail',
            memberid=member_id) +
        '#dues15')
コード例 #16
0
def send_dues15_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(2016, 1, 1):
        request.session.flash(
            'Member {0} was not a member in 2015. Therefore, you cannot send '
            'an invoice for 2015.'.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.dues15_invoice is True:
        invoice = Dues15Invoice.get_by_invoice_no(member.dues15_invoice_no)
        member.dues15_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 (Dues15Invoice.check_for_existing_dues15_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.dues15_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 = Dues15Invoice.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_dues15(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.dues15_invoice = True
        member.dues15_invoice_no = new_invoice_no  # irrelevant for investing
        member.dues15_invoice_date = datetime.now()
        member.dues15_token = randomstring
        member.dues15_start = dues_start

        if 'normal' in member.membership_type:  # only for normal members
            member.set_dues15_amount(dues_amount)
            # store some more info about invoice in invoice table
            invoice = Dues15Invoice(
                invoice_no=member.dues15_invoice_no,
                invoice_no_string=(u'C3S-dues2015-' +
                                   str(member.dues15_invoice_no).zfill(4)),
                invoice_date=member.dues15_invoice_date,
                invoice_amount=u'' + str(member.dues15_amount),
                member_id=member.id,
                membership_no=member.membership_number,
                email=member.email,
                token=member.dues15_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 'normal' in member.membership_type:
        start_quarter = string_start_quarter(member)
        invoice_url = (request.route_url(
            'make_dues15_invoice_no_pdf',
            email=member.email,
            code=member.dues15_token,
            i=str(member.dues15_invoice_no).zfill(4)))
        email_subject, email_body = make_dues_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) + '#dues15')
    if 'toolbox' in request.referrer:
        return HTTPFound(request.route_url('toolbox'))
    else:
        return get_memberhip_listing_redirect(request, member.id)
コード例 #17
0
def make_dues15_invoice_no_pdf(request):
    """
    Create invoice PDFs on-the-fly.

    This view checks supplied information (in URL) against info in database
    and returns
    - an error message OR
    - a PDF as receipt

    === ===========================================================
    URL http://app:port/dues_invoice_no/EMAIL/CAQJGCGUFW/C3S-dues15-0001.pdf
    === ===========================================================

    """
    email_address = request.matchdict['email']
    token = request.matchdict['code']
    invoice_number = request.matchdict['i']

    try:
        member = C3sMember.get_by_dues15_token(token)
        assert member is not None
        assert member.dues15_token == token
        assert member.email == email_address
    except AssertionError:
        request.session.flash(
            u"This member and token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    try:
        invoice = Dues15Invoice.get_by_invoice_no(
            invoice_number.lstrip('0'))
        assert invoice is not None
    except AssertionError:
        request.session.flash(
            u"No invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice token must match with token
    try:
        assert(invoice.token == token)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice must not be reversal
    try:
        assert(not invoice.is_reversal)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # return a pdf file
    pdf_file = make_invoice_pdf_pdflatex(member, invoice)
    response = Response(content_type='application/pdf')
    pdf_file.seek(0)  # rewind to beginning
    response.app_iter = open(pdf_file.name, "r")
    return response
コード例 #18
0
def send_dues15_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(2016,1,1):
        request.session.flash(
            'Member {0} was not a member in 2015. Therefore, you cannot send '
            'an invoice for 2015.'.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.dues15_invoice is True:
        invoice = Dues15Invoice.get_by_invoice_no(member.dues15_invoice_no)
        member.dues15_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 (Dues15Invoice.check_for_existing_dues15_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.dues15_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 = Dues15Invoice.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_dues15(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.dues15_invoice = True
        member.dues15_invoice_no = new_invoice_no  # irrelevant for investing
        member.dues15_invoice_date = datetime.now()
        member.dues15_token = randomstring
        member.dues15_start = dues_start

        if 'normal' in member.membership_type:  # only for normal members
            member.set_dues15_amount(dues_amount)
            # store some more info about invoice in invoice table
            invoice = Dues15Invoice(
                invoice_no=member.dues15_invoice_no,
                invoice_no_string=(
                    u'C3S-dues2015-' + str(member.dues15_invoice_no).zfill(4)),
                invoice_date=member.dues15_invoice_date,
                invoice_amount=u'' + str(member.dues15_amount),
                member_id=member.id,
                membership_no=member.membership_number,
                email=member.email,
                token=member.dues15_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 'normal' in member.membership_type:
        start_quarter = string_start_quarter(member)
        invoice_url = (
            request.route_url(
                'make_dues15_invoice_no_pdf',
                email=member.email,
                code=member.dues15_token,
                i=str(member.dues15_invoice_no).zfill(4)
            )
        )
        email_subject, email_body = make_dues_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) +
            '#dues15')
    if 'toolbox' in request.referrer:
        return HTTPFound(request.route_url('toolbox'))
    else:
        return get_memberhip_listing_redirect(request, member.id)
コード例 #19
0
def make_dues15_invoice_no_pdf(request):
    """
    Create invoice PDFs on-the-fly.

    This view checks supplied information (in URL) against info in database
    and returns
    - an error message OR
    - a PDF as receipt

    === ===========================================================
    URL http://app:port/dues_invoice_no/EMAIL/CAQJGCGUFW/C3S-dues15-0001.pdf
    === ===========================================================

    """
    token = request.matchdict['code']
    invoice_number = request.matchdict['i']

    try:
        member = C3sMember.get_by_dues15_token(token)
        assert member is not None
        assert member.dues15_token == token
    except AssertionError:
        request.session.flash(
            u"This member and token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    try:
        invoice = Dues15Invoice.get_by_invoice_no(invoice_number.lstrip('0'))
        assert invoice is not None
    except AssertionError:
        request.session.flash(
            u"No invoice found!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice token must match with token
    try:
        assert (invoice.token == token)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # sanity check: invoice must not be reversal
    try:
        assert (not invoice.is_reversal)
    except AssertionError:
        request.session.flash(
            u"Token did not match!",
            'message_to_user'  # message queue for user
        )
        return HTTPFound(request.route_url('error_page'))

    # return a pdf file
    pdf_file = make_invoice_pdf_pdflatex(member, invoice)
    response = Response(content_type='application/pdf')
    pdf_file.seek(0)  # rewind to beginning
    response.app_iter = open(pdf_file.name, "r")
    return response
コード例 #20
0
def dues15_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",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    # 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'])),
            'dues15_message_to_staff'  # message queue for user
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if DEBUG:
        print("DEBUG: member.dues15_amount: {}".format(member.dues15_amount))
        print("DEBUG: type(member.dues15_amount): {}".format(
            type(member.dues15_amount)))
        print("DEBUG: member.dues15_reduced: {}".format(member.dues15_reduced))
        print("DEBUG: member.dues15_amount_reduced: {}".format(
            member.dues15_amount_reduced))
        print("DEBUG: type(member.dues15_amount_reduced): {}".format(
            type(member.dues15_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.',
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    # check the reduction amount: same as default calculated amount?
    if ((member.dues15_reduced is False)
            and (member.dues15_amount == reduced_amount)):
        request.session.flash(
            u"Dieser Beitrag ist der default-Beitrag!",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if reduced_amount == member.dues15_amount_reduced:
        request.session.flash(
            u"Auf diesen Beitrag wurde schon reduziert!",
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    if member.dues15_reduced \
            and reduced_amount > member.dues15_amount_reduced \
            or reduced_amount > member.dues15_amount:
        request.session.flash(
            u'Beitrag darf nicht über den berechneten oder bereits'
            u'reduzierten Wert gesetzt werden.',
            'dues15_message_to_staff'  # message queue for staff
        )
        return HTTPFound(
            request.route_url('detail', memberid=member.id) + '#dues15')

    # prepare: get highest invoice no from db
    max_invoice_no = Dues15Invoice.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_dues15_reduced_amount(reduced_amount)
    request.session.flash('reduction to {}'.format(reduced_amount),
                          'dues15_message_to_staff')

    old_invoice = Dues15Invoice.get_by_invoice_no(member.dues15_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 = Dues15Invoice(
        invoice_no=new_invoice_no,
        invoice_no_string=(u'C3S-dues2015-' + 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.dues15_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 = Dues15Invoice(
            invoice_no=new_invoice_no + 1,
            invoice_no_string=(u'C3S-dues2015-' +
                               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.dues15_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.dues15_invoice_no = new_invoice_no + 1

        DBSession.flush()  # persist newer invoices

    reversal_url = (request.route_url(
        'make_dues15_reversal_invoice_pdf',
        email=member.email,
        code=member.dues15_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_dues15_invoice_no_pdf',
                                         email=member.email,
                                         code=member.dues15_token,
                                         i=str(new_invoice_no + 1).zfill(4)))
        email_subject, email_body = make_dues_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!',
                              'dues15_message_to_staff')
    else:
        request.session.flash('update email was sent to user!',
                              'dues15_message_to_staff')
    send_message(request, message)
    return HTTPFound(
        request.route_url('detail', memberid=member_id) + '#dues15')