Compare commits

...

17 commits

Author SHA1 Message Date
e150453801 fix: add startup hack for tempmon DB model 2025-03-05 10:34:52 -06:00
e2582ffec5 bump: version 0.22.6 → 0.22.7 2025-02-19 10:33:39 -06:00
a6508154cb docs: update intersphinx doc links per server migration 2025-02-18 12:13:28 -06:00
7348eec671 fix: stop using old config for logo image url on login page 2025-02-18 11:16:23 -06:00
4221fa50dd fix: fix warning msg for deprecated Grid param 2025-02-14 11:37:21 -06:00
e0ebd43e7a bump: version 0.22.5 → 0.22.6 2025-02-01 15:18:12 -06:00
c7ee9de9eb fix: register vue3 form component for products -> make batch 2024-12-28 16:43:22 -06:00
950db697a0 bump: version 0.22.4 → 0.22.5 2024-12-16 12:46:45 -06:00
358b3b75a5 fix: whoops this is latest rattail 2024-12-10 13:05:32 -06:00
7e559a01b3 fix: require newer rattail lib 2024-12-10 12:52:49 -06:00
23bdde245a fix: require newer wuttaweb 2024-12-10 12:34:34 -06:00
2c269b640b fix: let caller request safe HTML literal for rendered grid table
mostly just for convenience
2024-12-01 18:12:30 -06:00
Lance Edgar
f1c8ffedda bump: version 0.22.3 → 0.22.4 2024-11-22 12:57:04 -06:00
Lance Edgar
aace6033c5 fix: avoid error in product search for duplicated key 2024-11-20 20:17:21 -06:00
Lance Edgar
7171c7fb06 fix: use vmodel for confirm password widget input
since previously this did not work at all for butterball (vue3 +
oruga) - although it was never clear why per se..

Refs: #1
2024-11-19 20:53:23 -06:00
Lance Edgar
993f066f2c bump: version 0.22.2 → 0.22.3 2024-11-19 15:45:37 -06:00
Lance Edgar
980031f524 fix: avoid error for trainwreck query when not a customer
when viewing a person's profile, who does not have a customer record,
the trainwreck query can't really return anything since it normally
should be matching on the customer ID
2024-11-18 14:59:50 -06:00
10 changed files with 93 additions and 48 deletions

View file

@ -5,6 +5,41 @@ All notable changes to Tailbone will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## v0.22.7 (2025-02-19)
### Fix
- stop using old config for logo image url on login page
- fix warning msg for deprecated Grid param
## v0.22.6 (2025-02-01)
### Fix
- register vue3 form component for products -> make batch
## v0.22.5 (2024-12-16)
### Fix
- whoops this is latest rattail
- require newer rattail lib
- require newer wuttaweb
- let caller request safe HTML literal for rendered grid table
## v0.22.4 (2024-11-22)
### Fix
- avoid error in product search for duplicated key
- use vmodel for confirm password widget input
## v0.22.3 (2024-11-19)
### Fix
- avoid error for trainwreck query when not a customer
## v0.22.2 (2024-11-18) ## v0.22.2 (2024-11-18)
### Fix ### Fix

View file

@ -27,10 +27,10 @@ templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
intersphinx_mapping = { intersphinx_mapping = {
'rattail': ('https://rattailproject.org/docs/rattail/', None), 'rattail': ('https://docs.wuttaproject.org/rattail/', None),
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None), 'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None), 'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None),
'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None), 'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None),
} }
# allow todo entries to show up # allow todo entries to show up

View file

@ -6,7 +6,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "Tailbone" name = "Tailbone"
version = "0.22.2" version = "0.22.7"
description = "Backoffice Web Application for Rattail" description = "Backoffice Web Application for Rattail"
readme = "README.md" readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
@ -53,13 +53,13 @@ dependencies = [
"pyramid_mako", "pyramid_mako",
"pyramid_retry", "pyramid_retry",
"pyramid_tm", "pyramid_tm",
"rattail[db,bouncer]>=0.18.5", "rattail[db,bouncer]>=0.20.1",
"sa-filters", "sa-filters",
"simplejson", "simplejson",
"transaction", "transaction",
"waitress", "waitress",
"WebHelpers2", "WebHelpers2",
"WuttaWeb>=0.14.0", "WuttaWeb>=0.21.0",
"zope.sqlalchemy>=1.5", "zope.sqlalchemy>=1.5",
] ]

View file

