From 58354e7adff947b1c29892c45133e70f617368cc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 18 Jun 2023 14:08:36 -0500 Subject: [PATCH] Add views etc. for member equity payments --- tailbone/menus.py | 5 + .../templates/people/view_profile_buefy.mako | 4 + tailbone/views/master.py | 8 + tailbone/views/members.py | 148 +++++++++++++++++- tailbone/views/people.py | 3 + 5 files changed, 164 insertions(+), 4 deletions(-) diff --git a/tailbone/menus.py b/tailbone/menus.py index 8f98b91b..c26484f0 100644 --- a/tailbone/menus.py +++ b/tailbone/menus.py @@ -332,6 +332,11 @@ class MenuHandler(GenericHandler): 'route': 'members', 'perm': 'members.list', }, + { + 'title': "Member Equity Payments", + 'route': 'member_equity_payments', + 'perm': 'member_equity_payments.list', + }, { 'title': "Membership Types", 'route': 'membership_types', diff --git a/tailbone/templates/people/view_profile_buefy.mako b/tailbone/templates/people/view_profile_buefy.mako index 28b6e1d4..e1da8661 100644 --- a/tailbone/templates/people/view_profile_buefy.mako +++ b/tailbone/templates/people/view_profile_buefy.mako @@ -634,6 +634,10 @@ {{ member.withdrew }} + + {{ member.equity_total_display }} + +
${self.render_member_panel_buttons(member)} diff --git a/tailbone/views/master.py b/tailbone/views/master.py index c8324176..f90f8151 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -973,6 +973,14 @@ class MasterView(View): url = self.request.route_url('customers.view', uuid=customer.uuid) return tags.link_to(text, url) + def render_member(self, obj, field): + member = getattr(obj, field) + if not member: + return + text = str(member) + url = self.request.route_url('members.view', uuid=member.uuid) + return tags.link_to(text, url) + def render_email_key(self, obj, field): if hasattr(obj, field): email_key = getattr(obj, field) diff --git a/tailbone/views/members.py b/tailbone/views/members.py index 92c213ae..197efa41 100644 --- a/tailbone/views/members.py +++ b/tailbone/views/members.py @@ -29,12 +29,12 @@ from collections import OrderedDict import sqlalchemy as sa from rattail.db import model -from rattail.db.model import MembershipType, Member +from rattail.db.model import MembershipType, Member, MemberEquityPayment from deform import widget as dfwidget from webhelpers2.html import tags -from tailbone import grids +from tailbone import grids, forms from tailbone.views import MasterView @@ -107,6 +107,7 @@ class MemberView(MasterView): touchable = True has_versions = True configurable = True + supports_autocomplete = True labels = { 'id': "ID", @@ -131,12 +132,40 @@ class MemberView(MasterView): 'default_phone', 'membership_type', 'active', + 'equity_total', 'equity_current', 'equity_payment_due', 'joined', 'withdrew', ] + has_rows = True + model_row_class = MemberEquityPayment + rows_title = "Equity Payments" + + row_grid_columns = [ + 'amount', + 'received', + 'description', + 'transaction_identifier', + ] + + def should_expose_quickie_search(self): + if self.expose_quickie_search: + return True + app = self.get_rattail_app() + return app.get_people_handler().should_expose_quickie_search() + + def get_quickie_perm(self): + return 'people.quickie' + + def get_quickie_url(self): + return self.request.route_url('people.quickie') + + def get_quickie_placeholder(self): + app = self.get_rattail_app() + return app.get_people_handler().get_quickie_search_placeholder() + def configure_grid(self, g): super(MemberView, self).configure_grid(g) route_prefix = self.get_route_prefix() @@ -225,14 +254,17 @@ class MemberView(MasterView): return 'notice' def configure_form(self, f): - super(MemberView, self).configure_form(f) + super().configure_form(f) member = f.model_instance # date fields f.set_type('joined', 'date_jquery') + f.set_type('withdrew', 'date_jquery') + + # equity fields + f.set_renderer('equity_total', self.render_equity_total) f.set_type('equity_payment_due', 'date_jquery') f.set_type('equity_last_paid', 'date_jquery') - f.set_type('withdrew', 'date_jquery') # person if self.creating or self.editing: @@ -289,6 +321,11 @@ class MemberView(MasterView): 'withdrew', ) + def render_equity_total(self, member, field): + app = self.get_rattail_app() + total = sum([payment.amount for payment in member.equity_payments]) + return app.render_currency(total) + def template_kwargs_view(self, **kwargs): kwargs = super().template_kwargs_view(**kwargs) app = self.get_rattail_app() @@ -323,6 +360,25 @@ class MemberView(MasterView): url = self.request.route_url('membership_types.view', uuid=memtype.uuid) return tags.link_to(text, url) + def get_row_data(self, member): + model = self.model + return self.Session.query(model.MemberEquityPayment)\ + .filter(model.MemberEquityPayment.member == member) + + def get_parent(self, payment): + return payment.member + + def configure_row_grid(self, g): + super().configure_row_grid(g) + + g.set_type('amount', 'currency') + + g.set_sort_defaults('received', 'desc') + + def row_view_action_url(self, payment, i): + return self.request.route_url('member_equity_payments.view', + uuid=payment.uuid) + def configure_get_simple_settings(self): return [ @@ -337,6 +393,87 @@ class MemberView(MasterView): ] +class MemberEquityPaymentView(MasterView): + """ + Master view for the MemberEquityPayment class. + """ + model_class = model.MemberEquityPayment + route_prefix = 'member_equity_payments' + url_prefix = '/member-equity-payments' + has_versions = True + + grid_columns = [ + 'member', + 'amount', + 'received', + 'description', + 'transaction_identifier', + ] + + form_fields = [ + 'member', + 'amount', + 'received', + 'description', + 'transaction_identifier', + ] + + def query(self, session): + query = super().query(session) + model = self.model + + query = query.join(model.Member) + + return query + + def configure_grid(self, g): + super().configure_grid(g) + model = self.model + + g.set_joiner('member', lambda q: q.outerjoin(model.Person)) + g.set_sorter('member', model.Person.display_name) + g.set_link('member') + + g.set_type('amount', 'currency') + + g.set_sort_defaults('received', 'desc') + g.set_link('received') + + def configure_form(self, f): + super().configure_form(f) + model = self.model + payment = f.model_instance + + # member + if self.creating: + f.replace('member', 'member_uuid') + member_display = "" + if self.request.method == 'POST': + if self.request.POST.get('member_uuid'): + member = self.Session.get(model.Member, + self.request.POST['member_uuid']) + if member: + member_display = str(member) + elif self.editing: + member_display = str(payment.member or '') + members_url = self.request.route_url('members.autocomplete') + f.set_widget('member_uuid', forms.widgets.JQueryAutocompleteWidget( + field_display=member_display, service_url=members_url)) + f.set_label('member_uuid', "Member") + else: + f.set_readonly('member') + f.set_renderer('member', self.render_member) + + # amount + f.set_type('amount', 'currency') + + # received + if self.creating: + f.set_type('received', 'date_jquery') + else: + f.set_readonly('received') + + def defaults(config, **kwargs): base = globals() @@ -346,6 +483,9 @@ def defaults(config, **kwargs): MemberView = kwargs.get('MemberView', base['MemberView']) MemberView.defaults(config) + MemberEquityPaymentView = kwargs.get('MemberEquityPaymentView', base['MemberEquityPaymentView']) + MemberEquityPaymentView.defaults(config) + def includeme(config): defaults(config) diff --git a/tailbone/views/people.py b/tailbone/views/people.py index 77e4eaff..29b93b9a 100644 --- a/tailbone/views/people.py +++ b/tailbone/views/people.py @@ -715,12 +715,14 @@ class PersonView(MasterView): return list(data.values()) def get_context_member(self, member): + app = self.get_rattail_app() profile_url = None if member.person: profile_url = self.request.route_url('people.view_profile', uuid=member.person_uuid) key = self.get_member_key_field() + equity_total = sum([payment.amount for payment in member.equity_payments]) data = { 'uuid': member.uuid, '_key': getattr(member, key), @@ -736,6 +738,7 @@ class PersonView(MasterView): 'person_display_name': member.person.display_name if member.person else None, 'view_url': self.request.route_url('members.view', uuid=member.uuid), 'view_profile_url': profile_url, + 'equity_total_display': app.render_currency(equity_total), } membership_type = member.membership_type