fix: add optional Transactions tab for profile view

showing Trainwreck data by default
This commit is contained in:
Lance Edgar 2024-07-05 14:45:35 -05:00
parent b7d26b6b8c
commit 16bf13787d
3 changed files with 210 additions and 0 deletions

View file

@ -33,6 +33,20 @@
</b-field> </b-field>
</div> </div>
<h3 class="block is-size-3">Profile View</h3>
<div class="block" style="padding-left: 2rem; width: 50%;">
<b-field>
<b-checkbox name="tailbone.people.profile.expose_transactions"
v-model="simpleSettings['tailbone.people.profile.expose_transactions']"
native-value="true"
@input="settingsNeedSaved = true">
Show tab for Customer POS Transactions
</b-checkbox>
</b-field>
</div>
</%def> </%def>

View file

@ -1656,6 +1656,34 @@
</${b}-tab-item> </${b}-tab-item>
</%def> </%def>
% if expose_transactions:
<%def name="render_transactions_tab_template()">
<script type="text/x-template" id="transactions-tab-template">
<div>
<transactions-grid
ref="transactionsGrid"
/>
</div>
</script>
</%def>
<%def name="render_transactions_tab()">
<${b}-tab-item label="Transactions"
value="transactions"
% if not request.use_oruga:
icon-pack="fas"
% endif
icon="bars">
<transactions-tab ref="tab_transactions"
:person="person"
@profile-changed="profileChanged" />
</${b}-tab-item>
</%def>
% endif
<%def name="render_user_tab_template()"> <%def name="render_user_tab_template()">
<script type="text/x-template" id="user-tab-template"> <script type="text/x-template" id="user-tab-template">
<div> <div>
@ -1806,6 +1834,9 @@
% endif % endif
${self.render_employee_tab()} ${self.render_employee_tab()}
${self.render_notes_tab()} ${self.render_notes_tab()}
% if expose_transactions:
${self.render_transactions_tab()}
% endif
${self.render_user_tab()} ${self.render_user_tab()}
</%def> </%def>
@ -1941,6 +1972,12 @@
% endif % endif
${self.render_employee_tab_template()} ${self.render_employee_tab_template()}
${self.render_notes_tab_template()} ${self.render_notes_tab_template()}
% if expose_transactions:
${transactions_grid.render_complete(allow_save_defaults=False)|n}
${self.render_transactions_tab_template()}
% endif
${self.render_user_tab_template()} ${self.render_user_tab_template()}
${self.render_profile_info_template()} ${self.render_profile_info_template()}
</%def> </%def>
@ -2824,6 +2861,49 @@
</script> </script>
</%def> </%def>
% if expose_transactions:
<%def name="declare_transactions_tab_vars()">
<script type="text/javascript">
let TransactionsTabData = {}
let TransactionsTab = {
template: '#transactions-tab-template',
mixins: [TabMixin, SimpleRequestMixin],
props: {
person: Object,
},
computed: {},
methods: {
// nb. we override this completely, just tell the grid to refresh
refreshTab() {
this.refreshingTab = true
this.$refs.transactionsGrid.loadAsyncData(null, () => {
this.refreshed = Date.now()
this.refreshingTab = false
})
}
},
}
</script>
</%def>
<%def name="make_transactions_tab_component()">
${self.declare_transactions_tab_vars()}
<script type="text/javascript">
TransactionsTab.data = function() { return TransactionsTabData }
Vue.component('transactions-tab', TransactionsTab)
<% request.register_component('transactions-tab', 'TransactionsTab') %>
</script>
</%def>
% endif
<%def name="declare_user_tab_vars()"> <%def name="declare_user_tab_vars()">
<script type="text/javascript"> <script type="text/javascript">
@ -3086,6 +3166,19 @@
% endif % endif
${self.make_employee_tab_component()} ${self.make_employee_tab_component()}
${self.make_notes_tab_component()} ${self.make_notes_tab_component()}
% if expose_transactions:
<script type="text/javascript">
TransactionsGrid.data = function() { return TransactionsGridData }
Vue.component('transactions-grid', TransactionsGrid)
## TODO: why is this line not needed?
## <% request.register_component('transactions-grid', 'TransactionsGrid') %>
</script>
${self.make_transactions_tab_component()}
% endif
${self.make_user_tab_component()} ${self.make_user_tab_component()}
${self.make_profile_info_component()} ${self.make_profile_info_component()}
</%def> </%def>

View file

