Remove several references to "buefy" name

class methods, template filenames, etc.

also made various edits per newer conventions
This commit is contained in:
Lance Edgar 2024-04-14 19:54:29 -05:00
parent 96ba039299
commit c036932ce4
50 changed files with 373 additions and 361 deletions

View file

@ -33,10 +33,9 @@ from collections import OrderedDict
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from sqlalchemy.ext.associationproxy import AssociationProxy, ASSOCIATION_PROXY from sqlalchemy.ext.associationproxy import AssociationProxy, ASSOCIATION_PROXY
from wuttjamaican.util import UNSPECIFIED
from rattail.time import localtime from rattail.util import prettify, pretty_boolean
from rattail.util import prettify, pretty_boolean, pretty_quantity
from rattail.core import UNSPECIFIED
from rattail.db.util import get_fieldnames from rattail.db.util import get_fieldnames
import colander import colander
@ -50,10 +49,10 @@ from webhelpers2.html import tags, HTML
from tailbone.db import Session from tailbone.db import Session
from tailbone.util import raw_datetime, get_form_data, render_markdown from tailbone.util import raw_datetime, get_form_data, render_markdown
from . import types from tailbone.forms import types
from .widgets import (ReadonlyWidget, PlainDateWidget, from tailbone.forms.widgets import (ReadonlyWidget, PlainDateWidget,
JQueryDateWidget, JQueryTimeWidget, JQueryDateWidget, JQueryTimeWidget,
MultiFileUploadWidget) MultiFileUploadWidget)
from tailbone.exceptions import TailboneJSONFieldError from tailbone.exceptions import TailboneJSONFieldError
@ -225,7 +224,7 @@ class CustomSchemaNode(SQLAlchemySchemaNode):
if excludes: if excludes:
overrides['excludes'] = excludes overrides['excludes'] = excludes
return super(CustomSchemaNode, self).get_schema_from_relationship(prop, overrides) return super().get_schema_from_relationship(prop, overrides)
def dictify(self, obj): def dictify(self, obj):
""" Return a dictified version of `obj` using schema information. """ Return a dictified version of `obj` using schema information.
@ -234,7 +233,7 @@ class CustomSchemaNode(SQLAlchemySchemaNode):
This method was copied from upstream and modified to add automatic This method was copied from upstream and modified to add automatic
handling of "association proxy" fields. handling of "association proxy" fields.
""" """
dict_ = super(CustomSchemaNode, self).dictify(obj) dict_ = super().dictify(obj)
for node in self: for node in self:
name = node.name name = node.name
@ -967,7 +966,7 @@ class Form(object):
kwargs.setdefault(':configure-fields-help', 'configureFieldsHelp') kwargs.setdefault(':configure-fields-help', 'configureFieldsHelp')
return HTML.tag(self.component, **kwargs) return HTML.tag(self.component, **kwargs)
def render_buefy_field(self, fieldname, bfield_attrs={}): def render_field_complete(self, fieldname, bfield_attrs={}):
""" """
Render the given field in a Buefy-compatible way. Note that Render the given field in a Buefy-compatible way. Note that
this is meant to render *editable* fields, i.e. showing a this is meant to render *editable* fields, i.e. showing a
@ -1131,7 +1130,8 @@ class Form(object):
value = self.obtain_value(record, field_name) value = self.obtain_value(record, field_name)
if value is None: if value is None:
return "" return ""
value = localtime(self.request.rattail_config, value) app = self.get_rattail_app()
value = app.localtime(value)
return raw_datetime(self.request.rattail_config, value) return raw_datetime(self.request.rattail_config, value)
def render_duration(self, record, field_name): def render_duration(self, record, field_name):
@ -1160,7 +1160,8 @@ class Form(object):
value = self.obtain_value(obj, field) value = self.obtain_value(obj, field)
if value is None: if value is None:
return "" return ""
return pretty_quantity(value) app = self.get_rattail_app()
return app.render_quantity(value)
def render_percent(self, obj, field): def render_percent(self, obj, field):
app = self.request.rattail_config.get_app() app = self.request.rattail_config.get_app()

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -153,6 +153,7 @@ class DynamicCheckboxWidget(dfwidget.CheckboxWidget):
template = 'checkbox_dynamic' template = 'checkbox_dynamic'
# TODO: deprecate / remove this
class PlainSelectWidget(dfwidget.SelectWidget): class PlainSelectWidget(dfwidget.SelectWidget):
template = 'select_plain' template = 'select_plain'

View file

@ -1339,10 +1339,10 @@ class Grid(object):
includes the context menu items and grid tools. includes the context menu items and grid tools.
""" """
if 'grid_columns' not in kwargs: if 'grid_columns' not in kwargs:
kwargs['grid_columns'] = self.get_buefy_columns() kwargs['grid_columns'] = self.get_table_columns()
if 'grid_data' not in kwargs: if 'grid_data' not in kwargs:
kwargs['grid_data'] = self.get_buefy_data() kwargs['grid_data'] = self.get_table_data()
if 'static_data' not in kwargs: if 'static_data' not in kwargs:
kwargs['static_data'] = self.has_static_data() kwargs['static_data'] = self.has_static_data()
@ -1364,10 +1364,11 @@ class Grid(object):
warnings.warn("Grid.render_buefy() is deprecated; " warnings.warn("Grid.render_buefy() is deprecated; "
"please use Grid.render_complete() instead", "please use Grid.render_complete() instead",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
return self.render_complete(**kwargs)
def render_buefy_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,
**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
just a ``<b-table>`` element instead of the typical "full" grid. just a ``<b-table>`` element instead of the typical "full" grid.
@ -1377,7 +1378,7 @@ class Grid(object):
context['data_prop'] = data_prop context['data_prop'] = data_prop
context['empty_labels'] = empty_labels context['empty_labels'] = empty_labels
if 'grid_columns' not in context: if 'grid_columns' not in context:
context['grid_columns'] = self.get_buefy_columns() context['grid_columns'] = self.get_table_columns()
context.setdefault('paginated', False) context.setdefault('paginated', False)
if context['paginated']: if context['paginated']:
context.setdefault('per_page', 20) context.setdefault('per_page', 20)
@ -1572,10 +1573,10 @@ class Grid(object):
return True return True
return False return False
def get_buefy_columns(self): def get_table_columns(self):
""" """
Return a list of dicts representing all grid columns. Meant for use Return a list of dicts representing all grid columns. Meant
with Buefy table. for use with the client-side JS table.
""" """
columns = [] columns = []
for name in self.columns: for name in self.columns:
@ -1597,9 +1598,10 @@ class Grid(object):
if hasattr(rowobj, 'uuid'): if hasattr(rowobj, 'uuid'):
return rowobj.uuid return rowobj.uuid
def get_buefy_data(self): def get_table_data(self):
""" """
Returns a list of data rows for the grid, for use with Buefy table. Returns a list of data rows for the grid, for use with
client-side JS table.
""" """
# filter / sort / paginate to get "visible" data # filter / sort / paginate to get "visible" data
raw_data = self.make_visible_data() raw_data = self.make_visible_data()
@ -1635,8 +1637,8 @@ class Grid(object):
# instance, when the "display" version is different than raw data. # instance, when the "display" version is different than raw data.
# here is the hack we use for that. # here is the hack we use for that.
columns = list(self.columns) columns = list(self.columns)
if hasattr(self, 'buefy_data_columns'): if hasattr(self, 'raw_data_columns'):
columns.extend(self.buefy_data_columns) columns.extend(self.raw_data_columns)
# iterate over data fields # iterate over data fields
for name in columns: for name in columns:

View file

@ -154,7 +154,7 @@
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">
ThisPageData.groups = ${json.dumps(buefy_data)|n} ThisPageData.groups = ${json.dumps(settings_data)|n}
ThisPageData.showingGroup = ${json.dumps(current_group or '')|n} ThisPageData.showingGroup = ${json.dumps(current_group or '')|n}
</script> </script>

View file

@ -68,7 +68,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<tailbone-form></tailbone-form> <tailbone-form></tailbone-form>
<br /> <br />

View file

@ -34,7 +34,7 @@
</nav> </nav>
</%def> </%def>
<%def name="render_form()"> <%def name="render_form_template()">
<script type="text/x-template" id="${form.component}-template"> <script type="text/x-template" id="${form.component}-template">
<div class="product-info"> <div class="product-info">

View file

@ -1,7 +1,7 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/master/view_row.mako" /> <%inherit file="/master/view_row.mako" />
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<tailbone-form></tailbone-form> <tailbone-form></tailbone-form>
<br /> <br />

View file

@ -148,7 +148,7 @@
</div> </div>
</%def> </%def>
<%def name="render_form()"> <%def name="render_form_template()">
## TODO: should use self.render_form_buttons() ## TODO: should use self.render_form_buttons()
## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n} ## ${form.render_deform(form_id='batch-form', buttons=capture(self.render_form_buttons))|n}
${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n} ${form.render_deform(form_id='batch-form', buttons=capture(buttons))|n}
@ -206,7 +206,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<${form.component} @show-upload="showUploadDialog = true"> <${form.component} @show-upload="showUploadDialog = true">
</${form.component}> </${form.component}>

View file

@ -9,7 +9,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<tailbone-form @detach-person="detachPerson"> <tailbone-form @detach-person="detachPerson">
</tailbone-form> </tailbone-form>

View file

@ -1,7 +1,7 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" /> <%inherit file="/master/view.mako" />
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<${form.component} ref="mainForm" <${form.component} ref="mainForm"
% if master.has_perm('confirm_price'): % if master.has_perm('confirm_price'):

View file

@ -5,11 +5,11 @@
<%def name="render_form_buttons()"></%def> <%def name="render_form_buttons()"></%def>
<%def name="render_form()"> <%def name="render_form_template()">
${form.render_deform(buttons=capture(self.render_form_buttons))|n} ${form.render_deform(buttons=capture(self.render_form_buttons))|n}
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
${form.render_vuejs_component()} ${form.render_vuejs_component()}
</div> </div>
@ -18,7 +18,7 @@
<%def name="page_content()"> <%def name="page_content()">
<div class="form-wrapper"> <div class="form-wrapper">
<br /> <br />
${self.render_buefy_form()} ${self.render_form()}
</div> </div>
</%def> </%def>
@ -49,7 +49,7 @@
<%def name="render_this_page_template()"> <%def name="render_this_page_template()">
% if form is not Undefined: % if form is not Undefined:
${self.render_form()} ${self.render_form_template()}
% endif % endif
${parent.render_this_page_template()} ${parent.render_this_page_template()}
</%def> </%def>

View file

@ -18,7 +18,7 @@
<div class="panel-block"> <div class="panel-block">
<div> <div>
% for field in form.grouping[group]: % for field in form.grouping[group]:
${form.render_buefy_field(field)} ${form.render_field_complete(field)}
% endfor % endfor
</div> </div>
</div> </div>
@ -26,7 +26,7 @@
% endfor % endfor
% else: % else:
% for field in form.fields: % for field in form.fields:
${form.render_buefy_field(field)} ${form.render_field_complete(field)}
% endfor % endfor
% endif % endif
</section> </section>

View file

@ -1,7 +0,0 @@
## -*- coding: utf-8; -*-
## TODO: deprecate / remove this
## (tried to add deprecation warning here but it didn't seem to work)
<%def name="render_buefy_field(field, bfield_kwargs={})">
${form.render_buefy_field(field.name, bfield_attrs=bfield_kwargs)}
</%def>

View file

@ -3,12 +3,12 @@
<%def name="title()">Clone ${model_title}: ${instance_title}</%def> <%def name="title()">Clone ${model_title}: ${instance_title}</%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<br /> <br />
<b-notification :closable="false"> <b-notification :closable="false">
You are about to clone the following ${model_title} as a new record: You are about to clone the following ${model_title} as a new record:
</b-notification> </b-notification>
${parent.render_buefy_form()} ${parent.render_form()}
</%def> </%def>
<%def name="render_form_buttons()"> <%def name="render_form_buttons()">

View file

@ -3,12 +3,12 @@
<%def name="title()">Delete ${model_title}: ${instance_title}</%def> <%def name="title()">Delete ${model_title}: ${instance_title}</%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<br /> <br />
<b-notification type="is-danger" :closable="false"> <b-notification type="is-danger" :closable="false">
You are about to delete the following ${model_title} and all associated data: You are about to delete the following ${model_title} and all associated data:
</b-notification> </b-notification>
${parent.render_buefy_form()} ${parent.render_form()}
</%def> </%def>
<%def name="render_form_buttons()"> <%def name="render_form_buttons()">

View file

@ -7,7 +7,7 @@
${view_profiles_helper([instance])} ${view_profiles_helper([instance])}
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<tailbone-form v-on:make-user="makeUser"></tailbone-form> <tailbone-form v-on:make-user="makeUser"></tailbone-form>
</div> </div>

View file

@ -1403,10 +1403,10 @@
% if request.has_perm('people_profile.view_versions'): % if request.has_perm('people_profile.view_versions'):
${revisions_grid.render_buefy_table_element(data_prop='revisions', ${revisions_grid.render_table_element(data_prop='revisions',
show_footer=True, show_footer=True,
vshow='viewingHistory', vshow='viewingHistory',
loading='gettingRevisions')|n} loading='gettingRevisions')|n}
<b-modal :active.sync="showingRevisionDialog"> <b-modal :active.sync="showingRevisionDialog">

View file

@ -95,8 +95,8 @@
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">
ThisPageData.permissionGroups = ${json.dumps(buefy_perms)|n} ThisPageData.permissionGroups = ${json.dumps(perms_data)|n}
ThisPageData.sortedGroups = ${json.dumps(buefy_sorted_groups)|n} ThisPageData.sortedGroups = ${json.dumps(sorted_groups_data)|n}
</script> </script>
</%def> </%def>
@ -113,7 +113,7 @@
}, },
data() { data() {
return { return {
groupPermissions: ${json.dumps(buefy_perms.get(selected_group, {}).get('permissions', []))|n}, groupPermissions: ${json.dumps(perms_data.get(selected_group, {}).get('permissions', []))|n},
permissionGroupTerm: '', permissionGroupTerm: '',
permissionTerm: '', permissionTerm: '',
selectedGroup: ${json.dumps(selected_group)|n}, selectedGroup: ${json.dumps(selected_group)|n},

View file

@ -54,7 +54,7 @@
${h.end_form()} ${h.end_form()}
</%def> </%def>
<%def name="render_form()"> <%def name="render_form_template()">
<script type="text/x-template" id="${form.component}-template"> <script type="text/x-template" id="${form.component}-template">
${self.render_form_innards()} ${self.render_form_innards()}
</script> </script>

View file

@ -108,7 +108,7 @@
</%def> </%def>
<%def name="lookup_codes_grid()"> <%def name="lookup_codes_grid()">
${lookup_codes['grid'].render_buefy_table_element(data_prop='lookupCodesData')|n} ${lookup_codes['grid'].render_table_element(data_prop='lookupCodesData')|n}
</%def> </%def>
<%def name="lookup_codes_panel()"> <%def name="lookup_codes_panel()">
@ -121,7 +121,7 @@
</%def> </%def>
<%def name="sources_grid()"> <%def name="sources_grid()">
${vendor_sources['grid'].render_buefy_table_element(data_prop='vendorSourcesData')|n} ${vendor_sources['grid'].render_table_element(data_prop='vendorSourcesData')|n}
</%def> </%def>
<%def name="sources_panel()"> <%def name="sources_panel()">
@ -175,7 +175,7 @@
</p> </p>
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
${regular_price_history_grid.render_buefy_table_element(data_prop='regularPriceHistoryData', loading='regularPriceHistoryLoading', paginated=True, per_page=10)|n} ${regular_price_history_grid.render_table_element(data_prop='regularPriceHistoryData', loading='regularPriceHistoryLoading', paginated=True, per_page=10)|n}
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<b-button @click="showingPriceHistory_regular = false"> <b-button @click="showingPriceHistory_regular = false">
@ -194,7 +194,7 @@
</p> </p>
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
${current_price_history_grid.render_buefy_table_element(data_prop='currentPriceHistoryData', loading='currentPriceHistoryLoading', paginated=True, per_page=10)|n} ${current_price_history_grid.render_table_element(data_prop='currentPriceHistoryData', loading='currentPriceHistoryLoading', paginated=True, per_page=10)|n}
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<b-button @click="showingPriceHistory_current = false"> <b-button @click="showingPriceHistory_current = false">
@ -213,7 +213,7 @@
</p> </p>
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
${suggested_price_history_grid.render_buefy_table_element(data_prop='suggestedPriceHistoryData', loading='suggestedPriceHistoryLoading', paginated=True, per_page=10)|n} ${suggested_price_history_grid.render_table_element(data_prop='suggestedPriceHistoryData', loading='suggestedPriceHistoryLoading', paginated=True, per_page=10)|n}
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<b-button @click="showingPriceHistory_suggested = false"> <b-button @click="showingPriceHistory_suggested = false">
@ -232,7 +232,7 @@
</p> </p>
</header> </header>
<section class="modal-card-body"> <section class="modal-card-body">
${cost_history_grid.render_buefy_table_element(data_prop='costHistoryData', loading='costHistoryLoading', paginated=True, per_page=10)|n} ${cost_history_grid.render_table_element(data_prop='costHistoryData', loading='costHistoryLoading', paginated=True, per_page=10)|n}
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<b-button @click="showingCostHistory = false"> <b-button @click="showingCostHistory = false">
@ -289,7 +289,7 @@
% if request.rattail_config.versioning_enabled() and master.has_perm('versions'): % if request.rattail_config.versioning_enabled() and master.has_perm('versions'):
ThisPageData.showingPriceHistory_regular = false ThisPageData.showingPriceHistory_regular = false
ThisPageData.regularPriceHistoryDataRaw = ${json.dumps(regular_price_history_grid.get_buefy_data()['data'])|n} ThisPageData.regularPriceHistoryDataRaw = ${json.dumps(regular_price_history_grid.get_table_data()['data'])|n}
ThisPageData.regularPriceHistoryLoading = false ThisPageData.regularPriceHistoryLoading = false
ThisPage.computed.regularPriceHistoryData = function() { ThisPage.computed.regularPriceHistoryData = function() {
@ -318,7 +318,7 @@
} }
ThisPageData.showingPriceHistory_current = false ThisPageData.showingPriceHistory_current = false
ThisPageData.currentPriceHistoryDataRaw = ${json.dumps(current_price_history_grid.get_buefy_data()['data'])|n} ThisPageData.currentPriceHistoryDataRaw = ${json.dumps(current_price_history_grid.get_table_data()['data'])|n}
ThisPageData.currentPriceHistoryLoading = false ThisPageData.currentPriceHistoryLoading = false
ThisPage.computed.currentPriceHistoryData = function() { ThisPage.computed.currentPriceHistoryData = function() {
@ -348,7 +348,7 @@
} }
ThisPageData.showingPriceHistory_suggested = false ThisPageData.showingPriceHistory_suggested = false
ThisPageData.suggestedPriceHistoryDataRaw = ${json.dumps(suggested_price_history_grid.get_buefy_data()['data'])|n} ThisPageData.suggestedPriceHistoryDataRaw = ${json.dumps(suggested_price_history_grid.get_table_data()['data'])|n}
ThisPageData.suggestedPriceHistoryLoading = false ThisPageData.suggestedPriceHistoryLoading = false
ThisPage.computed.suggestedPriceHistoryData = function() { ThisPage.computed.suggestedPriceHistoryData = function() {
@ -377,7 +377,7 @@
} }
ThisPageData.showingCostHistory = false ThisPageData.showingCostHistory = false
ThisPageData.costHistoryDataRaw = ${json.dumps(cost_history_grid.get_buefy_data()['data'])|n} ThisPageData.costHistoryDataRaw = ${json.dumps(cost_history_grid.get_table_data()['data'])|n}
ThisPageData.costHistoryLoading = false ThisPageData.costHistoryLoading = false
ThisPage.computed.costHistoryData = function() { ThisPage.computed.costHistoryData = function() {

View file

@ -1,6 +1,5 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/form.mako" /> <%inherit file="/form.mako" />
<%namespace file="/forms/util.mako" import="render_buefy_field" />
<%def name="title()">Declare Credit for Row #${row.sequence}</%def> <%def name="title()">Declare Credit for Row #${row.sequence}</%def>
@ -11,7 +10,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<p class="block"> <p class="block">
Please select the "state" of the product, and enter the Please select the "state" of the product, and enter the
@ -31,22 +30,22 @@
if you need to "receive" instead of "convert" the product. if you need to "receive" instead of "convert" the product.
</p> </p>
${parent.render_buefy_form()} ${parent.render_form()}
</%def> </%def>
<%def name="buefy_form_body()"> <%def name="form_body()">
${render_buefy_field(dform['credit_type'])} ${form.render_field_complete('credit_type')}
${render_buefy_field(dform['quantity'])} ${form.render_field_complete('quantity')}
${render_buefy_field(dform['expiration_date'], bfield_kwargs={'v-show': "field_model_credit_type == 'expired'"})} ${form.render_field_complete('expiration_date', bfield_attrs={'v-show': "field_model_credit_type == 'expired'"})}
</%def> </%def>
<%def name="render_form()"> <%def name="render_form_template()">
${form.render_deform(buttons=capture(self.render_form_buttons), form_body=capture(self.buefy_form_body))|n} ${form.render_deform(buttons=capture(self.render_form_buttons), form_body=capture(self.form_body))|n}
</%def> </%def>

View file

@ -1,6 +1,5 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/form.mako" /> <%inherit file="/form.mako" />
<%namespace file="/forms/util.mako" import="render_buefy_field" />
<%def name="title()">Receive for Row #${row.sequence}</%def> <%def name="title()">Receive for Row #${row.sequence}</%def>
@ -11,7 +10,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<p class="block"> <p class="block">
Please select the "state" of the product, and enter the appropriate Please select the "state" of the product, and enter the appropriate
@ -28,22 +27,22 @@
if you need to "convert" some already-received amount, into a credit. if you need to "convert" some already-received amount, into a credit.
</p> </p>
${parent.render_buefy_form()} ${parent.render_form()}
</%def> </%def>
<%def name="buefy_form_body()"> <%def name="form_body()">
${render_buefy_field(dform['mode'])} ${form.render_field_complete('mode')}
${render_buefy_field(dform['quantity'])} ${form.render_field_complete('quantity')}
${render_buefy_field(dform['expiration_date'], bfield_kwargs={'v-show': "field_model_mode == 'expired'"})} ${form.render_field_complete('expiration_date', bfield_attrs={'v-show': "field_model_mode == 'expired'"})}
</%def> </%def>
<%def name="render_form()"> <%def name="render_form_template()">
${form.render_deform(buttons=capture(self.render_form_buttons), form_body=capture(self.buefy_form_body))|n} ${form.render_deform(buttons=capture(self.render_form_buttons), form_body=capture(self.form_body))|n}
</%def> </%def>

View file

@ -23,7 +23,7 @@
</style> </style>
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<p>Please select the type of report you wish to generate.</p> <p>Please select the type of report you wish to generate.</p>
<br /> <br />

View file

@ -5,7 +5,7 @@
<%def name="content_title()">New Report:&nbsp; ${report.name}</%def> <%def name="content_title()">New Report:&nbsp; ${report.name}</%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<p class="block"> <p class="block">
${report.__doc__} ${report.__doc__}

View file

@ -1,8 +1,8 @@
## -*- coding: utf-8; -*- ## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" /> <%inherit file="/master/view.mako" />
<%def name="render_buefy_form()"> <%def name="render_form()">
${parent.render_buefy_form()} ${parent.render_form()}
<email-preview-tools></email-preview-tools> <email-preview-tools></email-preview-tools>
</%def> </%def>

View file

@ -75,7 +75,7 @@
% endif % endif
</%def> </%def>
<%def name="render_buefy_form()"> <%def name="render_form()">
<div class="form"> <div class="form">
<${form.component} <${form.component}
% if master.has_perm('execute'): % if master.has_perm('execute'):

View file

@ -30,7 +30,7 @@
<b-select name="tailbone.${user.uuid}.buefy_css" <b-select name="tailbone.${user.uuid}.buefy_css"
v-model="simpleSettings['tailbone.${user.uuid}.buefy_css']" v-model="simpleSettings['tailbone.${user.uuid}.buefy_css']"
@input="settingsNeedSaved = true"> @input="settingsNeedSaved = true">
<option v-for="option in buefyCSSOptions" <option v-for="option in themeStyleOptions"
:key="option.value" :key="option.value"
:value="option.value"> :value="option.value">
{{ option.label }} {{ option.label }}
@ -46,7 +46,7 @@
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">
ThisPageData.buefyCSSOptions = ${json.dumps(buefy_css_options)|n} ThisPageData.themeStyleOptions = ${json.dumps(theme_style_options)|n}
</script> </script>
</%def> </%def>

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -32,23 +32,18 @@ import logging
import socket import socket
import subprocess import subprocess
import tempfile import tempfile
import warnings
import json import json
import markdown import markdown
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from rattail.db import model, Session as RattailSession
from rattail.db.util import short_session
from rattail.threads import Thread from rattail.threads import Thread
from rattail.util import prettify, simple_error from rattail.util import simple_error
from rattail.progress import SocketProgress
import colander import colander
import deform
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid.renderers import render_to_response
from pyramid.response import FileResponse
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import forms, grids from tailbone import forms, grids
@ -115,7 +110,7 @@ class BatchMasterView(MasterView):
} }
def __init__(self, request): def __init__(self, request):
super(BatchMasterView, self).__init__(request) super().__init__(request)
self.batch_handler = self.get_handler() self.batch_handler = self.get_handler()
# TODO: deprecate / remove this (?) # TODO: deprecate / remove this (?)
self.handler = self.batch_handler self.handler = self.batch_handler
@ -167,7 +162,7 @@ class BatchMasterView(MasterView):
return self.rattail_config.batch_filepath(batch.batch_key, batch.uuid, filename) return self.rattail_config.batch_filepath(batch.batch_key, batch.uuid, filename)
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super(BatchMasterView, self).template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)
batch = kwargs['instance'] batch = kwargs['instance']
kwargs['batch'] = batch kwargs['batch'] = batch
kwargs['handler'] = self.handler kwargs['handler'] = self.handler
@ -195,8 +190,8 @@ class BatchMasterView(MasterView):
g.set_click_handler('title', "autoFilterStatus(props.row)") g.set_click_handler('title', "autoFilterStatus(props.row)")
kwargs['status_breakdown_data'] = breakdown kwargs['status_breakdown_data'] = breakdown
kwargs['status_breakdown_grid'] = HTML.literal( kwargs['status_breakdown_grid'] = HTML.literal(
g.render_buefy_table_element(data_prop='statusBreakdownData', g.render_table_element(data_prop='statusBreakdownData',
empty_labels=True)) empty_labels=True))
return kwargs return kwargs
@ -288,7 +283,8 @@ class BatchMasterView(MasterView):
return not batch.executed and not batch.complete return not batch.executed and not batch.complete
def configure_grid(self, g): def configure_grid(self, g):
super(BatchMasterView, self).configure_grid(g) super().configure_grid(g)
model = self.model
# created_by # created_by
CreatedBy = orm.aliased(model.User) CreatedBy = orm.aliased(model.User)
@ -337,7 +333,7 @@ class BatchMasterView(MasterView):
return batch.id_str return batch.id_str
def configure_form(self, f): def configure_form(self, f):
super(BatchMasterView, self).configure_form(f) super().configure_form(f)
# id # id
f.set_readonly('id') f.set_readonly('id')
@ -436,9 +432,9 @@ class BatchMasterView(MasterView):
label = HTML.literal( label = HTML.literal(
'{{{{ togglingBatchComplete ? "Working, please wait..." : "{}" }}}}'.format(label)) '{{{{ togglingBatchComplete ? "Working, please wait..." : "{}" }}}}'.format(label))
submit = self.make_buefy_button(label, is_primary=True, submit = self.make_button(label, is_primary=True,
native_type='submit', native_type='submit',
**{':disabled': 'togglingBatchComplete'}) **{':disabled': 'togglingBatchComplete'})
form = [ form = [
begin_form, begin_form,
@ -603,7 +599,7 @@ class BatchMasterView(MasterView):
return True return True
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(BatchMasterView, self).configure_row_grid(g) super().configure_row_grid(g)
g.set_sort_defaults('sequence') g.set_sort_defaults('sequence')
g.set_link('sequence') g.set_link('sequence')
@ -644,7 +640,7 @@ class BatchMasterView(MasterView):
if batch.executed: if batch.executed:
self.request.session.flash("You cannot add new rows to a batch which has been executed") self.request.session.flash("You cannot add new rows to a batch which has been executed")
return self.redirect(self.get_action_url('view', batch)) return self.redirect(self.get_action_url('view', batch))
return super(BatchMasterView, self).create_row() return super().create_row()
def save_create_row_form(self, form): def save_create_row_form(self, form):
batch = self.get_instance() batch = self.get_instance()
@ -657,7 +653,7 @@ class BatchMasterView(MasterView):
self.handler.refresh_row(row) self.handler.refresh_row(row)
def configure_row_form(self, f): def configure_row_form(self, f):
super(BatchMasterView, self).configure_row_form(f) super().configure_row_form(f)
# sequence # sequence
f.set_readonly('sequence') f.set_readonly('sequence')
@ -681,9 +677,9 @@ class BatchMasterView(MasterView):
permission_prefix = self.get_permission_prefix() permission_prefix = self.get_permission_prefix()
if self.request.has_perm('{}.create_row'.format(permission_prefix)): if self.request.has_perm('{}.create_row'.format(permission_prefix)):
url = self.get_action_url('create_row', batch) url = self.get_action_url('create_row', batch)
return self.make_buefy_button("New Row", url=url, return self.make_button("New Row", url=url,
is_primary=True, is_primary=True,
icon_left='plus') icon_left='plus')
def make_batch_row_grid_tools(self, batch): def make_batch_row_grid_tools(self, batch):
pass pass
@ -719,7 +715,7 @@ class BatchMasterView(MasterView):
kwargs['main_actions'] = actions kwargs['main_actions'] = actions
return super(BatchMasterView, self).make_row_grid_kwargs(**kwargs) return super().make_row_grid_kwargs(**kwargs)
def make_row_grid_tools(self, batch): def make_row_grid_tools(self, batch):
return (self.make_default_row_grid_tools(batch) or '') + (self.make_batch_row_grid_tools(batch) or '') return (self.make_default_row_grid_tools(batch) or '') + (self.make_batch_row_grid_tools(batch) or '')
@ -852,8 +848,11 @@ class BatchMasterView(MasterView):
labels = kwargs.setdefault('labels', {}) labels = kwargs.setdefault('labels', {})
labels[field.name] = field.title labels[field.name] = field.title
# auto-convert select widgets for buefy theme # auto-convert select widgets for theme
if isinstance(field.widget, forms.widgets.PlainSelectWidget): if isinstance(field.widget, forms.widgets.PlainSelectWidget):
warnings.warn("PlainSelectWidget is deprecated; "
"please use deform.widget.SelectWidget instead",
DeprecationWarning)
field.widget = dfwidget.SelectWidget(values=field.widget.values) field.widget = dfwidget.SelectWidget(values=field.widget.values)
if not schema: if not schema:
@ -1022,7 +1021,8 @@ class BatchMasterView(MasterView):
cxn.close() cxn.close()
def catchup_versions(self, port, batch_uuid, username, *models): def catchup_versions(self, port, batch_uuid, username, *models):
with short_session() as s: app = self.get_rattail_app()
with app.short_session() as s:
batch = s.get(self.model_class, batch_uuid) batch = s.get(self.model_class, batch_uuid)
batch_id = batch.id_str batch_id = batch.id_str
description = str(batch) description = str(batch)
@ -1048,8 +1048,10 @@ class BatchMasterView(MasterView):
""" """
Thread target for populating batch data with progress indicator. Thread target for populating batch data with progress indicator.
""" """
app = self.get_rattail_app()
model = self.model
# mustn't use tailbone web session here # mustn't use tailbone web session here
session = RattailSession() session = app.make_session()
batch = session.get(self.model_class, batch_uuid) batch = session.get(self.model_class, batch_uuid)
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
@ -1107,7 +1109,9 @@ class BatchMasterView(MasterView):
# Refresh data for the batch, with progress. Note that we must use the # Refresh data for the batch, with progress. Note that we must use the
# rattail session here; can't use tailbone because it has web request # rattail session here; can't use tailbone because it has web request
# transaction binding etc. # transaction binding etc.
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
batch = session.get(self.model_class, batch_uuid) batch = session.get(self.model_class, batch_uuid)
cognizer = session.get(model.User, user_uuid) if user_uuid else None cognizer = session.get(model.User, user_uuid) if user_uuid else None
try: try:
@ -1160,7 +1164,9 @@ class BatchMasterView(MasterView):
""" """
Thread target for refreshing multiple batches with progress indicator. Thread target for refreshing multiple batches with progress indicator.
""" """
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
batches = batches.with_session(session).all() batches = batches.with_session(session).all()
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
@ -1257,7 +1263,7 @@ class BatchMasterView(MasterView):
self.handler.do_remove_row(row) self.handler.do_remove_row(row)
def delete_row_objects(self, rows): def delete_row_objects(self, rows):
deleted = super(BatchMasterView, self).delete_row_objects(rows) deleted = super().delete_row_objects(rows)
batch = self.get_instance() batch = self.get_instance()
# decrement rowcount for batch # decrement rowcount for batch
@ -1300,7 +1306,9 @@ class BatchMasterView(MasterView):
# Execute the batch, with progress. Note that we must use the rattail # Execute the batch, with progress. Note that we must use the rattail
# session here; can't use tailbone because it has web request # session here; can't use tailbone because it has web request
# transaction binding etc. # transaction binding etc.
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
batch = self.get_instance_for_key(key, session) batch = self.get_instance_for_key(key, session)
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
@ -1375,7 +1383,9 @@ class BatchMasterView(MasterView):
""" """
Thread target for executing multiple batches with progress indicator. Thread target for executing multiple batches with progress indicator.
""" """
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
batches = batches.with_session(session).all() batches = batches.with_session(session).all()
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
@ -1415,7 +1425,7 @@ class BatchMasterView(MasterView):
return self.get_index_url() return self.get_index_url()
def get_row_csv_fields(self): def get_row_csv_fields(self):
fields = super(BatchMasterView, self).get_row_csv_fields() fields = super().get_row_csv_fields()
fields = [field for field in fields fields = [field for field in fields
if field not in ('uuid', 'batch_uuid', 'removed')] if field not in ('uuid', 'batch_uuid', 'removed')]
return fields return fields
@ -1538,7 +1548,7 @@ class FileBatchMasterView(BatchMasterView):
return uploads return uploads
def configure_form(self, f): def configure_form(self, f):
super(FileBatchMasterView, self).configure_form(f) super().configure_form(f)
batch = f.model_instance batch = f.model_instance
# filename # filename

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -26,12 +26,12 @@ Views for handheld batches
from collections import OrderedDict from collections import OrderedDict
from rattail.db import model from rattail.db.model import HandheldBatch, HandheldBatchRow
import colander import colander
from deform import widget as dfwidget
from webhelpers2.html import tags from webhelpers2.html import tags
from tailbone import forms
from tailbone.views.batch import FileBatchMasterView from tailbone.views.batch import FileBatchMasterView
@ -46,14 +46,14 @@ class ExecutionOptions(colander.Schema):
action = colander.SchemaNode( action = colander.SchemaNode(
colander.String(), colander.String(),
validator=colander.OneOf(ACTION_OPTIONS), validator=colander.OneOf(ACTION_OPTIONS),
widget=forms.widgets.PlainSelectWidget(values=ACTION_OPTIONS.items())) widget=dfwidget.SelectWidget(values=ACTION_OPTIONS.items()))
class HandheldBatchView(FileBatchMasterView): class HandheldBatchView(FileBatchMasterView):
""" """
Master view for handheld batches. Master view for handheld batches.
""" """
model_class = model.HandheldBatch model_class = HandheldBatch
default_handler_spec = 'rattail.batch.handheld:HandheldBatchHandler' default_handler_spec = 'rattail.batch.handheld:HandheldBatchHandler'
model_title_plural = "Handheld Batches" model_title_plural = "Handheld Batches"
route_prefix = 'batch.handheld' route_prefix = 'batch.handheld'
@ -61,7 +61,7 @@ class HandheldBatchView(FileBatchMasterView):
execution_options_schema = ExecutionOptions execution_options_schema = ExecutionOptions
editable = False editable = False
model_row_class = model.HandheldBatchRow model_row_class = HandheldBatchRow
rows_creatable = False rows_creatable = False
rows_editable = True rows_editable = True
@ -116,7 +116,7 @@ class HandheldBatchView(FileBatchMasterView):
] ]
def configure_grid(self, g): def configure_grid(self, g):
super(HandheldBatchView, self).configure_grid(g) super().configure_grid(g)
device_types = OrderedDict(sorted(self.enum.HANDHELD_DEVICE_TYPE.items(), device_types = OrderedDict(sorted(self.enum.HANDHELD_DEVICE_TYPE.items(),
key=lambda item: item[1])) key=lambda item: item[1]))
g.set_enum('device_type', device_types) g.set_enum('device_type', device_types)
@ -126,7 +126,7 @@ class HandheldBatchView(FileBatchMasterView):
return 'notice' return 'notice'
def configure_form(self, f): def configure_form(self, f):
super(HandheldBatchView, self).configure_form(f) super().configure_form(f)
batch = f.model_instance batch = f.model_instance
# device_type # device_type
@ -156,13 +156,13 @@ class HandheldBatchView(FileBatchMasterView):
return tags.link_to(text, url) return tags.link_to(text, url)
def get_batch_kwargs(self, batch): def get_batch_kwargs(self, batch):
kwargs = super(HandheldBatchView, self).get_batch_kwargs(batch) kwargs = super().get_batch_kwargs(batch)
kwargs['device_type'] = batch.device_type kwargs['device_type'] = batch.device_type
kwargs['device_name'] = batch.device_name kwargs['device_name'] = batch.device_name
return kwargs return kwargs
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(HandheldBatchView, self).configure_row_grid(g) super().configure_row_grid(g)
g.set_type('cases', 'quantity') g.set_type('cases', 'quantity')
g.set_type('units', 'quantity') g.set_type('units', 'quantity')
g.set_label('brand_name', "Brand") g.set_label('brand_name', "Brand")
@ -172,7 +172,7 @@ class HandheldBatchView(FileBatchMasterView):
return 'warning' return 'warning'
def configure_row_form(self, f): def configure_row_form(self, f):
super(HandheldBatchView, self).configure_row_form(f) super().configure_row_form(f)
# readonly fields # readonly fields
f.set_readonly('upc') f.set_readonly('upc')
@ -188,7 +188,7 @@ class HandheldBatchView(FileBatchMasterView):
return self.request.route_url('batch.inventory.view', uuid=result.uuid) return self.request.route_url('batch.inventory.view', uuid=result.uuid)
elif kwargs['action'] == 'make_label_batch': elif kwargs['action'] == 'make_label_batch':
return self.request.route_url('labels.batch.view', uuid=result.uuid) return self.request.route_url('labels.batch.view', uuid=result.uuid)
return super(HandheldBatchView, self).get_execute_success_url(batch) return super().get_execute_success_url(batch)
def get_execute_results_success_url(self, result, **kwargs): def get_execute_results_success_url(self, result, **kwargs):
if result is True: if result is True:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -206,7 +206,7 @@ class POSBatchView(BatchMasterView):
) )
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='taxesData')) g.render_table_element(data_prop='taxesData'))
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super().template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -26,12 +26,12 @@ Views for generic product batches
from collections import OrderedDict from collections import OrderedDict
from rattail.db import model from rattail.db.model import ProductBatch, ProductBatchRow
import colander import colander
from deform import widget as dfwidget
from webhelpers2.html import HTML from webhelpers2.html import HTML
from tailbone import forms
from tailbone.views.batch import BatchMasterView from tailbone.views.batch import BatchMasterView
@ -46,15 +46,15 @@ class ExecutionOptions(colander.Schema):
action = colander.SchemaNode( action = colander.SchemaNode(
colander.String(), colander.String(),
validator=colander.OneOf(ACTION_OPTIONS), validator=colander.OneOf(ACTION_OPTIONS),
widget=forms.widgets.PlainSelectWidget(values=ACTION_OPTIONS.items())) widget=dfwidget.SelectWidget(values=ACTION_OPTIONS.items()))
class ProductBatchView(BatchMasterView): class ProductBatchView(BatchMasterView):
""" """
Master view for product batches. Master view for product batches.
""" """
model_class = model.ProductBatch model_class = ProductBatch
model_row_class = model.ProductBatchRow model_row_class = ProductBatchRow
default_handler_spec = 'rattail.batch.product:ProductBatchHandler' default_handler_spec = 'rattail.batch.product:ProductBatchHandler'
route_prefix = 'batch.product' route_prefix = 'batch.product'
url_prefix = '/batches/product' url_prefix = '/batches/product'
@ -129,7 +129,7 @@ class ProductBatchView(BatchMasterView):
] ]
def configure_form(self, f): def configure_form(self, f):
super(ProductBatchView, self).configure_form(f) super().configure_form(f)
# input_filename # input_filename
if self.creating: if self.creating:
@ -139,7 +139,8 @@ class ProductBatchView(BatchMasterView):
f.set_renderer('input_filename', self.render_downloadable_file) f.set_renderer('input_filename', self.render_downloadable_file)
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(ProductBatchView, self).configure_row_grid(g) super().configure_row_grid(g)
model = self.model
g.set_joiner('vendor', lambda q: q.outerjoin(model.Vendor)) g.set_joiner('vendor', lambda q: q.outerjoin(model.Vendor))
g.set_sorter('vendor', model.Vendor.name) g.set_sorter('vendor', model.Vendor.name)
@ -165,7 +166,7 @@ class ProductBatchView(BatchMasterView):
return 'warning' return 'warning'
def configure_row_form(self, f): def configure_row_form(self, f):
super(ProductBatchView, self).configure_row_form(f) super().configure_row_form(f)
f.set_type('upc', 'gpc') f.set_type('upc', 'gpc')
@ -204,10 +205,10 @@ class ProductBatchView(BatchMasterView):
return self.request.route_url('labels.batch.view', uuid=result.uuid) return self.request.route_url('labels.batch.view', uuid=result.uuid)
elif kwargs['action'] == 'make_pricing_batch': elif kwargs['action'] == 'make_pricing_batch':
return self.request.route_url('batch.pricing.view', uuid=result.uuid) return self.request.route_url('batch.pricing.view', uuid=result.uuid)
return super(ProductBatchView, self).get_execute_success_url(batch) return super().get_execute_success_url(batch)
def get_row_csv_fields(self): def get_row_csv_fields(self):
fields = super(ProductBatchView, self).get_row_csv_fields() fields = super().get_row_csv_fields()
if 'vendor_uuid' in fields: if 'vendor_uuid' in fields:
i = fields.index('vendor_uuid') i = fields.index('vendor_uuid')
@ -273,12 +274,12 @@ class ProductBatchView(BatchMasterView):
data['report_name'] = (report.name or '') if report else '' data['report_name'] = (report.name or '') if report else ''
def get_row_csv_row(self, row, fields): def get_row_csv_row(self, row, fields):
csvrow = super(ProductBatchView, self).get_row_csv_row(row, fields) csvrow = super().get_row_csv_row(row, fields)
self.supplement_row_data(row, fields, csvrow) self.supplement_row_data(row, fields, csvrow)
return csvrow return csvrow
def get_row_xlsx_row(self, row, fields): def get_row_xlsx_row(self, row, fields):
xlrow = super(ProductBatchView, self).get_row_xlsx_row(row, fields) xlrow = super().get_row_xlsx_row(row, fields)
self.supplement_row_data(row, fields, xlrow) self.supplement_row_data(row, fields, xlrow)
return xlrow return xlrow

