diff --git a/docs/api/sideshow.app.rst b/docs/api/sideshow.app.rst
new file mode 100644
index 0000000..7c738b1
--- /dev/null
+++ b/docs/api/sideshow.app.rst
@@ -0,0 +1,6 @@
+
+``sideshow.app``
+================
+
+.. automodule:: sideshow.app
+ :members:
diff --git a/docs/index.rst b/docs/index.rst
index 643578b..d91ae5e 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -30,6 +30,7 @@ For an online demo see https://demo.wuttaproject.org/
:caption: Package API:
api/sideshow
+ api/sideshow.app
api/sideshow.batch
api/sideshow.batch.neworder
api/sideshow.cli
diff --git a/pyproject.toml b/pyproject.toml
index e1d42a8..ff4a4bc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -51,6 +51,9 @@ sideshow_libcache = "sideshow.web.static:libcache"
[project.entry-points."paste.app_factory"]
"main" = "sideshow.web.app:main"
+[project.entry-points."wutta.app.providers"]
+sideshow = "sideshow.app:SideshowAppProvider"
+
[project.entry-points."wutta.batch.neworder"]
"sideshow" = "sideshow.batch.neworder:NewOrderBatchHandler"
diff --git a/src/sideshow/app.py b/src/sideshow/app.py
new file mode 100644
index 0000000..0fbcf2e
--- /dev/null
+++ b/src/sideshow/app.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Sideshow -- Case/Special Order Tracker
+# Copyright © 2024 Lance Edgar
+#
+# This file is part of Sideshow.
+#
+# Sideshow is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Sideshow is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Sideshow. If not, see .
+#
+################################################################################
+"""
+Sideshow app provider
+"""
+
+from wuttjamaican import app as base
+
+
+class SideshowAppProvider(base.AppProvider):
+ """
+ The :term:`app provider` for Sideshow.
+
+ This adds the :meth:`get_order_handler()` method to the :term:`app
+ handler`.
+ """
+
+ def get_order_handler(self, **kwargs):
+ """
+ Get the configured :term:`order handler` for the app.
+
+ You can specify a custom handler in your :term:`config file`
+ like:
+
+ .. code-block:: ini
+
+ [sideshow]
+ orders.handler_spec = poser.orders:PoserOrderHandler
+
+ :returns: Instance of :class:`~sideshow.orders.OrderHandler`.
+ """
+ if 'order_handler' not in self.__dict__:
+ spec = self.config.get('sideshow.orders.handler_spec',
+ default='sideshow.orders:OrderHandler')
+ self.order_handler = self.app.load_object(spec)(self.config)
+ return self.order_handler
diff --git a/src/sideshow/batch/neworder.py b/src/sideshow/batch/neworder.py
index bfa04ea..e328501 100644
--- a/src/sideshow/batch/neworder.py
+++ b/src/sideshow/batch/neworder.py
@@ -50,6 +50,14 @@ class NewOrderBatchHandler(BatchHandler):
"""
model_class = NewOrderBatch
+ def get_default_store_id(self):
+ """
+ Returns the configured default value for
+ :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`,
+ or ``None``.
+ """
+ return self.config.get('sideshow.orders.default_store_id')
+
def use_local_customers(self):
"""
Returns boolean indicating whether :term:`local customer`
@@ -165,6 +173,18 @@ class NewOrderBatchHandler(BatchHandler):
'label': customer.full_name}
return [result(c) for c in customers]
+ def init_batch(self, batch, session=None, progress=None, **kwargs):
+ """
+ Initialize a new batch.
+
+ This sets the
+ :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`,
+ if the batch does not yet have one and a default is
+ configured.
+ """
+ if not batch.store_id:
+ batch.store_id = self.get_default_store_id()
+
def set_customer(self, batch, customer_info, user=None):
"""
Set/update customer info for the batch.
diff --git a/src/sideshow/db/model/orders.py b/src/sideshow/db/model/orders.py
index 2cadeaa..5455d01 100644
--- a/src/sideshow/db/model/orders.py
+++ b/src/sideshow/db/model/orders.py
@@ -62,6 +62,15 @@ class Order(model.Base):
ID of the store to which the order pertains, if applicable.
""")
+ store = orm.relationship(
+ 'Store',
+ primaryjoin='Store.store_id == Order.store_id',
+ foreign_keys='Order.store_id',
+ doc="""
+ Reference to the :class:`~sideshow.db.model.stores.Store`
+ record, if applicable.
+ """)
+
customer_id = sa.Column(sa.String(length=20), nullable=True, doc="""
Proper account ID for the :term:`external customer` to which the
order pertains, if applicable.
diff --git a/src/sideshow/db/model/stores.py b/src/sideshow/db/model/stores.py
index b1956c1..4b01d02 100644
--- a/src/sideshow/db/model/stores.py
+++ b/src/sideshow/db/model/stores.py
@@ -51,4 +51,12 @@ class Store(model.Base):
""")
def __str__(self):
- return self.name or ""
+ return self.get_display()
+
+ def get_display(self):
+ """
+ Returns the display string for the store, e.g. "001 Acme Goods".
+ """
+ return ' '.join([(self.store_id or '').strip(),
+ (self.name or '').strip()])\
+ .strip()
diff --git a/src/sideshow/orders.py b/src/sideshow/orders.py
index 9f99e53..868cada 100644
--- a/src/sideshow/orders.py
+++ b/src/sideshow/orders.py
@@ -37,6 +37,14 @@ class OrderHandler(GenericHandler):
handler is responsible for creation logic.)
"""
+ def expose_store_id(self):
+ """
+ Returns boolean indicating whether the ``store_id`` field
+ should be exposed at all. This is false by default.
+ """
+ return self.config.get_bool('sideshow.orders.expose_store_id',
+ default=False)
+
def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False):
"""
Return the display text for a given order quantity.
diff --git a/src/sideshow/web/templates/order-items/view.mako b/src/sideshow/web/templates/order-items/view.mako
index 3cd210a..21cb8b2 100644
--- a/src/sideshow/web/templates/order-items/view.mako
+++ b/src/sideshow/web/templates/order-items/view.mako
@@ -29,6 +29,17 @@
${h.link_to(f"Order ID {order.order_id}", url('orders.view', uuid=order.uuid))} — Item #${item.sequence}
+ % if expose_store_id:
+
+
+ % if order.store:
+ ${h.link_to(order.store.get_display(), url('stores.view', uuid=order.store.uuid))}
+ % elif order.store_id:
+ ${order.store_id}
+ % endif
+
+
+ % endif
${order_qty_uom_text|n}
diff --git a/src/sideshow/web/templates/orders/configure.mako b/src/sideshow/web/templates/orders/configure.mako
index e247b2f..6af30b1 100644
--- a/src/sideshow/web/templates/orders/configure.mako
+++ b/src/sideshow/web/templates/orders/configure.mako
@@ -3,6 +3,28 @@
<%def name="form_content()">
+
Stores
+
+
+
+
+ Show/choose the Store ID for each order
+
+
+
+
+
+
+
+
+
Customers
@@ -14,6 +36,7 @@
+
Products
diff --git a/src/sideshow/web/templates/orders/create.mako b/src/sideshow/web/templates/orders/create.mako
index 2ac6f0a..7ec015a 100644
--- a/src/sideshow/web/templates/orders/create.mako
+++ b/src/sideshow/web/templates/orders/create.mako
@@ -42,7 +42,25 @@