@ -62,6 +62,17 @@ def make_rattail_config(settings):
# nb. this is for compaibility with wuttaweb # nb. this is for compaibility with wuttaweb
settings['wutta_config'] = rattail_config settings['wutta_config'] = rattail_config
# must import all sqlalchemy models before things get rolling,
# otherwise can have errors about continuum TransactionMeta class
# not yet mapped, when relevant pages are first requested...
# cf. https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models
# hat tip to https://stackoverflow.com/a/59241485
if getattr(rattail_config, 'tempmon_engine', None):
from rattail_tempmon.db import model as tempmon_model, Session as TempmonSession
tempmon_session = TempmonSession()
tempmon_session.query(tempmon_model.Appliance).first()
tempmon_session.close()
# configure database sessions # configure database sessions
if hasattr(rattail_config, 'appdb_engine'): if hasattr(rattail_config, 'appdb_engine'):
tailbone.db.Session.configure(bind=rattail_config.appdb_engine) tailbone.db.Session.configure(bind=rattail_config.appdb_engine)

View file

@ -235,7 +235,7 @@ class Grid(WuttaGrid):
if 'pageable' in kwargs: if 'pageable' in kwargs:
warnings.warn("pageable param is deprecated for Grid(); " warnings.warn("pageable param is deprecated for Grid(); "
"please use vue_tagname param instead", "please use paginated param instead",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
kwargs.setdefault('paginated', kwargs.pop('pageable')) kwargs.setdefault('paginated', kwargs.pop('pageable'))
@ -1223,6 +1223,7 @@ class Grid(WuttaGrid):
def render_table_element(self, template='/grids/b-table.mako', def render_table_element(self, template='/grids/b-table.mako',
data_prop='gridData', empty_labels=False, data_prop='gridData', empty_labels=False,
literal=False,
**kwargs): **kwargs):
""" """
This is intended for ad-hoc "small" grids with static data. Renders This is intended for ad-hoc "small" grids with static data. Renders
@ -1239,7 +1240,10 @@ class Grid(WuttaGrid):
if context['paginated']: if context['paginated']:
context.setdefault('per_page', 20) context.setdefault('per_page', 20)
context['view_click_handler'] = self.get_view_click_handler() context['view_click_handler'] = self.get_view_click_handler()
return render(template, context) result = render(template, context)
if literal:
result = HTML.literal(result)
return result
def get_view_click_handler(self): def get_view_click_handler(self):
""" """ """ """

View file

@ -1,6 +1,7 @@
<div i18n:domain="deform" tal:omit-tag="" <div i18n:domain="deform" tal:omit-tag=""
tal:define="oid oid|field.oid; tal:define="oid oid|field.oid;
name name|field.name; name name|field.name;
vmodel vmodel|'field_model_' + name;
css_class css_class|field.widget.css_class; css_class css_class|field.widget.css_class;
style style|field.widget.style;"> style style|field.widget.style;">
@ -8,7 +9,7 @@
${field.start_mapping()} ${field.start_mapping()}
<b-input type="password" <b-input type="password"
name="${name}" name="${name}"
value="${field.widget.redisplay and cstruct or ''}" v-model="${vmodel}"
tal:attributes="class string: form-control ${css_class or ''}; tal:attributes="class string: form-control ${css_class or ''};
style style; style style;
attributes|field.widget.attributes|{};" attributes|field.widget.attributes|{};"
@ -18,7 +19,6 @@
</b-input> </b-input>
<b-input type="password" <b-input type="password"
name="${name}-confirm" name="${name}-confirm"
value="${field.widget.redisplay and confirm or ''}"
tal:attributes="class string: form-control ${css_class or ''}; tal:attributes="class string: form-control ${css_class or ''};
style style; style style;
confirm_attributes|field.widget.confirm_attributes|{};" confirm_attributes|field.widget.confirm_attributes|{};"

View file

@ -55,19 +55,20 @@
</%def> </%def>
<%def name="render_form_template()"> <%def name="render_form_template()">
<script type="text/x-template" id="${form.component}-template"> <script type="text/x-template" id="${form.vue_tagname}-template">
${self.render_form_innards()} ${self.render_form_innards()}
</script> </script>
</%def> </%def>
<%def name="modify_vue_vars()"> <%def name="modify_vue_vars()">
${parent.modify_vue_vars()} ${parent.modify_vue_vars()}
<% request.register_component(form.vue_tagname, form.vue_component) %>
<script> <script>
## TODO: ugh, an awful lot of duplicated code here (from /forms/deform.mako) ## TODO: ugh, an awful lot of duplicated code here (from /forms/deform.mako)
let ${form.vue_component} = { let ${form.vue_component} = {
template: '#${form.component}-template', template: '#${form.vue_tagname}-template',
methods: { methods: {
## TODO: deprecate / remove the latter option here ## TODO: deprecate / remove the latter option here

View file

@ -44,28 +44,6 @@ class UserLogin(colander.MappingSchema):
widget=dfwidget.PasswordWidget()) widget=dfwidget.PasswordWidget())
@colander.deferred
def current_password_correct(node, kw):
request = kw['request']
app = request.rattail_config.get_app()
auth = app.get_auth_handler()
user = kw['user']
def validate(node, value):
if not auth.authenticate_user(Session(), user.username, value):
raise colander.Invalid(node, "The password is incorrect")
return validate
class ChangePassword(colander.MappingSchema):
current_password = colander.SchemaNode(colander.String(),
widget=dfwidget.PasswordWidget(),
validator=current_password_correct)
new_password = colander.SchemaNode(colander.String(),
widget=dfwidget.CheckedPasswordWidget())
class AuthenticationView(View): class AuthenticationView(View):
def forbidden(self): def forbidden(self):
@ -116,10 +94,6 @@ class AuthenticationView(View):
else: else:
self.request.session.flash("Invalid username or password", 'error') self.request.session.flash("Invalid username or password", 'error')
image_url = self.rattail_config.get(
'tailbone', 'main_image_url',
default=self.request.static_url('tailbone:static/img/home_logo.png'))
# nb. hacky..but necessary, to add the refs, for autofocus # nb. hacky..but necessary, to add the refs, for autofocus
# (also add key handler, so ENTER acts like TAB) # (also add key handler, so ENTER acts like TAB)
dform = form.make_deform_form() dform = form.make_deform_form()
@ -132,7 +106,6 @@ class AuthenticationView(View):
return { return {
'form': form, 'form': form,
'referrer': referrer, 'referrer': referrer,
'image_url': image_url,
'index_title': app.get_node_title(), 'index_title': app.get_node_title(),
'help_url': global_help_url(self.rattail_config), 'help_url': global_help_url(self.rattail_config),
} }
@ -181,7 +154,23 @@ class AuthenticationView(View):
self.request.user)) self.request.user))
return self.redirect(self.request.get_referrer()) return self.redirect(self.request.get_referrer())
schema = ChangePassword().bind(user=self.request.user, request=self.request) def check_user_password(node, value):
auth = self.app.get_auth_handler()
user = self.request.user
if not auth.check_user_password(user, value):
node.raise_invalid("The password is incorrect")
schema = colander.Schema()
schema.add(colander.SchemaNode(colander.String(),
name='current_password',
widget=dfwidget.PasswordWidget(),
validator=check_user_password))
schema.add(colander.SchemaNode(colander.String(),
name='new_password',
widget=dfwidget.CheckedPasswordWidget()))
form = forms.Form(schema=schema, request=self.request) form = forms.Form(schema=schema, request=self.request)
if form.validate(): if form.validate():
auth = self.app.get_auth_handler() auth = self.app.get_auth_handler()