View file

@ -341,7 +341,7 @@ class CustomerView(MasterView):
# people # people
if self.should_expose_people(): if self.should_expose_people():
if self.viewing: if self.viewing:
f.set_renderer('people', self.render_people_buefy) f.set_renderer('people', self.render_people)
else: else:
f.remove('people') f.remove('people')
else: else:
@ -463,20 +463,6 @@ class CustomerView(MasterView):
url = self.request.route_url('people.view', uuid=person.uuid) url = self.request.route_url('people.view', uuid=person.uuid)
return tags.link_to(text, url) return tags.link_to(text, url)
# TODO: remove if no longer used
def render_people(self, customer, field):
people = customer.people
if not people:
return ""
items = []
for person in people:
text = str(person)
url = self.request.route_url('people.view', uuid=person.uuid)
link = tags.link_to(text, url)
items.append(HTML.tag('li', c=[link]))
return HTML.tag('ul', c=items)
def render_shoppers(self, customer, field): def render_shoppers(self, customer, field):
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
permission_prefix = self.get_permission_prefix() permission_prefix = self.get_permission_prefix()
@ -504,9 +490,9 @@ class CustomerView(MasterView):
) )
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='shoppers')) g.render_table_element(data_prop='shoppers'))
def render_people_buefy(self, customer, field): def render_people(self, customer, field):
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
permission_prefix = self.get_permission_prefix() permission_prefix = self.get_permission_prefix()
@ -533,7 +519,7 @@ class CustomerView(MasterView):
click_handler="$emit('detach-person', props.row._action_url_detach)")) click_handler="$emit('detach-person', props.row._action_url_detach)"))
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='peopleData')) g.render_table_element(data_prop='peopleData'))
def render_groups(self, customer, field): def render_groups(self, customer, field):
groups = customer.groups groups = customer.groups

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -401,7 +401,7 @@ class CustomerOrderItemView(MasterView):
) )
table = HTML.literal( table = HTML.literal(
g.render_buefy_table_element(data_prop='eventsData')) g.render_table_element(data_prop='eventsData'))
elements = [table] elements = [table]
if self.has_perm('add_note'): if self.has_perm('add_note'):

View file

@ -144,7 +144,7 @@ class DepartmentView(MasterView):
g.main_actions.append(self.make_action('edit', icon='edit')) g.main_actions.append(self.make_action('edit', icon='edit'))
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='employeesData')) g.render_table_element(data_prop='employeesData'))
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super().template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)

View file

@ -30,7 +30,6 @@ import csv
import datetime import datetime
import getpass import getpass
import shutil import shutil
import tempfile
import logging import logging
from collections import OrderedDict from collections import OrderedDict
@ -40,12 +39,10 @@ from sqlalchemy import orm
import sqlalchemy_continuum as continuum import sqlalchemy_continuum as continuum
from sqlalchemy_utils.functions import get_primary_keys, get_columns from sqlalchemy_utils.functions import get_primary_keys, get_columns
from rattail.db import model, Session as RattailSession
from rattail.db.continuum import model_transaction_query from rattail.db.continuum import model_transaction_query
from rattail.util import simple_error, get_class_hierarchy from rattail.util import simple_error, get_class_hierarchy
from rattail.threads import Thread from rattail.threads import Thread
from rattail.csvutil import UnicodeDictWriter from rattail.csvutil import UnicodeDictWriter
from rattail.files import temp_path
from rattail.excel import ExcelWriter from rattail.excel import ExcelWriter
from rattail.gpc import GPC from rattail.gpc import GPC
@ -54,7 +51,6 @@ import deform
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid import httpexceptions from pyramid import httpexceptions
from pyramid.renderers import get_renderer, render_to_response, render from pyramid.renderers import get_renderer, render_to_response, render
from pyramid.response import FileResponse
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from webob.compat import cgi_FieldStorage from webob.compat import cgi_FieldStorage
@ -220,7 +216,8 @@ class MasterView(View):
to the current thread (one per request), this method should instead to the current thread (one per request), this method should instead
return e.g. a new independent ``rattail.db.Session`` instance. return e.g. a new independent ``rattail.db.Session`` instance.
""" """
return RattailSession() app = self.get_rattail_app()
return app.make_session()
@classmethod @classmethod
def get_grid_factory(cls): def get_grid_factory(cls):
@ -348,7 +345,7 @@ class MasterView(View):
# return grid data only, if partial page was requested # return grid data only, if partial page was requested
if self.request.params.get('partial'): if self.request.params.get('partial'):
return self.json_response(grid.get_buefy_data()) return self.json_response(grid.get_table_data())
context = { context = {
'grid': grid, 'grid': grid,
@ -719,10 +716,11 @@ class MasterView(View):
return obj return obj
def normalize_uploads(self, form, skip=None): def normalize_uploads(self, form, skip=None):
app = self.get_rattail_app()
uploads = {} uploads = {}
def normalize(filedict): def normalize(filedict):
tempdir = tempfile.mkdtemp() tempdir = app.make_temp_dir()
filepath = os.path.join(tempdir, filedict['filename']) filepath = os.path.join(tempdir, filedict['filename'])
tmpinfo = form.deform_form[node.name].widget.tmpstore.get(filedict['uid']) tmpinfo = form.deform_form[node.name].widget.tmpstore.get(filedict['uid'])
tmpdata = tmpinfo['fp'].read() tmpdata = tmpinfo['fp'].read()
@ -1114,7 +1112,8 @@ class MasterView(View):
Thread target for populating new object with progress indicator. Thread target for populating new object with progress indicator.
""" """
# mustn't use tailbone web session here # mustn't use tailbone web session here
session = RattailSession() app = self.get_rattail_app()
session = app.make_session()
obj = session.get(self.model_class, uuid) obj = session.get(self.model_class, uuid)
try: try:
self.populate_object(session, obj, progress=progress) self.populate_object(session, obj, progress=progress)
@ -1175,7 +1174,7 @@ class MasterView(View):
# return grid only, if partial page was requested # return grid only, if partial page was requested
if self.request.params.get('partial'): if self.request.params.get('partial'):
# render grid data only, as JSON # render grid data only, as JSON
return self.json_response(grid.get_buefy_data()) return self.json_response(grid.get_table_data())
context = { context = {
'instance': instance, 'instance': instance,
@ -1308,7 +1307,7 @@ class MasterView(View):
# return grid only, if partial page was requested # return grid only, if partial page was requested
if self.request.params.get('partial'): if self.request.params.get('partial'):
# render grid data only, as JSON # render grid data only, as JSON
return self.json_response(grid.get_buefy_data()) return self.json_response(grid.get_table_data())
return self.render_to_response('versions', { return self.render_to_response('versions', {
'instance': instance, 'instance': instance,
@ -1360,6 +1359,7 @@ class MasterView(View):
return classes return classes
def make_revisions_grid(self, obj, empty_data=False): def make_revisions_grid(self, obj, empty_data=False):
model = self.model
route_prefix = self.get_route_prefix() route_prefix = self.get_route_prefix()
row_url = lambda txn, i: self.request.route_url(f'{route_prefix}.version', row_url = lambda txn, i: self.request.route_url(f'{route_prefix}.version',
uuid=obj.uuid, uuid=obj.uuid,
@ -1396,8 +1396,8 @@ class MasterView(View):
grid = self.make_version_grid(**kwargs) grid = self.make_version_grid(**kwargs)
grid.set_joiner('user', lambda q: q.outerjoin(self.model.User)) grid.set_joiner('user', lambda q: q.outerjoin(model.User))
grid.set_sorter('user', self.model.User.username) grid.set_sorter('user', model.User.username)
grid.set_link('remote_addr') grid.set_link('remote_addr')
@ -1465,7 +1465,7 @@ class MasterView(View):
else: # no txnid, return grid data else: # no txnid, return grid data
obj = self.get_instance() obj = self.get_instance()
grid = self.make_revisions_grid(obj) grid = self.make_revisions_grid(obj)
return grid.get_buefy_data() return grid.get_table_data()
def view_version(self): def view_version(self):
""" """
@ -1770,16 +1770,10 @@ class MasterView(View):
path = self.download_path(obj, filename) path = self.download_path(obj, filename)
if not path or not os.path.exists(path): if not path or not os.path.exists(path):
raise self.notfound() raise self.notfound()
response = FileResponse(path, request=self.request) response = self.file_response(path)
response.content_length = os.path.getsize(path)
content_type = self.download_content_type(path, filename) content_type = self.download_content_type(path, filename)
if content_type: if content_type:
response.content_type = content_type response.content_type = content_type
# content-disposition
filename = os.path.basename(path)
response.content_disposition = str('attachment; filename="{}"'.format(filename))
return response return response
def download_content_type(self, path, filename): def download_content_type(self, path, filename):
@ -1856,7 +1850,7 @@ class MasterView(View):
View for deleting an existing model record. View for deleting an existing model record.
""" """
if not self.deletable: if not self.deletable:
raise httpexceptions.HTTPForbidden() raise self.forbidden()
self.deleting = True self.deleting = True
instance = self.get_instance() instance = self.get_instance()
@ -2111,7 +2105,9 @@ class MasterView(View):
""" """
Thread target for executing an object. Thread target for executing an object.
""" """
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
obj = self.get_instance_for_key(key, session) obj = self.get_instance_for_key(key, session)
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
@ -2926,11 +2922,11 @@ class MasterView(View):
normal.append(button) normal.append(button)
return normal return normal
def make_buefy_button(self, label, def make_button(self, label,
type=None, is_primary=False, type=None, is_primary=False,
url=None, target=None, is_external=False, url=None, target=None, is_external=False,
icon_left=None, icon_left=None,
**kwargs): **kwargs):
""" """
Make and return a HTML ``<b-button>`` literal. Make and return a HTML ``<b-button>`` literal.
""" """
@ -2983,7 +2979,7 @@ class MasterView(View):
assumed to be external, which affects the icon and causes assumed to be external, which affects the icon and causes
button click to open link in a new tab. button click to open link in a new tab.
""" """
# TODO: this should call make_buefy_button() # TODO: this should call make_button()
# nb. unfortunately HTML.tag() calls its first arg 'tag' and # nb. unfortunately HTML.tag() calls its first arg 'tag' and
# so we can't pass a kwarg with that name...so instead we # so we can't pass a kwarg with that name...so instead we
@ -4067,10 +4063,11 @@ class MasterView(View):
""" """
Download current *row* results as XLSX. Download current *row* results as XLSX.
""" """
app = self.get_rattail_app()
obj = self.get_instance() obj = self.get_instance()
results = self.get_effective_row_data(sort=True) results = self.get_effective_row_data(sort=True)
fields = self.get_row_xlsx_fields() fields = self.get_row_xlsx_fields()
path = temp_path(suffix='.xlsx') path = app.make_temp_file(suffix='.xlsx')
writer = ExcelWriter(path, fields, sheet_title=self.get_row_model_title_plural()) writer = ExcelWriter(path, fields, sheet_title=self.get_row_model_title_plural())
writer.write_header() writer.write_header()
@ -5039,6 +5036,7 @@ class MasterView(View):
""" """
Generic view for configuring some aspect of the software. Generic view for configuring some aspect of the software.
""" """
app = self.get_rattail_app()
if self.request.method == 'POST': if self.request.method == 'POST':
if self.request.POST.get('remove_settings'): if self.request.POST.get('remove_settings'):
self.configure_remove_settings() self.configure_remove_settings()
@ -5053,7 +5051,7 @@ class MasterView(View):
uploads = {} uploads = {}
for key, value in data.items(): for key, value in data.items():
if isinstance(value, cgi_FieldStorage): if isinstance(value, cgi_FieldStorage):
tempdir = tempfile.mkdtemp() tempdir = app.make_temp_dir()
filename = os.path.basename(value.filename) filename = os.path.basename(value.filename)
filepath = os.path.join(tempdir, filename) filepath = os.path.join(tempdir, filename)
with open(filepath, 'wb') as f: with open(filepath, 'wb') as f:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -29,10 +29,8 @@ from rattail.time import localtime
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid import httpexceptions
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
# from tailbone import forms
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView from tailbone.views import MasterView
from tailbone.util import raw_datetime from tailbone.util import raw_datetime
@ -83,15 +81,15 @@ class MessageView(MasterView):
def index(self): def index(self):
if not self.request.user: if not self.request.user:
raise httpexceptions.HTTPForbidden raise self.forbidden()
return super().index() return super().index()
def get_instance(self): def get_instance(self):
if not self.request.user: if not self.request.user:
raise httpexceptions.HTTPForbidden raise self.forbidden()
message = super().get_instance() message = super().get_instance()
if not self.associated_with(message): if not self.associated_with(message):
raise httpexceptions.HTTPForbidden raise self.forbidden()
return message return message
def associated_with(self, message): def associated_with(self, message):
@ -395,7 +393,7 @@ class MessageView(MasterView):
message = self.get_instance() message = self.get_instance()
recipient = self.get_recipient(message) recipient = self.get_recipient(message)
if not recipient: if not recipient:
raise httpexceptions.HTTPForbidden raise self.forbidden()
dest = self.request.GET.get('dest') dest = self.request.GET.get('dest')
if dest not in ('inbox', 'archive'): if dest not in ('inbox', 'archive'):
@ -520,7 +518,7 @@ class RecipientsWidgetBuefy(dfwidget.Widget):
""" """
Custom "message recipients" widget, for use with Buefy / Vue.js themes. Custom "message recipients" widget, for use with Buefy / Vue.js themes.
""" """
template = 'message_recipients_buefy' template = 'message_recipients'
def deserialize(self, field, pstruct): def deserialize(self, field, pstruct):
if pstruct is colander.null: if pstruct is colander.null:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -43,7 +43,7 @@ class PrincipalMasterView(MasterView):
def get_fallback_templates(self, template, **kwargs): def get_fallback_templates(self, template, **kwargs):
return [ return [
'/principal/{}.mako'.format(template), '/principal/{}.mako'.format(template),
] + super(PrincipalMasterView, self).get_fallback_templates(template, **kwargs) ] + super().get_fallback_templates(template, **kwargs)
def perm_sortkey(self, item): def perm_sortkey(self, item):
key, value = item key, value = item
@ -74,9 +74,9 @@ class PrincipalMasterView(MasterView):
context = {'permissions': sorted_perms, 'principals': principals} context = {'permissions': sorted_perms, 'principals': principals}
perms = self.get_buefy_perms_data(sorted_perms) perms = self.get_perms_data(sorted_perms)
context['buefy_perms'] = perms context['perms_data'] = perms
context['buefy_sorted_groups'] = list(perms) context['sorted_groups_data'] = list(perms)
if permission_group and permission_group not in perms: if permission_group and permission_group not in perms:
permission_group = None permission_group = None
@ -95,7 +95,7 @@ class PrincipalMasterView(MasterView):
return self.render_to_response('find_by_perm', context) return self.render_to_response('find_by_perm', context)
def get_buefy_perms_data(self, sorted_perms): def get_perms_data(self, sorted_perms):
data = OrderedDict() data = OrderedDict()
for gkey, group in sorted_perms: for gkey, group in sorted_perms:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -37,8 +37,7 @@ from rattail.db import model, api, auth, Session as RattailSession
from rattail.gpc import GPC from rattail.gpc import GPC
from rattail.threads import Thread from rattail.threads import Thread
from rattail.exceptions import LabelPrintingError from rattail.exceptions import LabelPrintingError
from rattail.util import load_object, pretty_quantity, simple_error from rattail.util import simple_error
from rattail.time import localtime, make_utc
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
@ -417,13 +416,13 @@ class ProductView(MasterView):
app = self.get_rattail_app() app = self.get_rattail_app()
if price.starts: if price.starts:
starts = localtime(self.rattail_config, price.starts, from_utc=True) starts = app.localtime(price.starts, from_utc=True)
starts = app.render_date(starts.date()) starts = app.render_date(starts.date())
else: else:
starts = "??" starts = "??"
if price.ends: if price.ends:
ends = localtime(self.rattail_config, price.ends, from_utc=True) ends = app.localtime(price.ends, from_utc=True)
ends = app.render_date(ends.date()) ends = app.render_date(ends.date())
else: else:
ends = "??" ends = "??"
@ -456,23 +455,25 @@ class ProductView(MasterView):
default=True) default=True)
def render_regular_price(self, product, field): def render_regular_price(self, product, field):
app = self.get_rattail_app()
text = self.render_price(product, field) text = self.render_price(product, field)
if text and self.show_price_effective_dates(): if text and self.show_price_effective_dates():
history = self.get_regular_price_history(product) history = self.get_regular_price_history(product)
if history: if history:
date = localtime(self.rattail_config, history[0]['changed'], from_utc=True).date() date = app.localtime(history[0]['changed'], from_utc=True).date()
text = "{} (as of {})".format(text, date) text = "{} (as of {})".format(text, date)
return self.add_price_history_link(text, 'regular') return self.add_price_history_link(text, 'regular')
def render_current_price(self, product, field): def render_current_price(self, product, field):
app = self.get_rattail_app()
text = self.render_price(product, field) text = self.render_price(product, field)
if text and self.show_price_effective_dates(): if text and self.show_price_effective_dates():
history = self.get_current_price_history(product) history = self.get_current_price_history(product)
if history: if history:
date = localtime(self.rattail_config, history[0]['changed'], from_utc=True).date() date = app.localtime(history[0]['changed'], from_utc=True).date()
text = "{} (as of {})".format(text, date) text = "{} (as of {})".format(text, date)
return self.add_price_history_link(text, 'current') return self.add_price_history_link(text, 'current')
@ -489,10 +490,11 @@ class ProductView(MasterView):
if not text: if not text:
return return
app = self.get_rattail_app()
if self.show_price_effective_dates(): if self.show_price_effective_dates():
history = self.get_suggested_price_history(product) history = self.get_suggested_price_history(product)
if history: if history:
date = localtime(self.rattail_config, history[0]['changed'], from_utc=True).date() date = app.localtime(history[0]['changed'], from_utc=True).date()
text = "{} (as of {})".format(text, date) text = "{} (as of {})".format(text, date)
text = self.warn_if_regprice_more_than_srp(product, text) text = self.warn_if_regprice_more_than_srp(product, text)
@ -526,13 +528,15 @@ class ProductView(MasterView):
inventory = product.inventory inventory = product.inventory
if not inventory: if not inventory:
return "" return ""
return pretty_quantity(inventory.on_hand) app = self.get_rattail_app()
return app.render_quantity(inventory.on_hand)
def render_on_order(self, product, column): def render_on_order(self, product, column):
inventory = product.inventory inventory = product.inventory
if not inventory: if not inventory:
return "" return ""
return pretty_quantity(inventory.on_order) app = self.get_rattail_app()
return app.render_quantity(inventory.on_order)
def template_kwargs_index(self, **kwargs): def template_kwargs_index(self, **kwargs):
kwargs = super().template_kwargs_index(**kwargs) kwargs = super().template_kwargs_index(**kwargs)
@ -1105,7 +1109,8 @@ class ProductView(MasterView):
value = product.inventory.on_hand value = product.inventory.on_hand
if not value: if not value:
return "" return ""
return pretty_quantity(value) app = self.get_rattail_app()
return app.render_quantity(value)
def render_inventory_on_order(self, product, field): def render_inventory_on_order(self, product, field):
if not product.inventory: if not product.inventory:
@ -1113,7 +1118,8 @@ class ProductView(MasterView):
value = product.inventory.on_order value = product.inventory.on_order
if not value: if not value:
return "" return ""
return pretty_quantity(value) app = self.get_rattail_app()
return app.render_quantity(value)
def price_history(self): def price_history(self):
""" """
@ -1136,7 +1142,7 @@ class ProductView(MasterView):
if price is not None: if price is not None:
history['price'] = float(price) history['price'] = float(price)
history['price_display'] = app.render_currency(price) history['price_display'] = app.render_currency(price)
changed = localtime(self.rattail_config, history['changed'], from_utc=True) changed = app.localtime(history['changed'], from_utc=True)
history['changed'] = str(changed) history['changed'] = str(changed)
history['changed_display_html'] = raw_datetime(self.rattail_config, changed) history['changed_display_html'] = raw_datetime(self.rattail_config, changed)
user = history.pop('changed_by') user = history.pop('changed_by')
@ -1149,6 +1155,7 @@ class ProductView(MasterView):
""" """
AJAX view for fetching cost history for a product. AJAX view for fetching cost history for a product.
""" """
app = self.get_rattail_app()
product = self.get_instance() product = self.get_instance()
data = self.get_cost_history(product) data = self.get_cost_history(product)
@ -1162,7 +1169,7 @@ class ProductView(MasterView):
history['cost_display'] = "${:0.2f}".format(cost) history['cost_display'] = "${:0.2f}".format(cost)
else: else:
history['cost_display'] = None history['cost_display'] = None
changed = localtime(self.rattail_config, history['changed'], from_utc=True) changed = app.localtime(history['changed'], from_utc=True)
history['changed'] = str(changed) history['changed'] = str(changed)
history['changed_display_html'] = raw_datetime(self.rattail_config, changed) history['changed_display_html'] = raw_datetime(self.rattail_config, changed)
user = history.pop('changed_by') user = history.pop('changed_by')
@ -1388,10 +1395,11 @@ class ProductView(MasterView):
Returns a sequence of "records" which corresponds to the given Returns a sequence of "records" which corresponds to the given
product's regular price history. product's regular price history.
""" """
app = self.get_rattail_app()
Transaction = continuum.transaction_class(model.Product) Transaction = continuum.transaction_class(model.Product)
ProductVersion = continuum.version_class(model.Product) ProductVersion = continuum.version_class(model.Product)
ProductPriceVersion = continuum.version_class(model.ProductPrice) ProductPriceVersion = continuum.version_class(model.ProductPrice)
now = make_utc() now = app.make_utc()
history = [] history = []
# first we find all relevant ProductVersion records # first we find all relevant ProductVersion records
@ -1457,10 +1465,11 @@ class ProductView(MasterView):
Returns a sequence of "records" which corresponds to the given Returns a sequence of "records" which corresponds to the given
product's current price history. product's current price history.
""" """
app = self.get_rattail_app()
Transaction = continuum.transaction_class(model.Product) Transaction = continuum.transaction_class(model.Product)
ProductVersion = continuum.version_class(model.Product) ProductVersion = continuum.version_class(model.Product)
ProductPriceVersion = continuum.version_class(model.ProductPrice) ProductPriceVersion = continuum.version_class(model.ProductPrice)
now = make_utc() now = app.make_utc()
history = [] history = []
# first we find all relevant ProductVersion records # first we find all relevant ProductVersion records
@ -1599,10 +1608,11 @@ class ProductView(MasterView):
Returns a sequence of "records" which corresponds to the given Returns a sequence of "records" which corresponds to the given
product's SRP history. product's SRP history.
""" """
app = self.get_rattail_app()
Transaction = continuum.transaction_class(model.Product) Transaction = continuum.transaction_class(model.Product)
ProductVersion = continuum.version_class(model.Product) ProductVersion = continuum.version_class(model.Product)
ProductPriceVersion = continuum.version_class(model.ProductPrice) ProductPriceVersion = continuum.version_class(model.ProductPrice)
now = make_utc() now = app.make_utc()
history = [] history = []
# first we find all relevant ProductVersion records # first we find all relevant ProductVersion records
@ -1668,10 +1678,11 @@ class ProductView(MasterView):
Returns a sequence of "records" which corresponds to the given Returns a sequence of "records" which corresponds to the given
product's cost history. product's cost history.
""" """
app = self.get_rattail_app()
Transaction = continuum.transaction_class(model.Product) Transaction = continuum.transaction_class(model.Product)
ProductVersion = continuum.version_class(model.Product) ProductVersion = continuum.version_class(model.Product)
ProductCostVersion = continuum.version_class(model.ProductCost) ProductCostVersion = continuum.version_class(model.ProductCost)
now = make_utc() now = app.make_utc()
history = [] history = []
# we just find all relevant (preferred!) ProductCostVersion records # we just find all relevant (preferred!) ProductCostVersion records
@ -1948,10 +1959,11 @@ class ProductView(MasterView):
""" """
View for making a new batch from current product grid query. View for making a new batch from current product grid query.
""" """
app = self.get_rattail_app()
supported = self.get_supported_batches() supported = self.get_supported_batches()
batch_options = [] batch_options = []
for key, info in list(supported.items()): for key, info in list(supported.items()):
handler = load_object(info['spec'])(self.rattail_config) handler = app.load_object(info['spec'])(self.rattail_config)
handler.spec = info['spec'] handler.spec = info['spec']
handler.option_key = key handler.option_key = key
handler.option_title = info.get('title', handler.get_model_title()) handler.option_title = info.get('title', handler.get_model_title())
@ -2448,19 +2460,19 @@ class PendingProductView(MasterView):
if (self.has_perm('ignore_product') if (self.has_perm('ignore_product')
and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING, and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY)): self.enum.PENDING_PRODUCT_STATUS_READY)):
buttons.append(self.make_buefy_button("Ignore Product", buttons.append(self.make_button("Ignore Product",
type='is-warning', type='is-warning',
icon_left='ban', icon_left='ban',
**{'@click': "$emit('ignore-product')"})) **{'@click': "$emit('ignore-product')"}))
if (self.has_perm('resolve_product') if (self.has_perm('resolve_product')
and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING, and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY, self.enum.PENDING_PRODUCT_STATUS_READY,
self.enum.PENDING_PRODUCT_STATUS_IGNORED)): self.enum.PENDING_PRODUCT_STATUS_IGNORED)):
buttons.append(self.make_buefy_button("Resolve Product", buttons.append(self.make_button("Resolve Product",
is_primary=True, is_primary=True,
icon_left='object-ungroup', icon_left='object-ungroup',
**{'@click': "$emit('resolve-product')"})) **{'@click': "$emit('resolve-product')"}))
if buttons: if buttons:
text = HTML.tag('span', class_='control', c=[text]) text = HTML.tag('span', class_='control', c=[text])

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -28,10 +28,9 @@ from rattail.db.model import PurchaseBatch, PurchaseBatchRow
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid import httpexceptions
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
from tailbone import forms, grids from tailbone import forms
from tailbone.views.batch import BatchMasterView from tailbone.views.batch import BatchMasterView
@ -826,7 +825,7 @@ class PurchasingBatchView(BatchMasterView):
def render_row_credits(self, row, field): def render_row_credits(self, row, field):
g = self.make_row_credits_grid(row) g = self.make_row_credits_grid(row)
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='rowData.credits')) g.render_table_element(data_prop='rowData.credits'))
# def before_create_row(self, form): # def before_create_row(self, form):
# row = form.fieldset.model # row = form.fieldset.model

View file

@ -310,8 +310,6 @@ class OrderingBatchView(PurchasingBatchView):
if not order_date: if not order_date:
order_date = localtime(self.rattail_config).date() order_date = localtime(self.rattail_config).date()
buefy_data = self.get_worksheet_buefy_data(departments)
return self.render_to_response('worksheet', { return self.render_to_response('worksheet', {
'batch': batch, 'batch': batch,
'order_date': order_date, 'order_date': order_date,
@ -324,10 +322,10 @@ class OrderingBatchView(PurchasingBatchView):
'get_upc': lambda p: p.upc.pretty() if p.upc else '', 'get_upc': lambda p: p.upc.pretty() if p.upc else '',
'header_columns': self.order_form_header_columns, 'header_columns': self.order_form_header_columns,
'ignore_cases': not self.handler.allow_cases(), 'ignore_cases': not self.handler.allow_cases(),
'worksheet_data': buefy_data, 'worksheet_data': self.get_worksheet_data(departments),
}) })
def get_worksheet_buefy_data(self, departments): def get_worksheet_data(self, departments):
data = {} data = {}
for department in departments.values(): for department in departments.values():
for subdepartment in department._order_subdepartments.values(): for subdepartment in department._order_subdepartments.values():

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -33,12 +33,10 @@ from collections import OrderedDict
import humanize import humanize
from rattail import pod from rattail import pod
from rattail.time import localtime, make_utc from rattail.util import prettify, simple_error
from rattail.util import pretty_quantity, prettify, simple_error
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from pyramid import httpexceptions
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
from tailbone import forms, grids from tailbone import forms, grids
@ -781,8 +779,8 @@ class ReceivingBatchView(PurchasingBatchView):
g.set_click_handler('title', "autoFilterPoVsInvoice(props.row)") g.set_click_handler('title', "autoFilterPoVsInvoice(props.row)")
kwargs['po_vs_invoice_breakdown_data'] = breakdown kwargs['po_vs_invoice_breakdown_data'] = breakdown
kwargs['po_vs_invoice_breakdown_grid'] = HTML.literal( kwargs['po_vs_invoice_breakdown_grid'] = HTML.literal(
g.render_buefy_table_element(data_prop='poVsInvoiceBreakdownData', g.render_table_element(data_prop='poVsInvoiceBreakdownData',
empty_labels=True)) empty_labels=True))
kwargs['allow_edit_catalog_unit_cost'] = self.allow_edit_catalog_unit_cost(batch) kwargs['allow_edit_catalog_unit_cost'] = self.allow_edit_catalog_unit_cost(batch)
kwargs['allow_edit_invoice_unit_cost'] = self.allow_edit_invoice_unit_cost(batch) kwargs['allow_edit_invoice_unit_cost'] = self.allow_edit_invoice_unit_cost(batch)
@ -1137,6 +1135,7 @@ class ReceivingBatchView(PurchasingBatchView):
""" """
Primary desktop view for row-level receiving. Primary desktop view for row-level receiving.
""" """
app = self.get_rattail_app()
# TODO: this code was largely copied from mobile_receive_row() but it # TODO: this code was largely copied from mobile_receive_row() but it
# tries to pave the way for shared logic, i.e. where the latter would # tries to pave the way for shared logic, i.e. where the latter would
# simply invoke this method and return the result. however we're not # simply invoke this method and return the result. however we're not
@ -1270,7 +1269,7 @@ class ReceivingBatchView(PurchasingBatchView):
if accounted_for: if accounted_for:
# some product accounted for; button should receive "remainder" only # some product accounted for; button should receive "remainder" only
if remainder: if remainder:
remainder = pretty_quantity(remainder) remainder = app.render_quantity(remainder)
context['quick_receive_quantity'] = remainder context['quick_receive_quantity'] = remainder
context['quick_receive_text'] = "Receive Remainder ({} {})".format(remainder, context['unit_uom']) context['quick_receive_text'] = "Receive Remainder ({} {})".format(remainder, context['unit_uom'])
else: else:
@ -1280,7 +1279,7 @@ class ReceivingBatchView(PurchasingBatchView):
else: # nothing yet accounted for, button should receive "all" else: # nothing yet accounted for, button should receive "all"
if not remainder: if not remainder:
raise ValueError("why is remainder empty?") raise ValueError("why is remainder empty?")
remainder = pretty_quantity(remainder) remainder = app.render_quantity(remainder)
context['quick_receive_quantity'] = remainder context['quick_receive_quantity'] = remainder
context['quick_receive_text'] = "Receive ALL ({} {})".format(remainder, context['unit_uom']) context['quick_receive_text'] = "Receive ALL ({} {})".format(remainder, context['unit_uom'])

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -32,9 +32,8 @@ import logging
from collections import OrderedDict from collections import OrderedDict
import rattail import rattail
from rattail.db import model, Session as RattailSession from rattail.db.model import ReportOutput
from rattail.files import resource_path from rattail.files import resource_path
from rattail.time import localtime
from rattail.threads import Thread from rattail.threads import Thread
from rattail.util import simple_error from rattail.util import simple_error
@ -81,6 +80,7 @@ class OrderingWorksheet(View):
upc_getter = staticmethod(get_upc) upc_getter = staticmethod(get_upc)
def __call__(self): def __call__(self):
model = self.model
if self.request.params.get('vendor'): if self.request.params.get('vendor'):
vendor = Session.get(model.Vendor, self.request.params['vendor']) vendor = Session.get(model.Vendor, self.request.params['vendor'])
if vendor: if vendor:
@ -104,7 +104,8 @@ class OrderingWorksheet(View):
""" """
Rendering engine for the ordering worksheet report. Rendering engine for the ordering worksheet report.
""" """
app = self.get_rattail_app()
model = self.model
q = Session.query(model.ProductCost) q = Session.query(model.ProductCost)
q = q.join(model.Product) q = q.join(model.Product)
q = q.filter(model.Product.deleted == False) q = q.filter(model.Product.deleted == False)
@ -127,7 +128,7 @@ class OrderingWorksheet(View):
key = '{0} {1}'.format(brand, product.description) key = '{0} {1}'.format(brand, product.description)
return key return key
now = localtime(self.request.rattail_config) now = app.localtime()
data = dict( data = dict(
vendor=vendor, vendor=vendor,
costs=costs, costs=costs,
@ -157,7 +158,7 @@ class InventoryWorksheet(View):
""" """
This is the "Inventory Worksheet" report. This is the "Inventory Worksheet" report.
""" """
model = self.model
departments = Session.query(model.Department) departments = Session.query(model.Department)
if self.request.params.get('department'): if self.request.params.get('department'):
@ -178,6 +179,8 @@ class InventoryWorksheet(View):
""" """
Generates the Inventory Worksheet report. Generates the Inventory Worksheet report.
""" """
app = self.get_rattail_app()
model = self.model
def get_products(subdepartment): def get_products(subdepartment):
q = Session.query(model.Product) q = Session.query(model.Product)
@ -191,7 +194,7 @@ class InventoryWorksheet(View):
q = q.order_by(model.Brand.name, model.Product.description) q = q.order_by(model.Brand.name, model.Product.description)
return q.all() return q.all()
now = localtime(self.request.rattail_config) now = app.localtime()
data = dict( data = dict(
date=now.strftime('%a %d %b %Y'), date=now.strftime('%a %d %b %Y'),
time=now.strftime('%I:%M %p'), time=now.strftime('%I:%M %p'),
@ -209,7 +212,7 @@ class ReportOutputView(ExportMasterView):
""" """
Master view for report output Master view for report output
""" """
model_class = model.ReportOutput model_class = ReportOutput
route_prefix = 'report_output' route_prefix = 'report_output'
url_prefix = '/reports/generated' url_prefix = '/reports/generated'
creatable = True creatable = True
@ -238,7 +241,7 @@ class ReportOutputView(ExportMasterView):
] ]
def __init__(self, request): def __init__(self, request):
super(ReportOutputView, self).__init__(request) super().__init__(request)
self.report_handler = self.get_report_handler() self.report_handler = self.get_report_handler()
def get_report_handler(self): def get_report_handler(self):
@ -246,7 +249,7 @@ class ReportOutputView(ExportMasterView):
return app.get_report_handler() return app.get_report_handler()
def configure_grid(self, g): def configure_grid(self, g):
super(ReportOutputView, self).configure_grid(g) super().configure_grid(g)
g.filters['report_name'].default_active = True g.filters['report_name'].default_active = True
g.filters['report_name'].default_verb = 'contains' g.filters['report_name'].default_verb = 'contains'
@ -254,7 +257,7 @@ class ReportOutputView(ExportMasterView):
g.set_link('filename') g.set_link('filename')
def configure_form(self, f): def configure_form(self, f):
super(ReportOutputView, self).configure_form(f) super().configure_form(f)
# report_type # report_type
f.set_renderer('report_type', self.render_report_type) f.set_renderer('report_type', self.render_report_type)
@ -282,10 +285,10 @@ class ReportOutputView(ExportMasterView):
# add help button if report has a link # add help button if report has a link
report = self.report_handler.get_report(type_key) report = self.report_handler.get_report(type_key)
if report and report.help_url: if report and report.help_url:
button = self.make_buefy_button("Help for this report", button = self.make_button("Help for this report",
url=report.help_url, url=report.help_url,
is_external=True, is_external=True,
icon_left='question-circle') icon_left='question-circle')
button = HTML.tag('div', class_='level-item', c=[button]) button = HTML.tag('div', class_='level-item', c=[button])
rendered = HTML.tag('div', class_='level-item', c=[rendered]) rendered = HTML.tag('div', class_='level-item', c=[rendered])
rendered = HTML.tag('div', class_='level-left', c=[rendered, button]) rendered = HTML.tag('div', class_='level-left', c=[rendered, button])
@ -311,7 +314,7 @@ class ReportOutputView(ExportMasterView):
labels={'key': "Name"}, labels={'key': "Name"},
) )
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='paramsData')) g.render_table_element(data_prop='paramsData'))
def get_params_context(self, report): def get_params_context(self, report):
params_data = [] params_data = []
@ -323,7 +326,7 @@ class ReportOutputView(ExportMasterView):
return params_data return params_data
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super(ReportOutputView, self).template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)
output = kwargs['instance'] output = kwargs['instance']
kwargs['params_data'] = self.get_params_context(output) kwargs['params_data'] = self.get_params_context(output)
@ -339,7 +342,7 @@ class ReportOutputView(ExportMasterView):
return kwargs return kwargs
def template_kwargs_delete(self, **kwargs): def template_kwargs_delete(self, **kwargs):
kwargs = super(ReportOutputView, self).template_kwargs_delete(**kwargs) kwargs = super().template_kwargs_delete(**kwargs)
report = kwargs['instance'] report = kwargs['instance']
kwargs['params_data'] = self.get_params_context(report) kwargs['params_data'] = self.get_params_context(report)
@ -496,7 +499,9 @@ class ReportOutputView(ExportMasterView):
resulting :class:`rattail:~rattail.db.model.reports.ReportOutput` resulting :class:`rattail:~rattail.db.model.reports.ReportOutput`
object. object.
""" """
session = RattailSession() app = self.get_rattail_app()
model = self.model
session = app.make_session()
user = session.get(model.User, user_uuid) user = session.get(model.User, user_uuid)
try: try:
output = self.report_handler.generate_output(session, report, params, user, progress=progress) output = self.report_handler.generate_output(session, report, params, user, progress=progress)
@ -603,7 +608,7 @@ class ProblemReportView(MasterView):
] ]
def __init__(self, request): def __init__(self, request):
super(ProblemReportView, self).__init__(request) super().__init__(request)
app = self.get_rattail_app() app = self.get_rattail_app()
self.problem_handler = app.get_problem_report_handler() self.problem_handler = app.get_problem_report_handler()
@ -660,7 +665,7 @@ class ProblemReportView(MasterView):
return ProblemReportSchema() return ProblemReportSchema()
def configure_form(self, f): def configure_form(self, f):
super(ProblemReportView, self).configure_form(f) super().configure_form(f)
# email_* # email_*
if self.editing: if self.editing:
@ -703,10 +708,10 @@ class ProblemReportView(MasterView):
g = self.get_grid_factory()('days', [], g = self.get_grid_factory()('days', [],
columns=['weekday_name', 'enabled'], columns=['weekday_name', 'enabled'],
labels={'weekday_name': "Weekday"}) labels={'weekday_name': "Weekday"})
return HTML.literal(g.render_buefy_table_element(data_prop='weekdaysData')) return HTML.literal(g.render_table_element(data_prop='weekdaysData'))
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super(ProblemReportView, self).template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)
report_info = kwargs['instance'] report_info = kwargs['instance']
data = [] data = []

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -29,7 +29,7 @@ import os
from sqlalchemy import orm from sqlalchemy import orm
from openpyxl.styles import Font, PatternFill from openpyxl.styles import Font, PatternFill
from rattail.db import model from rattail.db.model import Role
from rattail.db.auth import administrator_role, guest_role, authenticated_role from rattail.db.auth import administrator_role, guest_role, authenticated_role
from rattail.excel import ExcelWriter from rattail.excel import ExcelWriter
@ -46,7 +46,7 @@ class RoleView(PrincipalMasterView):
""" """
Master view for the Role model. Master view for the Role model.
""" """
model_class = model.Role model_class = Role
has_versions = True has_versions = True
touchable = True touchable = True
@ -77,7 +77,7 @@ class RoleView(PrincipalMasterView):
] ]
def configure_grid(self, g): def configure_grid(self, g):
super(RoleView, self).configure_grid(g) super().configure_grid(g)
# name # name
g.filters['name'].default_active = True g.filters['name'].default_active = True
@ -158,6 +158,7 @@ class RoleView(PrincipalMasterView):
return True return True
def unique_name(self, node, value): def unique_name(self, node, value):
model = self.model
query = self.Session.query(model.Role)\ query = self.Session.query(model.Role)\
.filter(model.Role.name == value) .filter(model.Role.name == value)
if self.editing: if self.editing:
@ -167,7 +168,7 @@ class RoleView(PrincipalMasterView):
raise colander.Invalid(node, "Name must be unique") raise colander.Invalid(node, "Name must be unique")
def configure_form(self, f): def configure_form(self, f):
super(RoleView, self).configure_form(f) super().configure_form(f)
role = f.model_instance role = f.model_instance
app = self.get_rattail_app() app = self.get_rattail_app()
auth = app.get_auth_handler() auth = app.get_auth_handler()
@ -265,7 +266,7 @@ class RoleView(PrincipalMasterView):
g.main_actions.append(self.make_action('edit', icon='edit')) g.main_actions.append(self.make_action('edit', icon='edit'))
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='usersData')) g.render_table_element(data_prop='usersData'))
def get_available_permissions(self): def get_available_permissions(self):
""" """
@ -322,7 +323,7 @@ class RoleView(PrincipalMasterView):
""" """
if data is None: if data is None:
data = form.validated data = form.validated
role = super(RoleView, self).objectify(form, data) role = super().objectify(form, data)
self.update_permissions(role, data['permissions']) self.update_permissions(role, data['permissions'])
return role return role
@ -345,6 +346,7 @@ class RoleView(PrincipalMasterView):
auth.revoke_permission(role, pkey) auth.revoke_permission(role, pkey)
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
model = self.model
role = kwargs['instance'] role = kwargs['instance']
if role.users: if role.users:
users = sorted(role.users, key=lambda u: u.username) users = sorted(role.users, key=lambda u: u.username)
@ -390,6 +392,7 @@ class RoleView(PrincipalMasterView):
def find_principals_with_permission(self, session, permission): def find_principals_with_permission(self, session, permission):
app = self.get_rattail_app() app = self.get_rattail_app()
model = self.model
auth = app.get_auth_handler() auth = app.get_auth_handler()
# TODO: this should search Permission table instead, and work backward to Role? # TODO: this should search Permission table instead, and work backward to Role?
@ -408,6 +411,7 @@ class RoleView(PrincipalMasterView):
Excel spreadsheet, and returns that file. Excel spreadsheet, and returns that file.
""" """
app = self.get_rattail_app() app = self.get_rattail_app()
model = self.model
auth = app.get_auth_handler() auth = app.get_auth_handler()
roles = self.Session.query(model.Role)\ roles = self.Session.query(model.Role)\

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -32,8 +32,8 @@ from collections import OrderedDict
import json import json
from rattail.db import model from rattail.db.model import Setting
from rattail.settings import Setting from rattail.settings import Setting as AppSetting
from rattail.util import import_module_path from rattail.util import import_module_path
import colander import colander
@ -81,7 +81,7 @@ class AppInfoView(MasterView):
return data return data
def configure_grid(self, g): def configure_grid(self, g):
super(AppInfoView, self).configure_grid(g) super().configure_grid(g)
g.sorters['name'] = g.make_simple_sorter('name', foldcase=True) g.sorters['name'] = g.make_simple_sorter('name', foldcase=True)
g.set_sort_defaults('name') g.set_sort_defaults('name')
@ -94,12 +94,12 @@ class AppInfoView(MasterView):
g.set_searchable('editable_project_location') g.set_searchable('editable_project_location')
def template_kwargs_index(self, **kwargs): def template_kwargs_index(self, **kwargs):
kwargs = super(AppInfoView, self).template_kwargs_index(**kwargs) kwargs = super().template_kwargs_index(**kwargs)
kwargs['configure_button_title'] = "Configure App" kwargs['configure_button_title'] = "Configure App"
return kwargs return kwargs
def configure_get_context(self, **kwargs): def configure_get_context(self, **kwargs):
context = super(AppInfoView, self).configure_get_context(**kwargs) context = super().configure_get_context(**kwargs)
weblibs = OrderedDict([ weblibs = OrderedDict([
('vue', "Vue"), ('vue', "Vue"),
@ -195,7 +195,7 @@ class SettingView(MasterView):
""" """
Master view for the settings model. Master view for the settings model.
""" """
model_class = model.Setting model_class = Setting
model_title = "Raw Setting" model_title = "Raw Setting"
model_title_plural = "Raw Settings" model_title_plural = "Raw Settings"
bulk_deletable = True bulk_deletable = True
@ -207,18 +207,19 @@ class SettingView(MasterView):
] ]
def configure_grid(self, g): def configure_grid(self, g):
super(SettingView, self).configure_grid(g) super().configure_grid(g)
g.filters['name'].default_active = True g.filters['name'].default_active = True
g.filters['name'].default_verb = 'contains' g.filters['name'].default_verb = 'contains'
g.set_sort_defaults('name') g.set_sort_defaults('name')
g.set_link('name') g.set_link('name')
def configure_form(self, f): def configure_form(self, f):
super(SettingView, self).configure_form(f) super().configure_form(f)
if self.creating: if self.creating:
f.set_validator('name', self.unique_name) f.set_validator('name', self.unique_name)
def unique_name(self, node, value): def unique_name(self, node, value):
model = self.model
setting = self.Session.get(model.Setting, value) setting = self.Session.get(model.Setting, value)
if setting: if setting:
raise colander.Invalid(node, "Setting name must be unique") raise colander.Invalid(node, "Setting name must be unique")
@ -245,7 +246,7 @@ class SettingView(MasterView):
self.rattail_config.beaker_invalidate_setting(setting.name) self.rattail_config.beaker_invalidate_setting(setting.name)
# otherwise delete like normal # otherwise delete like normal
super(SettingView, self).delete_instance(setting) super().delete_instance(setting)
# TODO: deprecate / remove this # TODO: deprecate / remove this
@ -307,14 +308,14 @@ class AppSettingsView(View):
'settings': settings, 'settings': settings,
'config_options': config_options, 'config_options': config_options,
} }
context['buefy_data'] = self.get_buefy_data(form, groups, settings) context['settings_data'] = self.get_settings_data(form, groups, settings)
# TODO: this seems hacky, and probably only needed if theme changes? # TODO: this seems hacky, and probably only needed if theme changes?
if current_group == '(All)': if current_group == '(All)':
current_group = '' current_group = ''
context['current_group'] = current_group context['current_group'] = current_group
return context return context
def get_buefy_data(self, form, groups, settings): def get_settings_data(self, form, groups, settings):
dform = form.make_deform_form() dform = form.make_deform_form()
grouped = dict([(label, []) grouped = dict([(label, [])
for label in groups]) for label in groups])
@ -407,7 +408,7 @@ class AppSettingsView(View):
module = import_module_path(module) module = import_module_path(module)
for name in dir(module): for name in dir(module):
obj = getattr(module, name) obj = getattr(module, name)
if isinstance(obj, type) and issubclass(obj, Setting) and obj is not Setting: if isinstance(obj, type) and issubclass(obj, AppSetting) and obj is not AppSetting:
if core_only and not obj.core: if core_only and not obj.core:
continue continue
# NOTE: we set this here, and reference it elsewhere # NOTE: we set this here, and reference it elsewhere

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -28,9 +28,8 @@ import datetime
import sqlalchemy as sa import sqlalchemy as sa
from rattail import enum from rattail.db import api
from rattail.db import model, api from rattail.time import get_sunday
from rattail.time import localtime, make_utc, get_sunday
from rattail.util import hours_as_decimal from rattail.util import hours_as_decimal
import colander import colander
@ -83,6 +82,8 @@ class TimeSheetView(View):
""" """
Determine date/store/dept context from user's session and/or defaults. Determine date/store/dept context from user's session and/or defaults.
""" """
app = self.get_rattail_app()
model = self.model
date = None date = None
date_key = 'timesheet.{}.date'.format(self.key) date_key = 'timesheet.{}.date'.format(self.key)
if date_key in self.request.session: if date_key in self.request.session:
@ -93,7 +94,7 @@ class TimeSheetView(View):
except ValueError: except ValueError:
pass pass
if not date: if not date:
date = localtime(self.rattail_config).date() date = app.today()
store = None store = None
department = None department = None
@ -113,7 +114,7 @@ class TimeSheetView(View):
store = api.get_store(Session(), store) store = api.get_store(Session(), store)
employees = Session.query(model.Employee)\ employees = Session.query(model.Employee)\
.filter(model.Employee.status == enum.EMPLOYEE_STATUS_CURRENT) .filter(model.Employee.status == self.enum.EMPLOYEE_STATUS_CURRENT)
if store: if store:
employees = employees.join(model.EmployeeStore)\ employees = employees.join(model.EmployeeStore)\
.filter(model.EmployeeStore.store == store) .filter(model.EmployeeStore.store == store)
@ -132,6 +133,8 @@ class TimeSheetView(View):
""" """
Determine employee/date context from user's session and/or defaults Determine employee/date context from user's session and/or defaults
""" """
app = self.get_rattail_app()
model = self.model
date = None date = None
date_key = 'timesheet.{}.employee.date'.format(self.key) date_key = 'timesheet.{}.employee.date'.format(self.key)
if date_key in self.request.session: if date_key in self.request.session:
@ -142,7 +145,7 @@ class TimeSheetView(View):
except ValueError: except ValueError:
pass pass
if not date: if not date:
date = localtime(self.rattail_config).date() date = app.today()
employee = None employee = None
employee_key = 'timesheet.{}.employee'.format(self.key) employee_key = 'timesheet.{}.employee'.format(self.key)
@ -191,7 +194,7 @@ class TimeSheetView(View):
stores = self.get_stores() stores = self.get_stores()
store_values = [(s.uuid, "{} - {}".format(s.id, s.name)) for s in stores] store_values = [(s.uuid, "{} - {}".format(s.id, s.name)) for s in stores]
store_values.insert(0, ('', "(all)")) store_values.insert(0, ('', "(all)"))
form.set_widget('store', forms.widgets.PlainSelectWidget(values=store_values)) form.set_widget('store', dfwidget.SelectWidget(values=store_values))
if context['store']: if context['store']:
form.set_default('store', context['store'].uuid) form.set_default('store', context['store'].uuid)
else: else:
@ -203,7 +206,7 @@ class TimeSheetView(View):
departments = self.get_departments() departments = self.get_departments()
department_values = [(d.uuid, d.name) for d in departments] department_values = [(d.uuid, d.name) for d in departments]
department_values.insert(0, ('', "(all)")) department_values.insert(0, ('', "(all)"))
form.set_widget('department', forms.widgets.PlainSelectWidget(values=department_values)) form.set_widget('department', dfwidget.SelectWidget(values=department_values))
if context['department']: if context['department']:
form.set_default('department', context['department'].uuid) form.set_default('department', context['department'].uuid)
else: else:
@ -292,6 +295,7 @@ class TimeSheetView(View):
self.request.session['timesheet.{}.{}'.format(mainkey, key)] = value self.request.session['timesheet.{}.{}'.format(mainkey, key)] = value
def get_stores(self): def get_stores(self):
model = self.model
return Session.query(model.Store).order_by(model.Store.id).all() return Session.query(model.Store).order_by(model.Store.id).all()
def get_store_options(self, stores): def get_store_options(self, stores):
@ -299,6 +303,7 @@ class TimeSheetView(View):
return tags.Options(options, prompt="(all)") return tags.Options(options, prompt="(all)")
def get_departments(self): def get_departments(self):
model = self.model
return Session.query(model.Department).order_by(model.Department.name).all() return Session.query(model.Department).order_by(model.Department.name).all()
def get_department_options(self, departments): def get_department_options(self, departments):
@ -402,6 +407,7 @@ class TimeSheetView(View):
the given params. The cached shift data is attached to each employee. the given params. The cached shift data is attached to each employee.
""" """
app = self.get_rattail_app() app = self.get_rattail_app()
model = self.model
# TODO: a bit hacky, this? display hours as HH:MM by default, but # TODO: a bit hacky, this? display hours as HH:MM by default, but
# check config in order to display as HH.HH for certain users # check config in order to display as HH.HH for certain users
@ -413,19 +419,19 @@ class TimeSheetView(View):
hours_style = 'pretty' hours_style = 'pretty'
shift_type = 'scheduled' if cls is model.ScheduledShift else 'worked' shift_type = 'scheduled' if cls is model.ScheduledShift else 'worked'
min_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[0], datetime.time(0))) min_time = app.localtime(datetime.datetime.combine(weekdays[0], datetime.time(0)))
max_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[-1] + datetime.timedelta(days=1), datetime.time(0))) max_time = app.localtime(datetime.datetime.combine(weekdays[-1] + datetime.timedelta(days=1), datetime.time(0)))
shifts = Session.query(cls)\ shifts = Session.query(cls)\
.filter(cls.employee_uuid.in_([e.uuid for e in employees]))\ .filter(cls.employee_uuid.in_([e.uuid for e in employees]))\
.filter(sa.or_( .filter(sa.or_(
sa.and_( sa.and_(
cls.start_time >= make_utc(min_time), cls.start_time >= app.make_utc(min_time),
cls.start_time < make_utc(max_time), cls.start_time < app.make_utc(max_time),
), ),
sa.and_( sa.and_(
cls.start_time == None, cls.start_time == None,
cls.end_time >= make_utc(min_time), cls.end_time >= app.make_utc(min_time),
cls.end_time < make_utc(max_time), cls.end_time < app.make_utc(max_time),
)))\ )))\
.all() .all()

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -80,7 +80,7 @@ class TableView(MasterView):
] ]
def __init__(self, request): def __init__(self, request):
super(TableView, self).__init__(request) super().__init__(request)
app = self.get_rattail_app() app = self.get_rattail_app()
self.db_handler = app.get_db_handler() self.db_handler = app.get_db_handler()
@ -102,7 +102,7 @@ class TableView(MasterView):
for row in result] for row in result]
def configure_grid(self, g): def configure_grid(self, g):
super(TableView, self).configure_grid(g) super().configure_grid(g)
# table_name # table_name
g.sorters['table_name'] = g.make_simple_sorter('table_name', foldcase=True) g.sorters['table_name'] = g.make_simple_sorter('table_name', foldcase=True)
@ -114,7 +114,7 @@ class TableView(MasterView):
g.sorters['row_count'] = g.make_simple_sorter('row_count') g.sorters['row_count'] = g.make_simple_sorter('row_count')
def configure_form(self, f): def configure_form(self, f):
super(TableView, self).configure_form(f) super().configure_form(f)
# TODO: should render this instead, by inspecting table # TODO: should render this instead, by inspecting table
if not self.creating: if not self.creating:
@ -169,7 +169,7 @@ class TableView(MasterView):
return TableSchema() return TableSchema()
def get_xref_buttons(self, table): def get_xref_buttons(self, table):
buttons = super(TableView, self).get_xref_buttons(table) buttons = super().get_xref_buttons(table)
if table.get('model_name'): if table.get('model_name'):
all_views = self.request.registry.settings['tailbone_model_views'] all_views = self.request.registry.settings['tailbone_model_views']
@ -182,15 +182,15 @@ class TableView(MasterView):
if self.request.has_perm('model_views.create'): if self.request.has_perm('model_views.create'):
url = self.request.route_url('model_views.create', url = self.request.route_url('model_views.create',
_query={'model_name': table['model_name']}) _query={'model_name': table['model_name']})
buttons.append(self.make_buefy_button("New View", buttons.append(self.make_button("New View",
is_primary=True, is_primary=True,
url=url, url=url,
icon_left='plus')) icon_left='plus'))
return buttons return buttons
def template_kwargs_create(self, **kwargs): def template_kwargs_create(self, **kwargs):
kwargs = super(TableView, self).template_kwargs_create(**kwargs) kwargs = super().template_kwargs_create(**kwargs)
app = self.get_rattail_app() app = self.get_rattail_app()
model = self.model model = self.model
@ -301,7 +301,7 @@ class TableView(MasterView):
return data return data
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(TableView, self).configure_row_grid(g) super().configure_row_grid(g)
g.sorters['sequence'] = g.make_simple_sorter('sequence') g.sorters['sequence'] = g.make_simple_sorter('sequence')
g.set_sort_defaults('sequence') g.set_sort_defaults('sequence')
@ -419,7 +419,7 @@ class TablesView(TableView):
def __init__(self, request): def __init__(self, request):
warnings.warn("TablesView is deprecated; please use TableView instead", warnings.warn("TablesView is deprecated; please use TableView instead",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
super(TablesView, self).__init__(request) super().__init__(request)
class TableSchema(colander.Schema): class TableSchema(colander.Schema):

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -98,4 +98,4 @@ class MasterView(views.MasterView):
main_actions=actions, main_actions=actions,
) )
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='probesData')) g.render_table_element(data_prop='probesData'))

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -202,7 +202,7 @@ class TransactionView(MasterView):
return 'warning' return 'warning'
def configure_form(self, f): def configure_form(self, f):
super(TransactionView, self).configure_form(f) super().configure_form(f)
# system # system
f.set_enum('system', self.enum.TRAINWRECK_SYSTEM) f.set_enum('system', self.enum.TRAINWRECK_SYSTEM)
@ -240,10 +240,10 @@ class TransactionView(MasterView):
request=self.request) request=self.request)
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='custorderXrefMarkersData')) g.render_table_element(data_prop='custorderXrefMarkersData'))
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super(TransactionView, self).template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)
form = kwargs['form'] form = kwargs['form']
if 'custorder_xref_markers' in form: if 'custorder_xref_markers' in form:
@ -266,7 +266,7 @@ class TransactionView(MasterView):
return item.transaction return item.transaction
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(TransactionView, self).configure_row_grid(g) super().configure_row_grid(g)
g.set_sort_defaults('sequence') g.set_sort_defaults('sequence')
g.set_type('unit_quantity', 'quantity') g.set_type('unit_quantity', 'quantity')
@ -286,7 +286,7 @@ class TransactionView(MasterView):
return "Trainwreck Line Item" return "Trainwreck Line Item"
def configure_row_form(self, f): def configure_row_form(self, f):
super(TransactionView, self).configure_row_form(f) super().configure_row_form(f)
# transaction # transaction
f.set_renderer('transaction', self.render_transaction) f.set_renderer('transaction', self.render_transaction)
@ -325,7 +325,7 @@ class TransactionView(MasterView):
request=self.request) request=self.request)
return HTML.literal( return HTML.literal(
g.render_buefy_table_element(data_prop='discountsData')) g.render_table_element(data_prop='discountsData'))
def template_kwargs_view_row(self, **kwargs): def template_kwargs_view_row(self, **kwargs):
form = kwargs['form'] form = kwargs['form']
@ -401,7 +401,7 @@ class TransactionView(MasterView):
] ]
def configure_get_context(self): def configure_get_context(self):
context = super(TransactionView, self).configure_get_context() context = super().configure_get_context()
app = self.get_rattail_app() app = self.get_rattail_app()
trainwreck_handler = app.get_trainwreck_handler() trainwreck_handler = app.get_trainwreck_handler()
@ -415,7 +415,7 @@ class TransactionView(MasterView):
return context return context
def configure_gather_settings(self, data): def configure_gather_settings(self, data):
settings = super(TransactionView, self).configure_gather_settings(data) settings = super().configure_gather_settings(data)
app = self.get_rattail_app() app = self.get_rattail_app()
trainwreck_handler = app.get_trainwreck_handler() trainwreck_handler = app.get_trainwreck_handler()
@ -432,7 +432,7 @@ class TransactionView(MasterView):
return settings return settings
def configure_remove_settings(self): def configure_remove_settings(self):
super(TransactionView, self).configure_remove_settings() super().configure_remove_settings()
app = self.get_rattail_app() app = self.get_rattail_app()
names = [ names = [

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar # Copyright © 2010-2024 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -84,7 +84,7 @@ class UserView(PrincipalMasterView):
] ]
def __init__(self, request): def __init__(self, request):
super(UserView, self).__init__(request) super().__init__(request)
app = self.get_rattail_app() app = self.get_rattail_app()
# always get a reference to the auth/merge handler # always get a reference to the auth/merge handler
@ -92,7 +92,7 @@ class UserView(PrincipalMasterView):
self.merge_handler = self.auth_handler self.merge_handler = self.auth_handler
def query(self, session): def query(self, session):
query = super(UserView, self).query(session) query = super().query(session)
model = self.model model = self.model
# bring in the related Person(s) # bring in the related Person(s)
@ -102,7 +102,7 @@ class UserView(PrincipalMasterView):
return query return query
def configure_grid(self, g): def configure_grid(self, g):
super(UserView, self).configure_grid(g) super().configure_grid(g)
model = self.model model = self.model
del g.filters['salt'] del g.filters['salt']
@ -177,7 +177,7 @@ class UserView(PrincipalMasterView):
raise colander.Invalid(node, "Person not found (you must *select* a record)") raise colander.Invalid(node, "Person not found (you must *select* a record)")
def configure_form(self, f): def configure_form(self, f):
super(UserView, self).configure_form(f) super().configure_form(f)
model = self.model model = self.model
user = f.model_instance user = f.model_instance
@ -290,12 +290,12 @@ class UserView(PrincipalMasterView):
self.make_action('delete', icon='trash', self.make_action('delete', icon='trash',
click_handler="$emit('api-token-delete', props.row)")]) click_handler="$emit('api-token-delete', props.row)")])
button = self.make_buefy_button("New", is_primary=True, button = self.make_button("New", is_primary=True,
icon_left='plus', icon_left='plus',
**{'@click': "$emit('api-new-token')"}) **{'@click': "$emit('api-new-token')"})
table = HTML.literal( table = HTML.literal(
g.render_buefy_table_element(data_prop='apiTokens')) g.render_table_element(data_prop='apiTokens'))
return HTML.tag('div', c=[button, table]) return HTML.tag('div', c=[button, table])
@ -329,7 +329,7 @@ class UserView(PrincipalMasterView):
'tokens': self.get_api_tokens(user)} 'tokens': self.get_api_tokens(user)}
def template_kwargs_view(self, **kwargs): def template_kwargs_view(self, **kwargs):
kwargs = super(UserView, self).template_kwargs_view(**kwargs) kwargs = super().template_kwargs_view(**kwargs)
user = kwargs['instance'] user = kwargs['instance']
kwargs['api_tokens_data'] = self.get_api_tokens(user) kwargs['api_tokens_data'] = self.get_api_tokens(user)
@ -377,7 +377,7 @@ class UserView(PrincipalMasterView):
# create/update user as per normal # create/update user as per normal
if data is None: if data is None:
data = form.validated data = form.validated
user = super(UserView, self).objectify(form, data) user = super().objectify(form, data)
# create/update person as needed # create/update person as needed
names = {} names = {}
@ -487,7 +487,7 @@ class UserView(PrincipalMasterView):
.filter(model.UserEvent.user == user) .filter(model.UserEvent.user == user)
def configure_row_grid(self, g): def configure_row_grid(self, g):
super(UserView, self).configure_row_grid(g) super().configure_row_grid(g)
g.width = 'half' g.width = 'half'
g.filterable = False g.filterable = False
g.set_sort_defaults('occurred', 'desc') g.set_sort_defaults('occurred', 'desc')
@ -588,7 +588,7 @@ class UserView(PrincipalMasterView):
'themes.style.{}'.format(name)) 'themes.style.{}'.format(name))
if css: if css:
options.append({'value': css, 'label': name}) options.append({'value': css, 'label': name})
context['buefy_css_options'] = options context['theme_style_options'] = options
return context return context
@ -699,12 +699,12 @@ class UserEventView(MasterView):
] ]
def get_data(self, session=None): def get_data(self, session=None):
query = super(UserEventView, self).get_data(session=session) query = super().get_data(session=session)
model = self.model model = self.model
return query.join(model.User) return query.join(model.User)
def configure_grid(self, g): def configure_grid(self, g):
super(UserEventView, self).configure_grid(g) super().configure_grid(g)
model = self.model model = self.model
g.set_joiner('person', lambda q: q.outerjoin(model.Person)) g.set_joiner('person', lambda q: q.outerjoin(model.Person))
g.set_sorter('user', model.User.username) g.set_sorter('user', model.User.username)