fix: add optional Transactions tab for profile view
showing Trainwreck data by default
This commit is contained in:
parent
b7d26b6b8c
commit
16bf13787d
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in a new issue