fix: consolidate some duplicated code
i feel like there was a reason i hadn't done that to begin with, but now can't recall for sure. pylint certainly thinks it is duplicated so mostly cleaning this up per its suggestion...
This commit is contained in:
		
							parent
							
								
									49261a696d
								
							
						
					
					
						commit
						4e7c8d393c
					
				
					 8 changed files with 402 additions and 738 deletions
				
			
		
							
								
								
									
										6
									
								
								docs/api/sideshow.web.util.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/api/sideshow.web.util.rst
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``sideshow.web.util``
 | 
				
			||||||
 | 
					=====================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. automodule:: sideshow.web.util
 | 
				
			||||||
 | 
					   :members:
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,7 @@ For an online demo see https://demo.wuttaproject.org/
 | 
				
			||||||
   api/sideshow.web.forms.schema
 | 
					   api/sideshow.web.forms.schema
 | 
				
			||||||
   api/sideshow.web.menus
 | 
					   api/sideshow.web.menus
 | 
				
			||||||
   api/sideshow.web.static
 | 
					   api/sideshow.web.static
 | 
				
			||||||
 | 
					   api/sideshow.web.util
 | 
				
			||||||
   api/sideshow.web.views
 | 
					   api/sideshow.web.views
 | 
				
			||||||
   api/sideshow.web.views.batch
 | 
					   api/sideshow.web.views.batch
 | 
				
			||||||
   api/sideshow.web.views.batch.neworder
 | 
					   api/sideshow.web.views.batch.neworder
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,8 +33,10 @@ from sqlalchemy.ext.declarative import declared_attr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from wuttjamaican.db import model
 | 
					from wuttjamaican.db import model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from sideshow.db.model.orders import OrderMixin, OrderItemMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NewOrderBatch(model.BatchMixin, model.Base):
 | 
					
 | 
				
			||||||
 | 
					class NewOrderBatch(model.BatchMixin, OrderMixin, model.Base):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    :term:`Batch <batch>` used for entering new :term:`orders <order>`
 | 
					    :term:`Batch <batch>` used for entering new :term:`orders <order>`
 | 
				
			||||||
    into the system.  Each batch ultimately becomes an
 | 
					    into the system.  Each batch ultimately becomes an
 | 
				
			||||||