@ -40,6 +40,7 @@ import colander
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import forms, grids from tailbone import forms, grids
from tailbone.db import TrainwreckSession
from tailbone.views import MasterView from tailbone.views import MasterView
from tailbone.util import raw_datetime from tailbone.util import raw_datetime
@ -487,13 +488,101 @@ class PersonView(MasterView):
'expose_customer_shoppers': self.customers_should_expose_shoppers(), 'expose_customer_shoppers': self.customers_should_expose_shoppers(),
'max_one_member': app.get_membership_handler().max_one_per_person(), 'max_one_member': app.get_membership_handler().max_one_per_person(),
'use_preferred_first_name': self.people_handler.should_use_preferred_first_name(), 'use_preferred_first_name': self.people_handler.should_use_preferred_first_name(),
'expose_transactions': self.should_expose_profile_transactions(),
} }
if context['expose_transactions']:
context['transactions_grid'] = self.profile_transactions_grid(person, empty=True)
if self.request.has_perm('people_profile.view_versions'): if self.request.has_perm('people_profile.view_versions'):
context['revisions_grid'] = self.profile_revisions_grid(person) context['revisions_grid'] = self.profile_revisions_grid(person)
return self.render_to_response('view_profile', context) return self.render_to_response('view_profile', context)
def should_expose_profile_transactions(self):
return self.rattail_config.get_bool('tailbone.people.profile.expose_transactions',
default=False)
def profile_transactions_grid(self, person, empty=False):
app = self.get_rattail_app()
trainwreck = app.get_trainwreck_handler()
model = trainwreck.get_model()
route_prefix = self.get_route_prefix()
if empty:
# TODO: surely there is a better way to have empty data..? but so
# much logic depends on a query, can't just pass empty list here
data = TrainwreckSession.query(model.Transaction)\
.filter(model.Transaction.uuid == 'bogus')
else:
data = self.profile_transactions_query(person)
factory = self.get_grid_factory()
g = factory(
f'{route_prefix}.profile.transactions.{person.uuid}',
data,
request=self.request,
model_class=model.Transaction,
ajax_data_url=self.get_action_url('view_profile_transactions', person),
columns=[
'start_time',
'end_time',
'system',
'terminal_id',
'receipt_number',
'cashier_name',
'customer_id',
'customer_name',
'total',
],
labels={
'terminal_id': "Terminal",
'customer_id': "Customer " + app.get_customer_key_label(),
},
filterable=True,
sortable=True,
pageable=True,
default_sortkey='end_time',
default_sortdir='desc',
component='transactions-grid',
)
if self.request.has_perm('trainwreck.transactions.view'):
url = lambda row, i: self.request.route_url('trainwreck.transactions.view',
uuid=row.uuid)
g.main_actions.append(grids.GridAction('view', icon='eye', url=url))
g.load_settings()
g.set_enum('system', self.enum.TRAINWRECK_SYSTEM)
g.set_type('total', 'currency')
return g
def profile_transactions_query(self, person):
"""
Method which must return the base query for the profile's POS
Transactions grid data.
"""
app = self.get_rattail_app()
customer = app.get_customer(person)
key_field = app.get_customer_key_field()
customer_key = getattr(customer, key_field)
if customer_key is not None:
customer_key = str(customer_key)
trainwreck = app.get_trainwreck_handler()
model = trainwreck.get_model()
query = TrainwreckSession.query(model.Transaction)\
.filter(model.Transaction.customer_id == customer_key)
return query
def profile_transactions_data(self):
"""
AJAX view to return new sorted, filtered data for transactions
grid within profile view.
"""
person = self.get_instance()
grid = self.profile_transactions_grid(person)
return grid.get_table_data()
def get_context_tabchecks(self, person): def get_context_tabchecks(self, person):
app = self.get_rattail_app() app = self.get_rattail_app()
membership = app.get_membership_handler() membership = app.get_membership_handler()
@ -1605,6 +1694,11 @@ class PersonView(MasterView):
{'section': 'rattail', {'section': 'rattail',
'option': 'people.handler'}, 'option': 'people.handler'},
# Profile View
{'section': 'tailbone',
'option': 'people.profile.expose_transactions',
'type': bool},
] ]
@classmethod @classmethod
@ -1873,6 +1967,15 @@ class PersonView(MasterView):
permission='people_profile.delete_note', permission='people_profile.delete_note',
renderer='json') renderer='json')
# profile - transactions data
config.add_route(f'{route_prefix}.view_profile_transactions',
f'{instance_url_prefix}/profile/transactions',
request_method='GET')
config.add_view(cls, attr='profile_transactions_data',
route_name=f'{route_prefix}.view_profile_transactions',
permission=f'{permission_prefix}.view_profile',
renderer='json')
# make user for person # make user for person
config.add_route('{}.make_user'.format(route_prefix), '{}/make-user'.format(url_prefix), config.add_route('{}.make_user'.format(route_prefix), '{}/make-user'.format(url_prefix),
request_method='POST') request_method='POST')