diff --git a/src/wuttaweb/forms/schema.py b/src/wuttaweb/forms/schema.py
index a7f5de2..0dabf64 100644
--- a/src/wuttaweb/forms/schema.py
+++ b/src/wuttaweb/forms/schema.py
@@ -177,6 +177,27 @@ class WuttaMoney(colander.Money):
return widgets.WuttaMoneyInputWidget(self.request, **kwargs)
+class WuttaQuantity(colander.Decimal):
+ """
+ Custom schema type for "quantity" fields.
+
+ This is a subclass of :class:`colander:colander.Decimal` but uses
+ :class:`~wuttaweb.forms.widgets.WuttaQuantityWidget` by default.
+
+ :param request: Current :term:`request` object.
+ """
+
+ def __init__(self, request, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.request = request
+ self.config = self.request.wutta_config
+ self.app = self.config.get_app()
+
+ def widget_maker(self, **kwargs):
+ """ """
+ return widgets.WuttaQuantityWidget(self.request, **kwargs)
+
+
class WuttaSet(colander.Set):
"""
Custom schema type for :class:`python:set` fields.
diff --git a/src/wuttaweb/forms/widgets.py b/src/wuttaweb/forms/widgets.py
index 7b3e4be..a6f33d2 100644
--- a/src/wuttaweb/forms/widgets.py
+++ b/src/wuttaweb/forms/widgets.py
@@ -226,6 +226,42 @@ class WuttaMoneyInputWidget(MoneyInputWidget):
return super().serialize(field, cstruct, **kw)
+class WuttaQuantityWidget(TextInputWidget):
+ """
+ Custom widget for "quantity" fields. This is used by default for
+ :class:`~wuttaweb.forms.schema.WuttaQuantity` type nodes.
+
+ The main purpose of this widget is to leverage
+ :meth:`~wuttjamaican:wuttjamaican.app.AppHandler.render_quantity()`
+ for the readonly display.
+
+ This is a subclass of
+ :class:`deform:deform.widget.TextInputWidget` and uses these
+ Deform templates:
+
+ * ``textinput``
+
+ :param request: Current :term:`request` object.
+ """
+
+ def __init__(self, request, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.request = request
+ self.config = self.request.wutta_config
+ self.app = self.config.get_app()
+
+ def serialize(self, field, cstruct, **kw):
+ """ """
+ readonly = kw.get('readonly', self.readonly)
+ if readonly:
+ if cstruct in (colander.null, None):
+ return HTML.tag('span')
+ cstruct = decimal.Decimal(cstruct)
+ return HTML.tag('span', c=[self.app.render_quantity(cstruct)])
+
+ return super().serialize(field, cstruct, **kw)
+
+
class FileDownloadWidget(Widget):
"""
Widget for use with :class:`~wuttaweb.forms.schema.FileDownload`
diff --git a/tests/forms/test_schema.py b/tests/forms/test_schema.py
index 117291d..4dfc962 100644
--- a/tests/forms/test_schema.py
+++ b/tests/forms/test_schema.py
@@ -89,6 +89,15 @@ class TestWuttaMoney(WebTestCase):
self.assertIsInstance(widget, widgets.WuttaMoneyInputWidget)
+class TestWuttaQuantity(WebTestCase):
+
+ def test_widget_maker(self):
+ enum = self.app.enum
+ typ = mod.WuttaQuantity(self.request)
+ widget = typ.widget_maker()
+ self.assertIsInstance(widget, widgets.WuttaQuantityWidget)
+
+
class TestObjectRef(DataTestCase):
def setUp(self):
diff --git a/tests/forms/test_widgets.py b/tests/forms/test_widgets.py
index 3e07902..7752dfe 100644
--- a/tests/forms/test_widgets.py
+++ b/tests/forms/test_widgets.py
@@ -125,7 +125,7 @@ class TestWuttaMoneyInputWidget(WebTestCase):
return mod.WuttaMoneyInputWidget(self.request, **kwargs)
def test_serialize(self):
- node = colander.SchemaNode(WuttaDateTime())
+ node = colander.SchemaNode(schema.WuttaMoney(self.request))
field = self.make_field(node)
widget = self.make_widget()
amount = decimal.Decimal('12.34')
@@ -143,6 +143,36 @@ class TestWuttaMoneyInputWidget(WebTestCase):
self.assertEqual(result, '')
+class TestWuttaQuantityWidget(WebTestCase):
+
+ def make_field(self, node, **kwargs):
+ # TODO: not sure why default renderer is in use even though
+ # pyramid_deform was included in setup? but this works..
+ kwargs.setdefault('renderer', deform.Form.default_renderer)
+ return deform.Field(node, **kwargs)
+
+ def make_widget(self, **kwargs):
+ return mod.WuttaQuantityWidget(self.request, **kwargs)
+
+ def test_serialize(self):
+ node = colander.SchemaNode(schema.WuttaQuantity(self.request))
+ field = self.make_field(node)
+ widget = self.make_widget()
+ amount = decimal.Decimal('42.00')
+
+ # editable widget has normal text input
+ result = widget.serialize(field, str(amount))
+ self.assertIn('42')
+
+ # readonly w/ null value
+ result = widget.serialize(field, None, readonly=True)
+ self.assertEqual(result, '')
+
+
class TestFileDownloadWidget(WebTestCase):
def make_field(self, node, **kwargs):