Improve views for taxes, esp. in POS batches

This commit is contained in:
Lance Edgar 2023-10-07 16:26:33 -05:00
parent eccb855d09
commit 07b1d0841e
7 changed files with 113 additions and 17 deletions

View file

@ -177,13 +177,18 @@ class GridFilter(object):
self.key = key self.key = key
self.config = config self.config = config
self.label = label or prettify(key) self.label = label or prettify(key)
self.verbs = verbs or self.get_default_verbs()
if value_renderer: if value_renderer:
self.set_value_renderer(value_renderer) self.set_value_renderer(value_renderer)
elif value_enum: elif value_enum:
self.set_choices(value_enum) self.set_choices(value_enum)
else: else:
self.set_value_renderer(self.value_renderer_factory) self.set_value_renderer(self.value_renderer_factory)
# nb. do this after setting choices, if applicable, since that
# could change default verbs
self.verbs = verbs or self.get_default_verbs()
self.default_active = default_active self.default_active = default_active
self.default_verb = default_verb self.default_verb = default_verb
self.default_value = default_value self.default_value = default_value
@ -461,6 +466,10 @@ class AlchemyStringFilter(AlchemyGridFilter):
""" """
Expose contains / does-not-contain verbs in addition to core. Expose contains / does-not-contain verbs in addition to core.
""" """
if self.choices:
return ['equal', 'not_equal', 'is_null', 'is_not_null', 'is_any']
return ['contains', 'does_not_contain', return ['contains', 'does_not_contain',
'contains_any_of', 'contains_any_of',
'equal', 'not_equal', 'equal_any_of', 'equal', 'not_equal', 'equal_any_of',

View file

@ -0,0 +1,13 @@
## -*- coding: utf-8; -*-
<%inherit file="/batch/view.mako" />
<%def name="modify_this_page_vars()">
${parent.modify_this_page_vars()}
<script type="text/javascript">
${form.component_studly}Data.taxesData = ${json.dumps(taxes_data)|n}
</script>
</%def>
${parent.body()}

View file

@ -26,6 +26,8 @@ Views for POS batches
from rattail.db.model import POSBatch, POSBatchRow from rattail.db.model import POSBatch, POSBatchRow
from webhelpers2.html import HTML
from tailbone.views.batch import BatchMasterView from tailbone.views.batch import BatchMasterView
@ -39,7 +41,11 @@ class POSBatchView(BatchMasterView):
route_prefix = 'batch.pos' route_prefix = 'batch.pos'
url_prefix = '/batch/pos' url_prefix = '/batch/pos'
creatable = False creatable = False
editable = False
cloneable = True cloneable = True
refreshable = False
rows_deletable = False
rows_bulk_deletable = False
labels = { labels = {
'terminal_id': "Terminal ID", 'terminal_id': "Terminal ID",
@ -66,8 +72,7 @@ class POSBatchView(BatchMasterView):
'params', 'params',
'rowcount', 'rowcount',
'sales_total', 'sales_total',
'tax1_total', 'taxes',
'tax2_total',
'tender_total', 'tender_total',
'balance', 'balance',
'void', 'void',
@ -89,6 +94,7 @@ class POSBatchView(BatchMasterView):
'quantity', 'quantity',
'sales_total', 'sales_total',
'tender_total', 'tender_total',
'tax_code',
'user', 'user',
] ]
@ -102,8 +108,7 @@ class POSBatchView(BatchMasterView):
'txn_price', 'txn_price',
'quantity', 'quantity',
'sales_total', 'sales_total',
'tax1_total', 'tax_code',
'tax2_total',
'tender_total', 'tender_total',
'tender', 'tender',
'void', 'void',
@ -126,8 +131,6 @@ class POSBatchView(BatchMasterView):
g.set_link('created_by') g.set_link('created_by')
g.set_type('sales_total', 'currency') g.set_type('sales_total', 'currency')
g.set_type('tax1_total', 'currency')
g.set_type('tax2_total', 'currency')
g.set_type('tender_total', 'currency') g.set_type('tender_total', 'currency')
# executed # executed
@ -149,13 +152,54 @@ class POSBatchView(BatchMasterView):
f.set_renderer('customer', self.render_customer) f.set_renderer('customer', self.render_customer)
f.set_type('sales_total', 'currency') f.set_type('sales_total', 'currency')
f.set_type('tax1_total', 'currency')
f.set_type('tax2_total', 'currency')
f.set_type('tender_total', 'currency') f.set_type('tender_total', 'currency')
f.set_type('tender_total', 'currency') f.set_type('tender_total', 'currency')
f.set_renderer('taxes', self.render_taxes)
f.set_renderer('balance', lambda batch, field: app.render_currency(batch.get_balance())) f.set_renderer('balance', lambda batch, field: app.render_currency(batch.get_balance()))
def render_taxes(self, batch, field):
route_prefix = self.get_route_prefix()
factory = self.get_grid_factory()
g = factory(
key=f'{route_prefix}.taxes',
data=[],
columns=[
'code',
'description',
'rate',
'total',
],
)
return HTML.literal(
g.render_buefy_table_element(data_prop='taxesData'))
def template_kwargs_view(self, **kwargs):
kwargs = super().template_kwargs_view(**kwargs)
app = self.get_rattail_app()
batch = kwargs['instance']
taxes = []
for btax in batch.taxes.values():
data = {
'uuid': btax.uuid,
'code': btax.tax_code,
'description': btax.tax.description,
'rate': app.render_percent(btax.tax_rate),
'total': app.render_currency(btax.tax_total),
}
taxes.append(data)
taxes.sort(key=lambda t: t['code'])
kwargs['taxes_data'] = taxes
kwargs['execute_enabled'] = False
kwargs['why_not_execute'] = "POS batch must be executed at POS"
return kwargs
def configure_row_grid(self, g): def configure_row_grid(self, g):
super().configure_row_grid(g) super().configure_row_grid(g)

View file

@ -861,6 +861,14 @@ class MasterView(View):
url = self.request.route_url('stores.view', uuid=store.uuid) url = self.request.route_url('stores.view', uuid=store.uuid)
return tags.link_to(text, url) return tags.link_to(text, url)
def render_tax(self, obj, field):
tax = getattr(obj, field)
if not tax:
return
text = str(tax)
url = self.request.route_url('taxes.view', uuid=tax.uuid)
return tags.link_to(text, url)
def render_tender(self, obj, field): def render_tender(self, obj, field):
tender = getattr(obj, field) tender = getattr(obj, field)
if not tender: if not tender:

View file

@ -366,6 +366,15 @@ class ProductView(MasterView):
g.set_renderer('cost', self.render_cost) g.set_renderer('cost', self.render_cost)
g.set_label('cost', "Unit Cost") g.set_label('cost', "Unit Cost")
# tax
g.set_joiner('tax', lambda q: q.outerjoin(model.Tax))
taxes = self.Session.query(model.Tax)\
.order_by(model.Tax.code)\
.all()
taxes = OrderedDict([(tax.uuid, tax.description)
for tax in taxes])
g.set_filter('tax', model.Tax.uuid, value_enum=taxes)
# report_code_name # report_code_name
g.set_joiner('report_code_name', lambda q: q.outerjoin(model.ReportCode)) g.set_joiner('report_code_name', lambda q: q.outerjoin(model.ReportCode))
g.set_filter('report_code_name', model.ReportCode.name) g.set_filter('report_code_name', model.ReportCode.name)
@ -810,7 +819,7 @@ class ProductView(MasterView):
raise self.notfound() raise self.notfound()
def configure_form(self, f): def configure_form(self, f):
super(ProductView, self).configure_form(f) super().configure_form(f)
product = f.model_instance product = f.model_instance
# department # department
@ -934,7 +943,7 @@ class ProductView(MasterView):
f.set_label('tax_uuid', "Tax") f.set_label('tax_uuid', "Tax")
else: else:
f.set_readonly('tax') f.set_readonly('tax')
# f.set_renderer('tax', self.render_tax) f.set_renderer('tax', self.render_tax)
# tax1/2/3 # tax1/2/3
f.set_readonly('tax1') f.set_readonly('tax1')

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2022 Lance Edgar # Copyright © 2010-2023 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -24,8 +24,6 @@
Tax Views Tax Views
""" """
from __future__ import unicode_literals, absolute_import
from rattail.db import model from rattail.db import model
from tailbone.views import MasterView from tailbone.views import MasterView
@ -53,12 +51,26 @@ class TaxView(MasterView):
] ]
def configure_grid(self, g): def configure_grid(self, g):
super(TaxView, self).configure_grid(g) super().configure_grid(g)
g.filters['description'].default_active = True
g.filters['description'].default_verb = 'contains' # code
g.set_sort_defaults('code') g.set_sort_defaults('code')
g.set_link('code') g.set_link('code')
# description
g.set_link('description') g.set_link('description')
g.filters['description'].default_active = True
g.filters['description'].default_verb = 'contains'
# rate
g.set_type('rate', 'percent')
def configure_form(self, f):
super().configure_form(f)
# rate
f.set_type('rate', 'percent')
# TODO: deprecate / remove this # TODO: deprecate / remove this
TaxesView = TaxView TaxesView = TaxView

View file

@ -43,6 +43,7 @@ def defaults(config, **kwargs):
config.include(mod('tailbone.views.reportcodes')) config.include(mod('tailbone.views.reportcodes'))
config.include(mod('tailbone.views.stores')) config.include(mod('tailbone.views.stores'))
config.include(mod('tailbone.views.subdepartments')) config.include(mod('tailbone.views.subdepartments'))
config.include(mod('tailbone.views.taxes'))
config.include(mod('tailbone.views.tenders')) config.include(mod('tailbone.views.tenders'))
config.include(mod('tailbone.views.uoms')) config.include(mod('tailbone.views.uoms'))
config.include(mod('tailbone.views.vendors')) config.include(mod('tailbone.views.vendors'))