| 
						 | 
					@ -73,25 +75,6 @@ class NewOrderBatch(model.BatchMixin, model.Base):
 | 
				
			||||||
        STATUS_OK: "ok",
 | 
					        STATUS_OK: "ok",
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    store_id = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=10),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    ID of the store to which the order pertains, 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See also :attr:`local_customer` and :attr:`pending_customer`.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local_customer_uuid = sa.Column(model.UUID(), nullable=True)
 | 
					    local_customer_uuid = sa.Column(model.UUID(), nullable=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @declared_attr
 | 
					    @declared_attr
 | 
				
			||||||
| 
						 | 
					@ -100,6 +83,7 @@ class NewOrderBatch(model.BatchMixin, model.Base):
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        return orm.relationship(
 | 
					        return orm.relationship(
 | 
				
			||||||
            "LocalCustomer",
 | 
					            "LocalCustomer",
 | 
				
			||||||
 | 
					            cascade_backrefs=False,
 | 
				
			||||||
            back_populates="new_order_batches",
 | 
					            back_populates="new_order_batches",
 | 
				
			||||||
            doc="""
 | 
					            doc="""
 | 
				
			||||||
            Reference to the
 | 
					            Reference to the
 | 
				
			||||||
| 
						 | 
					@ -118,6 +102,7 @@ class NewOrderBatch(model.BatchMixin, model.Base):
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        return orm.relationship(
 | 
					        return orm.relationship(
 | 
				
			||||||
            "PendingCustomer",
 | 
					            "PendingCustomer",
 | 
				
			||||||
 | 
					            cascade_backrefs=False,
 | 
				
			||||||
            back_populates="new_order_batches",
 | 
					            back_populates="new_order_batches",
 | 
				
			||||||
            doc="""
 | 
					            doc="""
 | 
				
			||||||
            Reference to the
 | 
					            Reference to the
 | 
				
			||||||
| 
						 | 
					@ -128,40 +113,8 @@ class NewOrderBatch(model.BatchMixin, model.Base):
 | 
				
			||||||
            """,
 | 
					            """,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    customer_name = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=100),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Name for the customer account.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    phone_number = sa.Column(
 | 
					class NewOrderBatchRow(model.BatchRowMixin, OrderItemMixin, model.Base):
 | 
				
			||||||
        sa.String(length=20),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Phone number for the customer.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    email_address = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=255),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Email address for the customer.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    total_price = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=10, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Full price (not including tax etc.) for all items on the order.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NewOrderBatchRow(model.BatchRowMixin, model.Base):
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Row of data within a :class:`NewOrderBatch`.  Each row ultimately
 | 
					    Row of data within a :class:`NewOrderBatch`.  Each row ultimately
 | 
				
			||||||
    becomes an :class:`~sideshow.db.model.orders.OrderItem`.
 | 
					    becomes an :class:`~sideshow.db.model.orders.OrderItem`.
 | 
				
			||||||
| 
						 | 
					@ -212,17 +165,6 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
 | 
				
			||||||
    Dict of possible status code -> label options.
 | 
					    Dict of possible status code -> label options.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    product_id = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=20),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Proper ID for the :term:`external product` which the order item
 | 
					 | 
				
			||||||
    represents, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See also :attr:`local_product` and :attr:`pending_product`.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local_product_uuid = sa.Column(model.UUID(), nullable=True)
 | 
					    local_product_uuid = sa.Column(model.UUID(), nullable=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @declared_attr
 | 
					    @declared_attr
 | 
				
			||||||
| 
						 | 
					@ -231,6 +173,7 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        return orm.relationship(
 | 
					        return orm.relationship(
 | 
				
			||||||
            "LocalProduct",
 | 
					            "LocalProduct",
 | 
				
			||||||
 | 
					            cascade_backrefs=False,
 | 
				
			||||||
            back_populates="new_order_batch_rows",
 | 
					            back_populates="new_order_batch_rows",
 | 
				
			||||||
            doc="""
 | 
					            doc="""
 | 
				
			||||||
            Reference to the
 | 
					            Reference to the
 | 
				
			||||||
| 
						 | 
					@ -249,6 +192,7 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        return orm.relationship(
 | 
					        return orm.relationship(
 | 
				
			||||||
            "PendingProduct",
 | 
					            "PendingProduct",
 | 
				
			||||||
 | 
					            cascade_backrefs=False,
 | 
				
			||||||
            back_populates="new_order_batch_rows",
 | 
					            back_populates="new_order_batch_rows",
 | 
				
			||||||
            doc="""
 | 
					            doc="""
 | 
				
			||||||
            Reference to the
 | 
					            Reference to the
 | 
				
			||||||
| 
						 | 
					@ -259,224 +203,5 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base):
 | 
				
			||||||
            """,
 | 
					            """,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    product_scancode = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=14),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Scancode for the product, as string.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       This column allows 14 chars, so can store a full GPC with check
 | 
					 | 
				
			||||||
       digit.  However as of writing the actual format used here does
 | 
					 | 
				
			||||||
       not matter to Sideshow logic; "anything" should work.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       That may change eventually, depending on POS integration
 | 
					 | 
				
			||||||
       scenarios that come up.  Maybe a config option to declare
 | 
					 | 
				
			||||||
       whether check digit should be included or not, etc.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_brand = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=100),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Brand name for the product - up to 100 chars.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_description = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=255),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Description for the product - up to 255 chars.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_size = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=30),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Size of the product, as string - up to 30 chars.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_weighed = sa.Column(
 | 
					 | 
				
			||||||
        sa.Boolean(),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Flag indicating the product is sold by weight; default is null.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    department_id = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=10),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    ID of the department to which the product belongs, if known.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    department_name = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=30),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Name of the department to which the product belongs, if known.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    special_order = sa.Column(
 | 
					 | 
				
			||||||
        sa.Boolean(),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Flag indicating the item is a "special order" - e.g. something not
 | 
					 | 
				
			||||||
    normally carried by the store.  Default is null.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    vendor_name = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=50),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Name of vendor from which product may be purchased, if known.  See
 | 
					 | 
				
			||||||
    also :attr:`vendor_item_code`.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    vendor_item_code = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=20),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Item code (SKU) to use when ordering this product from the vendor
 | 
					 | 
				
			||||||
    identified by :attr:`vendor_name`, if known.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    case_size = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=10, scale=4),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Case pack count for the product, if known.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If this is not set, then customer cannot order a "case" of the item.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    order_qty = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=10, scale=4),
 | 
					 | 
				
			||||||
        nullable=False,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Quantity (as decimal) of product being ordered.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This must be interpreted along with :attr:`order_uom` to determine
 | 
					 | 
				
			||||||
    the *complete* order quantity, e.g. "2 cases".
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    order_uom = sa.Column(
 | 
					 | 
				
			||||||
        sa.String(length=10),
 | 
					 | 
				
			||||||
        nullable=False,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Code indicating the unit of measure for product being ordered.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This should be one of the codes from
 | 
					 | 
				
			||||||
    :data:`~sideshow.enum.ORDER_UOM`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Sideshow will treat :data:`~sideshow.enum.ORDER_UOM_CASE`
 | 
					 | 
				
			||||||
    differently but :data:`~sideshow.enum.ORDER_UOM_UNIT` and others
 | 
					 | 
				
			||||||
    are all treated the same (i.e. "unit" is assumed).
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unit_cost = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=9, scale=5),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Cost of goods amount for one "unit" (not "case") of the product,
 | 
					 | 
				
			||||||
    as decimal to 4 places.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unit_price_reg = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=8, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Regular price for the item unit.  Unless a sale is in effect,
 | 
					 | 
				
			||||||
    :attr:`unit_price_quoted` will typically match this value.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unit_price_sale = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=8, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Sale price for the item unit, if applicable.  If set, then
 | 
					 | 
				
			||||||
    :attr:`unit_price_quoted` will typically match this value.  See
 | 
					 | 
				
			||||||
    also :attr:`sale_ends`.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sale_ends = sa.Column(
 | 
					 | 
				
			||||||
        sa.DateTime(timezone=True),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    End date/time for the sale in effect, if any.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is only relevant if :attr:`unit_price_sale` is set.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unit_price_quoted = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=8, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Quoted price for the item unit.  This is the "effective" unit
 | 
					 | 
				
			||||||
    price, which is used to calculate :attr:`total_price`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This price does *not* reflect the :attr:`discount_percent`.  It
 | 
					 | 
				
			||||||
    normally should match either :attr:`unit_price_reg` or
 | 
					 | 
				
			||||||
    :attr:`unit_price_sale`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See also :attr:`case_price_quoted`, if applicable.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    case_price_quoted = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=8, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Quoted price for a "case" of the item, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is mostly for display purposes; :attr:`unit_price_quoted` is
 | 
					 | 
				
			||||||
    used for calculations.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    discount_percent = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=5, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Discount percent to apply when calculating :attr:`total_price`, if
 | 
					 | 
				
			||||||
    applicable.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    total_price = sa.Column(
 | 
					 | 
				
			||||||
        sa.Numeric(precision=8, scale=3),
 | 
					 | 
				
			||||||
        nullable=True,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Full price (not including tax etc.) which the customer is quoted
 | 
					 | 
				
			||||||
    for the order item.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is calculated using values from:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    * :attr:`unit_price_quoted`
 | 
					 | 
				
			||||||
    * :attr:`order_qty`
 | 
					 | 
				
			||||||
    * :attr:`order_uom`
 | 
					 | 
				
			||||||
    * :attr:`case_size`
 | 
					 | 
				
			||||||
    * :attr:`discount_percent`
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return str(self.pending_product or self.product_description or "")
 | 
					        return str(self.pending_product or self.product_description or "")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,36 +33,12 @@ from sqlalchemy.ext.orderinglist import ordering_list
 | 
				
			||||||
from wuttjamaican.db import model
 | 
					from wuttjamaican.db import model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Order(model.Base):  # pylint: disable=too-few-public-methods
 | 
					class OrderMixin:  # pylint: disable=too-few-public-methods
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Represents an :term:`order` for a customer.  Each order has one or
 | 
					    Mixin class providing common columns for orders and new order
 | 
				
			||||||
    more :attr:`items`.
 | 
					    batches.
 | 
				
			||||||
 | 
					 | 
				
			||||||
    Usually, orders are created by way of a
 | 
					 | 
				
			||||||
    :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __tablename__ = "sideshow_order"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # TODO: this feels a bit hacky yet but it does avoid problems
 | 
					 | 
				
			||||||
    # showing the Orders grid for a PendingCustomer
 | 
					 | 
				
			||||||
    __colanderalchemy_config__ = {
 | 
					 | 
				
			||||||
        "excludes": ["items"],
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uuid = model.uuid_column()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    order_id = sa.Column(
 | 
					 | 
				
			||||||
        sa.Integer(),
 | 
					 | 
				
			||||||
        nullable=False,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Unique ID for the order.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    When the order is created from New Order Batch, this order ID will
 | 
					 | 
				
			||||||
    match the batch ID.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    store_id = sa.Column(
 | 
					    store_id = sa.Column(
 | 
				
			||||||
        sa.String(length=10),
 | 
					        sa.String(length=10),
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
| 
						 | 
					@ -71,16 +47,6 @@ class Order(model.Base):  # pylint: disable=too-few-public-methods
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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(
 | 
					    customer_id = sa.Column(
 | 
				
			||||||
        sa.String(length=20),
 | 
					        sa.String(length=20),
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
| 
						 | 
					@ -92,38 +58,6 @@ class Order(model.Base):  # pylint: disable=too-few-public-methods
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local_customer_uuid = model.uuid_fk_column(
 | 
					 | 
				
			||||||
        "sideshow_customer_local.uuid", nullable=True
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    local_customer = orm.relationship(
 | 
					 | 
				
			||||||
        "LocalCustomer",
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="orders",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the
 | 
					 | 
				
			||||||
        :class:`~sideshow.db.model.customers.LocalCustomer` record
 | 
					 | 
				
			||||||
        for the order, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        See also :attr:`customer_id` and :attr:`pending_customer`.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pending_customer_uuid = model.uuid_fk_column(
 | 
					 | 
				
			||||||
        "sideshow_customer_pending.uuid", nullable=True
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    pending_customer = orm.relationship(
 | 
					 | 
				
			||||||
        "PendingCustomer",
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="orders",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the
 | 
					 | 
				
			||||||
        :class:`~sideshow.db.model.customers.PendingCustomer` record
 | 
					 | 
				
			||||||
        for the order, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        See also :attr:`customer_id` and :attr:`local_customer`.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    customer_name = sa.Column(
 | 
					    customer_name = sa.Column(
 | 
				
			||||||
        sa.String(length=100),
 | 
					        sa.String(length=100),
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
| 
						 | 
					@ -156,76 +90,13 @@ class Order(model.Base):  # pylint: disable=too-few-public-methods
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    created = sa.Column(
 | 
					 | 
				
			||||||
        sa.DateTime(timezone=True),
 | 
					 | 
				
			||||||
        nullable=False,
 | 
					 | 
				
			||||||
        default=datetime.datetime.now,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    Timestamp when the order was created.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    If the order is created via New Order Batch, this will match the
 | 
					class OrderItemMixin:  # pylint: disable=too-few-public-methods
 | 
				
			||||||
    batch execution timestamp.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False)
 | 
					 | 
				
			||||||
    created_by = orm.relationship(
 | 
					 | 
				
			||||||
        model.User,
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the
 | 
					 | 
				
			||||||
        :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
 | 
					 | 
				
			||||||
        created the order.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    items = orm.relationship(
 | 
					 | 
				
			||||||
        "OrderItem",
 | 
					 | 
				
			||||||
        collection_class=ordering_list("sequence", count_from=1),
 | 
					 | 
				
			||||||
        cascade="all, delete-orphan",
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="order",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        List of :class:`OrderItem` records belonging to the order.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __str__(self):
 | 
					 | 
				
			||||||
        return str(self.order_id)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OrderItem(model.Base):
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Represents an :term:`order item` within an :class:`Order`.
 | 
					    Mixin class providing common columns for order items and new order
 | 
				
			||||||
 | 
					    batch rows.
 | 
				
			||||||
    Usually these are created from
 | 
					 | 
				
			||||||
    :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
 | 
					 | 
				
			||||||
    records.
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __tablename__ = "sideshow_order_item"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    uuid = model.uuid_column()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    order_uuid = model.uuid_fk_column("sideshow_order.uuid", nullable=False)
 | 
					 | 
				
			||||||
    order = orm.relationship(
 | 
					 | 
				
			||||||
        Order,
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="items",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the :class:`Order` to which the item belongs.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sequence = sa.Column(
 | 
					 | 
				
			||||||
        sa.Integer(),
 | 
					 | 
				
			||||||
        nullable=False,
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
    1-based numeric sequence for the item, i.e. its line number within
 | 
					 | 
				
			||||||
    the order.
 | 
					 | 
				
			||||||
    """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_id = sa.Column(
 | 
					    product_id = sa.Column(
 | 
				
			||||||
        sa.String(length=20),
 | 
					        sa.String(length=20),
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
| 
						 | 
					@ -237,38 +108,6 @@ class OrderItem(model.Base):
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local_product_uuid = model.uuid_fk_column(
 | 
					 | 
				
			||||||
        "sideshow_product_local.uuid", nullable=True
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    local_product = orm.relationship(
 | 
					 | 
				
			||||||
        "LocalProduct",
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="order_items",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the
 | 
					 | 
				
			||||||
        :class:`~sideshow.db.model.products.LocalProduct` record for
 | 
					 | 
				
			||||||
        the order item, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        See also :attr:`product_id` and :attr:`pending_product`.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pending_product_uuid = model.uuid_fk_column(
 | 
					 | 
				
			||||||
        "sideshow_product_pending.uuid", nullable=True
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    pending_product = orm.relationship(
 | 
					 | 
				
			||||||
        "PendingProduct",
 | 
					 | 
				
			||||||
        cascade_backrefs=False,
 | 
					 | 
				
			||||||
        back_populates="order_items",
 | 
					 | 
				
			||||||
        doc="""
 | 
					 | 
				
			||||||
        Reference to the
 | 
					 | 
				
			||||||
        :class:`~sideshow.db.model.products.PendingProduct` record for
 | 
					 | 
				
			||||||
        the order item, if applicable.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        See also :attr:`product_id` and :attr:`local_product`.
 | 
					 | 
				
			||||||
        """,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    product_scancode = sa.Column(
 | 
					    product_scancode = sa.Column(
 | 
				
			||||||
        sa.String(length=14),
 | 
					        sa.String(length=14),
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
| 
						 | 
					@ -367,6 +206,8 @@ class OrderItem(model.Base):
 | 
				
			||||||
        nullable=True,
 | 
					        nullable=True,
 | 
				
			||||||
        doc="""
 | 
					        doc="""
 | 
				
			||||||
    Case pack count for the product, if known.
 | 
					    Case pack count for the product, if known.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If this is not set, then customer cannot order a "case" of the item.
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -389,6 +230,10 @@ class OrderItem(model.Base):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    This should be one of the codes from
 | 
					    This should be one of the codes from
 | 
				
			||||||
    :data:`~sideshow.enum.ORDER_UOM`.
 | 
					    :data:`~sideshow.enum.ORDER_UOM`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Sideshow will treat :data:`~sideshow.enum.ORDER_UOM_CASE`
 | 
				
			||||||
 | 
					    differently but :data:`~sideshow.enum.ORDER_UOM_UNIT` and others
 | 
				
			||||||
 | 
					    are all treated the same (i.e. "unit" is assumed).
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -440,6 +285,8 @@ class OrderItem(model.Base):
 | 
				
			||||||
    This price does *not* reflect the :attr:`discount_percent`.  It
 | 
					    This price does *not* reflect the :attr:`discount_percent`.  It
 | 
				
			||||||
    normally should match either :attr:`unit_price_reg` or
 | 
					    normally should match either :attr:`unit_price_reg` or
 | 
				
			||||||
    :attr:`unit_price_sale`.
 | 
					    :attr:`unit_price_sale`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    See also :attr:`case_price_quoted`, if applicable.
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -480,6 +327,181 @@ class OrderItem(model.Base):
 | 
				
			||||||
    """,
 | 
					    """,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Order(OrderMixin, model.Base):  # pylint: disable=too-few-public-methods
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Represents an :term:`order` for a customer.  Each order has one or
 | 
				
			||||||
 | 
					    more :attr:`items`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Usually, orders are created by way of a
 | 
				
			||||||
 | 
					    :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __tablename__ = "sideshow_order"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO: this feels a bit hacky yet but it does avoid problems
 | 
				
			||||||
 | 
					    # showing the Orders grid for a PendingCustomer
 | 
				
			||||||
 | 
					    __colanderalchemy_config__ = {
 | 
				
			||||||
 | 
					        "excludes": ["items"],
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uuid = model.uuid_column()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    order_id = sa.Column(
 | 
				
			||||||
 | 
					        sa.Integer(),
 | 
				
			||||||
 | 
					        nullable=False,
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					    Unique ID for the order.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    When the order is created from New Order Batch, this order ID will
 | 
				
			||||||
 | 
					    match the batch ID.
 | 
				
			||||||
 | 
					    """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local_customer_uuid = model.uuid_fk_column(
 | 
				
			||||||
 | 
					        "sideshow_customer_local.uuid", nullable=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    local_customer = orm.relationship(
 | 
				
			||||||
 | 
					        "LocalCustomer",
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="orders",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the
 | 
				
			||||||
 | 
					        :class:`~sideshow.db.model.customers.LocalCustomer` record
 | 
				
			||||||
 | 
					        for the order, if applicable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        See also :attr:`customer_id` and :attr:`pending_customer`.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pending_customer_uuid = model.uuid_fk_column(
 | 
				
			||||||
 | 
					        "sideshow_customer_pending.uuid", nullable=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    pending_customer = orm.relationship(
 | 
				
			||||||
 | 
					        "PendingCustomer",
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="orders",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the
 | 
				
			||||||
 | 
					        :class:`~sideshow.db.model.customers.PendingCustomer` record
 | 
				
			||||||
 | 
					        for the order, if applicable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        See also :attr:`customer_id` and :attr:`local_customer`.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    created = sa.Column(
 | 
				
			||||||
 | 
					        sa.DateTime(timezone=True),
 | 
				
			||||||
 | 
					        nullable=False,
 | 
				
			||||||
 | 
					        default=datetime.datetime.now,
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					    Timestamp when the order was created.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the order is created via New Order Batch, this will match the
 | 
				
			||||||
 | 
					    batch execution timestamp.
 | 
				
			||||||
 | 
					    """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False)
 | 
				
			||||||
 | 
					    created_by = orm.relationship(
 | 
				
			||||||
 | 
					        model.User,
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the
 | 
				
			||||||
 | 
					        :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
 | 
				
			||||||
 | 
					        created the order.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    items = orm.relationship(
 | 
				
			||||||
 | 
					        "OrderItem",
 | 
				
			||||||
 | 
					        collection_class=ordering_list("sequence", count_from=1),
 | 
				
			||||||
 | 
					        cascade="all, delete-orphan",
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="order",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        List of :class:`OrderItem` records belonging to the order.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return str(self.order_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OrderItem(OrderItemMixin, model.Base):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Represents an :term:`order item` within an :class:`Order`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Usually these are created from
 | 
				
			||||||
 | 
					    :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
 | 
				
			||||||
 | 
					    records.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __tablename__ = "sideshow_order_item"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    uuid = model.uuid_column()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    order_uuid = model.uuid_fk_column("sideshow_order.uuid", nullable=False)
 | 
				
			||||||
 | 
					    order = orm.relationship(
 | 
				
			||||||
 | 
					        Order,
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="items",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the :class:`Order` to which the item belongs.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sequence = sa.Column(
 | 
				
			||||||
 | 
					        sa.Integer(),
 | 
				
			||||||
 | 
					        nullable=False,
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					    1-based numeric sequence for the item, i.e. its line number within
 | 
				
			||||||
 | 
					    the order.
 | 
				
			||||||
 | 
					    """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local_product_uuid = model.uuid_fk_column(
 | 
				
			||||||
 | 
					        "sideshow_product_local.uuid", nullable=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    local_product = orm.relationship(
 | 
				
			||||||
 | 
					        "LocalProduct",
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="order_items",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the
 | 
				
			||||||
 | 
					        :class:`~sideshow.db.model.products.LocalProduct` record for
 | 
				
			||||||
 | 
					        the order item, if applicable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        See also :attr:`product_id` and :attr:`pending_product`.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pending_product_uuid = model.uuid_fk_column(
 | 
				
			||||||
 | 
					        "sideshow_product_pending.uuid", nullable=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    pending_product = orm.relationship(
 | 
				
			||||||
 | 
					        "PendingProduct",
 | 
				
			||||||
 | 
					        cascade_backrefs=False,
 | 
				
			||||||
 | 
					        back_populates="order_items",
 | 
				
			||||||
 | 
					        doc="""
 | 
				
			||||||
 | 
					        Reference to the
 | 
				
			||||||
 | 
					        :class:`~sideshow.db.model.products.PendingProduct` record for
 | 
				
			||||||
 | 
					        the order item, if applicable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        See also :attr:`product_id` and :attr:`local_product`.
 | 
				
			||||||
 | 
					        """,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    status_code = sa.Column(
 | 
					    status_code = sa.Column(
 | 
				
			||||||
        sa.Integer(),
 | 
					        sa.Integer(),
 | 
				
			||||||
        nullable=False,
 | 
					        nullable=False,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										102
									
								
								src/sideshow/web/util.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/sideshow/web/util.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,102 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8; -*-
 | 
				
			||||||
 | 
					################################################################################
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  Sideshow -- Case/Special Order Tracker
 | 
				
			||||||
 | 
					#  Copyright © 2024-2025 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 <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					################################################################################
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Web Utility Functions
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_new_order_batches_grid(request, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Make and return the grid for the New Order Batches field.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = request.wutta_config
 | 
				
			||||||
 | 
					    app = config.get_app()
 | 
				
			||||||
 | 
					    model = app.model
 | 
				
			||||||
 | 
					    web = app.get_web_handler()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if "key" not in kwargs:
 | 
				
			||||||
 | 
					        route_prefix = kwargs.pop("route_prefix")
 | 
				
			||||||
 | 
					        kwargs["key"] = f"{route_prefix}.view.new_order_batches"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    kwargs.setdefault("model_class", model.NewOrderBatch)
 | 
				
			||||||
 | 
					    kwargs.setdefault(
 | 
				
			||||||
 | 
					        "columns",
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            "id",
 | 
				
			||||||
 | 
					            "total_price",
 | 
				
			||||||
 | 
					            "created",
 | 
				
			||||||
 | 
					            "created_by",
 | 
				
			||||||
 | 
					            "executed",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    kwargs.setdefault("labels", {"id": "Batch ID"})
 | 
				
			||||||
 | 
					    kwargs.setdefault("renderers", {"id": "batch_id", "total_price": "currency"})
 | 
				
			||||||
 | 
					    grid = web.make_grid(request, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if request.has_perm("neworder_batches.view"):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def view_url(batch, i):  # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					            return request.route_url("neworder_batches.view", uuid=batch.uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        grid.add_action("view", icon="eye", url=view_url)
 | 
				
			||||||
 | 
					        grid.set_link("id")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return grid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_orders_grid(request, **kwargs):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Make and return the grid for the Orders field.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    config = request.wutta_config
 | 
				
			||||||
 | 
					    app = config.get_app()
 | 
				
			||||||
 | 
					    model = app.model
 | 
				
			||||||
 | 
					    web = app.get_web_handler()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if "key" not in kwargs:
 | 
				
			||||||
 | 
					        route_prefix = kwargs.pop("route_prefix")
 | 
				
			||||||
 | 
					        kwargs["key"] = f"{route_prefix}.view.orders"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    kwargs.setdefault("model_class", model.Order)
 | 
				
			||||||
 | 
					    kwargs.setdefault(
 | 
				
			||||||
 | 
					        "columns",
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            "order_id",
 | 
				
			||||||
 | 
					            "total_price",
 | 
				
			||||||
 | 
					            "created",
 | 
				
			||||||
 | 
					            "created_by",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    kwargs.setdefault("labels", {"order_id": "Order ID"})
 | 
				
			||||||
 | 
					    kwargs.setdefault("renderers", {"total_price": "currency"})
 | 
				
			||||||
 | 
					    grid = web.make_grid(request, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if request.has_perm("orders.view"):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def view_url(order, i):  # pylint: disable=unused-argument
 | 
				
			||||||
 | 
					            return request.route_url("orders.view", uuid=order.uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        grid.add_action("view", icon="eye", url=view_url)
 | 
				
			||||||
 | 
					        grid.set_link("order_id")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return grid
 | 
				
			||||||
| 
						 | 
					@ -25,9 +25,11 @@ Views for Customers
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from wuttaweb.views import MasterView
 | 
					from wuttaweb.views import MasterView
 | 
				
			||||||
from wuttaweb.forms.schema import UserRef, WuttaEnum
 | 
					from wuttaweb.forms.schema import WuttaEnum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sideshow.db.model import LocalCustomer, PendingCustomer
 | 
					from sideshow.db.model import LocalCustomer, PendingCustomer
 | 
				
			||||||
 | 
					from sideshow.web.views.shared import PendingMixin
 | 
				
			||||||
 | 
					from sideshow.web.util import make_new_order_batches_grid, make_orders_grid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LocalCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
					class LocalCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
| 
						 | 
					@ -120,72 +122,20 @@ class LocalCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the Orders field.
 | 
					        Make and return the grid for the Orders field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					        return make_orders_grid(
 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					            self.request, route_prefix=self.get_route_prefix(), data=customer.orders
 | 
				
			||||||
 | 
					 | 
				
			||||||
        grid = self.make_grid(
 | 
					 | 
				
			||||||
            key=f"{route_prefix}.view.orders",
 | 
					 | 
				
			||||||
            model_class=model.Order,
 | 
					 | 
				
			||||||
            data=customer.orders,
 | 
					 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "order_id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "order_id": "Order ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        grid.set_renderer("total_price", grid.render_currency)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.request.has_perm("orders.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(order, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("orders.view", uuid=order.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("order_id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def make_new_order_batches_grid(self, customer):
 | 
					    def make_new_order_batches_grid(self, customer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the New Order Batches field.
 | 
					        Make and return the grid for the New Order Batches field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					        return make_new_order_batches_grid(
 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					            self.request,
 | 
				
			||||||
 | 
					            route_prefix=self.get_route_prefix(),
 | 
				
			||||||
        grid = self.make_grid(
 | 
					 | 
				
			||||||
            key=f"{route_prefix}.view.new_order_batches",
 | 
					 | 
				
			||||||
            model_class=model.NewOrderBatch,
 | 
					 | 
				
			||||||
            data=customer.new_order_batches,
 | 
					            data=customer.new_order_batches,
 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
                "executed",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "id": "Batch ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "id": "batch_id",
 | 
					 | 
				
			||||||
                "total_price": "currency",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("neworder_batches.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(batch, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("neworder_batches.view", uuid=batch.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def objectify(self, form):  # pylint: disable=empty-docstring
 | 
					    def objectify(self, form):  # pylint: disable=empty-docstring
 | 
				
			||||||
        """ """
 | 
					        """ """
 | 
				
			||||||
        customer = super().objectify(form)
 | 
					        customer = super().objectify(form)
 | 
				
			||||||
| 
						 | 
					@ -197,7 +147,7 @@ class LocalCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        return customer
 | 
					        return customer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PendingCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
					class PendingCustomerView(PendingMixin, MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Master view for
 | 
					    Master view for
 | 
				
			||||||
    :class:`~sideshow.db.model.customers.PendingCustomer`; route
 | 
					    :class:`~sideshow.db.model.customers.PendingCustomer`; route
 | 
				
			||||||
| 
						 | 
					@ -270,7 +220,8 @@ class PendingCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        f = form
 | 
					        f = form
 | 
				
			||||||
        super().configure_form(f)
 | 
					        super().configure_form(f)
 | 
				
			||||||
        enum = self.app.enum
 | 
					        enum = self.app.enum
 | 
				
			||||||
        customer = f.model_instance
 | 
					
 | 
				
			||||||
 | 
					        self.configure_form_pending(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # customer_id
 | 
					        # customer_id
 | 
				
			||||||
        if self.creating:
 | 
					        if self.creating:
 | 
				
			||||||
| 
						 | 
					@ -285,101 +236,24 @@ class PendingCustomerView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
            f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus))
 | 
					            f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus))
 | 
				
			||||||
            f.set_readonly("status")
 | 
					            f.set_readonly("status")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # created
 | 
					 | 
				
			||||||
        if self.creating:
 | 
					 | 
				
			||||||
            f.remove("created")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_readonly("created")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # created_by
 | 
					 | 
				
			||||||
        if self.creating:
 | 
					 | 
				
			||||||
            f.remove("created_by")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_node("created_by", UserRef(self.request))
 | 
					 | 
				
			||||||
            f.set_readonly("created_by")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # orders
 | 
					 | 
				
			||||||
        if self.creating or self.editing:
 | 
					 | 
				
			||||||
            f.remove("orders")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_grid("orders", self.make_orders_grid(customer))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # new_order_batches
 | 
					 | 
				
			||||||
        if self.creating or self.editing:
 | 
					 | 
				
			||||||
            f.remove("new_order_batches")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def make_orders_grid(self, customer):
 | 
					    def make_orders_grid(self, customer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the Orders field.
 | 
					        Make and return the grid for the Orders field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					        return make_orders_grid(
 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					            self.request, route_prefix=self.get_route_prefix(), data=customer.orders
 | 
				
			||||||
 | 
					 | 
				
			||||||
        grid = self.make_grid(
 | 
					 | 
				
			||||||
            key=f"{route_prefix}.view.orders",
 | 
					 | 
				
			||||||
            model_class=model.Order,
 | 
					 | 
				
			||||||
            data=customer.orders,
 | 
					 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "order_id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "order_id": "Order ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        grid.set_renderer("total_price", grid.render_currency)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.request.has_perm("orders.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(order, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("orders.view", uuid=order.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("order_id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def make_new_order_batches_grid(self, customer):
 | 
					    def make_new_order_batches_grid(self, customer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the New Order Batches field.
 | 
					        Make and return the grid for the New Order Batches field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					        return make_new_order_batches_grid(
 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					            self.request,
 | 
				
			||||||
 | 
					            route_prefix=self.get_route_prefix(),
 | 
				
			||||||
        grid = self.make_grid(
 | 
					 | 
				
			||||||
            key=f"{route_prefix}.view.new_order_batches",
 | 
					 | 
				
			||||||
            model_class=model.NewOrderBatch,
 | 
					 | 
				
			||||||
            data=customer.new_order_batches,
 | 
					            data=customer.new_order_batches,
 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
                "executed",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "id": "Batch ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "id": "batch_id",
 | 
					 | 
				
			||||||
                "total_price": "currency",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("neworder_batches.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(batch, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("neworder_batches.view", uuid=batch.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def objectify(self, form):  # pylint: disable=empty-docstring
 | 
					    def objectify(self, form):  # pylint: disable=empty-docstring
 | 
				
			||||||
        """ """
 | 
					        """ """
 | 
				
			||||||
        enum = self.app.enum
 | 
					        enum = self.app.enum
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,10 +25,12 @@ Views for Products
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from wuttaweb.views import MasterView
 | 
					from wuttaweb.views import MasterView
 | 
				
			||||||
from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity
 | 
					from wuttaweb.forms.schema import WuttaMoney, WuttaQuantity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sideshow.enum import PendingProductStatus
 | 
					from sideshow.enum import PendingProductStatus
 | 
				
			||||||
from sideshow.db.model import LocalProduct, PendingProduct
 | 
					from sideshow.db.model import LocalProduct, PendingProduct
 | 
				
			||||||
 | 
					from sideshow.web.views.shared import PendingMixin
 | 
				
			||||||
 | 
					from sideshow.web.util import make_new_order_batches_grid, make_orders_grid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LocalProductView(MasterView):  # pylint: disable=abstract-method
 | 
					class LocalProductView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
| 
						 | 
					@ -153,82 +155,28 @@ class LocalProductView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the Orders field.
 | 
					        Make and return the grid for the Orders field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        orders = {item.order for item in product.order_items}
 | 
					        orders = {item.order for item in product.order_items}
 | 
				
			||||||
        orders = sorted(orders, key=lambda order: order.order_id)
 | 
					        orders = sorted(orders, key=lambda order: order.order_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid = self.make_grid(
 | 
					        return make_orders_grid(
 | 
				
			||||||
            key=f"{route_prefix}.view.orders",
 | 
					            self.request, route_prefix=self.get_route_prefix(), data=orders
 | 
				
			||||||
            model_class=model.Order,
 | 
					 | 
				
			||||||
            data=orders,
 | 
					 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "order_id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "order_id": "Order ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "total_price": "currency",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("orders.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(order, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("orders.view", uuid=order.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("order_id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def make_new_order_batches_grid(self, product):
 | 
					    def make_new_order_batches_grid(self, product):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the New Order Batches field.
 | 
					        Make and return the grid for the New Order Batches field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        batches = {row.batch for row in product.new_order_batch_rows}
 | 
					        batches = {row.batch for row in product.new_order_batch_rows}
 | 
				
			||||||
        batches = sorted(batches, key=lambda batch: batch.id)
 | 
					        batches = sorted(batches, key=lambda batch: batch.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid = self.make_grid(
 | 
					        return make_new_order_batches_grid(
 | 
				
			||||||
            key=f"{route_prefix}.view.new_order_batches",
 | 
					            self.request,
 | 
				
			||||||
            model_class=model.NewOrderBatch,
 | 
					            route_prefix=self.get_route_prefix(),
 | 
				
			||||||
            data=batches,
 | 
					            data=batches,
 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
                "executed",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "id": "Batch ID",
 | 
					 | 
				
			||||||
                "status_code": "Status",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "id": "batch_id",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("neworder_batches.view"):
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            def view_url(batch, i):  # pylint: disable=unused-argument
 | 
					class PendingProductView(PendingMixin, MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
                return self.request.route_url("neworder_batches.view", uuid=batch.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PendingProductView(MasterView):  # pylint: disable=abstract-method
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Master view for
 | 
					    Master view for
 | 
				
			||||||
    :class:`~sideshow.db.model.products.PendingProduct`; route
 | 
					    :class:`~sideshow.db.model.products.PendingProduct`; route
 | 
				
			||||||
| 
						 | 
					@ -330,7 +278,8 @@ class PendingProductView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        """ """
 | 
					        """ """
 | 
				
			||||||
        f = form
 | 
					        f = form
 | 
				
			||||||
        super().configure_form(f)
 | 
					        super().configure_form(f)
 | 
				
			||||||
        product = f.model_instance
 | 
					
 | 
				
			||||||
 | 
					        self.configure_form_pending(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # product_id
 | 
					        # product_id
 | 
				
			||||||
        if self.creating:
 | 
					        if self.creating:
 | 
				
			||||||
| 
						 | 
					@ -344,109 +293,30 @@ class PendingProductView(MasterView):  # pylint: disable=abstract-method
 | 
				
			||||||
        # notes
 | 
					        # notes
 | 
				
			||||||
        f.set_widget("notes", "notes")
 | 
					        f.set_widget("notes", "notes")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # created
 | 
					 | 
				
			||||||
        if self.creating:
 | 
					 | 
				
			||||||
            f.remove("created")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_readonly("created")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # created_by
 | 
					 | 
				
			||||||
        if self.creating:
 | 
					 | 
				
			||||||
            f.remove("created_by")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_node("created_by", UserRef(self.request))
 | 
					 | 
				
			||||||
            f.set_readonly("created_by")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # orders
 | 
					 | 
				
			||||||
        if self.creating or self.editing:
 | 
					 | 
				
			||||||
            f.remove("orders")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_grid("orders", self.make_orders_grid(product))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # new_order_batches
 | 
					 | 
				
			||||||
        if self.creating or self.editing:
 | 
					 | 
				
			||||||
            f.remove("new_order_batches")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            f.set_grid("new_order_batches", self.make_new_order_batches_grid(product))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def make_orders_grid(self, product):
 | 
					    def make_orders_grid(self, product):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the Orders field.
 | 
					        Make and return the grid for the Orders field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        orders = {item.order for item in product.order_items}
 | 
					        orders = {item.order for item in product.order_items}
 | 
				
			||||||
        orders = sorted(orders, key=lambda order: order.order_id)
 | 
					        orders = sorted(orders, key=lambda order: order.order_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid = self.make_grid(
 | 
					        return make_orders_grid(
 | 
				
			||||||
            key=f"{route_prefix}.view.orders",
 | 
					            self.request, route_prefix=self.get_route_prefix(), data=orders
 | 
				
			||||||
            model_class=model.Order,
 | 
					 | 
				
			||||||
            data=orders,
 | 
					 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "order_id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "order_id": "Order ID",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "total_price": "currency",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("orders.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(order, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("orders.view", uuid=order.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("order_id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def make_new_order_batches_grid(self, product):
 | 
					    def make_new_order_batches_grid(self, product):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Make and return the grid for the New Order Batches field.
 | 
					        Make and return the grid for the New Order Batches field.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        model = self.app.model
 | 
					 | 
				
			||||||
        route_prefix = self.get_route_prefix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        batches = {row.batch for row in product.new_order_batch_rows}
 | 
					        batches = {row.batch for row in product.new_order_batch_rows}
 | 
				
			||||||
        batches = sorted(batches, key=lambda batch: batch.id)
 | 
					        batches = sorted(batches, key=lambda batch: batch.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        grid = self.make_grid(
 | 
					        return make_new_order_batches_grid(
 | 
				
			||||||
            key=f"{route_prefix}.view.new_order_batches",
 | 
					            self.request,
 | 
				
			||||||
            model_class=model.NewOrderBatch,
 | 
					            route_prefix=self.get_route_prefix(),
 | 
				
			||||||
            data=batches,
 | 
					            data=batches,
 | 
				
			||||||
            columns=[
 | 
					 | 
				
			||||||
                "id",
 | 
					 | 
				
			||||||
                "total_price",
 | 
					 | 
				
			||||||
                "created",
 | 
					 | 
				
			||||||
                "created_by",
 | 
					 | 
				
			||||||
                "executed",
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            labels={
 | 
					 | 
				
			||||||
                "id": "Batch ID",
 | 
					 | 
				
			||||||
                "status_code": "Status",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            renderers={
 | 
					 | 
				
			||||||
                "id": "batch_id",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.has_perm("neworder_batches.view"):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            def view_url(batch, i):  # pylint: disable=unused-argument
 | 
					 | 
				
			||||||
                return self.request.route_url("neworder_batches.view", uuid=batch.uuid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            grid.add_action("view", icon="eye", url=view_url)
 | 
					 | 
				
			||||||
            grid.set_link("id")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return grid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_template_context(self, context):  # pylint: disable=empty-docstring
 | 
					    def get_template_context(self, context):  # pylint: disable=empty-docstring
 | 
				
			||||||
        """ """
 | 
					        """ """
 | 
				
			||||||
        enum = self.app.enum
 | 
					        enum = self.app.enum
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										64
									
								
								src/sideshow/web/views/shared.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/sideshow/web/views/shared.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					# -*- coding: utf-8; -*-
 | 
				
			||||||
 | 
					################################################################################
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  Sideshow -- Case/Special Order Tracker
 | 
				
			||||||
 | 
					#  Copyright © 2024-2025 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 <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					################################################################################
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Shared View Logic
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from wuttaweb.forms.schema import UserRef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PendingMixin:  # pylint: disable=too-few-public-methods
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Mixin class with logic shared by Pending Customer and Pending
 | 
				
			||||||
 | 
					    Product views.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def configure_form_pending(self, form):  # pylint: disable=empty-docstring
 | 
				
			||||||
 | 
					        """ """
 | 
				
			||||||
 | 
					        f = form
 | 
				
			||||||
 | 
					        obj = f.model_instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # created
 | 
				
			||||||
 | 
					        if self.creating:
 | 
				
			||||||
 | 
					            f.remove("created")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            f.set_readonly("created")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # created_by
 | 
				
			||||||
 | 
					        if self.creating:
 | 
				
			||||||
 | 
					            f.remove("created_by")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            f.set_node("created_by", UserRef(self.request))
 | 
				
			||||||
 | 
					            f.set_readonly("created_by")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # orders
 | 
				
			||||||
 | 
					        if self.creating or self.editing:
 | 
				
			||||||
 | 
					            f.remove("orders")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            f.set_grid("orders", self.make_orders_grid(obj))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # new_order_batches
 | 
				
			||||||
 | 
					        if self.creating or self.editing:
 | 
				
			||||||
 | 
					            f.remove("new_order_batches")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            f.set_grid("new_order_batches", self.make_new_order_batches_grid(obj))
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue