diff --git a/CHANGELOG.md b/CHANGELOG.md
index aee19700..c974b3a6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,96 @@ All notable changes to Tailbone will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## v0.22.7 (2025-02-19)
+
+### Fix
+
+- stop using old config for logo image url on login page
+- fix warning msg for deprecated Grid param
+
+## v0.22.6 (2025-02-01)
+
+### Fix
+
+- register vue3 form component for products -> make batch
+
+## v0.22.5 (2024-12-16)
+
+### Fix
+
+- whoops this is latest rattail
+- require newer rattail lib
+- require newer wuttaweb
+- let caller request safe HTML literal for rendered grid table
+
+## v0.22.4 (2024-11-22)
+
+### Fix
+
+- avoid error in product search for duplicated key
+- use vmodel for confirm password widget input
+
+## v0.22.3 (2024-11-19)
+
+### Fix
+
+- avoid error for trainwreck query when not a customer
+
+## v0.22.2 (2024-11-18)
+
+### Fix
+
+- use local/custom enum for continuum operations
+- add basic master view for Product Costs
+- show continuum operation type when viewing version history
+- always define `app` attr for ViewSupplement
+- avoid deprecated import
+
+## v0.22.1 (2024-11-02)
+
+### Fix
+
+- fix submit button for running problem report
+- avoid deprecated grid method
+
+## v0.22.0 (2024-10-22)
+
+### Feat
+
+- add support for new ordering batch from parsed file
+
+### Fix
+
+- avoid deprecated method to suggest username
+
+## v0.21.11 (2024-10-03)
+
+### Fix
+
+- custom method for adding grid action
+- become/stop root should redirect to previous url
+
+## v0.21.10 (2024-09-15)
+
+### Fix
+
+- update project repo links, kallithea -> forgejo
+- use better icon for submit button on login page
+- wrap notes text for batch view
+- expose datasync consumer batch size via configure page
+
+## v0.21.9 (2024-08-28)
+
+### Fix
+
+- render custom attrs in form component tag
+
+## v0.21.8 (2024-08-28)
+
+### Fix
+
+- ignore session kwarg for `MasterView.make_row_grid()`
+
## v0.21.7 (2024-08-28)
### Fix
diff --git a/README.rst b/README.md
similarity index 56%
rename from README.rst
rename to README.md
index 0cffc62d..74c007f6 100644
--- a/README.rst
+++ b/README.md
@@ -1,10 +1,8 @@
-Tailbone
-========
+# Tailbone
Tailbone is an extensible web application based on Rattail. It provides a
"back-office network environment" (BONE) for use in managing retail data.
-Please see Rattail's `home page`_ for more information.
-
-.. _home page: http://rattailproject.org/
+Please see Rattail's [home page](http://rattailproject.org/) for more
+information.
diff --git a/docs/conf.py b/docs/conf.py
index 52e384f5..ade4c92a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -27,10 +27,10 @@ templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
intersphinx_mapping = {
- 'rattail': ('https://rattailproject.org/docs/rattail/', None),
+ 'rattail': ('https://docs.wuttaproject.org/rattail/', None),
'webhelpers2': ('https://webhelpers2.readthedocs.io/en/latest/', None),
- 'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None),
- 'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None),
+ 'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None),
+ 'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None),
}
# allow todo entries to show up
diff --git a/pyproject.toml b/pyproject.toml
index 45a2adc9..a7214a8e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,9 +6,9 @@ build-backend = "hatchling.build"
[project]
name = "Tailbone"
-version = "0.21.7"
+version = "0.22.7"
description = "Backoffice Web Application for Rattail"
-readme = "README.rst"
+readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
license = {text = "GNU GPL v3+"}
classifiers = [
@@ -53,13 +53,13 @@ dependencies = [
"pyramid_mako",
"pyramid_retry",
"pyramid_tm",
- "rattail[db,bouncer]>=0.18.5",
+ "rattail[db,bouncer]>=0.20.1",
"sa-filters",
"simplejson",
"transaction",
"waitress",
"WebHelpers2",
- "WuttaWeb>=0.14.0",
+ "WuttaWeb>=0.21.0",
"zope.sqlalchemy>=1.5",
]
@@ -84,9 +84,9 @@ tailbone = "tailbone.config:ConfigExtension"
[project.urls]
Homepage = "https://rattailproject.org"
-Repository = "https://kallithea.rattailproject.org/rattail-project/tailbone"
-Issues = "https://redmine.rattailproject.org/projects/tailbone/issues"
-Changelog = "https://kallithea.rattailproject.org/rattail-project/tailbone/files/master/CHANGELOG.md"
+Repository = "https://forgejo.wuttaproject.org/rattail/tailbone"
+Issues = "https://forgejo.wuttaproject.org/rattail/tailbone/issues"
+Changelog = "https://forgejo.wuttaproject.org/rattail/tailbone/src/branch/master/CHANGELOG.md"
[tool.commitizen]
diff --git a/tailbone/api/batch/receiving.py b/tailbone/api/batch/receiving.py
index daa4290f..b23bff55 100644
--- a/tailbone/api/batch/receiving.py
+++ b/tailbone/api/batch/receiving.py
@@ -29,8 +29,7 @@ import logging
import humanize
import sqlalchemy as sa
-from rattail.db import model
-from rattail.util import pretty_quantity
+from rattail.db.model import PurchaseBatch, PurchaseBatchRow
from cornice import Service
from deform import widget as dfwidget
@@ -45,7 +44,7 @@ log = logging.getLogger(__name__)
class ReceivingBatchViews(APIBatchView):
- model_class = model.PurchaseBatch
+ model_class = PurchaseBatch
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'receivingbatchviews'
permission_prefix = 'receiving'
@@ -55,7 +54,8 @@ class ReceivingBatchViews(APIBatchView):
supports_execute = True
def base_query(self):
- query = super(ReceivingBatchViews, self).base_query()
+ model = self.app.model
+ query = super().base_query()
query = query.filter(model.PurchaseBatch.mode == self.enum.PURCHASE_BATCH_MODE_RECEIVING)
return query
@@ -85,7 +85,7 @@ class ReceivingBatchViews(APIBatchView):
# assume "receive from PO" if given a PO key
if data.get('purchase_key'):
- data['receiving_workflow'] = 'from_po'
+ data['workflow'] = 'from_po'
return super().create_object(data)
@@ -120,6 +120,7 @@ class ReceivingBatchViews(APIBatchView):
return self._get(obj=batch)
def eligible_purchases(self):
+ model = self.app.model
uuid = self.request.params.get('vendor_uuid')
vendor = self.Session.get(model.Vendor, uuid) if uuid else None
if not vendor:
@@ -176,7 +177,7 @@ class ReceivingBatchViews(APIBatchView):
class ReceivingBatchRowViews(APIBatchRowView):
- model_class = model.PurchaseBatchRow
+ model_class = PurchaseBatchRow
default_handler_spec = 'rattail.batch.purchase:PurchaseBatchHandler'
route_prefix = 'receiving.rows'
permission_prefix = 'receiving'
@@ -185,7 +186,8 @@ class ReceivingBatchRowViews(APIBatchRowView):
supports_quick_entry = True
def make_filter_spec(self):
- filters = super(ReceivingBatchRowViews, self).make_filter_spec()
+ model = self.app.model
+ filters = super().make_filter_spec()
if filters:
# must translate certain convenience filters
@@ -296,11 +298,11 @@ class ReceivingBatchRowViews(APIBatchRowView):
return filters
def normalize(self, row):
- data = super(ReceivingBatchRowViews, self).normalize(row)
+ data = super().normalize(row)
+ model = self.app.model
batch = row.batch
- app = self.get_rattail_app()
- prodder = app.get_products_handler()
+ prodder = self.app.get_products_handler()
data['product_uuid'] = row.product_uuid
data['item_id'] = row.item_id
@@ -375,7 +377,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
if accounted_for:
# some product accounted for; button should receive "remainder" only
if remainder:
- remainder = pretty_quantity(remainder)
+ remainder = self.app.render_quantity(remainder)
data['quick_receive_quantity'] = remainder
data['quick_receive_text'] = "Receive Remainder ({} {})".format(
remainder, data['unit_uom'])
@@ -386,7 +388,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
else: # nothing yet accounted for, button should receive "all"
if not remainder:
log.warning("quick receive remainder is empty for row %s", row.uuid)
- remainder = pretty_quantity(remainder)
+ remainder = self.app.render_quantity(remainder)
data['quick_receive_quantity'] = remainder
data['quick_receive_text'] = "Receive ALL ({} {})".format(
remainder, data['unit_uom'])
@@ -414,7 +416,7 @@ class ReceivingBatchRowViews(APIBatchRowView):
data['received_alert'] = None
if self.batch_handler.get_units_confirmed(row):
msg = "You have already received some of this product; last update was {}.".format(
- humanize.naturaltime(app.make_utc() - row.modified))
+ humanize.naturaltime(self.app.make_utc() - row.modified))
data['received_alert'] = msg
return data
@@ -423,6 +425,8 @@ class ReceivingBatchRowViews(APIBatchRowView):
"""
View which handles "receiving" against a particular batch row.
"""
+ model = self.app.model
+
# first do basic input validation
schema = ReceiveRow().bind(session=self.Session())
form = forms.Form(schema=schema, request=self.request)
diff --git a/tailbone/api/master.py b/tailbone/api/master.py
index 2d17339e..551d6428 100644
--- a/tailbone/api/master.py
+++ b/tailbone/api/master.py
@@ -26,7 +26,6 @@ Tailbone Web API - Master View
import json
-from rattail.config import parse_bool
from rattail.db.util import get_fieldnames
from cornice import resource, Service
@@ -185,7 +184,7 @@ class APIMasterView(APIView):
if sortcol:
spec = {
'field': sortcol.field_name,
- 'direction': 'asc' if parse_bool(self.request.params['ascending']) else 'desc',
+ 'direction': 'asc' if self.config.parse_bool(self.request.params['ascending']) else 'desc',
}
if sortcol.model_name:
spec['model'] = sortcol.model_name
diff --git a/tailbone/app.py b/tailbone/app.py
index b7262866..d2d0c5ef 100644
--- a/tailbone/app.py
+++ b/tailbone/app.py
@@ -62,6 +62,17 @@ def make_rattail_config(settings):
# nb. this is for compaibility with wuttaweb
settings['wutta_config'] = rattail_config
+ # must import all sqlalchemy models before things get rolling,
+ # otherwise can have errors about continuum TransactionMeta class
+ # not yet mapped, when relevant pages are first requested...
+ # cf. https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models
+ # hat tip to https://stackoverflow.com/a/59241485
+ if getattr(rattail_config, 'tempmon_engine', None):
+ from rattail_tempmon.db import model as tempmon_model, Session as TempmonSession
+ tempmon_session = TempmonSession()
+ tempmon_session.query(tempmon_model.Appliance).first()
+ tempmon_session.close()
+
# configure database sessions
if hasattr(rattail_config, 'appdb_engine'):
tailbone.db.Session.configure(bind=rattail_config.appdb_engine)
diff --git a/tailbone/diffs.py b/tailbone/diffs.py
index 98253c57..2e582b15 100644
--- a/tailbone/diffs.py
+++ b/tailbone/diffs.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
+# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@@ -270,9 +270,21 @@ class VersionDiff(Diff):
for field in self.fields:
values[field] = {'before': self.render_old_value(field),
'after': self.render_new_value(field)}
+
+ operation = None
+ if self.version.operation_type == continuum.Operation.INSERT:
+ operation = 'INSERT'
+ elif self.version.operation_type == continuum.Operation.UPDATE:
+ operation = 'UPDATE'
+ elif self.version.operation_type == continuum.Operation.DELETE:
+ operation = 'DELETE'
+ else:
+ operation = self.version.operation_type
+
return {
'key': id(self.version),
'model_title': self.title,
+ 'operation': operation,
'diff_class': self.nature,
'fields': self.fields,
'values': values,
diff --git a/tailbone/forms/core.py b/tailbone/forms/core.py
index b5020975..4024557b 100644
--- a/tailbone/forms/core.py
+++ b/tailbone/forms/core.py
@@ -401,6 +401,8 @@ class Form(object):
self.edit_help_url = edit_help_url
self.route_prefix = route_prefix
+ self.button_icon_submit = kwargs.get('button_icon_submit', 'save')
+
def __iter__(self):
return iter(self.fields)
@@ -1037,9 +1039,9 @@ class Form(object):
def render_vue_tag(self, **kwargs):
""" """
- return self.render_vuejs_component()
+ return self.render_vuejs_component(**kwargs)
- def render_vuejs_component(self):
+ def render_vuejs_component(self, **kwargs):
"""
Render the Vue.js component HTML for the form.
@@ -1050,10 +1052,11 @@ class Form(object):