View file

@ -564,15 +564,19 @@ class PersonView(MasterView):
Method which must return the base query for the profile's POS Method which must return the base query for the profile's POS
Transactions grid data. Transactions grid data.
""" """
app = self.get_rattail_app() customer = self.app.get_customer(person)
customer = app.get_customer(person)
key_field = app.get_customer_key_field() if customer:
key_field = self.app.get_customer_key_field()
customer_key = getattr(customer, key_field) customer_key = getattr(customer, key_field)
if customer_key is not None: if customer_key is not None:
customer_key = str(customer_key) customer_key = str(customer_key)
else:
# nb. this should *not* match anything, so query returns
# no results..
customer_key = person.uuid
trainwreck = app.get_trainwreck_handler() trainwreck = self.app.get_trainwreck_handler()
model = trainwreck.get_model() model = trainwreck.get_model()
query = TrainwreckSession.query(model.Transaction)\ query = TrainwreckSession.query(model.Transaction)\
.filter(model.Transaction.customer_id == customer_key) .filter(model.Transaction.customer_id == customer_key)

View file

@ -1857,7 +1857,8 @@ class ProductView(MasterView):
lookup_fields.append('alt_code') lookup_fields.append('alt_code')
if lookup_fields: if lookup_fields:
product = self.products_handler.locate_product_for_entry( product = self.products_handler.locate_product_for_entry(
session, term, lookup_fields=lookup_fields) session, term, lookup_fields=lookup_fields,
first_if_multiple=True)
if product: if product:
final_results.append(self.search_normalize_result(product)) final_results.append(self.search_normalize_result(product))