From 2107e9ee1dc272f3bc5f7839312e163cbd8b9da9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 31 Aug 2025 12:59:28 -0500 Subject: [PATCH] fix: format all code with black and from now on should not deviate from that... --- docs/conf.py | 36 +- src/sideshow/app.py | 7 +- src/sideshow/batch/neworder.py | 341 +-- src/sideshow/cli/base.py | 3 +- src/sideshow/cli/install.py | 12 +- src/sideshow/config.py | 25 +- .../13af2ffbc0e0_add_order_item_vendor.py | 33 +- .../7a6df83afbd4_initial_order_tables.py | 522 ++-- .../versions/a4273360d379_add_stores.py | 26 +- .../fd8a2527bd30_add_ignored_status.py | 61 +- src/sideshow/db/model/batch/neworder.py | 294 ++- src/sideshow/db/model/customers.py | 121 +- src/sideshow/db/model/orders.py | 422 +++- src/sideshow/db/model/products.py | 192 +- src/sideshow/db/model/stores.py | 36 +- src/sideshow/enum.py | 204 +- src/sideshow/orders.py | 84 +- src/sideshow/testing.py | 10 +- src/sideshow/web/__init__.py | 6 +- src/sideshow/web/app.py | 13 +- src/sideshow/web/forms/schema.py | 10 +- src/sideshow/web/menus.py | 121 +- src/sideshow/web/static/__init__.py | 16 +- src/sideshow/web/views/__init__.py | 19 +- src/sideshow/web/views/batch/neworder.py | 148 +- src/sideshow/web/views/common.py | 89 +- src/sideshow/web/views/customers.py | 343 +-- src/sideshow/web/views/orders.py | 1676 +++++++------ src/sideshow/web/views/products.py | 489 ++-- src/sideshow/web/views/stores.py | 29 +- tasks.py | 14 +- tests/batch/test_neworder.py | 1058 ++++---- tests/cli/test_install.py | 2 +- tests/db/model/batch/test_neworder.py | 4 +- tests/db/model/test_orders.py | 16 +- tests/db/model/test_products.py | 12 +- tests/test_config.py | 4 +- tests/test_orders.py | 376 ++- tests/web/forms/test_schema.py | 40 +- tests/web/test_app.py | 2 +- tests/web/test_menus.py | 21 +- tests/web/views/batch/test_neworder.py | 67 +- tests/web/views/test_common.py | 4 +- tests/web/views/test_customers.py | 172 +- tests/web/views/test_orders.py | 2174 ++++++++++------- tests/web/views/test_products.py | 302 ++- tests/web/views/test_stores.py | 50 +- 47 files changed, 5729 insertions(+), 3977 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fa83196..8eff8b8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,36 +8,36 @@ from importlib.metadata import version as get_version -project = 'Sideshow' -copyright = '2025, Lance Edgar' -author = 'Lance Edgar' -release = get_version('Sideshow') +project = "Sideshow" +copyright = "2025, Lance Edgar" +author = "Lance Edgar" +release = get_version("Sideshow") # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', - 'sphinx.ext.todo', - 'sphinxcontrib.programoutput', - 'enum_tools.autoenum', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", + "sphinx.ext.todo", + "sphinxcontrib.programoutput", + "enum_tools.autoenum", ] -templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] intersphinx_mapping = { - 'pyramid': ('https://docs.pylonsproject.org/projects/pyramid/en/latest/', None), - 'python': ('https://docs.python.org/3/', None), - 'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None), - 'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None), + "pyramid": ("https://docs.pylonsproject.org/projects/pyramid/en/latest/", None), + "python": ("https://docs.python.org/3/", None), + "wuttjamaican": ("https://docs.wuttaproject.org/wuttjamaican/", None), + "wuttaweb": ("https://docs.wuttaproject.org/wuttaweb/", None), } # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'furo' -html_static_path = ['_static'] +html_theme = "furo" +html_static_path = ["_static"] diff --git a/src/sideshow/app.py b/src/sideshow/app.py index 0fbcf2e..ec2f85a 100644 --- a/src/sideshow/app.py +++ b/src/sideshow/app.py @@ -49,8 +49,9 @@ class SideshowAppProvider(base.AppProvider): :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') + 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 959869b..fd0a05e 100644 --- a/src/sideshow/batch/neworder.py +++ b/src/sideshow/batch/neworder.py @@ -49,6 +49,7 @@ class NewOrderBatchHandler(BatchHandler): After the batch has executed the :term:`order handler` takes over responsibility for the rest of the order lifecycle. """ + model_class = NewOrderBatch def get_default_store_id(self): @@ -57,7 +58,7 @@ class NewOrderBatchHandler(BatchHandler): :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`, or ``None``. """ - return self.config.get('sideshow.orders.default_store_id') + return self.config.get("sideshow.orders.default_store_id") def use_local_customers(self): """ @@ -65,8 +66,7 @@ class NewOrderBatchHandler(BatchHandler): accounts should be used. This is true by default, but may be false for :term:`external customer` lookups. """ - return self.config.get_bool('sideshow.orders.use_local_customers', - default=True) + return self.config.get_bool("sideshow.orders.use_local_customers", default=True) def use_local_products(self): """ @@ -74,8 +74,7 @@ class NewOrderBatchHandler(BatchHandler): records should be used. This is true by default, but may be false for :term:`external product` lookups. """ - return self.config.get_bool('sideshow.orders.use_local_products', - default=True) + return self.config.get_bool("sideshow.orders.use_local_products", default=True) def allow_unknown_products(self): """ @@ -86,24 +85,27 @@ class NewOrderBatchHandler(BatchHandler): when creating an order. This can be disabled, to force user to choose existing local/external product. """ - return self.config.get_bool('sideshow.orders.allow_unknown_products', - default=True) + return self.config.get_bool( + "sideshow.orders.allow_unknown_products", default=True + ) def allow_item_discounts(self): """ Returns boolean indicating whether per-item discounts are allowed when creating an order. """ - return self.config.get_bool('sideshow.orders.allow_item_discounts', - default=False) + return self.config.get_bool( + "sideshow.orders.allow_item_discounts", default=False + ) def allow_item_discounts_if_on_sale(self): """ Returns boolean indicating whether per-item discounts are allowed even when the item is already on sale. """ - return self.config.get_bool('sideshow.orders.allow_item_discounts_if_on_sale', - default=False) + return self.config.get_bool( + "sideshow.orders.allow_item_discounts_if_on_sale", default=False + ) def get_default_item_discount(self): """ @@ -111,7 +113,7 @@ class NewOrderBatchHandler(BatchHandler): :rtype: :class:`~python:decimal.Decimal` or ``None`` """ - discount = self.config.get('sideshow.orders.default_item_discount') + discount = self.config.get("sideshow.orders.default_item_discount") if discount: return decimal.Decimal(discount) @@ -157,8 +159,9 @@ class NewOrderBatchHandler(BatchHandler): query = session.query(model.LocalCustomer) # filter query - criteria = [model.LocalCustomer.full_name.ilike(f'%{word}%') - for word in term.split()] + criteria = [ + model.LocalCustomer.full_name.ilike(f"%{word}%") for word in term.split() + ] query = query.filter(sa.and_(*criteria)) # sort query @@ -170,8 +173,8 @@ class NewOrderBatchHandler(BatchHandler): # get results def result(customer): - return {'value': customer.uuid.hex, - 'label': customer.full_name} + return {"value": customer.uuid.hex, "label": customer.full_name} + return [result(c) for c in customers] def init_batch(self, batch, session=None, progress=None, **kwargs): @@ -240,7 +243,7 @@ class NewOrderBatchHandler(BatchHandler): batch.local_customer = customer self.refresh_batch_from_local_customer(batch) - else: # external customer_id + else: # external customer_id batch.customer_id = customer_info self.refresh_batch_from_external_customer(batch) @@ -251,22 +254,25 @@ class NewOrderBatchHandler(BatchHandler): batch.local_customer = None customer = batch.pending_customer if not customer: - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user or batch.created_by) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, + created_by=user or batch.created_by, + ) session.add(customer) batch.pending_customer = customer fields = [ - 'full_name', - 'first_name', - 'last_name', - 'phone_number', - 'email_address', + "full_name", + "first_name", + "last_name", + "phone_number", + "email_address", ] for key in fields: setattr(customer, key, customer_info.get(key)) - if 'full_name' not in customer_info: - customer.full_name = self.app.make_full_name(customer.first_name, - customer.last_name) + if "full_name" not in customer_info: + customer.full_name = self.app.make_full_name( + customer.first_name, customer.last_name + ) self.refresh_batch_from_pending_customer(batch) else: @@ -361,14 +367,18 @@ class NewOrderBatchHandler(BatchHandler): # filter query criteria = [] for word in term.split(): - criteria.append(sa.or_( - model.LocalProduct.brand_name.ilike(f'%{word}%'), - model.LocalProduct.description.ilike(f'%{word}%'))) + criteria.append( + sa.or_( + model.LocalProduct.brand_name.ilike(f"%{word}%"), + model.LocalProduct.description.ilike(f"%{word}%"), + ) + ) query = query.filter(sa.and_(*criteria)) # sort query - query = query.order_by(model.LocalProduct.brand_name, - model.LocalProduct.description) + query = query.order_by( + model.LocalProduct.brand_name, model.LocalProduct.description + ) # get data # TODO: need max_results option @@ -376,8 +386,8 @@ class NewOrderBatchHandler(BatchHandler): # get results def result(product): - return {'value': product.uuid.hex, - 'label': product.full_description} + return {"value": product.uuid.hex, "label": product.full_description} + return [result(c) for c in products] def get_default_uom_choices(self): @@ -392,8 +402,7 @@ class NewOrderBatchHandler(BatchHandler): corresponding to the UOM code and label, respectively. """ enum = self.app.enum - return [{'key': key, 'value': val} - for key, val in enum.ORDER_UOM.items()] + return [{"key": key, "value": val} for key, val in enum.ORDER_UOM.items()] def get_product_info_external(self, session, product_id, user=None): """ @@ -514,20 +523,20 @@ class NewOrderBatchHandler(BatchHandler): * ``product_id`` will be set to the product UUID as string """ return { - 'product_id': product.uuid.hex, - 'scancode': product.scancode, - 'brand_name': product.brand_name, - 'description': product.description, - 'size': product.size, - 'full_description': product.full_description, - 'weighed': product.weighed, - 'special_order': product.special_order, - 'department_id': product.department_id, - 'department_name': product.department_name, - 'case_size': product.case_size, - 'unit_price_reg': product.unit_price_reg, - 'vendor_name': product.vendor_name, - 'vendor_item_code': product.vendor_item_code, + "product_id": product.uuid.hex, + "scancode": product.scancode, + "brand_name": product.brand_name, + "description": product.description, + "size": product.size, + "full_description": product.full_description, + "weighed": product.weighed, + "special_order": product.special_order, + "department_id": product.department_id, + "department_name": product.department_name, + "case_size": product.case_size, + "unit_price_reg": product.unit_price_reg, + "vendor_name": product.vendor_name, + "vendor_item_code": product.vendor_item_code, } def get_past_orders(self, batch): @@ -605,36 +614,51 @@ class NewOrderBatchHandler(BatchHandler): products[product.uuid] = self.normalize_local_product(product) elif item.product_id and item.product_id not in products: products[item.product_id] = self.get_product_info_external( - session, item.product_id, user=user) + session, item.product_id, user=user + ) products = list(products.values()) for product in products: - price = product['unit_price_reg'] + price = product["unit_price_reg"] - if 'unit_price_reg_display' not in product: - product['unit_price_reg_display'] = self.app.render_currency(price) + if "unit_price_reg_display" not in product: + product["unit_price_reg_display"] = self.app.render_currency(price) - if 'unit_price_quoted' not in product: - product['unit_price_quoted'] = price + if "unit_price_quoted" not in product: + product["unit_price_quoted"] = price - if 'unit_price_quoted_display' not in product: - product['unit_price_quoted_display'] = product['unit_price_reg_display'] + if "unit_price_quoted_display" not in product: + product["unit_price_quoted_display"] = product["unit_price_reg_display"] - if ('case_price_quoted' not in product - and product.get('unit_price_quoted') is not None - and product.get('case_size') is not None): - product['case_price_quoted'] = product['unit_price_quoted'] * product['case_size'] + if ( + "case_price_quoted" not in product + and product.get("unit_price_quoted") is not None + and product.get("case_size") is not None + ): + product["case_price_quoted"] = ( + product["unit_price_quoted"] * product["case_size"] + ) - if ('case_price_quoted_display' not in product - and 'case_price_quoted' in product): - product['case_price_quoted_display'] = self.app.render_currency( - product['case_price_quoted']) + if ( + "case_price_quoted_display" not in product + and "case_price_quoted" in product + ): + product["case_price_quoted_display"] = self.app.render_currency( + product["case_price_quoted"] + ) return products - def add_item(self, batch, product_info, order_qty, order_uom, - discount_percent=None, user=None): + def add_item( + self, + batch, + product_info, + order_qty, + order_uom, + discount_percent=None, + user=None, + ): """ Add a new item/row to the batch, for given product and quantity. @@ -686,7 +710,7 @@ class NewOrderBatchHandler(BatchHandler): raise ValueError("local product not found") row.local_product = local - else: # external product_id + else: # external product_id row.product_id = product_info else: @@ -695,23 +719,25 @@ class NewOrderBatchHandler(BatchHandler): raise TypeError("unknown/pending product not allowed for new orders") row.product_id = None row.local_product = None - pending = model.PendingProduct(status=enum.PendingProductStatus.PENDING, - created_by=user or batch.created_by) + pending = model.PendingProduct( + status=enum.PendingProductStatus.PENDING, + created_by=user or batch.created_by, + ) fields = [ - 'scancode', - 'brand_name', - 'description', - 'size', - 'weighed', - 'department_id', - 'department_name', - 'special_order', - 'vendor_name', - 'vendor_item_code', - 'case_size', - 'unit_cost', - 'unit_price_reg', - 'notes', + "scancode", + "brand_name", + "description", + "size", + "weighed", + "department_id", + "department_name", + "special_order", + "vendor_name", + "vendor_item_code", + "case_size", + "unit_cost", + "unit_price_reg", + "notes", ] for key in fields: setattr(pending, key, product_info.get(key)) @@ -735,8 +761,9 @@ class NewOrderBatchHandler(BatchHandler): session.flush() return row - def update_item(self, row, product_info, order_qty, order_uom, - discount_percent=None, user=None): + def update_item( + self, row, product_info, order_qty, order_uom, discount_percent=None, user=None + ): """ Update an item/row, per given product and quantity. @@ -783,7 +810,7 @@ class NewOrderBatchHandler(BatchHandler): raise ValueError("local product not found") row.local_product = local - else: # external product_id + else: # external product_id row.product_id = product_info else: @@ -794,25 +821,27 @@ class NewOrderBatchHandler(BatchHandler): row.local_product = None pending = row.pending_product if not pending: - pending = model.PendingProduct(status=enum.PendingProductStatus.PENDING, - created_by=user or row.batch.created_by) + pending = model.PendingProduct( + status=enum.PendingProductStatus.PENDING, + created_by=user or row.batch.created_by, + ) session.add(pending) row.pending_product = pending fields = [ - 'scancode', - 'brand_name', - 'description', - 'size', - 'weighed', - 'department_id', - 'department_name', - 'special_order', - 'vendor_name', - 'vendor_item_code', - 'case_size', - 'unit_cost', - 'unit_price_reg', - 'notes', + "scancode", + "brand_name", + "description", + "size", + "weighed", + "department_id", + "department_name", + "special_order", + "vendor_name", + "vendor_item_code", + "case_size", + "unit_cost", + "unit_price_reg", + "notes", ] for key in fields: setattr(pending, key, product_info.get(key)) @@ -885,8 +914,8 @@ class NewOrderBatchHandler(BatchHandler): row.unit_price_quoted = None row.case_price_quoted = None if row.unit_price_sale is not None and ( - not row.sale_ends - or row.sale_ends > datetime.datetime.now()): + not row.sale_ends or row.sale_ends > datetime.datetime.now() + ): row.unit_price_quoted = row.unit_price_sale else: row.unit_price_quoted = row.unit_price_reg @@ -901,22 +930,22 @@ class NewOrderBatchHandler(BatchHandler): # row.total_price = row.case_price_quoted * row.order_qty if row.unit_price_quoted is not None and row.case_size is not None: row.total_price = row.unit_price_quoted * row.case_size * row.order_qty - else: # ORDER_UOM_UNIT (or similar) + else: # ORDER_UOM_UNIT (or similar) if row.unit_price_quoted is not None: row.total_price = row.unit_price_quoted * row.order_qty if row.total_price is not None: if row.discount_percent and self.allow_item_discounts(): - row.total_price = (float(row.total_price) - * (100 - float(row.discount_percent)) - / 100.0) - row.total_price = decimal.Decimal(f'{row.total_price:0.2f}') + row.total_price = ( + float(row.total_price) * (100 - float(row.discount_percent)) / 100.0 + ) + row.total_price = decimal.Decimal(f"{row.total_price:0.2f}") # update batch if total price changed if row.total_price != old_total: batch = row.batch - batch.total_price = ((batch.total_price or 0) - + (row.total_price or 0) - - (old_total or 0)) + batch.total_price = ( + (batch.total_price or 0) + (row.total_price or 0) - (old_total or 0) + ) # all ok row.status_code = row.STATUS_OK @@ -1046,8 +1075,7 @@ class NewOrderBatchHandler(BatchHandler): are "effective" - i.e. rows with other status codes will not be created as proper order items. """ - return [row for row in batch.rows - if row.status_code == row.STATUS_OK] + return [row for row in batch.rows if row.status_code == row.STATUS_OK] def execute(self, batch, user=None, progress=None, **kwargs): """ @@ -1152,7 +1180,7 @@ class NewOrderBatchHandler(BatchHandler): row.pending_product = None session.delete(pending) - else: # external products; pending should be marked 'ready' + else: # external products; pending should be marked 'ready' for row in rows: pending = row.pending_product if pending: @@ -1181,48 +1209,47 @@ class NewOrderBatchHandler(BatchHandler): session = self.app.get_session(batch) batch_fields = [ - 'store_id', - 'customer_id', - 'local_customer', - 'pending_customer', - 'customer_name', - 'phone_number', - 'email_address', - 'total_price', + "store_id", + "customer_id", + "local_customer", + "pending_customer", + "customer_name", + "phone_number", + "email_address", + "total_price", ] row_fields = [ - 'product_id', - 'local_product', - 'pending_product', - 'product_scancode', - 'product_brand', - 'product_description', - 'product_size', - 'product_weighed', - 'department_id', - 'department_name', - 'vendor_name', - 'vendor_item_code', - 'case_size', - 'order_qty', - 'order_uom', - 'unit_cost', - 'unit_price_quoted', - 'case_price_quoted', - 'unit_price_reg', - 'unit_price_sale', - 'sale_ends', - 'discount_percent', - 'total_price', - 'special_order', + "product_id", + "local_product", + "pending_product", + "product_scancode", + "product_brand", + "product_description", + "product_size", + "product_weighed", + "department_id", + "department_name", + "vendor_name", + "vendor_item_code", + "case_size", + "order_qty", + "order_uom", + "unit_cost", + "unit_price_quoted", + "case_price_quoted", + "unit_price_reg", + "unit_price_sale", + "sale_ends", + "discount_percent", + "total_price", + "special_order", ] # make order - kw = dict([(field, getattr(batch, field)) - for field in batch_fields]) - kw['order_id'] = batch.id - kw['created_by'] = user + kw = dict([(field, getattr(batch, field)) for field in batch_fields]) + kw["order_id"] = batch.id + kw["created_by"] = user order = model.Order(**kw) session.add(order) session.flush() @@ -1230,16 +1257,16 @@ class NewOrderBatchHandler(BatchHandler): def convert(row, i): # make order item - kw = dict([(field, getattr(row, field)) - for field in row_fields]) + kw = dict([(field, getattr(row, field)) for field in row_fields]) item = model.OrderItem(**kw) order.items.append(item) # set item status self.set_initial_item_status(item, user) - self.app.progress_loop(convert, rows, progress, - message="Converting batch rows to order items") + self.app.progress_loop( + convert, rows, progress, message="Converting batch rows to order items" + ) session.flush() return order diff --git a/src/sideshow/cli/base.py b/src/sideshow/cli/base.py index 63b430e..58c7425 100644 --- a/src/sideshow/cli/base.py +++ b/src/sideshow/cli/base.py @@ -35,6 +35,5 @@ from wuttjamaican.cli import make_typer sideshow_typer = make_typer( - name='sideshow', - help="Sideshow -- Case/Special Order Tracker" + name="sideshow", help="Sideshow -- Case/Special Order Tracker" ) diff --git a/src/sideshow/cli/install.py b/src/sideshow/cli/install.py index 065aa37..06231f6 100644 --- a/src/sideshow/cli/install.py +++ b/src/sideshow/cli/install.py @@ -31,15 +31,17 @@ from .base import sideshow_typer @sideshow_typer.command() def install( - ctx: typer.Context, + ctx: typer.Context, ): """ Install the Sideshow app """ config = ctx.parent.wutta_config app = config.get_app() - install = app.get_install_handler(pkg_name='sideshow', - app_title="Sideshow", - pypi_name='Sideshow', - egg_name='Sideshow') + install = app.get_install_handler( + pkg_name="sideshow", + app_title="Sideshow", + pypi_name="Sideshow", + egg_name="Sideshow", + ) install.run() diff --git a/src/sideshow/config.py b/src/sideshow/config.py index a010262..e58c368 100644 --- a/src/sideshow/config.py +++ b/src/sideshow/config.py @@ -33,26 +33,31 @@ class SideshowConfig(WuttaConfigExtension): This establishes some config defaults specific to Sideshow. """ - key = 'sideshow' + + key = "sideshow" def configure(self, config): """ """ # app info - config.setdefault(f'{config.appname}.app_title', "Sideshow") - config.setdefault(f'{config.appname}.app_dist', "Sideshow") + config.setdefault(f"{config.appname}.app_title", "Sideshow") + config.setdefault(f"{config.appname}.app_dist", "Sideshow") # app model, enum - config.setdefault(f'{config.appname}.model_spec', 'sideshow.db.model') - config.setdefault(f'{config.appname}.enum_spec', 'sideshow.enum') + config.setdefault(f"{config.appname}.model_spec", "sideshow.db.model") + config.setdefault(f"{config.appname}.enum_spec", "sideshow.enum") # batch handlers - config.setdefault(f'{config.appname}.batch.neworder.handler.default_spec', - 'sideshow.batch.neworder:NewOrderBatchHandler') + config.setdefault( + f"{config.appname}.batch.neworder.handler.default_spec", + "sideshow.batch.neworder:NewOrderBatchHandler", + ) # web app menu - config.setdefault(f'{config.appname}.web.menus.handler.default_spec', - 'sideshow.web.menus:SideshowMenuHandler') + config.setdefault( + f"{config.appname}.web.menus.handler.default_spec", + "sideshow.web.menus:SideshowMenuHandler", + ) # web app libcache - config.setdefault('wuttaweb.static_libcache.module', 'sideshow.web.static') + config.setdefault("wuttaweb.static_libcache.module", "sideshow.web.static") diff --git a/src/sideshow/db/alembic/versions/13af2ffbc0e0_add_order_item_vendor.py b/src/sideshow/db/alembic/versions/13af2ffbc0e0_add_order_item_vendor.py index 52fe552..7d514f5 100644 --- a/src/sideshow/db/alembic/versions/13af2ffbc0e0_add_order_item_vendor.py +++ b/src/sideshow/db/alembic/versions/13af2ffbc0e0_add_order_item_vendor.py @@ -5,6 +5,7 @@ Revises: a4273360d379 Create Date: 2025-02-19 19:36:30.308840 """ + from typing import Sequence, Union from alembic import op @@ -13,8 +14,8 @@ import wuttjamaican.db.util # revision identifiers, used by Alembic. -revision: str = '13af2ffbc0e0' -down_revision: Union[str, None] = 'a4273360d379' +revision: str = "13af2ffbc0e0" +down_revision: Union[str, None] = "a4273360d379" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -22,20 +23,32 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # sideshow_batch_neworder_row - op.add_column('sideshow_batch_neworder_row', sa.Column('vendor_name', sa.String(length=50), nullable=True)) - op.add_column('sideshow_batch_neworder_row', sa.Column('vendor_item_code', sa.String(length=20), nullable=True)) + op.add_column( + "sideshow_batch_neworder_row", + sa.Column("vendor_name", sa.String(length=50), nullable=True), + ) + op.add_column( + "sideshow_batch_neworder_row", + sa.Column("vendor_item_code", sa.String(length=20), nullable=True), + ) # sideshow_order_item - op.add_column('sideshow_order_item', sa.Column('vendor_name', sa.String(length=50), nullable=True)) - op.add_column('sideshow_order_item', sa.Column('vendor_item_code', sa.String(length=20), nullable=True)) + op.add_column( + "sideshow_order_item", + sa.Column("vendor_name", sa.String(length=50), nullable=True), + ) + op.add_column( + "sideshow_order_item", + sa.Column("vendor_item_code", sa.String(length=20), nullable=True), + ) def downgrade() -> None: # sideshow_order_item - op.drop_column('sideshow_order_item', 'vendor_item_code') - op.drop_column('sideshow_order_item', 'vendor_name') + op.drop_column("sideshow_order_item", "vendor_item_code") + op.drop_column("sideshow_order_item", "vendor_name") # sideshow_batch_neworder_row - op.drop_column('sideshow_batch_neworder_row', 'vendor_item_code') - op.drop_column('sideshow_batch_neworder_row', 'vendor_name') + op.drop_column("sideshow_batch_neworder_row", "vendor_item_code") + op.drop_column("sideshow_batch_neworder_row", "vendor_name") diff --git a/src/sideshow/db/alembic/versions/7a6df83afbd4_initial_order_tables.py b/src/sideshow/db/alembic/versions/7a6df83afbd4_initial_order_tables.py index 6a0a589..7f8612c 100644 --- a/src/sideshow/db/alembic/versions/7a6df83afbd4_initial_order_tables.py +++ b/src/sideshow/db/alembic/versions/7a6df83afbd4_initial_order_tables.py @@ -1,10 +1,11 @@ """initial order tables Revision ID: 7a6df83afbd4 -Revises: +Revises: Create Date: 2024-12-30 18:53:51.358163 """ + from typing import Sequence, Union from alembic import op @@ -14,253 +15,364 @@ import wuttjamaican.db.util # revision identifiers, used by Alembic. -revision: str = '7a6df83afbd4' +revision: str = "7a6df83afbd4" down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = ('sideshow',) +branch_labels: Union[str, Sequence[str], None] = ("sideshow",) depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # enums - sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').create(op.get_bind()) - sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').create(op.get_bind()) + sa.Enum("PENDING", "READY", "RESOLVED", name="pendingcustomerstatus").create( + op.get_bind() + ) + sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").create( + op.get_bind() + ) # sideshow_customer_pending - op.create_table('sideshow_customer_pending', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('customer_id', sa.String(length=20), nullable=True), - sa.Column('full_name', sa.String(length=100), nullable=True), - sa.Column('first_name', sa.String(length=50), nullable=True), - sa.Column('last_name', sa.String(length=50), nullable=True), - sa.Column('phone_number', sa.String(length=20), nullable=True), - sa.Column('email_address', sa.String(length=255), nullable=True), - sa.Column('status', postgresql.ENUM('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus', create_type=False), nullable=False), - sa.Column('created', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_customer_pending_created_by_uuid_user')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_customer_pending')) - ) + op.create_table( + "sideshow_customer_pending", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("customer_id", sa.String(length=20), nullable=True), + sa.Column("full_name", sa.String(length=100), nullable=True), + sa.Column("first_name", sa.String(length=50), nullable=True), + sa.Column("last_name", sa.String(length=50), nullable=True), + sa.Column("phone_number", sa.String(length=20), nullable=True), + sa.Column("email_address", sa.String(length=255), nullable=True), + sa.Column( + "status", + postgresql.ENUM( + "PENDING", + "READY", + "RESOLVED", + name="pendingcustomerstatus", + create_type=False, + ), + nullable=False, + ), + sa.Column("created", sa.DateTime(timezone=True), nullable=False), + sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.ForeignKeyConstraint( + ["created_by_uuid"], + ["user.uuid"], + name=op.f("fk_sideshow_customer_pending_created_by_uuid_user"), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_customer_pending")), + ) # sideshow_customer_local - op.create_table('sideshow_customer_local', - sa.Column('full_name', sa.String(length=100), nullable=True), - sa.Column('first_name', sa.String(length=50), nullable=True), - sa.Column('last_name', sa.String(length=50), nullable=True), - sa.Column('phone_number', sa.String(length=20), nullable=True), - sa.Column('email_address', sa.String(length=255), nullable=True), - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('external_id', sa.String(length=20), nullable=True), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_customer_local')) - ) + op.create_table( + "sideshow_customer_local", + sa.Column("full_name", sa.String(length=100), nullable=True), + sa.Column("first_name", sa.String(length=50), nullable=True), + sa.Column("last_name", sa.String(length=50), nullable=True), + sa.Column("phone_number", sa.String(length=20), nullable=True), + sa.Column("email_address", sa.String(length=255), nullable=True), + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("external_id", sa.String(length=20), nullable=True), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_customer_local")), + ) # sideshow_product_pending - op.create_table('sideshow_product_pending', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('product_id', sa.String(length=20), nullable=True), - sa.Column('scancode', sa.String(length=14), nullable=True), - sa.Column('department_id', sa.String(length=10), nullable=True), - sa.Column('department_name', sa.String(length=30), nullable=True), - sa.Column('brand_name', sa.String(length=100), nullable=True), - sa.Column('description', sa.String(length=255), nullable=True), - sa.Column('size', sa.String(length=30), nullable=True), - sa.Column('weighed', sa.Boolean(), nullable=True), - sa.Column('vendor_name', sa.String(length=50), nullable=True), - sa.Column('vendor_item_code', sa.String(length=20), nullable=True), - sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), - sa.Column('case_size', sa.Numeric(precision=9, scale=4), nullable=True), - sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('special_order', sa.Boolean(), nullable=True), - sa.Column('notes', sa.Text(), nullable=True), - sa.Column('status', postgresql.ENUM('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus', create_type=False), nullable=False), - sa.Column('created', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_product_pending_created_by_uuid_user')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_product_pending')) - ) + op.create_table( + "sideshow_product_pending", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("product_id", sa.String(length=20), nullable=True), + sa.Column("scancode", sa.String(length=14), nullable=True), + sa.Column("department_id", sa.String(length=10), nullable=True), + sa.Column("department_name", sa.String(length=30), nullable=True), + sa.Column("brand_name", sa.String(length=100), nullable=True), + sa.Column("description", sa.String(length=255), nullable=True), + sa.Column("size", sa.String(length=30), nullable=True), + sa.Column("weighed", sa.Boolean(), nullable=True), + sa.Column("vendor_name", sa.String(length=50), nullable=True), + sa.Column("vendor_item_code", sa.String(length=20), nullable=True), + sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True), + sa.Column("case_size", sa.Numeric(precision=9, scale=4), nullable=True), + sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("special_order", sa.Boolean(), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column( + "status", + postgresql.ENUM( + "PENDING", + "READY", + "RESOLVED", + name="pendingproductstatus", + create_type=False, + ), + nullable=False, + ), + sa.Column("created", sa.DateTime(timezone=True), nullable=False), + sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.ForeignKeyConstraint( + ["created_by_uuid"], + ["user.uuid"], + name=op.f("fk_sideshow_product_pending_created_by_uuid_user"), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_product_pending")), + ) # sideshow_product_local - op.create_table('sideshow_product_local', - sa.Column('scancode', sa.String(length=14), nullable=True), - sa.Column('brand_name', sa.String(length=100), nullable=True), - sa.Column('description', sa.String(length=255), nullable=True), - sa.Column('size', sa.String(length=30), nullable=True), - sa.Column('weighed', sa.Boolean(), nullable=True), - sa.Column('department_id', sa.String(length=10), nullable=True), - sa.Column('department_name', sa.String(length=30), nullable=True), - sa.Column('special_order', sa.Boolean(), nullable=True), - sa.Column('vendor_name', sa.String(length=50), nullable=True), - sa.Column('vendor_item_code', sa.String(length=20), nullable=True), - sa.Column('case_size', sa.Numeric(precision=9, scale=4), nullable=True), - sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), - sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('notes', sa.Text(), nullable=True), - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('external_id', sa.String(length=20), nullable=True), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_product_local')) - ) + op.create_table( + "sideshow_product_local", + sa.Column("scancode", sa.String(length=14), nullable=True), + sa.Column("brand_name", sa.String(length=100), nullable=True), + sa.Column("description", sa.String(length=255), nullable=True), + sa.Column("size", sa.String(length=30), nullable=True), + sa.Column("weighed", sa.Boolean(), nullable=True), + sa.Column("department_id", sa.String(length=10), nullable=True), + sa.Column("department_name", sa.String(length=30), nullable=True), + sa.Column("special_order", sa.Boolean(), nullable=True), + sa.Column("vendor_name", sa.String(length=50), nullable=True), + sa.Column("vendor_item_code", sa.String(length=20), nullable=True), + sa.Column("case_size", sa.Numeric(precision=9, scale=4), nullable=True), + sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True), + sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("external_id", sa.String(length=20), nullable=True), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_product_local")), + ) # sideshow_order - op.create_table('sideshow_order', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('order_id', sa.Integer(), nullable=False), - sa.Column('store_id', sa.String(length=10), nullable=True), - sa.Column('customer_id', sa.String(length=20), nullable=True), - sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('customer_name', sa.String(length=100), nullable=True), - sa.Column('phone_number', sa.String(length=20), nullable=True), - sa.Column('email_address', sa.String(length=255), nullable=True), - sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True), - sa.Column('created', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid'], name=op.f('fk_order_local_customer_uuid_local_customer')), - sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid'], name=op.f('fk_order_pending_customer_uuid_pending_customer')), - sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_order_created_by_uuid_user')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_order')) - ) + op.create_table( + "sideshow_order", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("order_id", sa.Integer(), nullable=False), + sa.Column("store_id", sa.String(length=10), nullable=True), + sa.Column("customer_id", sa.String(length=20), nullable=True), + sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("customer_name", sa.String(length=100), nullable=True), + sa.Column("phone_number", sa.String(length=20), nullable=True), + sa.Column("email_address", sa.String(length=255), nullable=True), + sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True), + sa.Column("created", sa.DateTime(timezone=True), nullable=False), + sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.ForeignKeyConstraint( + ["local_customer_uuid"], + ["sideshow_customer_local.uuid"], + name=op.f("fk_order_local_customer_uuid_local_customer"), + ), + sa.ForeignKeyConstraint( + ["pending_customer_uuid"], + ["sideshow_customer_pending.uuid"], + name=op.f("fk_order_pending_customer_uuid_pending_customer"), + ), + sa.ForeignKeyConstraint( + ["created_by_uuid"], + ["user.uuid"], + name=op.f("fk_order_created_by_uuid_user"), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_order")), + ) # sideshow_order_item - op.create_table('sideshow_order_item', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('order_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('sequence', sa.Integer(), nullable=False), - sa.Column('product_id', sa.String(length=20), nullable=True), - sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('product_scancode', sa.String(length=14), nullable=True), - sa.Column('product_brand', sa.String(length=100), nullable=True), - sa.Column('product_description', sa.String(length=255), nullable=True), - sa.Column('product_size', sa.String(length=30), nullable=True), - sa.Column('product_weighed', sa.Boolean(), nullable=True), - sa.Column('department_id', sa.String(length=10), nullable=True), - sa.Column('department_name', sa.String(length=30), nullable=True), - sa.Column('special_order', sa.Boolean(), nullable=True), - sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True), - sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False), - sa.Column('order_uom', sa.String(length=10), nullable=False), - sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), - sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True), - sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True), - sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('status_code', sa.Integer(), nullable=False), - sa.Column('paid_amount', sa.Numeric(precision=8, scale=3), nullable=False), - sa.Column('payment_transaction_number', sa.String(length=20), nullable=True), - sa.ForeignKeyConstraint(['order_uuid'], ['sideshow_order.uuid'], name=op.f('fk_sideshow_order_item_order_uuid_order')), - sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid'], name=op.f('fk_sideshow_order_item_local_product_uuid_local_product')), - sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid'], name=op.f('fk_sideshow_order_item_pending_product_uuid_pending_product')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_order_item')) - ) + op.create_table( + "sideshow_order_item", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("order_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("sequence", sa.Integer(), nullable=False), + sa.Column("product_id", sa.String(length=20), nullable=True), + sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("product_scancode", sa.String(length=14), nullable=True), + sa.Column("product_brand", sa.String(length=100), nullable=True), + sa.Column("product_description", sa.String(length=255), nullable=True), + sa.Column("product_size", sa.String(length=30), nullable=True), + sa.Column("product_weighed", sa.Boolean(), nullable=True), + sa.Column("department_id", sa.String(length=10), nullable=True), + sa.Column("department_name", sa.String(length=30), nullable=True), + sa.Column("special_order", sa.Boolean(), nullable=True), + sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True), + sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False), + sa.Column("order_uom", sa.String(length=10), nullable=False), + sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True), + sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True), + sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True), + sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("status_code", sa.Integer(), nullable=False), + sa.Column("paid_amount", sa.Numeric(precision=8, scale=3), nullable=False), + sa.Column("payment_transaction_number", sa.String(length=20), nullable=True), + sa.ForeignKeyConstraint( + ["order_uuid"], + ["sideshow_order.uuid"], + name=op.f("fk_sideshow_order_item_order_uuid_order"), + ), + sa.ForeignKeyConstraint( + ["local_product_uuid"], + ["sideshow_product_local.uuid"], + name=op.f("fk_sideshow_order_item_local_product_uuid_local_product"), + ), + sa.ForeignKeyConstraint( + ["pending_product_uuid"], + ["sideshow_product_pending.uuid"], + name=op.f("fk_sideshow_order_item_pending_product_uuid_pending_product"), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_order_item")), + ) # sideshow_order_item_event - op.create_table('sideshow_order_item_event', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('item_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('type_code', sa.Integer(), nullable=False), - sa.Column('occurred', sa.DateTime(timezone=True), nullable=False), - sa.Column('actor_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('note', sa.Text(), nullable=True), - sa.ForeignKeyConstraint(['actor_uuid'], ['user.uuid'], name=op.f('fk_sideshow_order_item_event_actor_uuid_user')), - sa.ForeignKeyConstraint(['item_uuid'], ['sideshow_order_item.uuid'], name=op.f('fk_sideshow_order_item_event_item_uuid_sideshow_order_item')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_order_item_event')) - ) + op.create_table( + "sideshow_order_item_event", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("item_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("type_code", sa.Integer(), nullable=False), + sa.Column("occurred", sa.DateTime(timezone=True), nullable=False), + sa.Column("actor_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("note", sa.Text(), nullable=True), + sa.ForeignKeyConstraint( + ["actor_uuid"], + ["user.uuid"], + name=op.f("fk_sideshow_order_item_event_actor_uuid_user"), + ), + sa.ForeignKeyConstraint( + ["item_uuid"], + ["sideshow_order_item.uuid"], + name=op.f("fk_sideshow_order_item_event_item_uuid_sideshow_order_item"), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_order_item_event")), + ) # sideshow_batch_neworder - op.create_table('sideshow_batch_neworder', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('description', sa.String(length=255), nullable=True), - sa.Column('notes', sa.Text(), nullable=True), - sa.Column('row_count', sa.Integer(), nullable=True), - sa.Column('status_code', sa.Integer(), nullable=True), - sa.Column('status_text', sa.String(length=255), nullable=True), - sa.Column('created', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_by_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('executed', sa.DateTime(timezone=True), nullable=True), - sa.Column('executed_by_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('store_id', sa.String(length=10), nullable=True), - sa.Column('customer_id', sa.String(length=20), nullable=True), - sa.Column('local_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('pending_customer_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('customer_name', sa.String(length=100), nullable=True), - sa.Column('phone_number', sa.String(length=20), nullable=True), - sa.Column('email_address', sa.String(length=255), nullable=True), - sa.Column('total_price', sa.Numeric(precision=10, scale=3), nullable=True), - sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_batch_neworder_created_by_uuid_user')), - sa.ForeignKeyConstraint(['executed_by_uuid'], ['user.uuid'], name=op.f('fk_sideshow_batch_neworder_executed_by_uuid_user')), - sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid'], name=op.f('fk_sideshow_batch_neworder_local_customer_uuid_local_customer')), - sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid'], name=op.f('fk_sideshow_batch_neworder_pending_customer_uuid_pending_customer')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_batch_neworder')) - ) + op.create_table( + "sideshow_batch_neworder", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("description", sa.String(length=255), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column("row_count", sa.Integer(), nullable=True), + sa.Column("status_code", sa.Integer(), nullable=True), + sa.Column("status_text", sa.String(length=255), nullable=True), + sa.Column("created", sa.DateTime(timezone=True), nullable=False), + sa.Column("created_by_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("executed", sa.DateTime(timezone=True), nullable=True), + sa.Column("executed_by_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("store_id", sa.String(length=10), nullable=True), + sa.Column("customer_id", sa.String(length=20), nullable=True), + sa.Column("local_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("pending_customer_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("customer_name", sa.String(length=100), nullable=True), + sa.Column("phone_number", sa.String(length=20), nullable=True), + sa.Column("email_address", sa.String(length=255), nullable=True), + sa.Column("total_price", sa.Numeric(precision=10, scale=3), nullable=True), + sa.ForeignKeyConstraint( + ["created_by_uuid"], + ["user.uuid"], + name=op.f("fk_sideshow_batch_neworder_created_by_uuid_user"), + ), + sa.ForeignKeyConstraint( + ["executed_by_uuid"], + ["user.uuid"], + name=op.f("fk_sideshow_batch_neworder_executed_by_uuid_user"), + ), + sa.ForeignKeyConstraint( + ["local_customer_uuid"], + ["sideshow_customer_local.uuid"], + name=op.f("fk_sideshow_batch_neworder_local_customer_uuid_local_customer"), + ), + sa.ForeignKeyConstraint( + ["pending_customer_uuid"], + ["sideshow_customer_pending.uuid"], + name=op.f( + "fk_sideshow_batch_neworder_pending_customer_uuid_pending_customer" + ), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_batch_neworder")), + ) # sideshow_batch_neworder_row - op.create_table('sideshow_batch_neworder_row', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('batch_uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('sequence', sa.Integer(), nullable=False), - sa.Column('status_text', sa.String(length=255), nullable=True), - sa.Column('modified', sa.DateTime(timezone=True), nullable=True), - sa.Column('product_id', sa.String(length=20), nullable=True), - sa.Column('local_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('pending_product_uuid', wuttjamaican.db.util.UUID(), nullable=True), - sa.Column('product_scancode', sa.String(length=14), nullable=True), - sa.Column('product_brand', sa.String(length=100), nullable=True), - sa.Column('product_description', sa.String(length=255), nullable=True), - sa.Column('product_size', sa.String(length=30), nullable=True), - sa.Column('product_weighed', sa.Boolean(), nullable=True), - sa.Column('department_id', sa.String(length=10), nullable=True), - sa.Column('department_name', sa.String(length=30), nullable=True), - sa.Column('special_order', sa.Boolean(), nullable=True), - sa.Column('case_size', sa.Numeric(precision=10, scale=4), nullable=True), - sa.Column('order_qty', sa.Numeric(precision=10, scale=4), nullable=False), - sa.Column('order_uom', sa.String(length=10), nullable=False), - sa.Column('unit_cost', sa.Numeric(precision=9, scale=5), nullable=True), - sa.Column('unit_price_reg', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('unit_price_sale', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('sale_ends', sa.DateTime(timezone=True), nullable=True), - sa.Column('unit_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('case_price_quoted', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=True), - sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True), - sa.Column('status_code', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['batch_uuid'], ['sideshow_batch_neworder.uuid'], name=op.f('fk_sideshow_batch_neworder_row_batch_uuid_batch_neworder')), - sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid'], name=op.f('fk_sideshow_batch_neworder_row_local_product_uuid_local_product')), - sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid'], name=op.f('fk_sideshow_batch_neworder_row_pending_product_uuid_pending_product')), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_batch_neworder_row')) - ) + op.create_table( + "sideshow_batch_neworder_row", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("batch_uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("sequence", sa.Integer(), nullable=False), + sa.Column("status_text", sa.String(length=255), nullable=True), + sa.Column("modified", sa.DateTime(timezone=True), nullable=True), + sa.Column("product_id", sa.String(length=20), nullable=True), + sa.Column("local_product_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("pending_product_uuid", wuttjamaican.db.util.UUID(), nullable=True), + sa.Column("product_scancode", sa.String(length=14), nullable=True), + sa.Column("product_brand", sa.String(length=100), nullable=True), + sa.Column("product_description", sa.String(length=255), nullable=True), + sa.Column("product_size", sa.String(length=30), nullable=True), + sa.Column("product_weighed", sa.Boolean(), nullable=True), + sa.Column("department_id", sa.String(length=10), nullable=True), + sa.Column("department_name", sa.String(length=30), nullable=True), + sa.Column("special_order", sa.Boolean(), nullable=True), + sa.Column("case_size", sa.Numeric(precision=10, scale=4), nullable=True), + sa.Column("order_qty", sa.Numeric(precision=10, scale=4), nullable=False), + sa.Column("order_uom", sa.String(length=10), nullable=False), + sa.Column("unit_cost", sa.Numeric(precision=9, scale=5), nullable=True), + sa.Column("unit_price_reg", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("unit_price_sale", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("sale_ends", sa.DateTime(timezone=True), nullable=True), + sa.Column("unit_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("case_price_quoted", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("discount_percent", sa.Numeric(precision=5, scale=3), nullable=True), + sa.Column("total_price", sa.Numeric(precision=8, scale=3), nullable=True), + sa.Column("status_code", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["batch_uuid"], + ["sideshow_batch_neworder.uuid"], + name=op.f("fk_sideshow_batch_neworder_row_batch_uuid_batch_neworder"), + ), + sa.ForeignKeyConstraint( + ["local_product_uuid"], + ["sideshow_product_local.uuid"], + name=op.f( + "fk_sideshow_batch_neworder_row_local_product_uuid_local_product" + ), + ), + sa.ForeignKeyConstraint( + ["pending_product_uuid"], + ["sideshow_product_pending.uuid"], + name=op.f( + "fk_sideshow_batch_neworder_row_pending_product_uuid_pending_product" + ), + ), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_batch_neworder_row")), + ) def downgrade() -> None: # sideshow_batch_neworder* - op.drop_table('sideshow_batch_neworder_row') - op.drop_table('sideshow_batch_neworder') + op.drop_table("sideshow_batch_neworder_row") + op.drop_table("sideshow_batch_neworder") # sideshow_order_item_event - op.drop_table('sideshow_order_item_event') + op.drop_table("sideshow_order_item_event") # sideshow_order_item - op.drop_table('sideshow_order_item') + op.drop_table("sideshow_order_item") # sideshow_order - op.drop_table('sideshow_order') + op.drop_table("sideshow_order") # sideshow_product_local - op.drop_table('sideshow_product_local') + op.drop_table("sideshow_product_local") # sideshow_product_pending - op.drop_table('sideshow_product_pending') + op.drop_table("sideshow_product_pending") # sideshow_customer_local - op.drop_table('sideshow_customer_local') + op.drop_table("sideshow_customer_local") # sideshow_customer_pending - op.drop_table('sideshow_customer_pending') + op.drop_table("sideshow_customer_pending") # enums - sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingproductstatus').drop(op.get_bind()) - sa.Enum('PENDING', 'READY', 'RESOLVED', name='pendingcustomerstatus').drop(op.get_bind()) + sa.Enum("PENDING", "READY", "RESOLVED", name="pendingproductstatus").drop( + op.get_bind() + ) + sa.Enum("PENDING", "READY", "RESOLVED", name="pendingcustomerstatus").drop( + op.get_bind() + ) diff --git a/src/sideshow/db/alembic/versions/a4273360d379_add_stores.py b/src/sideshow/db/alembic/versions/a4273360d379_add_stores.py index 79e6242..ba4e03b 100644 --- a/src/sideshow/db/alembic/versions/a4273360d379_add_stores.py +++ b/src/sideshow/db/alembic/versions/a4273360d379_add_stores.py @@ -5,6 +5,7 @@ Revises: 7a6df83afbd4 Create Date: 2025-01-27 17:48:20.638664 """ + from typing import Sequence, Union from alembic import op @@ -13,8 +14,8 @@ import wuttjamaican.db.util # revision identifiers, used by Alembic. -revision: str = 'a4273360d379' -down_revision: Union[str, None] = '7a6df83afbd4' +revision: str = "a4273360d379" +down_revision: Union[str, None] = "7a6df83afbd4" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -22,18 +23,19 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: # sideshow_store - op.create_table('sideshow_store', - sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), - sa.Column('store_id', sa.String(length=10), nullable=False), - sa.Column('name', sa.String(length=100), nullable=False), - sa.Column('archived', sa.Boolean(), nullable=False), - sa.PrimaryKeyConstraint('uuid', name=op.f('pk_sideshow_store')), - sa.UniqueConstraint('store_id', name=op.f('uq_sideshow_store_store_id')), - sa.UniqueConstraint('name', name=op.f('uq_sideshow_store_name')) - ) + op.create_table( + "sideshow_store", + sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), + sa.Column("store_id", sa.String(length=10), nullable=False), + sa.Column("name", sa.String(length=100), nullable=False), + sa.Column("archived", sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint("uuid", name=op.f("pk_sideshow_store")), + sa.UniqueConstraint("store_id", name=op.f("uq_sideshow_store_store_id")), + sa.UniqueConstraint("name", name=op.f("uq_sideshow_store_name")), + ) def downgrade() -> None: # sideshow_store - op.drop_table('sideshow_store') + op.drop_table("sideshow_store") diff --git a/src/sideshow/db/alembic/versions/fd8a2527bd30_add_ignored_status.py b/src/sideshow/db/alembic/versions/fd8a2527bd30_add_ignored_status.py index 63bb061..618899a 100644 --- a/src/sideshow/db/alembic/versions/fd8a2527bd30_add_ignored_status.py +++ b/src/sideshow/db/alembic/versions/fd8a2527bd30_add_ignored_status.py @@ -5,6 +5,7 @@ Revises: 13af2ffbc0e0 Create Date: 2025-02-20 12:08:27.374172 """ + from typing import Sequence, Union from alembic import op @@ -13,8 +14,8 @@ import wuttjamaican.db.util from alembic_postgresql_enum import TableReference # revision identifiers, used by Alembic. -revision: str = 'fd8a2527bd30' -down_revision: Union[str, None] = '13af2ffbc0e0' +revision: str = "fd8a2527bd30" +down_revision: Union[str, None] = "13af2ffbc0e0" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None @@ -23,19 +24,31 @@ def upgrade() -> None: # pendingcustomerstatus op.sync_enum_values( - enum_schema='public', - enum_name='pendingcustomerstatus', - new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'], - affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')], + enum_schema="public", + enum_name="pendingcustomerstatus", + new_values=["PENDING", "READY", "RESOLVED", "IGNORED"], + affected_columns=[ + TableReference( + table_schema="public", + table_name="sideshow_customer_pending", + column_name="status", + ) + ], enum_values_to_rename=[], ) # pendingproductstatus op.sync_enum_values( - enum_schema='public', - enum_name='pendingproductstatus', - new_values=['PENDING', 'READY', 'RESOLVED', 'IGNORED'], - affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')], + enum_schema="public", + enum_name="pendingproductstatus", + new_values=["PENDING", "READY", "RESOLVED", "IGNORED"], + affected_columns=[ + TableReference( + table_schema="public", + table_name="sideshow_product_pending", + column_name="status", + ) + ], enum_values_to_rename=[], ) @@ -44,18 +57,30 @@ def downgrade() -> None: # pendingproductstatus op.sync_enum_values( - enum_schema='public', - enum_name='pendingproductstatus', - new_values=['PENDING', 'READY', 'RESOLVED'], - affected_columns=[TableReference(table_schema='public', table_name='sideshow_product_pending', column_name='status')], + enum_schema="public", + enum_name="pendingproductstatus", + new_values=["PENDING", "READY", "RESOLVED"], + affected_columns=[ + TableReference( + table_schema="public", + table_name="sideshow_product_pending", + column_name="status", + ) + ], enum_values_to_rename=[], ) # pendingcustomerstatus op.sync_enum_values( - enum_schema='public', - enum_name='pendingcustomerstatus', - new_values=['PENDING', 'READY', 'RESOLVED'], - affected_columns=[TableReference(table_schema='public', table_name='sideshow_customer_pending', column_name='status')], + enum_schema="public", + enum_name="pendingcustomerstatus", + new_values=["PENDING", "READY", "RESOLVED"], + affected_columns=[ + TableReference( + table_schema="public", + table_name="sideshow_customer_pending", + column_name="status", + ) + ], enum_values_to_rename=[], ) diff --git a/src/sideshow/db/model/batch/neworder.py b/src/sideshow/db/model/batch/neworder.py index 6a64372..bae0a79 100644 --- a/src/sideshow/db/model/batch/neworder.py +++ b/src/sideshow/db/model/batch/neworder.py @@ -47,10 +47,11 @@ class NewOrderBatch(model.BatchMixin, model.Base): Generic batch attributes (undocumented below) are inherited from :class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchMixin`. """ - __tablename__ = 'sideshow_batch_neworder' - __batchrow_class__ = 'NewOrderBatchRow' - batch_type = 'neworder' + __tablename__ = "sideshow_batch_neworder" + __batchrow_class__ = "NewOrderBatchRow" + + batch_type = "neworder" """ Official :term:`batch type` key. """ @@ -58,72 +59,102 @@ class NewOrderBatch(model.BatchMixin, model.Base): @declared_attr def __table_args__(cls): return cls.__default_table_args__() + ( - sa.ForeignKeyConstraint(['local_customer_uuid'], ['sideshow_customer_local.uuid']), - sa.ForeignKeyConstraint(['pending_customer_uuid'], ['sideshow_customer_pending.uuid']), + sa.ForeignKeyConstraint( + ["local_customer_uuid"], ["sideshow_customer_local.uuid"] + ), + sa.ForeignKeyConstraint( + ["pending_customer_uuid"], ["sideshow_customer_pending.uuid"] + ), ) - STATUS_OK = 1 + STATUS_OK = 1 STATUS = { - STATUS_OK : "ok", + STATUS_OK: "ok", } - store_id = sa.Column(sa.String(length=10), nullable=True, doc=""" + 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=""" + 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) @declared_attr def local_customer(cls): return orm.relationship( - 'LocalCustomer', - back_populates='new_order_batches', + "LocalCustomer", + back_populates="new_order_batches", 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 = sa.Column(model.UUID(), nullable=True) @declared_attr def pending_customer(cls): return orm.relationship( - 'PendingCustomer', - back_populates='new_order_batches', + "PendingCustomer", + back_populates="new_order_batches", 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(sa.String(length=100), nullable=True, doc=""" + customer_name = sa.Column( + sa.String(length=100), + nullable=True, + doc=""" Name for the customer account. - """) + """, + ) - phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" + phone_number = sa.Column( + 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 = 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=""" + 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): @@ -134,82 +165,96 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): Generic row attributes (undocumented below) are inherited from :class:`~wuttjamaican:wuttjamaican.db.model.batch.BatchRowMixin`. """ - __tablename__ = 'sideshow_batch_neworder_row' + + __tablename__ = "sideshow_batch_neworder_row" __batch_class__ = NewOrderBatch @declared_attr def __table_args__(cls): return cls.__default_table_args__() + ( - sa.ForeignKeyConstraint(['local_product_uuid'], ['sideshow_product_local.uuid']), - sa.ForeignKeyConstraint(['pending_product_uuid'], ['sideshow_product_pending.uuid']), + sa.ForeignKeyConstraint( + ["local_product_uuid"], ["sideshow_product_local.uuid"] + ), + sa.ForeignKeyConstraint( + ["pending_product_uuid"], ["sideshow_product_pending.uuid"] + ), ) - STATUS_OK = 1 + STATUS_OK = 1 """ This is the default value for :attr:`status_code`. All rows are considered "OK" if they have either a :attr:`product_id` or :attr:`pending_product`. """ - STATUS_MISSING_PRODUCT = 2 + STATUS_MISSING_PRODUCT = 2 """ Status code indicating the row has no :attr:`product_id` or :attr:`pending_product` set. """ - STATUS_MISSING_ORDER_QTY = 3 + STATUS_MISSING_ORDER_QTY = 3 """ Status code indicating the row has no :attr:`order_qty` and/or :attr:`order_uom` set. """ STATUS = { - STATUS_OK : "ok", - STATUS_MISSING_PRODUCT : "missing product", - STATUS_MISSING_ORDER_QTY : "missing order qty/uom", + STATUS_OK: "ok", + STATUS_MISSING_PRODUCT: "missing product", + STATUS_MISSING_ORDER_QTY: "missing order qty/uom", } """ Dict of possible status code -> label options. """ - product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" + 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) @declared_attr def local_product(cls): return orm.relationship( - 'LocalProduct', - back_populates='new_order_batch_rows', + "LocalProduct", + back_populates="new_order_batch_rows", 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 = sa.Column(model.UUID(), nullable=True) @declared_attr def pending_product(cls): return orm.relationship( - 'PendingProduct', - back_populates='new_order_batch_rows', + "PendingProduct", + back_populates="new_order_batch_rows", 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(sa.String(length=14), nullable=True, doc=""" + product_scancode = sa.Column( + sa.String(length=14), + nullable=True, + doc=""" Scancode for the product, as string. .. note:: @@ -221,61 +266,109 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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_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=""" + 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=""" + 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 @@ -284,31 +377,51 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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`. @@ -317,21 +430,33 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): :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=""" + 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 = 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=""" + 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. @@ -342,7 +467,8 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): * :attr:`order_uom` * :attr:`case_size` * :attr:`discount_percent` - """) + """, + ) def __str__(self): return str(self.pending_product or self.product_description or "") diff --git a/src/sideshow/db/model/customers.py b/src/sideshow/db/model/customers.py index aa62098..b106a13 100644 --- a/src/sideshow/db/model/customers.py +++ b/src/sideshow/db/model/customers.py @@ -42,25 +42,45 @@ class CustomerMixin: * :class:`PendingCustomer` """ - full_name = sa.Column(sa.String(length=100), nullable=True, doc=""" + full_name = sa.Column( + sa.String(length=100), + nullable=True, + doc=""" Full display name for the customer account. - """) + """, + ) - first_name = sa.Column(sa.String(length=50), nullable=True, doc=""" + first_name = sa.Column( + sa.String(length=50), + nullable=True, + doc=""" First name of the customer. - """) + """, + ) - last_name = sa.Column(sa.String(length=50), nullable=True, doc=""" + last_name = sa.Column( + sa.String(length=50), + nullable=True, + doc=""" Last name of the customer. - """) + """, + ) - phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" + phone_number = sa.Column( + 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 = sa.Column( + sa.String(length=255), + nullable=True, + doc=""" Email address for the customer. - """) + """, + ) def __str__(self): return self.full_name or "" @@ -78,35 +98,42 @@ class LocalCustomer(CustomerMixin, model.Base): :term:`pending customer` is executed, a new record is added to this local customers table, for lookup next time. """ - __tablename__ = 'sideshow_customer_local' + + __tablename__ = "sideshow_customer_local" uuid = model.uuid_column() - external_id = sa.Column(sa.String(length=20), nullable=True, doc=""" + external_id = sa.Column( + sa.String(length=20), + nullable=True, + doc=""" ID of the proper customer account associated with this record, if applicable. - """) + """, + ) orders = orm.relationship( - 'Order', - order_by='Order.order_id.desc()', - back_populates='local_customer', + "Order", + order_by="Order.order_id.desc()", + back_populates="local_customer", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.orders.Order` records associated with this customer. - """) + """, + ) new_order_batches = orm.relationship( - 'NewOrderBatch', - order_by='NewOrderBatch.id.desc()', - back_populates='local_customer', + "NewOrderBatch", + order_by="NewOrderBatch.id.desc()", + back_populates="local_customer", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatch` records associated with this customer. - """) + """, + ) class PendingCustomer(CustomerMixin, model.Base): @@ -121,25 +148,38 @@ class PendingCustomer(CustomerMixin, model.Base): is executed, a new record is added to the :term:`local customers ` table, for lookup next time. """ - __tablename__ = 'sideshow_customer_pending' + + __tablename__ = "sideshow_customer_pending" uuid = model.uuid_column() - customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" + customer_id = sa.Column( + sa.String(length=20), + nullable=True, + doc=""" ID of the proper customer account associated with this record, if applicable. - """) + """, + ) - status = sa.Column(sa.Enum(PendingCustomerStatus), nullable=False, doc=""" + status = sa.Column( + sa.Enum(PendingCustomerStatus), + nullable=False, + doc=""" Status code for the customer record. - """) + """, + ) - created = sa.Column(sa.DateTime(timezone=True), nullable=False, - default=datetime.datetime.now, doc=""" + created = sa.Column( + sa.DateTime(timezone=True), + nullable=False, + default=datetime.datetime.now, + doc=""" Timestamp when the customer record was created. - """) + """, + ) - created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) + created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False) created_by = orm.relationship( model.User, cascade_backrefs=False, @@ -147,25 +187,28 @@ class PendingCustomer(CustomerMixin, model.Base): Reference to the :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who created the customer record. - """) + """, + ) orders = orm.relationship( - 'Order', - order_by='Order.order_id.desc()', + "Order", + order_by="Order.order_id.desc()", cascade_backrefs=False, - back_populates='pending_customer', + back_populates="pending_customer", doc=""" List of :class:`~sideshow.db.model.orders.Order` records associated with this customer. - """) + """, + ) new_order_batches = orm.relationship( - 'NewOrderBatch', - order_by='NewOrderBatch.id.desc()', + "NewOrderBatch", + order_by="NewOrderBatch.id.desc()", cascade_backrefs=False, - back_populates='pending_customer', + back_populates="pending_customer", doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatch` records associated with this customer. - """) + """, + ) diff --git a/src/sideshow/db/model/orders.py b/src/sideshow/db/model/orders.py index 8d01d27..c5c9218 100644 --- a/src/sideshow/db/model/orders.py +++ b/src/sideshow/db/model/orders.py @@ -41,93 +41,134 @@ class Order(model.Base): Usually, orders are created by way of a :class:`~sideshow.db.model.batch.neworder.NewOrderBatch`. """ - __tablename__ = 'sideshow_order' + + __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'], + "excludes": ["items"], } uuid = model.uuid_column() - order_id = sa.Column(sa.Integer(), nullable=False, doc=""" + 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(sa.String(length=10), nullable=True, doc=""" + store_id = sa.Column( + sa.String(length=10), + nullable=True, + doc=""" 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', + "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=""" + 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 = model.uuid_fk_column('sideshow_customer_local.uuid', nullable=True) + local_customer_uuid = model.uuid_fk_column( + "sideshow_customer_local.uuid", nullable=True + ) local_customer = orm.relationship( - 'LocalCustomer', + "LocalCustomer", cascade_backrefs=False, - back_populates='orders', + 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_uuid = model.uuid_fk_column( + "sideshow_customer_pending.uuid", nullable=True + ) pending_customer = orm.relationship( - 'PendingCustomer', + "PendingCustomer", cascade_backrefs=False, - back_populates='orders', + 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(sa.String(length=100), nullable=True, doc=""" + customer_name = sa.Column( + sa.String(length=100), + nullable=True, + doc=""" Name for the customer account. - """) + """, + ) - phone_number = sa.Column(sa.String(length=20), nullable=True, doc=""" + phone_number = sa.Column( + 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 = 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=""" + 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. - """) + """, + ) - created = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" + 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_uuid = model.uuid_fk_column("user.uuid", nullable=False) created_by = orm.relationship( model.User, cascade_backrefs=False, @@ -135,17 +176,19 @@ class Order(model.Base): 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', + "OrderItem", + collection_class=ordering_list("sequence", count_from=1), + cascade="all, delete-orphan", cascade_backrefs=False, - back_populates='order', + back_populates="order", doc=""" List of :class:`OrderItem` records belonging to the order. - """) + """, + ) def __str__(self): return str(self.order_id) @@ -159,58 +202,77 @@ class OrderItem(model.Base): :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` records. """ - __tablename__ = 'sideshow_order_item' + + __tablename__ = "sideshow_order_item" uuid = model.uuid_column() - order_uuid = model.uuid_fk_column('sideshow_order.uuid', nullable=False) + order_uuid = model.uuid_fk_column("sideshow_order.uuid", nullable=False) order = orm.relationship( Order, cascade_backrefs=False, - back_populates='items', + back_populates="items", doc=""" Reference to the :class:`Order` to which the item belongs. - """) + """, + ) - sequence = sa.Column(sa.Integer(), nullable=False, doc=""" + 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(sa.String(length=20), nullable=True, doc=""" + 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 = model.uuid_fk_column('sideshow_product_local.uuid', nullable=True) + local_product_uuid = model.uuid_fk_column( + "sideshow_product_local.uuid", nullable=True + ) local_product = orm.relationship( - 'LocalProduct', + "LocalProduct", cascade_backrefs=False, - back_populates='order_items', + 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_uuid = model.uuid_fk_column( + "sideshow_product_pending.uuid", nullable=True + ) pending_product = orm.relationship( - 'PendingProduct', + "PendingProduct", cascade_backrefs=False, - back_populates='order_items', + 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(sa.String(length=14), nullable=True, doc=""" + product_scancode = sa.Column( + sa.String(length=14), + nullable=True, + doc=""" Scancode for the product, as string. .. note:: @@ -222,109 +284,189 @@ class OrderItem(model.Base): 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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_size = sa.Column( + sa.Numeric(precision=10, scale=4), + nullable=True, + doc=""" Case pack count for the product, if known. - """) + """, + ) - order_qty = sa.Column(sa.Numeric(precision=10, scale=4), nullable=False, doc=""" + 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=""" + 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`. - """) + """, + ) - unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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`. - """) + """, + ) - case_price_quoted = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" + 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 = 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=""" + 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. @@ -335,41 +477,57 @@ class OrderItem(model.Base): * :attr:`order_uom` * :attr:`case_size` * :attr:`discount_percent` - """) + """, + ) - status_code = sa.Column(sa.Integer(), nullable=False, doc=""" + status_code = sa.Column( + sa.Integer(), + nullable=False, + doc=""" Code indicating current status for the order item. - """) + """, + ) - paid_amount = sa.Column(sa.Numeric(precision=8, scale=3), nullable=False, default=0, doc=""" + paid_amount = sa.Column( + sa.Numeric(precision=8, scale=3), + nullable=False, + default=0, + doc=""" Amount which the customer has paid toward the :attr:`total_price` of the item. - """) + """, + ) - payment_transaction_number = sa.Column(sa.String(length=20), nullable=True, doc=""" + payment_transaction_number = sa.Column( + sa.String(length=20), + nullable=True, + doc=""" Transaction number in which payment for the order was taken, if applicable/known. - """) + """, + ) events = orm.relationship( - 'OrderItemEvent', - order_by='OrderItemEvent.occurred, OrderItemEvent.uuid', - cascade='all, delete-orphan', + "OrderItemEvent", + order_by="OrderItemEvent.occurred, OrderItemEvent.uuid", + cascade="all, delete-orphan", cascade_backrefs=False, - back_populates='item', + back_populates="item", doc=""" List of :class:`OrderItemEvent` records for the item. - """) + """, + ) @property def full_description(self): """ """ fields = [ - self.product_brand or '', - self.product_description or '', - self.product_size or ''] + self.product_brand or "", + self.product_description or "", + self.product_size or "", + ] fields = [f.strip() for f in fields if f.strip()] - return ' '.join(fields) + return " ".join(fields) def __str__(self): return self.full_description @@ -379,8 +537,8 @@ class OrderItem(model.Base): Convenience method to add a new :class:`OrderItemEvent` for the item. """ - kwargs['type_code'] = type_code - kwargs['actor'] = user + kwargs["type_code"] = type_code + kwargs["actor"] = user self.events.append(OrderItemEvent(**kwargs)) @@ -388,37 +546,53 @@ class OrderItemEvent(model.Base): """ An event in the life of an :term:`order item`. """ - __tablename__ = 'sideshow_order_item_event' + + __tablename__ = "sideshow_order_item_event" uuid = model.uuid_column() - item_uuid = model.uuid_fk_column('sideshow_order_item.uuid', nullable=False) + item_uuid = model.uuid_fk_column("sideshow_order_item.uuid", nullable=False) item = orm.relationship( OrderItem, cascade_backrefs=False, - back_populates='events', + back_populates="events", doc=""" Reference to the :class:`OrderItem` to which the event pertains. - """) + """, + ) - type_code = sa.Column(sa.Integer, nullable=False, doc=""" + type_code = sa.Column( + sa.Integer, + nullable=False, + doc=""" Code indicating the type of event; values must be defined in :data:`~sideshow.enum.ORDER_ITEM_EVENT`. - """) + """, + ) - occurred = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" + occurred = sa.Column( + sa.DateTime(timezone=True), + nullable=False, + default=datetime.datetime.now, + doc=""" Date and time when the event occurred. - """) + """, + ) - actor_uuid = model.uuid_fk_column('user.uuid', nullable=False) + actor_uuid = model.uuid_fk_column("user.uuid", nullable=False) actor = orm.relationship( model.User, doc=""" :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who performed the action. - """) + """, + ) - note = sa.Column(sa.Text(), nullable=True, doc=""" + note = sa.Column( + sa.Text(), + nullable=True, + doc=""" Optional note recorded for the event. - """) + """, + ) diff --git a/src/sideshow/db/model/products.py b/src/sideshow/db/model/products.py index 7c088d9..6842104 100644 --- a/src/sideshow/db/model/products.py +++ b/src/sideshow/db/model/products.py @@ -42,7 +42,10 @@ class ProductMixin: * :class:`PendingProduct` """ - scancode = sa.Column(sa.String(length=14), nullable=True, doc=""" + scancode = sa.Column( + sa.String(length=14), + nullable=True, + doc=""" Scancode for the product, as string. .. note:: @@ -54,73 +57,123 @@ class ProductMixin: 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. - """) + """, + ) - brand_name = sa.Column(sa.String(length=100), nullable=True, doc=""" + brand_name = sa.Column( + sa.String(length=100), + nullable=True, + doc=""" Brand name for the product - up to 100 chars. - """) + """, + ) - description = sa.Column(sa.String(length=255), nullable=True, doc=""" + description = sa.Column( + sa.String(length=255), + nullable=True, + doc=""" Description for the product - up to 255 chars. - """) + """, + ) - size = sa.Column(sa.String(length=30), nullable=True, doc=""" + size = sa.Column( + sa.String(length=30), + nullable=True, + doc=""" Size of the product, as string - up to 30 chars. - """) + """, + ) - weighed = sa.Column(sa.Boolean(), nullable=True, doc=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=""" + 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=9, scale=4), nullable=True, doc=""" + case_size = sa.Column( + sa.Numeric(precision=9, scale=4), + nullable=True, + doc=""" Case pack count for the product, if known. - """) + """, + ) - unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" + 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=""" + unit_price_reg = sa.Column( + sa.Numeric(precision=8, scale=3), + nullable=True, + doc=""" Regular price for a "unit" of the product. - """) + """, + ) - notes = sa.Column(sa.Text(), nullable=True, doc=""" + notes = sa.Column( + sa.Text(), + nullable=True, + doc=""" Arbitrary notes regarding the product, if applicable. - """) + """, + ) @property def full_description(self): """ """ - fields = [ - self.brand_name or '', - self.description or '', - self.size or ''] + fields = [self.brand_name or "", self.description or "", self.size or ""] fields = [f.strip() for f in fields if f.strip()] - return ' '.join(fields) + return " ".join(fields) def __str__(self): return self.full_description @@ -139,33 +192,40 @@ class LocalProduct(ProductMixin, model.Base): record(s) will be added to this local products table, for lookup next time. """ - __tablename__ = 'sideshow_product_local' + + __tablename__ = "sideshow_product_local" uuid = model.uuid_column() - external_id = sa.Column(sa.String(length=20), nullable=True, doc=""" + external_id = sa.Column( + sa.String(length=20), + nullable=True, + doc=""" ID of the true external product associated with this record, if applicable. - """) + """, + ) order_items = orm.relationship( - 'OrderItem', - back_populates='local_product', + "OrderItem", + back_populates="local_product", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.orders.OrderItem` records associated with this product. - """) + """, + ) new_order_batch_rows = orm.relationship( - 'NewOrderBatchRow', - back_populates='local_product', + "NewOrderBatchRow", + back_populates="local_product", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` records associated with this product. - """) + """, + ) class PendingProduct(ProductMixin, model.Base): @@ -180,25 +240,38 @@ class PendingProduct(ProductMixin, model.Base): is executed, new record(s) will be added to the :term:`local products ` table, for lookup next time. """ - __tablename__ = 'sideshow_product_pending' + + __tablename__ = "sideshow_product_pending" uuid = model.uuid_column() - product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" + product_id = sa.Column( + sa.String(length=20), + nullable=True, + doc=""" ID of the :term:`external product` associated with this record, if applicable/known. - """) + """, + ) - status = sa.Column(sa.Enum(PendingProductStatus), nullable=False, doc=""" + status = sa.Column( + sa.Enum(PendingProductStatus), + nullable=False, + doc=""" Status code for the product record. - """) + """, + ) - created = sa.Column(sa.DateTime(timezone=True), nullable=False, - default=datetime.datetime.now, doc=""" + created = sa.Column( + sa.DateTime(timezone=True), + nullable=False, + default=datetime.datetime.now, + doc=""" Timestamp when the product record was created. - """) + """, + ) - created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) + created_by_uuid = model.uuid_fk_column("user.uuid", nullable=False) created_by = orm.relationship( model.User, cascade_backrefs=False, @@ -206,23 +279,26 @@ class PendingProduct(ProductMixin, model.Base): Reference to the :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who created the product record. - """) + """, + ) order_items = orm.relationship( - 'OrderItem', - back_populates='pending_product', + "OrderItem", + back_populates="pending_product", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.orders.OrderItem` records associated with this product. - """) + """, + ) new_order_batch_rows = orm.relationship( - 'NewOrderBatchRow', - back_populates='pending_product', + "NewOrderBatchRow", + back_populates="pending_product", cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` records associated with this product. - """) + """, + ) diff --git a/src/sideshow/db/model/stores.py b/src/sideshow/db/model/stores.py index 4b01d02..4f6d7c2 100644 --- a/src/sideshow/db/model/stores.py +++ b/src/sideshow/db/model/stores.py @@ -33,22 +33,38 @@ class Store(model.Base): """ Represents a physical location for the business. """ - __tablename__ = 'sideshow_store' + + __tablename__ = "sideshow_store" uuid = model.uuid_column() - store_id = sa.Column(sa.String(length=10), nullable=False, unique=True, doc=""" + store_id = sa.Column( + sa.String(length=10), + nullable=False, + unique=True, + doc=""" Unique ID for the store. - """) + """, + ) - name = sa.Column(sa.String(length=100), nullable=False, unique=True, doc=""" + name = sa.Column( + sa.String(length=100), + nullable=False, + unique=True, + doc=""" Display name for the store (must be unique!). - """) + """, + ) - archived = sa.Column(sa.Boolean(), nullable=False, default=False, doc=""" + archived = sa.Column( + sa.Boolean(), + nullable=False, + default=False, + doc=""" Indicates the store has been "retired" essentially, and mostly hidden from view. - """) + """, + ) def __str__(self): return self.get_display() @@ -57,6 +73,6 @@ class Store(model.Base): """ Returns the display string for the store, e.g. "001 Acme Goods". """ - return ' '.join([(self.store_id or '').strip(), - (self.name or '').strip()])\ - .strip() + return " ".join( + [(self.store_id or "").strip(), (self.name or "").strip()] + ).strip() diff --git a/src/sideshow/enum.py b/src/sideshow/enum.py index 4031824..e9abd8a 100644 --- a/src/sideshow/enum.py +++ b/src/sideshow/enum.py @@ -30,7 +30,7 @@ from collections import OrderedDict from wuttjamaican.enum import * -ORDER_UOM_CASE = 'CS' +ORDER_UOM_CASE = "CS" """ UOM code for ordering a "case" of product. @@ -38,7 +38,7 @@ Sideshow will treat "case" orders somewhat differently as compared to "unit" orders. """ -ORDER_UOM_UNIT = 'EA' +ORDER_UOM_UNIT = "EA" """ UOM code for ordering a "unit" of product. @@ -47,7 +47,7 @@ the same by Sideshow, whereas "case" orders are treated somewhat differently. """ -ORDER_UOM_KILOGRAM = 'KG' +ORDER_UOM_KILOGRAM = "KG" """ UOM code for ordering a "kilogram" of product. @@ -57,7 +57,7 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is true. """ -ORDER_UOM_POUND = 'LB' +ORDER_UOM_POUND = "LB" """ UOM code for ordering a "pound" of product. @@ -67,12 +67,14 @@ e.g. :attr:`~sideshow.db.model.orders.OrderItem.product_weighed` is true. """ -ORDER_UOM = OrderedDict([ - (ORDER_UOM_CASE, "Cases"), - (ORDER_UOM_UNIT, "Units"), - (ORDER_UOM_KILOGRAM, "Kilograms"), - (ORDER_UOM_POUND, "Pounds"), -]) +ORDER_UOM = OrderedDict( + [ + (ORDER_UOM_CASE, "Cases"), + (ORDER_UOM_UNIT, "Units"), + (ORDER_UOM_KILOGRAM, "Kilograms"), + (ORDER_UOM_POUND, "Pounds"), + ] +) """ Dict of possible code -> label options for ordering unit of measure. @@ -88,10 +90,11 @@ class PendingCustomerStatus(Enum): Enum values for :attr:`sideshow.db.model.customers.PendingCustomer.status`. """ - PENDING = 'pending' - READY = 'ready' - RESOLVED = 'resolved' - IGNORED = 'ignored' + + PENDING = "pending" + READY = "ready" + RESOLVED = "resolved" + IGNORED = "ignored" class PendingProductStatus(Enum): @@ -99,29 +102,30 @@ class PendingProductStatus(Enum): Enum values for :attr:`sideshow.db.model.products.PendingProduct.status`. """ - PENDING = 'pending' - READY = 'ready' - RESOLVED = 'resolved' - IGNORED = 'ignored' + + PENDING = "pending" + READY = "ready" + RESOLVED = "resolved" + IGNORED = "ignored" ######################################## # Order Item Status ######################################## -ORDER_ITEM_STATUS_UNINITIATED = 1 +ORDER_ITEM_STATUS_UNINITIATED = 1 """ Indicates the item is "not yet initiated" - this probably is not useful but exists as a possibility just in case. """ -ORDER_ITEM_STATUS_INITIATED = 10 +ORDER_ITEM_STATUS_INITIATED = 10 """ Indicates the item is "initiated" (aka. created) but not yet "ready" for buyer/PO. This may imply the price needs confirmation etc. """ -ORDER_ITEM_STATUS_PAID_BEFORE = 50 +ORDER_ITEM_STATUS_PAID_BEFORE = 50 """ Indicates the customer has fully paid for the item, up-front before the buyer places PO etc. It implies the item is not yet "ready" for @@ -129,101 +133,103 @@ some reason. """ # TODO: deprecate / remove this one -ORDER_ITEM_STATUS_PAID = ORDER_ITEM_STATUS_PAID_BEFORE +ORDER_ITEM_STATUS_PAID = ORDER_ITEM_STATUS_PAID_BEFORE -ORDER_ITEM_STATUS_READY = 100 +ORDER_ITEM_STATUS_READY = 100 """ Indicates the item is "ready" for buyer to include it on a vendor purchase order. """ -ORDER_ITEM_STATUS_PLACED = 200 +ORDER_ITEM_STATUS_PLACED = 200 """ Indicates the buyer has placed a vendor purchase order which includes this item. The item is thereby "on order" until the truck arrives. """ -ORDER_ITEM_STATUS_RECEIVED = 300 +ORDER_ITEM_STATUS_RECEIVED = 300 """ Indicates the item has been received as part of a vendor delivery. The item is thereby "on hand" until customer comes in for pickup. """ -ORDER_ITEM_STATUS_CONTACTED = 350 +ORDER_ITEM_STATUS_CONTACTED = 350 """ Indicates the customer has been notified that the item is "on hand" and awaiting their pickup. """ -ORDER_ITEM_STATUS_CONTACT_FAILED = 375 +ORDER_ITEM_STATUS_CONTACT_FAILED = 375 """ Indicates the attempt(s) to notify customer have failed. The item is on hand but the customer does not know to pickup. """ -ORDER_ITEM_STATUS_DELIVERED = 500 +ORDER_ITEM_STATUS_DELIVERED = 500 """ Indicates the customer has picked up the item. """ -ORDER_ITEM_STATUS_PAID_AFTER = 550 +ORDER_ITEM_STATUS_PAID_AFTER = 550 """ Indicates the customer has fully paid for the item, as part of their pickup. This completes the cycle for orders which require payment on the tail end. """ -ORDER_ITEM_STATUS_CANCELED = 900 +ORDER_ITEM_STATUS_CANCELED = 900 """ Indicates the order item has been canceled. """ -ORDER_ITEM_STATUS_REFUND_PENDING = 910 +ORDER_ITEM_STATUS_REFUND_PENDING = 910 """ Indicates the order item has been canceled, and the customer is due a (pending) refund. """ -ORDER_ITEM_STATUS_REFUNDED = 920 +ORDER_ITEM_STATUS_REFUNDED = 920 """ Indicates the order item has been canceled, and the customer has been given a refund. """ -ORDER_ITEM_STATUS_RESTOCKED = 930 +ORDER_ITEM_STATUS_RESTOCKED = 930 """ Indicates the product has been restocked, e.g. after the order item was canceled. """ -ORDER_ITEM_STATUS_EXPIRED = 940 +ORDER_ITEM_STATUS_EXPIRED = 940 """ Indicates the order item and/or product has expired. """ -ORDER_ITEM_STATUS_INACTIVE = 950 +ORDER_ITEM_STATUS_INACTIVE = 950 """ Indicates the order item has become inactive. """ -ORDER_ITEM_STATUS = OrderedDict([ - (ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"), - (ORDER_ITEM_STATUS_INITIATED, "initiated"), - (ORDER_ITEM_STATUS_PAID_BEFORE, "paid"), - (ORDER_ITEM_STATUS_READY, "ready"), - (ORDER_ITEM_STATUS_PLACED, "placed"), - (ORDER_ITEM_STATUS_RECEIVED, "received"), - (ORDER_ITEM_STATUS_CONTACTED, "contacted"), - (ORDER_ITEM_STATUS_CONTACT_FAILED, "contact failed"), - (ORDER_ITEM_STATUS_DELIVERED, "delivered"), - (ORDER_ITEM_STATUS_PAID_AFTER, "paid"), - (ORDER_ITEM_STATUS_CANCELED, "canceled"), - (ORDER_ITEM_STATUS_REFUND_PENDING, "refund pending"), - (ORDER_ITEM_STATUS_REFUNDED, "refunded"), - (ORDER_ITEM_STATUS_RESTOCKED, "restocked"), - (ORDER_ITEM_STATUS_EXPIRED, "expired"), - (ORDER_ITEM_STATUS_INACTIVE, "inactive"), -]) +ORDER_ITEM_STATUS = OrderedDict( + [ + (ORDER_ITEM_STATUS_UNINITIATED, "uninitiated"), + (ORDER_ITEM_STATUS_INITIATED, "initiated"), + (ORDER_ITEM_STATUS_PAID_BEFORE, "paid"), + (ORDER_ITEM_STATUS_READY, "ready"), + (ORDER_ITEM_STATUS_PLACED, "placed"), + (ORDER_ITEM_STATUS_RECEIVED, "received"), + (ORDER_ITEM_STATUS_CONTACTED, "contacted"), + (ORDER_ITEM_STATUS_CONTACT_FAILED, "contact failed"), + (ORDER_ITEM_STATUS_DELIVERED, "delivered"), + (ORDER_ITEM_STATUS_PAID_AFTER, "paid"), + (ORDER_ITEM_STATUS_CANCELED, "canceled"), + (ORDER_ITEM_STATUS_REFUND_PENDING, "refund pending"), + (ORDER_ITEM_STATUS_REFUNDED, "refunded"), + (ORDER_ITEM_STATUS_RESTOCKED, "restocked"), + (ORDER_ITEM_STATUS_EXPIRED, "expired"), + (ORDER_ITEM_STATUS_INACTIVE, "inactive"), + ] +) """ Dict of possible code -> label options for :term:`order item` status. @@ -237,19 +243,19 @@ These codes are referenced by: # Order Item Event Type ######################################## -ORDER_ITEM_EVENT_INITIATED = 10 +ORDER_ITEM_EVENT_INITIATED = 10 """ Indicates the item was "initiated" - this occurs when the :term:`order` is first created. """ -ORDER_ITEM_EVENT_PRICE_CONFIRMED = 20 +ORDER_ITEM_EVENT_PRICE_CONFIRMED = 20 """ Indicates the item's price was confirmed by a user who is authorized to do that. """ -ORDER_ITEM_EVENT_PAYMENT_RECEIVED = 50 +ORDER_ITEM_EVENT_PAYMENT_RECEIVED = 50 """ Indicates payment was received for the item. This may occur toward the beginning, or toward the end, of the item's life cycle depending @@ -257,9 +263,9 @@ on app configuration etc. """ # TODO: deprecate / remove this -ORDER_ITEM_EVENT_PAID = ORDER_ITEM_EVENT_PAYMENT_RECEIVED +ORDER_ITEM_EVENT_PAID = ORDER_ITEM_EVENT_PAYMENT_RECEIVED -ORDER_ITEM_EVENT_READY = 100 +ORDER_ITEM_EVENT_READY = 100 """ Indicates the item has become "ready" for buyer placement on a new vendor purchase order. Often this will occur when the :term:`order` @@ -267,128 +273,130 @@ is first created, if the data is suitable. However this may be delayed if e.g. the price needs confirmation. """ -ORDER_ITEM_EVENT_CUSTOMER_RESOLVED = 120 +ORDER_ITEM_EVENT_CUSTOMER_RESOLVED = 120 """ Indicates the customer for the :term:`order` has been assigned to a "proper" (existing) account. This may happen (after the fact) if the order was first created with a new/unknown customer. """ -ORDER_ITEM_EVENT_PRODUCT_RESOLVED = 140 +ORDER_ITEM_EVENT_PRODUCT_RESOLVED = 140 """ Indicates the product for the :term:`order item` has been assigned to a "proper" (existing) product record. This may happen (after the fact) if the order was first created with a new/unknown product. """ -ORDER_ITEM_EVENT_PLACED = 200 +ORDER_ITEM_EVENT_PLACED = 200 """ Indicates the buyer has placed a vendor purchase order which includes this item. So the item is "on order" until the truck arrives. """ -ORDER_ITEM_EVENT_REORDER = 210 +ORDER_ITEM_EVENT_REORDER = 210 """ Indicates the item was not received with the delivery on which it was expected, and must be re-ordered from vendor. """ -ORDER_ITEM_EVENT_RECEIVED = 300 +ORDER_ITEM_EVENT_RECEIVED = 300 """ Indicates the receiver has found the item while receiving a vendor delivery. The item is set aside and is "on hand" until customer comes in to pick it up. """ -ORDER_ITEM_EVENT_CONTACTED = 350 +ORDER_ITEM_EVENT_CONTACTED = 350 """ Indicates the customer has been contacted, to notify them of the item being on hand and ready for pickup. """ -ORDER_ITEM_EVENT_CONTACT_FAILED = 375 +ORDER_ITEM_EVENT_CONTACT_FAILED = 375 """ Indicates an attempt was made to contact the customer, to notify them of item being on hand, but the attempt failed, e.g. due to bad phone or email on file. """ -ORDER_ITEM_EVENT_DELIVERED = 500 +ORDER_ITEM_EVENT_DELIVERED = 500 """ Indicates the customer has picked up the item. """ -ORDER_ITEM_EVENT_STATUS_CHANGE = 700 +ORDER_ITEM_EVENT_STATUS_CHANGE = 700 """ Indicates a manual status change was made. Such an event should ideally contain a note with further explanation. """ -ORDER_ITEM_EVENT_NOTE_ADDED = 750 +ORDER_ITEM_EVENT_NOTE_ADDED = 750 """ Indicates an arbitrary note was added. """ -ORDER_ITEM_EVENT_CANCELED = 900 +ORDER_ITEM_EVENT_CANCELED = 900 """ Indicates the :term:`order item` was canceled. """ -ORDER_ITEM_EVENT_REFUND_PENDING = 910 +ORDER_ITEM_EVENT_REFUND_PENDING = 910 """ Indicates the customer is due a (pending) refund for the item. """ -ORDER_ITEM_EVENT_REFUNDED = 920 +ORDER_ITEM_EVENT_REFUNDED = 920 """ Indicates the customer has been refunded for the item. """ -ORDER_ITEM_EVENT_RESTOCKED = 930 +ORDER_ITEM_EVENT_RESTOCKED = 930 """ Indicates the product has been restocked, e.g. due to the order item being canceled. """ -ORDER_ITEM_EVENT_EXPIRED = 940 +ORDER_ITEM_EVENT_EXPIRED = 940 """ Indicates the order item (or its product) has expired. """ -ORDER_ITEM_EVENT_INACTIVE = 950 +ORDER_ITEM_EVENT_INACTIVE = 950 """ Indicates the order item has become inactive. """ -ORDER_ITEM_EVENT_OTHER = 999 +ORDER_ITEM_EVENT_OTHER = 999 """ Arbitrary event type which does not signify anything in particular. If used, the event should be given an explanatory note. """ -ORDER_ITEM_EVENT = OrderedDict([ - (ORDER_ITEM_EVENT_INITIATED, "initiated"), - (ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"), - (ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"), - (ORDER_ITEM_EVENT_READY, "ready to proceed"), - (ORDER_ITEM_EVENT_CUSTOMER_RESOLVED, "customer resolved"), - (ORDER_ITEM_EVENT_PRODUCT_RESOLVED, "product resolved"), - (ORDER_ITEM_EVENT_PLACED, "placed with vendor"), - (ORDER_ITEM_EVENT_REORDER, "marked for re-order"), - (ORDER_ITEM_EVENT_RECEIVED, "received from vendor"), - (ORDER_ITEM_EVENT_CONTACTED, "customer contacted"), - (ORDER_ITEM_EVENT_CONTACT_FAILED, "contact failed"), - (ORDER_ITEM_EVENT_DELIVERED, "delivered"), - (ORDER_ITEM_EVENT_STATUS_CHANGE, "changed status"), - (ORDER_ITEM_EVENT_NOTE_ADDED, "added note"), - (ORDER_ITEM_EVENT_CANCELED, "canceled"), - (ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"), - (ORDER_ITEM_EVENT_REFUNDED, "refunded"), - (ORDER_ITEM_EVENT_RESTOCKED, "restocked"), - (ORDER_ITEM_EVENT_EXPIRED, "expired"), - (ORDER_ITEM_EVENT_INACTIVE, "inactive"), - (ORDER_ITEM_EVENT_OTHER, "other"), -]) +ORDER_ITEM_EVENT = OrderedDict( + [ + (ORDER_ITEM_EVENT_INITIATED, "initiated"), + (ORDER_ITEM_EVENT_PRICE_CONFIRMED, "price confirmed"), + (ORDER_ITEM_EVENT_PAYMENT_RECEIVED, "payment received"), + (ORDER_ITEM_EVENT_READY, "ready to proceed"), + (ORDER_ITEM_EVENT_CUSTOMER_RESOLVED, "customer resolved"), + (ORDER_ITEM_EVENT_PRODUCT_RESOLVED, "product resolved"), + (ORDER_ITEM_EVENT_PLACED, "placed with vendor"), + (ORDER_ITEM_EVENT_REORDER, "marked for re-order"), + (ORDER_ITEM_EVENT_RECEIVED, "received from vendor"), + (ORDER_ITEM_EVENT_CONTACTED, "customer contacted"), + (ORDER_ITEM_EVENT_CONTACT_FAILED, "contact failed"), + (ORDER_ITEM_EVENT_DELIVERED, "delivered"), + (ORDER_ITEM_EVENT_STATUS_CHANGE, "changed status"), + (ORDER_ITEM_EVENT_NOTE_ADDED, "added note"), + (ORDER_ITEM_EVENT_CANCELED, "canceled"), + (ORDER_ITEM_EVENT_REFUND_PENDING, "refund pending"), + (ORDER_ITEM_EVENT_REFUNDED, "refunded"), + (ORDER_ITEM_EVENT_RESTOCKED, "restocked"), + (ORDER_ITEM_EVENT_EXPIRED, "expired"), + (ORDER_ITEM_EVENT_INACTIVE, "inactive"), + (ORDER_ITEM_EVENT_OTHER, "other"), + ] +) """ Dict of possible code -> label options for :term:`order item` event types. diff --git a/src/sideshow/orders.py b/src/sideshow/orders.py index 6f4dd49..b0e639e 100644 --- a/src/sideshow/orders.py +++ b/src/sideshow/orders.py @@ -42,8 +42,7 @@ class OrderHandler(GenericHandler): 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) + 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): """ @@ -69,15 +68,15 @@ class OrderHandler(GenericHandler): if order_uom == enum.ORDER_UOM_CASE: if case_size is None: - case_qty = unit_qty = '??' + case_qty = unit_qty = "??" else: case_qty = self.app.render_quantity(case_size) unit_qty = self.app.render_quantity(order_qty * case_size) CS = enum.ORDER_UOM[enum.ORDER_UOM_CASE] EA = enum.ORDER_UOM[enum.ORDER_UOM_UNIT] order_qty = self.app.render_quantity(order_qty) - times = '×' if html else 'x' - return (f"{order_qty} {CS} ({times} {case_qty} = {unit_qty} {EA})") + times = "×" if html else "x" + return f"{order_qty} {CS} ({times} {case_qty} = {unit_qty} {EA})" # units unit_qty = self.app.render_quantity(order_qty) @@ -97,13 +96,15 @@ class OrderHandler(GenericHandler): ``None``. """ enum = self.app.enum - if status_code in (enum.ORDER_ITEM_STATUS_CANCELED, - enum.ORDER_ITEM_STATUS_REFUND_PENDING, - enum.ORDER_ITEM_STATUS_REFUNDED, - enum.ORDER_ITEM_STATUS_RESTOCKED, - enum.ORDER_ITEM_STATUS_EXPIRED, - enum.ORDER_ITEM_STATUS_INACTIVE): - return 'warning' + if status_code in ( + enum.ORDER_ITEM_STATUS_CANCELED, + enum.ORDER_ITEM_STATUS_REFUND_PENDING, + enum.ORDER_ITEM_STATUS_REFUNDED, + enum.ORDER_ITEM_STATUS_RESTOCKED, + enum.ORDER_ITEM_STATUS_EXPIRED, + enum.ORDER_ITEM_STATUS_INACTIVE, + ): + return "warning" def resolve_pending_product(self, pending_product, product_info, user, note=None): """ @@ -152,35 +153,39 @@ class OrderHandler(GenericHandler): raise ValueError("pending product does not have 'ready' status") info = product_info - pending_product.product_id = info['product_id'] + pending_product.product_id = info["product_id"] pending_product.status = enum.PendingProductStatus.RESOLVED - items = session.query(model.OrderItem)\ - .filter(model.OrderItem.pending_product == pending_product)\ - .filter(model.OrderItem.product_id == None)\ - .all() + items = ( + session.query(model.OrderItem) + .filter(model.OrderItem.pending_product == pending_product) + .filter(model.OrderItem.product_id == None) + .all() + ) for item in items: - item.product_id = info['product_id'] - item.product_scancode = info['scancode'] - item.product_brand = info['brand_name'] - item.product_description = info['description'] - item.product_size = info['size'] - item.product_weighed = info['weighed'] - item.department_id = info['department_id'] - item.department_name = info['department_name'] - item.special_order = info['special_order'] - item.vendor_name = info['vendor_name'] - item.vendor_item_code = info['vendor_item_code'] - item.case_size = info['case_size'] - item.unit_cost = info['unit_cost'] - item.unit_price_reg = info['unit_price_reg'] + item.product_id = info["product_id"] + item.product_scancode = info["scancode"] + item.product_brand = info["brand_name"] + item.product_description = info["description"] + item.product_size = info["size"] + item.product_weighed = info["weighed"] + item.department_id = info["department_id"] + item.department_name = info["department_name"] + item.special_order = info["special_order"] + item.vendor_name = info["vendor_name"] + item.vendor_item_code = info["vendor_item_code"] + item.case_size = info["case_size"] + item.unit_cost = info["unit_cost"] + item.unit_price_reg = info["unit_price_reg"] item.add_event(enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED, user) if note: item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note) - def process_placement(self, items, user, vendor_name=None, po_number=None, note=None): + def process_placement( + self, items, user, vendor_name=None, po_number=None, note=None + ): """ Process the "placement" step for the given order items. @@ -221,8 +226,15 @@ class OrderHandler(GenericHandler): item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, user, note=note) item.status_code = enum.ORDER_ITEM_STATUS_PLACED - def process_receiving(self, items, user, vendor_name=None, - invoice_number=None, po_number=None, note=None): + def process_receiving( + self, + items, + user, + vendor_name=None, + invoice_number=None, + po_number=None, + note=None, + ): """ Process the "receiving" step for the given order items. @@ -252,7 +264,9 @@ class OrderHandler(GenericHandler): received = None if invoice_number and po_number and vendor_name: - received = f"invoice {invoice_number} (PO {po_number}) from vendor {vendor_name}" + received = ( + f"invoice {invoice_number} (PO {po_number}) from vendor {vendor_name}" + ) elif invoice_number and vendor_name: received = f"invoice {invoice_number} from vendor {vendor_name}" elif po_number and vendor_name: diff --git a/src/sideshow/testing.py b/src/sideshow/testing.py index 150c978..f1fccfd 100644 --- a/src/sideshow/testing.py +++ b/src/sideshow/testing.py @@ -31,8 +31,10 @@ class WebTestCase(base.WebTestCase): def make_config(self, **kwargs): config = super().make_config(**kwargs) - config.setdefault('wutta.model_spec', 'sideshow.db.model') - config.setdefault('wutta.enum_spec', 'sideshow.enum') - config.setdefault(f'{config.appname}.batch.neworder.handler.default_spec', - 'sideshow.batch.neworder:NewOrderBatchHandler') + config.setdefault("wutta.model_spec", "sideshow.db.model") + config.setdefault("wutta.enum_spec", "sideshow.enum") + config.setdefault( + f"{config.appname}.batch.neworder.handler.default_spec", + "sideshow.batch.neworder:NewOrderBatchHandler", + ) return config diff --git a/src/sideshow/web/__init__.py b/src/sideshow/web/__init__.py index b86b498..34f0b6f 100644 --- a/src/sideshow/web/__init__.py +++ b/src/sideshow/web/__init__.py @@ -26,6 +26,6 @@ Sideshow web app def includeme(config): - config.include('sideshow.web.static') - config.include('wuttaweb.subscribers') - config.include('sideshow.web.views') + config.include("sideshow.web.static") + config.include("wuttaweb.subscribers") + config.include("sideshow.web.views") diff --git a/src/sideshow/web/app.py b/src/sideshow/web/app.py index ab79f4e..8d10424 100644 --- a/src/sideshow/web/app.py +++ b/src/sideshow/web/app.py @@ -32,17 +32,20 @@ def main(global_config, **settings): Make and return the WSGI app (Paste entry point). """ # prefer Sideshow templates over wuttaweb - settings.setdefault('mako.directories', [ - 'sideshow.web:templates', - 'wuttaweb:templates', - ]) + settings.setdefault( + "mako.directories", + [ + "sideshow.web:templates", + "wuttaweb:templates", + ], + ) # make config objects wutta_config = base.make_wutta_config(settings) pyramid_config = base.make_pyramid_config(settings) # bring in the rest of Sideshow - pyramid_config.include('sideshow.web') + pyramid_config.include("sideshow.web") return pyramid_config.make_wsgi_app() diff --git a/src/sideshow/web/forms/schema.py b/src/sideshow/web/forms/schema.py index 85269c9..c990523 100644 --- a/src/sideshow/web/forms/schema.py +++ b/src/sideshow/web/forms/schema.py @@ -48,7 +48,7 @@ class OrderRef(ObjectRef): def get_object_url(self, order): """ """ - return self.request.route_url('orders.view', uuid=order.uuid) + return self.request.route_url("orders.view", uuid=order.uuid) class LocalCustomerRef(ObjectRef): @@ -73,7 +73,7 @@ class LocalCustomerRef(ObjectRef): def get_object_url(self, customer): """ """ - return self.request.route_url('local_customers.view', uuid=customer.uuid) + return self.request.route_url("local_customers.view", uuid=customer.uuid) class PendingCustomerRef(ObjectRef): @@ -98,7 +98,7 @@ class PendingCustomerRef(ObjectRef): def get_object_url(self, customer): """ """ - return self.request.route_url('pending_customers.view', uuid=customer.uuid) + return self.request.route_url("pending_customers.view", uuid=customer.uuid) class LocalProductRef(ObjectRef): @@ -122,7 +122,7 @@ class LocalProductRef(ObjectRef): def get_object_url(self, product): """ """ - return self.request.route_url('local_products.view', uuid=product.uuid) + return self.request.route_url("local_products.view", uuid=product.uuid) class PendingProductRef(ObjectRef): @@ -147,4 +147,4 @@ class PendingProductRef(ObjectRef): def get_object_url(self, product): """ """ - return self.request.route_url('pending_products.view', uuid=product.uuid) + return self.request.route_url("pending_products.view", uuid=product.uuid) diff --git a/src/sideshow/web/menus.py b/src/sideshow/web/menus.py index 9da61c0..14fb027 100644 --- a/src/sideshow/web/menus.py +++ b/src/sideshow/web/menus.py @@ -48,45 +48,45 @@ class SideshowMenuHandler(base.MenuHandler): Generate the Orders menu. """ return { - 'title': "Orders", - 'type': 'menu', - 'items': [ + "title": "Orders", + "type": "menu", + "items": [ { - 'title': "Create New Order", - 'route': 'orders.create', - 'perm': 'orders.create', + "title": "Create New Order", + "route": "orders.create", + "perm": "orders.create", }, - {'type': 'sep'}, + {"type": "sep"}, { - 'title': "Placement", - 'route': 'order_items_placement', - 'perm': 'order_items_placement.list', + "title": "Placement", + "route": "order_items_placement", + "perm": "order_items_placement.list", }, { - 'title': "Receiving", - 'route': 'order_items_receiving', - 'perm': 'order_items_receiving.list', + "title": "Receiving", + "route": "order_items_receiving", + "perm": "order_items_receiving.list", }, { - 'title': "Contact", - 'route': 'order_items_contact', - 'perm': 'order_items_contact.list', + "title": "Contact", + "route": "order_items_contact", + "perm": "order_items_contact.list", }, { - 'title': "Delivery", - 'route': 'order_items_delivery', - 'perm': 'order_items_delivery.list', + "title": "Delivery", + "route": "order_items_delivery", + "perm": "order_items_delivery.list", }, - {'type': 'sep'}, + {"type": "sep"}, { - 'title': "All Order Items", - 'route': 'order_items', - 'perm': 'order_items.list', + "title": "All Order Items", + "route": "order_items", + "perm": "order_items.list", }, { - 'title': "All Orders", - 'route': 'orders', - 'perm': 'orders.list', + "title": "All Orders", + "route": "orders", + "perm": "orders.list", }, ], } @@ -96,18 +96,18 @@ class SideshowMenuHandler(base.MenuHandler): Generate the Customers menu. """ return { - 'title': "Customers", - 'type': 'menu', - 'items': [ + "title": "Customers", + "type": "menu", + "items": [ { - 'title': "Local Customers", - 'route': 'local_customers', - 'perm': 'local_customers.list', + "title": "Local Customers", + "route": "local_customers", + "perm": "local_customers.list", }, { - 'title': "Pending Customers", - 'route': 'pending_customers', - 'perm': 'pending_customers.list', + "title": "Pending Customers", + "route": "pending_customers", + "perm": "pending_customers.list", }, ], } @@ -117,18 +117,18 @@ class SideshowMenuHandler(base.MenuHandler): Generate the Products menu. """ return { - 'title': "Products", - 'type': 'menu', - 'items': [ + "title": "Products", + "type": "menu", + "items": [ { - 'title': "Local Products", - 'route': 'local_products', - 'perm': 'local_products.list', + "title": "Local Products", + "route": "local_products", + "perm": "local_products.list", }, { - 'title': "Pending Products", - 'route': 'pending_products', - 'perm': 'pending_products.list', + "title": "Pending Products", + "route": "pending_products", + "perm": "pending_products.list", }, ], } @@ -138,13 +138,13 @@ class SideshowMenuHandler(base.MenuHandler): Generate the Batch menu. """ return { - 'title': "Batches", - 'type': 'menu', - 'items': [ + "title": "Batches", + "type": "menu", + "items": [ { - 'title': "New Orders", - 'route': 'neworder_batches', - 'perm': 'neworder_batches.list', + "title": "New Orders", + "route": "neworder_batches", + "perm": "neworder_batches.list", }, ], } @@ -154,20 +154,23 @@ class SideshowMenuHandler(base.MenuHandler): Generate the "Other" menu. """ return { - 'title': "Other", - 'type': 'menu', - 'items': [], + "title": "Other", + "type": "menu", + "items": [], } def make_admin_menu(self, request, **kwargs): """ """ - kwargs['include_people'] = True + kwargs["include_people"] = True menu = super().make_admin_menu(request, **kwargs) - menu['items'].insert(0, { - 'title': "Stores", - 'route': 'stores', - 'perm': 'stores.list', - }) + menu["items"].insert( + 0, + { + "title": "Stores", + "route": "stores", + "perm": "stores.list", + }, + ) return menu diff --git a/src/sideshow/web/static/__init__.py b/src/sideshow/web/static/__init__.py index 36dcc2f..43d30aa 100644 --- a/src/sideshow/web/static/__init__.py +++ b/src/sideshow/web/static/__init__.py @@ -28,12 +28,12 @@ from fanstatic import Library, Resource # # libcache -libcache = Library('sideshow_libcache', 'libcache') -vue_js = Resource(libcache, 'vue-2.6.14.min.js') -vue_resource_js = Resource(libcache, 'vue-resource-1.5.3.min.js') -buefy_js = Resource(libcache, 'buefy-0.9.25.min.js') -buefy_css = Resource(libcache, 'buefy-0.9.25.min.css') -fontawesome_js = Resource(libcache, 'fontawesome-5.3.1-all.min.js') +libcache = Library("sideshow_libcache", "libcache") +vue_js = Resource(libcache, "vue-2.6.14.min.js") +vue_resource_js = Resource(libcache, "vue-resource-1.5.3.min.js") +buefy_js = Resource(libcache, "buefy-0.9.25.min.js") +buefy_css = Resource(libcache, "buefy-0.9.25.min.css") +fontawesome_js = Resource(libcache, "fontawesome-5.3.1-all.min.js") # bb_vue_js = Resource(libcache, 'vue.esm-browser-3.3.11.prod.js') # bb_oruga_js = Resource(libcache, 'oruga-0.8.10.js') # bb_oruga_bulma_js = Resource(libcache, 'oruga-bulma-0.3.0.js') @@ -44,5 +44,5 @@ fontawesome_js = Resource(libcache, 'fontawesome-5.3.1-all.min.js') def includeme(config): - config.include('wuttaweb.static') - config.add_static_view('sideshow', 'sideshow.web:static', cache_max_age=3600) + config.include("wuttaweb.static") + config.add_static_view("sideshow", "sideshow.web:static", cache_max_age=3600) diff --git a/src/sideshow/web/views/__init__.py b/src/sideshow/web/views/__init__.py index e5a14ac..3879f8c 100644 --- a/src/sideshow/web/views/__init__.py +++ b/src/sideshow/web/views/__init__.py @@ -30,15 +30,18 @@ from wuttaweb.views import essential def includeme(config): # core views for wuttaweb - essential.defaults(config, **{ - 'wuttaweb.views.common': 'sideshow.web.views.common', - }) + essential.defaults( + config, + **{ + "wuttaweb.views.common": "sideshow.web.views.common", + }, + ) # sideshow views - config.include('sideshow.web.views.stores') - config.include('sideshow.web.views.customers') - config.include('sideshow.web.views.products') - config.include('sideshow.web.views.orders') + config.include("sideshow.web.views.stores") + config.include("sideshow.web.views.customers") + config.include("sideshow.web.views.products") + config.include("sideshow.web.views.orders") # batch views - config.include('sideshow.web.views.batch.neworder') + config.include("sideshow.web.views.batch.neworder") diff --git a/src/sideshow/web/views/batch/neworder.py b/src/sideshow/web/views/batch/neworder.py index 5103517..d847a14 100644 --- a/src/sideshow/web/views/batch/neworder.py +++ b/src/sideshow/web/views/batch/neworder.py @@ -52,78 +52,79 @@ class NewOrderBatchView(BatchMasterView): since those should be handled by :class:`~sideshow.web.views.orders.OrderView` instead. """ + model_class = NewOrderBatch model_title = "New Order Batch" model_title_plural = "New Order Batches" - route_prefix = 'neworder_batches' - url_prefix = '/batch/neworder' + route_prefix = "neworder_batches" + url_prefix = "/batch/neworder" creatable = False editable = False labels = { - 'store_id': "Store ID", - 'customer_id': "Customer ID", + "store_id": "Store ID", + "customer_id": "Customer ID", } grid_columns = [ - 'id', - 'store_id', - 'customer_id', - 'customer_name', - 'phone_number', - 'email_address', - 'total_price', - 'row_count', - 'created', - 'created_by', - 'executed', + "id", + "store_id", + "customer_id", + "customer_name", + "phone_number", + "email_address", + "total_price", + "row_count", + "created", + "created_by", + "executed", ] filter_defaults = { - 'executed': {'active': True, 'verb': 'is_null'}, + "executed": {"active": True, "verb": "is_null"}, } form_fields = [ - 'id', - 'store_id', - 'customer_id', - 'local_customer', - 'pending_customer', - 'customer_name', - 'phone_number', - 'email_address', - 'total_price', - 'row_count', - 'status_code', - 'created', - 'created_by', - 'executed', - 'executed_by', + "id", + "store_id", + "customer_id", + "local_customer", + "pending_customer", + "customer_name", + "phone_number", + "email_address", + "total_price", + "row_count", + "status_code", + "created", + "created_by", + "executed", + "executed_by", ] row_labels = { - 'product_scancode': "Scancode", - 'product_brand': "Brand", - 'product_description': "Description", - 'product_size': "Size", - 'order_uom': "Order UOM", + "product_scancode": "Scancode", + "product_brand": "Brand", + "product_description": "Description", + "product_size": "Size", + "order_uom": "Order UOM", } row_grid_columns = [ - 'sequence', - 'product_scancode', - 'product_brand', - 'product_description', - 'product_size', - 'special_order', - 'unit_price_quoted', - 'case_size', - 'case_price_quoted', - 'order_qty', - 'order_uom', - 'discount_percent', - 'total_price', - 'status_code', + "sequence", + "product_scancode", + "product_brand", + "product_description", + "product_size", + "special_order", + "unit_price_quoted", + "case_size", + "case_price_quoted", + "order_qty", + "order_uom", + "discount_percent", + "total_price", + "status_code", ] def __init__(self, request, context=None): @@ -141,10 +142,10 @@ class NewOrderBatchView(BatchMasterView): # store_id if not self.order_handler.expose_store_id(): - g.remove('store_id') + g.remove("store_id") # total_price - g.set_renderer('total_price', 'currency') + g.set_renderer("total_price", "currency") def configure_form(self, f): """ """ @@ -152,16 +153,16 @@ class NewOrderBatchView(BatchMasterView): # store_id if not self.order_handler.expose_store_id(): - f.remove('store_id') + f.remove("store_id") # local_customer - f.set_node('local_customer', LocalCustomerRef(self.request)) + f.set_node("local_customer", LocalCustomerRef(self.request)) # pending_customer - f.set_node('pending_customer', PendingCustomerRef(self.request)) + f.set_node("pending_customer", PendingCustomerRef(self.request)) # total_price - f.set_node('total_price', WuttaMoney(self.request)) + f.set_node("total_price", WuttaMoney(self.request)) def configure_row_grid(self, g): """ """ @@ -170,22 +171,22 @@ class NewOrderBatchView(BatchMasterView): # TODO # order_uom - #g.set_renderer('order_uom', self.grid_render_enum, enum=enum.ORDER_UOM) + # g.set_renderer('order_uom', self.grid_render_enum, enum=enum.ORDER_UOM) # unit_price_quoted - g.set_label('unit_price_quoted', "Unit Price", column_only=True) - g.set_renderer('unit_price_quoted', 'currency') + g.set_label("unit_price_quoted", "Unit Price", column_only=True) + g.set_renderer("unit_price_quoted", "currency") # case_price_quoted - g.set_label('case_price_quoted', "Case Price", column_only=True) - g.set_renderer('case_price_quoted', 'currency') + g.set_label("case_price_quoted", "Case Price", column_only=True) + g.set_renderer("case_price_quoted", "currency") # discount_percent - g.set_renderer('discount_percent', 'percent') - g.set_label('discount_percent', "Disc. %", column_only=True) + g.set_renderer("discount_percent", "percent") + g.set_label("discount_percent", "Disc. %", column_only=True) # total_price - g.set_renderer('total_price', 'currency') + g.set_renderer("total_price", "currency") def get_xref_buttons(self, batch): """ @@ -197,14 +198,19 @@ class NewOrderBatchView(BatchMasterView): model = self.app.model session = self.Session() - if batch.executed and self.request.has_perm('orders.view'): - order = session.query(model.Order)\ - .filter(model.Order.order_id == batch.id)\ - .first() + if batch.executed and self.request.has_perm("orders.view"): + order = ( + session.query(model.Order) + .filter(model.Order.order_id == batch.id) + .first() + ) if order: - url = self.request.route_url('orders.view', uuid=order.uuid) + url = self.request.route_url("orders.view", uuid=order.uuid) buttons.append( - self.make_button("View the Order", primary=True, icon_left='eye', url=url)) + self.make_button( + "View the Order", primary=True, icon_left="eye", url=url + ) + ) return buttons @@ -212,7 +218,7 @@ class NewOrderBatchView(BatchMasterView): def defaults(config, **kwargs): base = globals() - NewOrderBatchView = kwargs.get('NewOrderBatchView', base['NewOrderBatchView']) + NewOrderBatchView = kwargs.get("NewOrderBatchView", base["NewOrderBatchView"]) NewOrderBatchView.defaults(config) diff --git a/src/sideshow/web/views/common.py b/src/sideshow/web/views/common.py index 8e19ef7..c0debb0 100644 --- a/src/sideshow/web/views/common.py +++ b/src/sideshow/web/views/common.py @@ -46,54 +46,55 @@ class CommonView(base.CommonView): auth = self.app.get_auth_handler() admin = model.Role(name="Order Admin") - admin.notes = ("this role was auto-created; " - "you can change or remove it as needed.") + admin.notes = ( + "this role was auto-created; " "you can change or remove it as needed." + ) session.add(admin) user.roles.append(admin) order_admin_perms = [ - 'local_customers.list', - 'local_customers.view', - 'local_products.list', - 'local_products.view', - 'neworder_batches.list', - 'neworder_batches.view', - 'order_items.add_note', - 'order_items.change_status', - 'order_items.list', - 'order_items.view', - 'order_items_contact.add_note', - 'order_items_contact.change_status', - 'order_items_contact.list', - 'order_items_contact.process_contact', - 'order_items_contact.view', - 'order_items_delivery.add_note', - 'order_items_delivery.change_status', - 'order_items_delivery.list', - 'order_items_delivery.process_delivery', - 'order_items_delivery.process_restock', - 'order_items_delivery.view', - 'order_items_placement.add_note', - 'order_items_placement.change_status', - 'order_items_placement.list', - 'order_items_placement.process_placement', - 'order_items_placement.view', - 'order_items_receiving.add_note', - 'order_items_receiving.change_status', - 'order_items_receiving.list', - 'order_items_receiving.process_receiving', - 'order_items_receiving.process_reorder', - 'order_items_receiving.view', - 'orders.configure', - 'orders.create', - 'orders.create_unknown_product', - 'orders.list', - 'orders.view', - 'pending_customers.list', - 'pending_customers.view', - 'pending_products.list', - 'pending_products.view', + "local_customers.list", + "local_customers.view", + "local_products.list", + "local_products.view", + "neworder_batches.list", + "neworder_batches.view", + "order_items.add_note", + "order_items.change_status", + "order_items.list", + "order_items.view", + "order_items_contact.add_note", + "order_items_contact.change_status", + "order_items_contact.list", + "order_items_contact.process_contact", + "order_items_contact.view", + "order_items_delivery.add_note", + "order_items_delivery.change_status", + "order_items_delivery.list", + "order_items_delivery.process_delivery", + "order_items_delivery.process_restock", + "order_items_delivery.view", + "order_items_placement.add_note", + "order_items_placement.change_status", + "order_items_placement.list", + "order_items_placement.process_placement", + "order_items_placement.view", + "order_items_receiving.add_note", + "order_items_receiving.change_status", + "order_items_receiving.list", + "order_items_receiving.process_receiving", + "order_items_receiving.process_reorder", + "order_items_receiving.view", + "orders.configure", + "orders.create", + "orders.create_unknown_product", + "orders.list", + "orders.view", + "pending_customers.list", + "pending_customers.view", + "pending_products.list", + "pending_products.view", ] for perm in order_admin_perms: @@ -101,4 +102,4 @@ class CommonView(base.CommonView): def includeme(config): - base.defaults(config, **{'CommonView': CommonView}) + base.defaults(config, **{"CommonView": CommonView}) diff --git a/src/sideshow/web/views/customers.py b/src/sideshow/web/views/customers.py index 850ec5e..eb252c3 100644 --- a/src/sideshow/web/views/customers.py +++ b/src/sideshow/web/views/customers.py @@ -44,35 +44,36 @@ class LocalCustomerView(MasterView): * ``/local/customers/XXX/edit`` * ``/local/customers/XXX/delete`` """ + model_class = LocalCustomer model_title = "Local Customer" - route_prefix = 'local_customers' - url_prefix = '/local/customers' + route_prefix = "local_customers" + url_prefix = "/local/customers" labels = { - 'external_id': "External ID", + "external_id": "External ID", } grid_columns = [ - 'external_id', - 'full_name', - 'first_name', - 'last_name', - 'phone_number', - 'email_address', + "external_id", + "full_name", + "first_name", + "last_name", + "phone_number", + "email_address", ] - sort_defaults = 'full_name' + sort_defaults = "full_name" form_fields = [ - 'external_id', - 'full_name', - 'first_name', - 'last_name', - 'phone_number', - 'email_address', - 'orders', - 'new_order_batches', + "external_id", + "full_name", + "first_name", + "last_name", + "phone_number", + "email_address", + "orders", + "new_order_batches", ] def configure_grid(self, g): @@ -80,11 +81,11 @@ class LocalCustomerView(MasterView): super().configure_grid(g) # links - g.set_link('full_name') - g.set_link('first_name') - g.set_link('last_name') - g.set_link('phone_number') - g.set_link('email_address') + g.set_link("full_name") + g.set_link("first_name") + g.set_link("last_name") + g.set_link("phone_number") + g.set_link("email_address") def configure_form(self, f): """ """ @@ -93,25 +94,25 @@ class LocalCustomerView(MasterView): # external_id if self.creating: - f.remove('external_id') + f.remove("external_id") else: - f.set_readonly('external_id') + f.set_readonly("external_id") # full_name if self.creating or self.editing: - f.remove('full_name') + f.remove("full_name") # orders if self.creating or self.editing: - f.remove('orders') + f.remove("orders") else: - f.set_grid('orders', self.make_orders_grid(customer)) + f.set_grid("orders", self.make_orders_grid(customer)) # new_order_batches if self.creating or self.editing: - f.remove('new_order_batches') + f.remove("new_order_batches") else: - f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) + f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer)) def make_orders_grid(self, customer): """ @@ -120,24 +121,28 @@ class LocalCustomerView(MasterView): model = self.app.model route_prefix = self.get_route_prefix() - 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) + 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'): - url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('order_id') + if self.request.has_perm("orders.view"): + url = lambda order, i: self.request.route_url( + "orders.view", uuid=order.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("order_id") return grid @@ -148,28 +153,32 @@ class LocalCustomerView(MasterView): model = self.app.model 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, - columns=[ - 'id', - 'total_price', - 'created', - 'created_by', - 'executed', - ], - labels={ - 'id': "Batch ID", - }, - renderers={ - 'id': 'batch_id', - 'total_price': 'currency', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.new_order_batches", + model_class=model.NewOrderBatch, + 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'): - url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('id') + if self.request.has_perm("neworder_batches.view"): + url = lambda batch, i: self.request.route_url( + "neworder_batches.view", uuid=batch.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("id") return grid @@ -178,8 +187,9 @@ class LocalCustomerView(MasterView): enum = self.app.enum customer = super().objectify(form) - customer.full_name = self.app.make_full_name(customer.first_name, - customer.last_name) + customer.full_name = self.app.make_full_name( + customer.first_name, customer.last_name + ) return customer @@ -198,41 +208,42 @@ class PendingCustomerView(MasterView): * ``/pending/customers/XXX/edit`` * ``/pending/customers/XXX/delete`` """ + model_class = PendingCustomer model_title = "Pending Customer" - route_prefix = 'pending_customers' - url_prefix = '/pending/customers' + route_prefix = "pending_customers" + url_prefix = "/pending/customers" labels = { - 'customer_id': "Customer ID", + "customer_id": "Customer ID", } grid_columns = [ - 'full_name', - 'first_name', - 'last_name', - 'phone_number', - 'email_address', - 'customer_id', - 'status', - 'created', - 'created_by', + "full_name", + "first_name", + "last_name", + "phone_number", + "email_address", + "customer_id", + "status", + "created", + "created_by", ] - sort_defaults = 'full_name' + sort_defaults = "full_name" form_fields = [ - 'customer_id', - 'full_name', - 'first_name', - 'last_name', - 'phone_number', - 'email_address', - 'status', - 'created', - 'created_by', - 'orders', - 'new_order_batches', + "customer_id", + "full_name", + "first_name", + "last_name", + "phone_number", + "email_address", + "status", + "created", + "created_by", + "orders", + "new_order_batches", ] def configure_grid(self, g): @@ -241,14 +252,14 @@ class PendingCustomerView(MasterView): enum = self.app.enum # status - g.set_renderer('status', self.grid_render_enum, enum=enum.PendingCustomerStatus) + g.set_renderer("status", self.grid_render_enum, enum=enum.PendingCustomerStatus) # links - g.set_link('full_name') - g.set_link('first_name') - g.set_link('last_name') - g.set_link('phone_number') - g.set_link('email_address') + g.set_link("full_name") + g.set_link("first_name") + g.set_link("last_name") + g.set_link("phone_number") + g.set_link("email_address") def configure_form(self, f): """ """ @@ -258,41 +269,41 @@ class PendingCustomerView(MasterView): # customer_id if self.creating: - f.remove('customer_id') + f.remove("customer_id") else: - f.set_readonly('customer_id') + f.set_readonly("customer_id") # status if self.creating: - f.remove('status') + f.remove("status") else: - f.set_node('status', WuttaEnum(self.request, enum.PendingCustomerStatus)) - f.set_readonly('status') + f.set_node("status", WuttaEnum(self.request, enum.PendingCustomerStatus)) + f.set_readonly("status") # created if self.creating: - f.remove('created') + f.remove("created") else: - f.set_readonly('created') + f.set_readonly("created") # created_by if self.creating: - f.remove('created_by') + f.remove("created_by") else: - f.set_node('created_by', UserRef(self.request)) - f.set_readonly('created_by') + f.set_node("created_by", UserRef(self.request)) + f.set_readonly("created_by") # orders if self.creating or self.editing: - f.remove('orders') + f.remove("orders") else: - f.set_grid('orders', self.make_orders_grid(customer)) + f.set_grid("orders", self.make_orders_grid(customer)) # new_order_batches if self.creating or self.editing: - f.remove('new_order_batches') + f.remove("new_order_batches") else: - f.set_grid('new_order_batches', self.make_new_order_batches_grid(customer)) + f.set_grid("new_order_batches", self.make_new_order_batches_grid(customer)) def make_orders_grid(self, customer): """ @@ -301,24 +312,28 @@ class PendingCustomerView(MasterView): model = self.app.model route_prefix = self.get_route_prefix() - 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) + 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'): - url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('order_id') + if self.request.has_perm("orders.view"): + url = lambda order, i: self.request.route_url( + "orders.view", uuid=order.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("order_id") return grid @@ -329,28 +344,32 @@ class PendingCustomerView(MasterView): model = self.app.model 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, - columns=[ - 'id', - 'total_price', - 'created', - 'created_by', - 'executed', - ], - labels={ - 'id': "Batch ID", - }, - renderers={ - 'id': 'batch_id', - 'total_price': 'currency', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.new_order_batches", + model_class=model.NewOrderBatch, + 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'): - url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('id') + if self.request.has_perm("neworder_batches.view"): + url = lambda batch, i: self.request.route_url( + "neworder_batches.view", uuid=batch.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("id") return grid @@ -371,16 +390,20 @@ class PendingCustomerView(MasterView): # avoid deleting if still referenced by order(s) for order in customer.orders: - self.request.session.flash(f"Cannot delete {model_title} still attached " - "to Order(s)", 'warning') - raise self.redirect(self.get_action_url('view', customer)) + self.request.session.flash( + f"Cannot delete {model_title} still attached " "to Order(s)", "warning" + ) + raise self.redirect(self.get_action_url("view", customer)) # avoid deleting if still referenced by new order batch(es) for batch in customer.new_order_batches: if not batch.executed: - self.request.session.flash(f"Cannot delete {model_title} still attached " - "to New Order Batch(es)", 'warning') - raise self.redirect(self.get_action_url('view', customer)) + self.request.session.flash( + f"Cannot delete {model_title} still attached " + "to New Order Batch(es)", + "warning", + ) + raise self.redirect(self.get_action_url("view", customer)) # go ahead and delete per usual super().delete_instance(customer) @@ -389,10 +412,10 @@ class PendingCustomerView(MasterView): def defaults(config, **kwargs): base = globals() - LocalCustomerView = kwargs.get('LocalCustomerView', base['LocalCustomerView']) + LocalCustomerView = kwargs.get("LocalCustomerView", base["LocalCustomerView"]) LocalCustomerView.defaults(config) - PendingCustomerView = kwargs.get('PendingCustomerView', base['PendingCustomerView']) + PendingCustomerView = kwargs.get("PendingCustomerView", base["PendingCustomerView"]) PendingCustomerView.defaults(config) diff --git a/src/sideshow/web/views/orders.py b/src/sideshow/web/views/orders.py index c18cbd7..dc5fbd8 100644 --- a/src/sideshow/web/views/orders.py +++ b/src/sideshow/web/views/orders.py @@ -36,14 +36,24 @@ from sqlalchemy import orm from webhelpers2.html import tags, HTML from wuttaweb.views import MasterView -from wuttaweb.forms.schema import UserRef, WuttaMoney, WuttaQuantity, WuttaEnum, WuttaDictEnum +from wuttaweb.forms.schema import ( + UserRef, + WuttaMoney, + WuttaQuantity, + WuttaEnum, + WuttaDictEnum, +) from wuttaweb.util import make_json_safe from sideshow.db.model import Order, OrderItem from sideshow.batch.neworder import NewOrderBatchHandler -from sideshow.web.forms.schema import (OrderRef, - LocalCustomerRef, LocalProductRef, - PendingCustomerRef, PendingProductRef) +from sideshow.web.forms.schema import ( + OrderRef, + LocalCustomerRef, + LocalProductRef, + PendingCustomerRef, + PendingProductRef, +) log = logging.getLogger(__name__) @@ -75,91 +85,92 @@ class OrderView(MasterView): Reference to the :term:`new order batch` handler. This gets set in the constructor. """ + model_class = Order editable = False configurable = True labels = { - 'order_id': "Order ID", - 'store_id': "Store ID", - 'customer_id': "Customer ID", + "order_id": "Order ID", + "store_id": "Store ID", + "customer_id": "Customer ID", } grid_columns = [ - 'order_id', - 'store_id', - 'customer_id', - 'customer_name', - 'total_price', - 'created', - 'created_by', + "order_id", + "store_id", + "customer_id", + "customer_name", + "total_price", + "created", + "created_by", ] - sort_defaults = ('order_id', 'desc') + sort_defaults = ("order_id", "desc") form_fields = [ - 'order_id', - 'store_id', - 'customer_id', - 'local_customer', - 'pending_customer', - 'customer_name', - 'phone_number', - 'email_address', - 'total_price', - 'created', - 'created_by', + "order_id", + "store_id", + "customer_id", + "local_customer", + "pending_customer", + "customer_name", + "phone_number", + "email_address", + "total_price", + "created", + "created_by", ] has_rows = True row_model_class = OrderItem rows_title = "Order Items" - rows_sort_defaults = 'sequence' + rows_sort_defaults = "sequence" rows_viewable = True row_labels = { - 'product_scancode': "Scancode", - 'product_brand': "Brand", - 'product_description': "Description", - 'product_size': "Size", - 'department_name': "Department", - 'order_uom': "Order UOM", - 'status_code': "Status", + "product_scancode": "Scancode", + "product_brand": "Brand", + "product_description": "Description", + "product_size": "Size", + "department_name": "Department", + "order_uom": "Order UOM", + "status_code": "Status", } row_grid_columns = [ - 'sequence', - 'product_scancode', - 'product_brand', - 'product_description', - 'product_size', - 'department_name', - 'special_order', - 'order_qty', - 'order_uom', - 'discount_percent', - 'total_price', - 'status_code', + "sequence", + "product_scancode", + "product_brand", + "product_description", + "product_size", + "department_name", + "special_order", + "order_qty", + "order_uom", + "discount_percent", + "total_price", + "status_code", ] PENDING_PRODUCT_ENTRY_FIELDS = [ - 'scancode', - 'brand_name', - 'description', - 'size', - 'department_id', - 'department_name', - 'vendor_name', - 'vendor_item_code', - 'case_size', - 'unit_cost', - 'unit_price_reg', + "scancode", + "brand_name", + "description", + "size", + "department_id", + "department_name", + "vendor_name", + "vendor_item_code", + "case_size", + "unit_cost", + "unit_price_reg", ] def __init__(self, request, context=None): super().__init__(request, context=context) self.order_handler = self.app.get_order_handler() - self.batch_handler = self.app.get_batch_handler('neworder') + self.batch_handler = self.app.get_batch_handler("neworder") def configure_grid(self, g): """ """ @@ -167,19 +178,19 @@ class OrderView(MasterView): # store_id if not self.order_handler.expose_store_id(): - g.remove('store_id') + g.remove("store_id") # order_id - g.set_link('order_id') + g.set_link("order_id") # customer_id - g.set_link('customer_id') + g.set_link("customer_id") # customer_name - g.set_link('customer_name') + g.set_link("customer_name") # total_price - g.set_renderer('total_price', g.render_currency) + g.set_renderer("total_price", g.render_currency) def create(self): """ @@ -220,83 +231,96 @@ class OrderView(MasterView): context = self.get_context_customer(batch) - if self.request.method == 'POST': + if self.request.method == "POST": # first we check for traditional form post - action = self.request.POST.get('action') + action = self.request.POST.get("action") post_actions = [ - 'start_over', - 'cancel_order', + "start_over", + "cancel_order", ] if action in post_actions: return getattr(self, action)(batch) # okay then, we'll assume newer JSON-style post params data = dict(self.request.json_body) - action = data.pop('action') + action = data.pop("action") json_actions = [ - 'set_store', - 'assign_customer', - 'unassign_customer', + "set_store", + "assign_customer", + "unassign_customer", # 'update_phone_number', # 'update_email_address', - 'set_pending_customer', + "set_pending_customer", # 'get_customer_info', # # 'set_customer_data', - 'get_product_info', - 'get_past_products', - 'add_item', - 'update_item', - 'delete_item', - 'submit_order', + "get_product_info", + "get_past_products", + "add_item", + "update_item", + "delete_item", + "submit_order", ] if action in json_actions: try: result = getattr(self, action)(batch, data) except Exception as error: log.warning("error calling json action for order", exc_info=True) - result = {'error': self.app.render_error(error)} + result = {"error": self.app.render_error(error)} return self.json_response(result) - return self.json_response({'error': "unknown form action"}) + return self.json_response({"error": "unknown form action"}) - context.update({ - 'batch': batch, - 'normalized_batch': self.normalize_batch(batch), - 'order_items': [self.normalize_row(row) - for row in batch.rows], - 'default_uom_choices': self.batch_handler.get_default_uom_choices(), - 'default_uom': None, # TODO? - 'expose_store_id': self.order_handler.expose_store_id(), - 'allow_item_discounts': self.batch_handler.allow_item_discounts(), - 'allow_unknown_products': (self.batch_handler.allow_unknown_products() - and self.has_perm('create_unknown_product')), - 'pending_product_required_fields': self.get_pending_product_required_fields(), - 'allow_past_item_reorder': True, # TODO: make configurable? - }) + context.update( + { + "batch": batch, + "normalized_batch": self.normalize_batch(batch), + "order_items": [self.normalize_row(row) for row in batch.rows], + "default_uom_choices": self.batch_handler.get_default_uom_choices(), + "default_uom": None, # TODO? + "expose_store_id": self.order_handler.expose_store_id(), + "allow_item_discounts": self.batch_handler.allow_item_discounts(), + "allow_unknown_products": ( + self.batch_handler.allow_unknown_products() + and self.has_perm("create_unknown_product") + ), + "pending_product_required_fields": self.get_pending_product_required_fields(), + "allow_past_item_reorder": True, # TODO: make configurable? + } + ) - if context['expose_store_id']: - stores = session.query(model.Store)\ - .filter(model.Store.archived == False)\ - .order_by(model.Store.store_id)\ - .all() - context['stores'] = [{'store_id': store.store_id, 'display': store.get_display()} - for store in stores] + if context["expose_store_id"]: + stores = ( + session.query(model.Store) + .filter(model.Store.archived == False) + .order_by(model.Store.store_id) + .all() + ) + context["stores"] = [ + {"store_id": store.store_id, "display": store.get_display()} + for store in stores + ] # set default so things just work if not batch.store_id: batch.store_id = self.batch_handler.get_default_store_id() - if context['allow_item_discounts']: - context['allow_item_discounts_if_on_sale'] = self.batch_handler\ - .allow_item_discounts_if_on_sale() + if context["allow_item_discounts"]: + context["allow_item_discounts_if_on_sale"] = ( + self.batch_handler.allow_item_discounts_if_on_sale() + ) # nb. render quantity so that '10.0' => '10' - context['default_item_discount'] = self.app.render_quantity( - self.batch_handler.get_default_item_discount()) - context['dept_item_discounts'] = dict([(d['department_id'], d['default_item_discount']) - for d in self.get_dept_item_discounts()]) + context["default_item_discount"] = self.app.render_quantity( + self.batch_handler.get_default_item_discount() + ) + context["dept_item_discounts"] = dict( + [ + (d["department_id"], d["default_item_discount"]) + for d in self.get_dept_item_discounts() + ] + ) - return self.render_to_response('create', context) + return self.render_to_response("create", context) def get_current_batch(self): """ @@ -319,10 +343,12 @@ class OrderView(MasterView): try: # there should be at most *one* new batch per user - batch = session.query(model.NewOrderBatch)\ - .filter(model.NewOrderBatch.created_by == user)\ - .filter(model.NewOrderBatch.executed == None)\ - .one() + batch = ( + session.query(model.NewOrderBatch) + .filter(model.NewOrderBatch.created_by == user) + .filter(model.NewOrderBatch.executed == None) + .one() + ) except orm.exc.NoResultFound: # no batch yet for this user, so make one @@ -346,15 +372,19 @@ class OrderView(MasterView): ``value`` and ``label`` keys. """ session = self.Session() - term = self.request.GET.get('term', '').strip() + term = self.request.GET.get("term", "").strip() if not term: return [] handler = self.batch_handler if handler.use_local_customers(): - return handler.autocomplete_customers_local(session, term, user=self.request.user) + return handler.autocomplete_customers_local( + session, term, user=self.request.user + ) else: - return handler.autocomplete_customers_external(session, term, user=self.request.user) + return handler.autocomplete_customers_external( + session, term, user=self.request.user + ) def product_autocomplete(self): """ @@ -370,23 +400,28 @@ class OrderView(MasterView): ``value`` and ``label`` keys. """ session = self.Session() - term = self.request.GET.get('term', '').strip() + term = self.request.GET.get("term", "").strip() if not term: return [] handler = self.batch_handler if handler.use_local_products(): - return handler.autocomplete_products_local(session, term, user=self.request.user) + return handler.autocomplete_products_local( + session, term, user=self.request.user + ) else: - return handler.autocomplete_products_external(session, term, user=self.request.user) + return handler.autocomplete_products_external( + session, term, user=self.request.user + ) def get_pending_product_required_fields(self): """ """ required = [] for field in self.PENDING_PRODUCT_ENTRY_FIELDS: require = self.config.get_bool( - f'sideshow.orders.unknown_product.fields.{field}.required') - if require is None and field == 'description': + f"sideshow.orders.unknown_product.fields.{field}.required" + ) + if require is None and field == "description": require = True if require: required.append(field) @@ -408,25 +443,37 @@ class OrderView(MasterView): """ model = self.app.model session = self.Session() - pattern = re.compile(r'^sideshow\.orders\.departments\.([^.]+)\.default_item_discount$') + pattern = re.compile( + r"^sideshow\.orders\.departments\.([^.]+)\.default_item_discount$" + ) dept_item_discounts = [] - settings = session.query(model.Setting)\ - .filter(model.Setting.name.like('sideshow.orders.departments.%.default_item_discount'))\ - .all() + settings = ( + session.query(model.Setting) + .filter( + model.Setting.name.like( + "sideshow.orders.departments.%.default_item_discount" + ) + ) + .all() + ) for setting in settings: match = pattern.match(setting.name) if not match: log.warning("invalid setting name: %s", setting.name) continue deptid = match.group(1) - name = self.app.get_setting(session, f'sideshow.orders.departments.{deptid}.name') - dept_item_discounts.append({ - 'department_id': deptid, - 'department_name': name, - 'default_item_discount': setting.value, - }) - dept_item_discounts.sort(key=lambda d: d['department_name']) + name = self.app.get_setting( + session, f"sideshow.orders.departments.{deptid}.name" + ) + dept_item_discounts.append( + { + "department_id": deptid, + "department_name": name, + "default_item_discount": setting.value, + } + ) + dept_item_discounts.sort(key=lambda d: d["department_name"]) return dept_item_discounts def start_over(self, batch): @@ -447,7 +494,7 @@ class OrderView(MasterView): # send back to "create order" which makes new batch route_prefix = self.get_route_prefix() - url = self.request.route_url(f'{route_prefix}.create') + url = self.request.route_url(f"{route_prefix}.create") return self.redirect(url) def cancel_order(self, batch): @@ -480,9 +527,9 @@ class OrderView(MasterView): This is a "batch action" method which may be called from :meth:`create()`. """ - store_id = data.get('store_id') + store_id = data.get("store_id") if not store_id: - return {'error': "Must provide store_id"} + return {"error": "Must provide store_id"} batch.store_id = store_id return self.get_context_customer(batch) @@ -490,12 +537,12 @@ class OrderView(MasterView): def get_context_customer(self, batch): """ """ context = { - 'store_id': batch.store_id, - 'customer_is_known': True, - 'customer_id': None, - 'customer_name': batch.customer_name, - 'phone_number': batch.phone_number, - 'email_address': batch.email_address, + "store_id": batch.store_id, + "customer_is_known": True, + "customer_id": None, + "customer_name": batch.customer_name, + "phone_number": batch.phone_number, + "email_address": batch.email_address, } # customer_id @@ -503,26 +550,31 @@ class OrderView(MasterView): if use_local: local = batch.local_customer if local: - context['customer_id'] = local.uuid.hex - else: # use external - context['customer_id'] = batch.customer_id + context["customer_id"] = local.uuid.hex + else: # use external + context["customer_id"] = batch.customer_id # pending customer pending = batch.pending_customer if pending: - context.update({ - 'new_customer_first_name': pending.first_name, - 'new_customer_last_name': pending.last_name, - 'new_customer_full_name': pending.full_name, - 'new_customer_phone': pending.phone_number, - 'new_customer_email': pending.email_address, - }) + context.update( + { + "new_customer_first_name": pending.first_name, + "new_customer_last_name": pending.last_name, + "new_customer_full_name": pending.full_name, + "new_customer_phone": pending.phone_number, + "new_customer_email": pending.email_address, + } + ) # declare customer "not known" only if pending is in use - if (pending - and not batch.customer_id and not batch.local_customer - and batch.customer_name): - context['customer_is_known'] = False + if ( + pending + and not batch.customer_id + and not batch.local_customer + and batch.customer_name + ): + context["customer_is_known"] = False return context @@ -540,9 +592,9 @@ class OrderView(MasterView): * :meth:`unassign_customer()` * :meth:`set_pending_customer()` """ - customer_id = data.get('customer_id') + customer_id = data.get("customer_id") if not customer_id: - return {'error': "Must provide customer_id"} + return {"error": "Must provide customer_id"} self.batch_handler.set_customer(batch, customer_id) return self.get_context_customer(batch) @@ -598,9 +650,9 @@ class OrderView(MasterView): :returns: Dict of product info. """ - product_id = data.get('product_id') + product_id = data.get("product_id") if not product_id: - return {'error': "Must specify a product ID"} + return {"error": "Must specify a product ID"} session = self.Session() use_local = self.batch_handler.use_local_products() @@ -609,31 +661,42 @@ class OrderView(MasterView): else: data = self.batch_handler.get_product_info_external(session, product_id) - if 'error' in data: + if "error" in data: return data - if 'unit_price_reg' in data and 'unit_price_reg_display' not in data: - data['unit_price_reg_display'] = self.app.render_currency(data['unit_price_reg']) + if "unit_price_reg" in data and "unit_price_reg_display" not in data: + data["unit_price_reg_display"] = self.app.render_currency( + data["unit_price_reg"] + ) - if 'unit_price_reg' in data and 'unit_price_quoted' not in data: - data['unit_price_quoted'] = data['unit_price_reg'] + if "unit_price_reg" in data and "unit_price_quoted" not in data: + data["unit_price_quoted"] = data["unit_price_reg"] - if 'unit_price_quoted' in data and 'unit_price_quoted_display' not in data: - data['unit_price_quoted_display'] = self.app.render_currency(data['unit_price_quoted']) + if "unit_price_quoted" in data and "unit_price_quoted_display" not in data: + data["unit_price_quoted_display"] = self.app.render_currency( + data["unit_price_quoted"] + ) - if 'case_price_quoted' not in data: - if data.get('unit_price_quoted') is not None and data.get('case_size') is not None: - data['case_price_quoted'] = data['unit_price_quoted'] * data['case_size'] + if "case_price_quoted" not in data: + if ( + data.get("unit_price_quoted") is not None + and data.get("case_size") is not None + ): + data["case_price_quoted"] = ( + data["unit_price_quoted"] * data["case_size"] + ) - if 'case_price_quoted' in data and 'case_price_quoted_display' not in data: - data['case_price_quoted_display'] = self.app.render_currency(data['case_price_quoted']) + if "case_price_quoted" in data and "case_price_quoted_display" not in data: + data["case_price_quoted_display"] = self.app.render_currency( + data["case_price_quoted"] + ) decimal_fields = [ - 'case_size', - 'unit_price_reg', - 'unit_price_quoted', - 'case_price_quoted', - 'default_item_discount', + "case_size", + "unit_price_reg", + "unit_price_quoted", + "case_price_quoted", + "default_item_discount", ] for field in decimal_fields: @@ -670,14 +733,14 @@ class OrderView(MasterView): * :meth:`update_item()` * :meth:`delete_item()` """ - kw = {'user': self.request.user} - if 'discount_percent' in data and self.batch_handler.allow_item_discounts(): - kw['discount_percent'] = data['discount_percent'] - row = self.batch_handler.add_item(batch, data['product_info'], - data['order_qty'], data['order_uom'], **kw) + kw = {"user": self.request.user} + if "discount_percent" in data and self.batch_handler.allow_item_discounts(): + kw["discount_percent"] = data["discount_percent"] + row = self.batch_handler.add_item( + batch, data["product_info"], data["order_qty"], data["order_uom"], **kw + ) - return {'batch': self.normalize_batch(batch), - 'row': self.normalize_row(row)} + return {"batch": self.normalize_batch(batch), "row": self.normalize_row(row)} def update_item(self, batch, data): """ @@ -692,25 +755,25 @@ class OrderView(MasterView): model = self.app.model session = self.Session() - uuid = data.get('uuid') + uuid = data.get("uuid") if not uuid: - return {'error': "Must specify row UUID"} + return {"error": "Must specify row UUID"} row = session.get(model.NewOrderBatchRow, uuid) if not row: - return {'error': "Row not found"} + return {"error": "Row not found"} if row.batch is not batch: - return {'error': "Row is for wrong batch"} + return {"error": "Row is for wrong batch"} - kw = {'user': self.request.user} - if 'discount_percent' in data and self.batch_handler.allow_item_discounts(): - kw['discount_percent'] = data['discount_percent'] - self.batch_handler.update_item(row, data['product_info'], - data['order_qty'], data['order_uom'], **kw) + kw = {"user": self.request.user} + if "discount_percent" in data and self.batch_handler.allow_item_discounts(): + kw["discount_percent"] = data["discount_percent"] + self.batch_handler.update_item( + row, data["product_info"], data["order_qty"], data["order_uom"], **kw + ) - return {'batch': self.normalize_batch(batch), - 'row': self.normalize_row(row)} + return {"batch": self.normalize_batch(batch), "row": self.normalize_row(row)} def delete_item(self, batch, data): """ @@ -725,19 +788,19 @@ class OrderView(MasterView): model = self.app.model session = self.app.get_session(batch) - uuid = data.get('uuid') + uuid = data.get("uuid") if not uuid: - return {'error': "Must specify a row UUID"} + return {"error": "Must specify a row UUID"} row = session.get(model.NewOrderBatchRow, uuid) if not row: - return {'error': "Row not found"} + return {"error": "Row not found"} if row.batch is not batch: - return {'error': "Row is for wrong batch"} + return {"error": "Row is for wrong batch"} self.batch_handler.do_remove_row(row) - return {'batch': self.normalize_batch(batch)} + return {"batch": self.normalize_batch(batch)} def submit_order(self, batch, data): """ @@ -753,60 +816,73 @@ class OrderView(MasterView): user = self.request.user reason = self.batch_handler.why_not_execute(batch, user=user) if reason: - return {'error': reason} + return {"error": reason} try: order = self.batch_handler.do_execute(batch, user) except Exception as error: - log.warning("failed to execute new order batch: %s", batch, - exc_info=True) - return {'error': self.app.render_error(error)} + log.warning("failed to execute new order batch: %s", batch, exc_info=True) + return {"error": self.app.render_error(error)} return { - 'next_url': self.get_action_url('view', order), + "next_url": self.get_action_url("view", order), } def normalize_batch(self, batch): """ """ return { - 'uuid': batch.uuid.hex, - 'total_price': str(batch.total_price or 0), - 'total_price_display': self.app.render_currency(batch.total_price), - 'status_code': batch.status_code, - 'status_text': batch.status_text, + "uuid": batch.uuid.hex, + "total_price": str(batch.total_price or 0), + "total_price_display": self.app.render_currency(batch.total_price), + "status_code": batch.status_code, + "status_text": batch.status_text, } def normalize_row(self, row): """ """ data = { - 'uuid': row.uuid.hex, - 'sequence': row.sequence, - 'product_id': None, - 'product_scancode': row.product_scancode, - 'product_brand': row.product_brand, - 'product_description': row.product_description, - 'product_size': row.product_size, - 'product_full_description': self.app.make_full_name(row.product_brand, - row.product_description, - row.product_size), - 'product_weighed': row.product_weighed, - 'department_id': row.department_id, - 'department_name': row.department_name, - 'special_order': row.special_order, - 'vendor_name': row.vendor_name, - 'vendor_item_code': row.vendor_item_code, - 'case_size': float(row.case_size) if row.case_size is not None else None, - 'order_qty': float(row.order_qty), - 'order_uom': row.order_uom, - 'discount_percent': self.app.render_quantity(row.discount_percent), - 'unit_price_quoted': float(row.unit_price_quoted) if row.unit_price_quoted is not None else None, - 'unit_price_quoted_display': self.app.render_currency(row.unit_price_quoted), - 'case_price_quoted': float(row.case_price_quoted) if row.case_price_quoted is not None else None, - 'case_price_quoted_display': self.app.render_currency(row.case_price_quoted), - 'total_price': float(row.total_price) if row.total_price is not None else None, - 'total_price_display': self.app.render_currency(row.total_price), - 'status_code': row.status_code, - 'status_text': row.status_text, + "uuid": row.uuid.hex, + "sequence": row.sequence, + "product_id": None, + "product_scancode": row.product_scancode, + "product_brand": row.product_brand, + "product_description": row.product_description, + "product_size": row.product_size, + "product_full_description": self.app.make_full_name( + row.product_brand, row.product_description, row.product_size + ), + "product_weighed": row.product_weighed, + "department_id": row.department_id, + "department_name": row.department_name, + "special_order": row.special_order, + "vendor_name": row.vendor_name, + "vendor_item_code": row.vendor_item_code, + "case_size": float(row.case_size) if row.case_size is not None else None, + "order_qty": float(row.order_qty), + "order_uom": row.order_uom, + "discount_percent": self.app.render_quantity(row.discount_percent), + "unit_price_quoted": ( + float(row.unit_price_quoted) + if row.unit_price_quoted is not None + else None + ), + "unit_price_quoted_display": self.app.render_currency( + row.unit_price_quoted + ), + "case_price_quoted": ( + float(row.case_price_quoted) + if row.case_price_quoted is not None + else None + ), + "case_price_quoted_display": self.app.render_currency( + row.case_price_quoted + ), + "total_price": ( + float(row.total_price) if row.total_price is not None else None + ), + "total_price_display": self.app.render_currency(row.total_price), + "status_code": row.status_code, + "status_text": row.status_text, } use_local = self.batch_handler.use_local_products() @@ -814,53 +890,66 @@ class OrderView(MasterView): # product_id if use_local: if row.local_product: - data['product_id'] = row.local_product.uuid.hex + data["product_id"] = row.local_product.uuid.hex else: - data['product_id'] = row.product_id + data["product_id"] = row.product_id # vendor_name if use_local: if row.local_product: - data['vendor_name'] = row.local_product.vendor_name - else: # use external - pass # TODO - if not data.get('product_id') and row.pending_product: - data['vendor_name'] = row.pending_product.vendor_name + data["vendor_name"] = row.local_product.vendor_name + else: # use external + pass # TODO + if not data.get("product_id") and row.pending_product: + data["vendor_name"] = row.pending_product.vendor_name if row.unit_price_reg: - data['unit_price_reg'] = float(row.unit_price_reg) - data['unit_price_reg_display'] = self.app.render_currency(row.unit_price_reg) + data["unit_price_reg"] = float(row.unit_price_reg) + data["unit_price_reg_display"] = self.app.render_currency( + row.unit_price_reg + ) if row.unit_price_sale: - data['unit_price_sale'] = float(row.unit_price_sale) - data['unit_price_sale_display'] = self.app.render_currency(row.unit_price_sale) + data["unit_price_sale"] = float(row.unit_price_sale) + data["unit_price_sale_display"] = self.app.render_currency( + row.unit_price_sale + ) if row.sale_ends: sale_ends = row.sale_ends - data['sale_ends'] = str(row.sale_ends) - data['sale_ends_display'] = self.app.render_date(row.sale_ends) + data["sale_ends"] = str(row.sale_ends) + data["sale_ends_display"] = self.app.render_date(row.sale_ends) if row.pending_product: pending = row.pending_product - data['pending_product'] = { - 'uuid': pending.uuid.hex, - 'scancode': pending.scancode, - 'brand_name': pending.brand_name, - 'description': pending.description, - 'size': pending.size, - 'department_id': pending.department_id, - 'department_name': pending.department_name, - 'unit_price_reg': float(pending.unit_price_reg) if pending.unit_price_reg is not None else None, - 'vendor_name': pending.vendor_name, - 'vendor_item_code': pending.vendor_item_code, - 'unit_cost': float(pending.unit_cost) if pending.unit_cost is not None else None, - 'case_size': float(pending.case_size) if pending.case_size is not None else None, - 'notes': pending.notes, - 'special_order': pending.special_order, + data["pending_product"] = { + "uuid": pending.uuid.hex, + "scancode": pending.scancode, + "brand_name": pending.brand_name, + "description": pending.description, + "size": pending.size, + "department_id": pending.department_id, + "department_name": pending.department_name, + "unit_price_reg": ( + float(pending.unit_price_reg) + if pending.unit_price_reg is not None + else None + ), + "vendor_name": pending.vendor_name, + "vendor_item_code": pending.vendor_item_code, + "unit_cost": ( + float(pending.unit_cost) if pending.unit_cost is not None else None + ), + "case_size": ( + float(pending.case_size) if pending.case_size is not None else None + ), + "notes": pending.notes, + "special_order": pending.special_order, } # display text for order qty/uom - data['order_qty_display'] = self.order_handler.get_order_qty_uom_text( - row.order_qty, row.order_uom, case_size=row.case_size, html=True) + data["order_qty_display"] = self.order_handler.get_order_qty_uom_text( + row.order_qty, row.order_uom, case_size=row.case_size, html=True + ) return data @@ -875,26 +964,26 @@ class OrderView(MasterView): # store_id if not self.order_handler.expose_store_id(): - f.remove('store_id') + f.remove("store_id") # local_customer if order.customer_id and not order.local_customer: - f.remove('local_customer') + f.remove("local_customer") else: - f.set_node('local_customer', LocalCustomerRef(self.request)) + f.set_node("local_customer", LocalCustomerRef(self.request)) # pending_customer if order.customer_id or order.local_customer: - f.remove('pending_customer') + f.remove("pending_customer") else: - f.set_node('pending_customer', PendingCustomerRef(self.request)) + f.set_node("pending_customer", PendingCustomerRef(self.request)) # total_price - f.set_node('total_price', WuttaMoney(self.request)) + f.set_node("total_price", WuttaMoney(self.request)) # created_by - f.set_node('created_by', UserRef(self.request)) - f.set_readonly('created_by') + f.set_node("created_by", UserRef(self.request)) + f.set_readonly("created_by") def get_xref_buttons(self, order): """ """ @@ -902,14 +991,19 @@ class OrderView(MasterView): model = self.app.model session = self.Session() - if self.request.has_perm('neworder_batches.view'): - batch = session.query(model.NewOrderBatch)\ - .filter(model.NewOrderBatch.id == order.order_id)\ - .first() + if self.request.has_perm("neworder_batches.view"): + batch = ( + session.query(model.NewOrderBatch) + .filter(model.NewOrderBatch.id == order.order_id) + .first() + ) if batch: - url = self.request.route_url('neworder_batches.view', uuid=batch.uuid) + url = self.request.route_url("neworder_batches.view", uuid=batch.uuid) buttons.append( - self.make_button("View the Batch", primary=True, icon_left='eye', url=url)) + self.make_button( + "View the Batch", primary=True, icon_left="eye", url=url + ) + ) return buttons @@ -917,8 +1011,7 @@ class OrderView(MasterView): """ """ model = self.app.model session = self.Session() - return session.query(model.OrderItem)\ - .filter(model.OrderItem.order == order) + return session.query(model.OrderItem).filter(model.OrderItem.order == order) def configure_row_grid(self, g): """ """ @@ -926,34 +1019,34 @@ class OrderView(MasterView): # enum = self.app.enum # sequence - g.set_label('sequence', "Seq.", column_only=True) - g.set_link('sequence') + g.set_label("sequence", "Seq.", column_only=True) + g.set_link("sequence") # product_scancode - g.set_link('product_scancode') + g.set_link("product_scancode") # product_brand - g.set_link('product_brand') + g.set_link("product_brand") # product_description - g.set_link('product_description') + g.set_link("product_description") # product_size - g.set_link('product_size') + g.set_link("product_size") # TODO # order_uom - #g.set_renderer('order_uom', self.grid_render_enum, enum=enum.OrderUOM) + # g.set_renderer('order_uom', self.grid_render_enum, enum=enum.OrderUOM) # discount_percent - g.set_renderer('discount_percent', 'percent') - g.set_label('discount_percent', "Disc. %", column_only=True) + g.set_renderer("discount_percent", "percent") + g.set_label("discount_percent", "Disc. %", column_only=True) # total_price - g.set_renderer('total_price', g.render_currency) + g.set_renderer("total_price", g.render_currency) # status_code - g.set_renderer('status_code', self.render_status_code) + g.set_renderer("status_code", self.render_status_code) # TODO: upstream should set this automatically g.row_class = self.row_grid_row_class @@ -962,7 +1055,7 @@ class OrderView(MasterView): """ """ variant = self.order_handler.item_status_to_variant(item.status_code) if variant: - return f'has-background-{variant}' + return f"has-background-{variant}" def render_status_code(self, item, key, value): """ """ @@ -971,50 +1064,49 @@ class OrderView(MasterView): def get_row_action_url_view(self, item, i): """ """ - return self.request.route_url('order_items.view', uuid=item.uuid) + return self.request.route_url("order_items.view", uuid=item.uuid) def configure_get_simple_settings(self): """ """ settings = [ - # stores - {'name': 'sideshow.orders.expose_store_id', - 'type': bool}, - {'name': 'sideshow.orders.default_store_id'}, - + {"name": "sideshow.orders.expose_store_id", "type": bool}, + {"name": "sideshow.orders.default_store_id"}, # customers - {'name': 'sideshow.orders.use_local_customers', - # nb. this is really a bool but we present as string in config UI - #'type': bool, - 'default': 'true'}, - + { + "name": "sideshow.orders.use_local_customers", + # nb. this is really a bool but we present as string in config UI + #'type': bool, + "default": "true", + }, # products - {'name': 'sideshow.orders.use_local_products', - # nb. this is really a bool but we present as string in config UI - #'type': bool, - 'default': 'true'}, - {'name': 'sideshow.orders.allow_unknown_products', - 'type': bool, - 'default': True}, - + { + "name": "sideshow.orders.use_local_products", + # nb. this is really a bool but we present as string in config UI + #'type': bool, + "default": "true", + }, + { + "name": "sideshow.orders.allow_unknown_products", + "type": bool, + "default": True, + }, # pricing - {'name': 'sideshow.orders.allow_item_discounts', - 'type': bool}, - {'name': 'sideshow.orders.allow_item_discounts_if_on_sale', - 'type': bool}, - {'name': 'sideshow.orders.default_item_discount', - 'type': float}, - + {"name": "sideshow.orders.allow_item_discounts", "type": bool}, + {"name": "sideshow.orders.allow_item_discounts_if_on_sale", "type": bool}, + {"name": "sideshow.orders.default_item_discount", "type": float}, # batches - {'name': 'wutta.batch.neworder.handler.spec'}, + {"name": "wutta.batch.neworder.handler.spec"}, ] # required fields for new product entry for field in self.PENDING_PRODUCT_ENTRY_FIELDS: - setting = {'name': f'sideshow.orders.unknown_product.fields.{field}.required', - 'type': bool} - if field == 'description': - setting['default'] = True + setting = { + "name": f"sideshow.orders.unknown_product.fields.{field}.required", + "type": bool, + } + if field == "description": + setting["default"] = True settings.append(setting) return settings @@ -1023,26 +1115,36 @@ class OrderView(MasterView): """ """ context = super().configure_get_context(**kwargs) - context['pending_product_fields'] = self.PENDING_PRODUCT_ENTRY_FIELDS + context["pending_product_fields"] = self.PENDING_PRODUCT_ENTRY_FIELDS - handlers = self.app.get_batch_handler_specs('neworder') - handlers = [{'spec': spec} for spec in handlers] - context['batch_handlers'] = handlers + handlers = self.app.get_batch_handler_specs("neworder") + handlers = [{"spec": spec} for spec in handlers] + context["batch_handlers"] = handlers - context['dept_item_discounts'] = self.get_dept_item_discounts() + context["dept_item_discounts"] = self.get_dept_item_discounts() return context def configure_gather_settings(self, data, simple_settings=None): """ """ - settings = super().configure_gather_settings(data, simple_settings=simple_settings) + settings = super().configure_gather_settings( + data, simple_settings=simple_settings + ) - for dept in json.loads(data['dept_item_discounts']): - deptid = dept['department_id'] - settings.append({'name': f'sideshow.orders.departments.{deptid}.name', - 'value': dept['department_name']}) - settings.append({'name': f'sideshow.orders.departments.{deptid}.default_item_discount', - 'value': dept['default_item_discount']}) + for dept in json.loads(data["dept_item_discounts"]): + deptid = dept["department_id"] + settings.append( + { + "name": f"sideshow.orders.departments.{deptid}.name", + "value": dept["department_name"], + } + ) + settings.append( + { + "name": f"sideshow.orders.departments.{deptid}.default_item_discount", + "value": dept["default_item_discount"], + } + ) return settings @@ -1053,15 +1155,21 @@ class OrderView(MasterView): super().configure_remove_settings(**kwargs) - to_delete = session.query(model.Setting)\ - .filter(sa.or_( - model.Setting.name.like('sideshow.orders.departments.%.name'), - model.Setting.name.like('sideshow.orders.departments.%.default_item_discount')))\ - .all() + to_delete = ( + session.query(model.Setting) + .filter( + sa.or_( + model.Setting.name.like("sideshow.orders.departments.%.name"), + model.Setting.name.like( + "sideshow.orders.departments.%.default_item_discount" + ), + ) + ) + .all() + ) for setting in to_delete: self.app.delete_setting(session, setting.name) - @classmethod def defaults(cls, config): cls._order_defaults(config) @@ -1076,32 +1184,44 @@ class OrderView(MasterView): model_title_plural = cls.get_model_title_plural() # fix perm group - config.add_wutta_permission_group(permission_prefix, - model_title_plural, - overwrite=False) + config.add_wutta_permission_group( + permission_prefix, model_title_plural, overwrite=False + ) # extra perm required to create order with unknown/pending product - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.create_unknown_product', - f"Create new {model_title} for unknown/pending product") + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.create_unknown_product", + f"Create new {model_title} for unknown/pending product", + ) # customer autocomplete - config.add_route(f'{route_prefix}.customer_autocomplete', - f'{url_prefix}/customer-autocomplete', - request_method='GET') - config.add_view(cls, attr='customer_autocomplete', - route_name=f'{route_prefix}.customer_autocomplete', - renderer='json', - permission=f'{permission_prefix}.list') + config.add_route( + f"{route_prefix}.customer_autocomplete", + f"{url_prefix}/customer-autocomplete", + request_method="GET", + ) + config.add_view( + cls, + attr="customer_autocomplete", + route_name=f"{route_prefix}.customer_autocomplete", + renderer="json", + permission=f"{permission_prefix}.list", + ) # product autocomplete - config.add_route(f'{route_prefix}.product_autocomplete', - f'{url_prefix}/product-autocomplete', - request_method='GET') - config.add_view(cls, attr='product_autocomplete', - route_name=f'{route_prefix}.product_autocomplete', - renderer='json', - permission=f'{permission_prefix}.list') + config.add_route( + f"{route_prefix}.product_autocomplete", + f"{url_prefix}/product-autocomplete", + request_method="GET", + ) + config.add_view( + cls, + attr="product_autocomplete", + route_name=f"{route_prefix}.product_autocomplete", + renderer="json", + permission=f"{permission_prefix}.list", + ) class OrderItemView(MasterView): @@ -1131,77 +1251,78 @@ class OrderItemView(MasterView): Reference to the :term:`order handler` as returned by :meth:`get_order_handler()`. """ + model_class = OrderItem model_title = "Order Item (All)" model_title_plural = "Order Items (All)" - route_prefix = 'order_items' - url_prefix = '/order-items' + route_prefix = "order_items" + url_prefix = "/order-items" creatable = False editable = False deletable = False labels = { - 'order_id': "Order ID", - 'store_id': "Store ID", - 'product_id': "Product ID", - 'product_scancode': "Scancode", - 'product_brand': "Brand", - 'product_description': "Description", - 'product_size': "Size", - 'product_weighed': "Sold by Weight", - 'department_id': "Department ID", - 'order_uom': "Order UOM", - 'status_code': "Status", + "order_id": "Order ID", + "store_id": "Store ID", + "product_id": "Product ID", + "product_scancode": "Scancode", + "product_brand": "Brand", + "product_description": "Description", + "product_size": "Size", + "product_weighed": "Sold by Weight", + "department_id": "Department ID", + "order_uom": "Order UOM", + "status_code": "Status", } grid_columns = [ - 'order_id', - 'store_id', - 'customer_name', + "order_id", + "store_id", + "customer_name", # 'sequence', - 'product_scancode', - 'product_brand', - 'product_description', - 'product_size', - 'department_name', - 'special_order', - 'order_qty', - 'order_uom', - 'total_price', - 'status_code', + "product_scancode", + "product_brand", + "product_description", + "product_size", + "department_name", + "special_order", + "order_qty", + "order_uom", + "total_price", + "status_code", ] - sort_defaults = ('order_id', 'desc') + sort_defaults = ("order_id", "desc") form_fields = [ - 'order', + "order", # 'customer_name', - 'sequence', - 'product_id', - 'local_product', - 'pending_product', - 'product_scancode', - 'product_brand', - 'product_description', - 'product_size', - 'product_weighed', - 'department_id', - 'department_name', - 'special_order', - 'case_size', - 'unit_cost', - 'unit_price_reg', - 'unit_price_sale', - 'sale_ends', - 'unit_price_quoted', - 'case_price_quoted', - 'order_qty', - 'order_uom', - 'discount_percent', - 'total_price', - 'status_code', - 'paid_amount', - 'payment_transaction_number', + "sequence", + "product_id", + "local_product", + "pending_product", + "product_scancode", + "product_brand", + "product_description", + "product_size", + "product_weighed", + "department_id", + "department_name", + "special_order", + "case_size", + "unit_cost", + "unit_price_reg", + "unit_price_sale", + "sale_ends", + "unit_price_quoted", + "case_price_quoted", + "order_qty", + "order_uom", + "discount_percent", + "total_price", + "status_code", + "paid_amount", + "payment_transaction_number", ] def __init__(self, request, context=None): @@ -1211,7 +1332,7 @@ class OrderItemView(MasterView): def get_fallback_templates(self, template): """ """ templates = super().get_fallback_templates(template) - templates.insert(0, f'/order-items/{template}.mako') + templates.insert(0, f"/order-items/{template}.mako") return templates def get_query(self, session=None): @@ -1228,47 +1349,47 @@ class OrderItemView(MasterView): # store_id if not self.order_handler.expose_store_id(): - g.remove('store_id') + g.remove("store_id") # order_id - g.set_sorter('order_id', model.Order.order_id) - g.set_renderer('order_id', self.render_order_attr) - g.set_link('order_id') + g.set_sorter("order_id", model.Order.order_id) + g.set_renderer("order_id", self.render_order_attr) + g.set_link("order_id") # store_id - g.set_sorter('store_id', model.Order.store_id) - g.set_renderer('store_id', self.render_order_attr) + g.set_sorter("store_id", model.Order.store_id) + g.set_renderer("store_id", self.render_order_attr) # customer_name - g.set_label('customer_name', "Customer", column_only=True) - g.set_renderer('customer_name', self.render_order_attr) - g.set_sorter('customer_name', model.Order.customer_name) - g.set_filter('customer_name', model.Order.customer_name) + g.set_label("customer_name", "Customer", column_only=True) + g.set_renderer("customer_name", self.render_order_attr) + g.set_sorter("customer_name", model.Order.customer_name) + g.set_filter("customer_name", model.Order.customer_name) # # sequence # g.set_label('sequence', "Seq.", column_only=True) # product_scancode - g.set_link('product_scancode') + g.set_link("product_scancode") # product_brand - g.set_link('product_brand') + g.set_link("product_brand") # product_description - g.set_link('product_description') + g.set_link("product_description") # product_size - g.set_link('product_size') + g.set_link("product_size") # order_uom # TODO - #g.set_renderer('order_uom', self.grid_render_enum, enum=enum.OrderUOM) + # g.set_renderer('order_uom', self.grid_render_enum, enum=enum.OrderUOM) # total_price - g.set_renderer('total_price', g.render_currency) + g.set_renderer("total_price", g.render_currency) # status_code - g.set_renderer('status_code', self.render_status_code) + g.set_renderer("status_code", self.render_status_code) def render_order_attr(self, item, key, value): """ """ @@ -1284,7 +1405,7 @@ class OrderItemView(MasterView): """ """ variant = self.order_handler.item_status_to_variant(item.status_code) if variant: - return f'has-background-{variant}' + return f"has-background-{variant}" def configure_form(self, f): """ """ @@ -1293,46 +1414,46 @@ class OrderItemView(MasterView): item = f.model_instance # order - f.set_node('order', OrderRef(self.request)) + f.set_node("order", OrderRef(self.request)) # local_product - f.set_node('local_product', LocalProductRef(self.request)) + f.set_node("local_product", LocalProductRef(self.request)) # pending_product if item.product_id or item.local_product: - f.remove('pending_product') + f.remove("pending_product") else: - f.set_node('pending_product', PendingProductRef(self.request)) + f.set_node("pending_product", PendingProductRef(self.request)) # order_qty - f.set_node('order_qty', WuttaQuantity(self.request)) + f.set_node("order_qty", WuttaQuantity(self.request)) # order_uom - f.set_node('order_uom', WuttaDictEnum(self.request, enum.ORDER_UOM)) + f.set_node("order_uom", WuttaDictEnum(self.request, enum.ORDER_UOM)) # case_size - f.set_node('case_size', WuttaQuantity(self.request)) + f.set_node("case_size", WuttaQuantity(self.request)) # unit_cost - f.set_node('unit_cost', WuttaMoney(self.request, scale=4)) + f.set_node("unit_cost", WuttaMoney(self.request, scale=4)) # unit_price_reg - f.set_node('unit_price_reg', WuttaMoney(self.request)) + f.set_node("unit_price_reg", WuttaMoney(self.request)) # unit_price_quoted - f.set_node('unit_price_quoted', WuttaMoney(self.request)) + f.set_node("unit_price_quoted", WuttaMoney(self.request)) # case_price_quoted - f.set_node('case_price_quoted', WuttaMoney(self.request)) + f.set_node("case_price_quoted", WuttaMoney(self.request)) # total_price - f.set_node('total_price', WuttaMoney(self.request)) + f.set_node("total_price", WuttaMoney(self.request)) # status - f.set_node('status_code', WuttaDictEnum(self.request, enum.ORDER_ITEM_STATUS)) + f.set_node("status_code", WuttaDictEnum(self.request, enum.ORDER_ITEM_STATUS)) # paid_amount - f.set_node('paid_amount', WuttaMoney(self.request)) + f.set_node("paid_amount", WuttaMoney(self.request)) def get_template_context(self, context): """ """ @@ -1340,38 +1461,47 @@ class OrderItemView(MasterView): model = self.app.model enum = self.app.enum route_prefix = self.get_route_prefix() - item = context['instance'] - form = context['form'] + item = context["instance"] + form = context["form"] - context['expose_store_id'] = self.order_handler.expose_store_id() + context["expose_store_id"] = self.order_handler.expose_store_id() - context['item'] = item - context['order'] = item.order - context['order_qty_uom_text'] = self.order_handler.get_order_qty_uom_text( - item.order_qty, item.order_uom, case_size=item.case_size, html=True) - context['item_status_variant'] = self.order_handler.item_status_to_variant(item.status_code) + context["item"] = item + context["order"] = item.order + context["order_qty_uom_text"] = self.order_handler.get_order_qty_uom_text( + item.order_qty, item.order_uom, case_size=item.case_size, html=True + ) + context["item_status_variant"] = self.order_handler.item_status_to_variant( + item.status_code + ) - grid = self.make_grid(key=f'{route_prefix}.view.events', - model_class=model.OrderItemEvent, - data=item.events, - columns=[ - 'occurred', - 'actor', - 'type_code', - 'note', - ], - labels={ - 'occurred': "Date/Time", - 'actor': "User", - 'type_code': "Event Type", - }) - grid.set_renderer('type_code', lambda e, k, v: enum.ORDER_ITEM_EVENT[v]) - grid.set_renderer('note', self.render_event_note) - if self.request.has_perm('users.view'): - grid.set_renderer('actor', lambda e, k, v: tags.link_to( - e.actor, self.request.route_url('users.view', uuid=e.actor.uuid))) + grid = self.make_grid( + key=f"{route_prefix}.view.events", + model_class=model.OrderItemEvent, + data=item.events, + columns=[ + "occurred", + "actor", + "type_code", + "note", + ], + labels={ + "occurred": "Date/Time", + "actor": "User", + "type_code": "Event Type", + }, + ) + grid.set_renderer("type_code", lambda e, k, v: enum.ORDER_ITEM_EVENT[v]) + grid.set_renderer("note", self.render_event_note) + if self.request.has_perm("users.view"): + grid.set_renderer( + "actor", + lambda e, k, v: tags.link_to( + e.actor, self.request.route_url("users.view", uuid=e.actor.uuid) + ), + ) form.add_grid_vue_context(grid) - context['events_grid'] = grid + context["events_grid"] = grid return context @@ -1379,20 +1509,25 @@ class OrderItemView(MasterView): """ """ enum = self.app.enum if event.type_code == enum.ORDER_ITEM_EVENT_NOTE_ADDED: - return HTML.tag('span', class_='has-background-info-light', - style='padding: 0.25rem 0.5rem;', - c=[value]) + return HTML.tag( + "span", + class_="has-background-info-light", + style="padding: 0.25rem 0.5rem;", + c=[value], + ) return value def get_xref_buttons(self, item): """ """ buttons = super().get_xref_buttons(item) - if self.request.has_perm('orders.view'): - url = self.request.route_url('orders.view', uuid=item.order_uuid) + if self.request.has_perm("orders.view"): + url = self.request.route_url("orders.view", uuid=item.order_uuid) buttons.append( - self.make_button("View the Order", url=url, - primary=True, icon_left='eye')) + self.make_button( + "View the Order", url=url, primary=True, icon_left="eye" + ) + ) return buttons @@ -1404,10 +1539,13 @@ class OrderItemView(MasterView): enum = self.app.enum item = self.get_instance() - item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, self.request.user, - note=self.request.POST['note']) + item.add_event( + enum.ORDER_ITEM_EVENT_NOTE_ADDED, + self.request.user, + note=self.request.POST["note"], + ) - return self.redirect(self.get_action_url('view', item)) + return self.redirect(self.get_action_url("view", item)) def change_status(self): """ @@ -1418,14 +1556,14 @@ class OrderItemView(MasterView): enum = self.app.enum main_item = self.get_instance() session = self.Session() - redirect = self.redirect(self.get_action_url('view', main_item)) + redirect = self.redirect(self.get_action_url("view", main_item)) - extra_note = self.request.POST.get('note') + extra_note = self.request.POST.get("note") # validate new status - new_status_code = int(self.request.POST['new_status']) + new_status_code = int(self.request.POST["new_status"]) if new_status_code not in enum.ORDER_ITEM_STATUS: - self.request.session.flash("Invalid status code", 'error') + self.request.session.flash("Invalid status code", "error") return redirect new_status_text = enum.ORDER_ITEM_STATUS[new_status_code] @@ -1444,15 +1582,19 @@ class OrderItemView(MasterView): # event: change status note = 'status changed from "{}" to "{}"'.format( - enum.ORDER_ITEM_STATUS[item.status_code], - new_status_text) - item.add_event(enum.ORDER_ITEM_EVENT_STATUS_CHANGE, - self.request.user, note=note) + enum.ORDER_ITEM_STATUS[item.status_code], new_status_text + ) + item.add_event( + enum.ORDER_ITEM_EVENT_STATUS_CHANGE, self.request.user, note=note + ) # event: add note if extra_note: - item.add_event(enum.ORDER_ITEM_EVENT_NOTE_ADDED, - self.request.user, note=extra_note) + item.add_event( + enum.ORDER_ITEM_EVENT_NOTE_ADDED, + self.request.user, + note=extra_note, + ) # new status item.status_code = new_status_code @@ -1480,7 +1622,7 @@ class OrderItemView(MasterView): if uuids is None: uuids = [] elif isinstance(uuids, str): - uuids = uuids.split(',') + uuids = uuids.split(",") items = [] for uuid in uuids: @@ -1490,13 +1632,13 @@ class OrderItemView(MasterView): try: item = session.get(model.OrderItem, uuid) except sa.exc.StatementError: - pass # nb. invalid UUID + pass # nb. invalid UUID else: if item: items.append(item) if not items: - self.request.session.flash("Must specify valid order item(s).", 'warning') + self.request.session.flash("Must specify valid order item(s).", "warning") raise self.redirect(self.get_index_url()) return items @@ -1517,33 +1659,47 @@ class OrderItemView(MasterView): model_title_plural = cls.get_model_title_plural() # fix perm group - config.add_wutta_permission_group(permission_prefix, - model_title_plural, - overwrite=False) + config.add_wutta_permission_group( + permission_prefix, model_title_plural, overwrite=False + ) # add note - config.add_route(f'{route_prefix}.add_note', - f'{instance_url_prefix}/add_note', - request_method='POST') - config.add_view(cls, attr='add_note', - route_name=f'{route_prefix}.add_note', - renderer='json', - permission=f'{permission_prefix}.add_note') - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.add_note', - f"Add note for {model_title}") + config.add_route( + f"{route_prefix}.add_note", + f"{instance_url_prefix}/add_note", + request_method="POST", + ) + config.add_view( + cls, + attr="add_note", + route_name=f"{route_prefix}.add_note", + renderer="json", + permission=f"{permission_prefix}.add_note", + ) + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.add_note", + f"Add note for {model_title}", + ) # change status - config.add_route(f'{route_prefix}.change_status', - f'{instance_url_prefix}/change-status', - request_method='POST') - config.add_view(cls, attr='change_status', - route_name=f'{route_prefix}.change_status', - renderer='json', - permission=f'{permission_prefix}.change_status') - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.change_status', - f"Change status for {model_title}") + config.add_route( + f"{route_prefix}.change_status", + f"{instance_url_prefix}/change-status", + request_method="POST", + ) + config.add_view( + cls, + attr="change_status", + route_name=f"{route_prefix}.change_status", + renderer="json", + permission=f"{permission_prefix}.change_status", + ) + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.change_status", + f"Change status for {model_title}", + ) class PlacementView(OrderItemView): @@ -1562,29 +1718,30 @@ class PlacementView(OrderItemView): * ``/placement/`` * ``/placement/XXX`` """ + model_title = "Order Item (Placement)" model_title_plural = "Order Items (Placement)" - route_prefix = 'order_items_placement' - url_prefix = '/placement' + route_prefix = "order_items_placement" + url_prefix = "/placement" grid_columns = [ - 'order_id', - 'store_id', - 'customer_name', - 'product_brand', - 'product_description', - 'product_size', - 'department_name', - 'special_order', - 'vendor_name', - 'vendor_item_code', - 'order_qty', - 'order_uom', - 'total_price', + "order_id", + "store_id", + "customer_name", + "product_brand", + "product_description", + "product_size", + "department_name", + "special_order", + "vendor_name", + "vendor_item_code", + "order_qty", + "order_uom", + "total_price", ] filter_defaults = { - 'vendor_name': {'active': True}, + "vendor_name": {"active": True}, } def get_query(self, session=None): @@ -1599,16 +1756,21 @@ class PlacementView(OrderItemView): super().configure_grid(g) # checkable - if self.has_perm('process_placement'): + if self.has_perm("process_placement"): g.checkable = True # tool button: Order Placed - if self.has_perm('process_placement'): - button = self.make_button("Order Placed", primary=True, - icon_left='arrow-circle-right', - **{'@click': "$emit('process-placement', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_placement') + if self.has_perm("process_placement"): + button = self.make_button( + "Order Placed", + primary=True, + icon_left="arrow-circle-right", + **{ + "@click": "$emit('process-placement', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_placement") def process_placement(self): """ @@ -1630,15 +1792,18 @@ class PlacementView(OrderItemView): the :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - vendor_name = self.request.POST.get('vendor_name', '').strip() or None - po_number = self.request.POST.get('po_number', '').strip() or None - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + vendor_name = self.request.POST.get("vendor_name", "").strip() or None + po_number = self.request.POST.get("po_number", "").strip() or None + note = self.request.POST.get("note", "").strip() or None - self.order_handler.process_placement(items, self.request.user, - vendor_name=vendor_name, - po_number=po_number, - note=note) + self.order_handler.process_placement( + items, + self.request.user, + vendor_name=vendor_name, + po_number=po_number, + note=note, + ) self.request.session.flash(f"{len(items)} Order Items were marked as placed") return self.redirect(self.get_index_url()) @@ -1657,15 +1822,22 @@ class PlacementView(OrderItemView): model_title_plural = cls.get_model_title_plural() # process placement - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_placement', - f"Process placement for {model_title_plural}") - config.add_route(f'{route_prefix}.process_placement', - f'{url_prefix}/process-placement', - request_method='POST') - config.add_view(cls, attr='process_placement', - route_name=f'{route_prefix}.process_placement', - permission=f'{permission_prefix}.process_placement') + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_placement", + f"Process placement for {model_title_plural}", + ) + config.add_route( + f"{route_prefix}.process_placement", + f"{url_prefix}/process-placement", + request_method="POST", + ) + config.add_view( + cls, + attr="process_placement", + route_name=f"{route_prefix}.process_placement", + permission=f"{permission_prefix}.process_placement", + ) class ReceivingView(OrderItemView): @@ -1684,29 +1856,30 @@ class ReceivingView(OrderItemView): * ``/receiving/`` * ``/receiving/XXX`` """ + model_title = "Order Item (Receiving)" model_title_plural = "Order Items (Receiving)" - route_prefix = 'order_items_receiving' - url_prefix = '/receiving' + route_prefix = "order_items_receiving" + url_prefix = "/receiving" grid_columns = [ - 'order_id', - 'store_id', - 'customer_name', - 'product_brand', - 'product_description', - 'product_size', - 'department_name', - 'special_order', - 'vendor_name', - 'vendor_item_code', - 'order_qty', - 'order_uom', - 'total_price', + "order_id", + "store_id", + "customer_name", + "product_brand", + "product_description", + "product_size", + "department_name", + "special_order", + "vendor_name", + "vendor_item_code", + "order_qty", + "order_uom", + "total_price", ] filter_defaults = { - 'vendor_name': {'active': True}, + "vendor_name": {"active": True}, } def get_query(self, session=None): @@ -1714,31 +1887,42 @@ class ReceivingView(OrderItemView): query = super().get_query(session=session) model = self.app.model enum = self.app.enum - return query.filter(model.OrderItem.status_code == enum.ORDER_ITEM_STATUS_PLACED) + return query.filter( + model.OrderItem.status_code == enum.ORDER_ITEM_STATUS_PLACED + ) def configure_grid(self, g): """ """ super().configure_grid(g) # checkable - if self.has_any_perm('process_receiving', 'process_reorder'): + if self.has_any_perm("process_receiving", "process_reorder"): g.checkable = True # tool button: Received - if self.has_perm('process_receiving'): - button = self.make_button("Received", primary=True, - icon_left='arrow-circle-right', - **{'@click': "$emit('process-receiving', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_receiving') + if self.has_perm("process_receiving"): + button = self.make_button( + "Received", + primary=True, + icon_left="arrow-circle-right", + **{ + "@click": "$emit('process-receiving', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_receiving") # tool button: Re-Order - if self.has_perm('process_reorder'): - button = self.make_button("Re-Order", - icon_left='redo', - **{'@click': "$emit('process-reorder', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_reorder') + if self.has_perm("process_reorder"): + button = self.make_button( + "Re-Order", + icon_left="redo", + **{ + "@click": "$emit('process-reorder', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_reorder") def process_receiving(self): """ @@ -1762,17 +1946,20 @@ class ReceivingView(OrderItemView): the :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - vendor_name = self.request.POST.get('vendor_name', '').strip() or None - invoice_number = self.request.POST.get('invoice_number', '').strip() or None - po_number = self.request.POST.get('po_number', '').strip() or None - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + vendor_name = self.request.POST.get("vendor_name", "").strip() or None + invoice_number = self.request.POST.get("invoice_number", "").strip() or None + po_number = self.request.POST.get("po_number", "").strip() or None + note = self.request.POST.get("note", "").strip() or None - self.order_handler.process_receiving(items, self.request.user, - vendor_name=vendor_name, - invoice_number=invoice_number, - po_number=po_number, - note=note) + self.order_handler.process_receiving( + items, + self.request.user, + vendor_name=vendor_name, + invoice_number=invoice_number, + po_number=po_number, + note=note, + ) self.request.session.flash(f"{len(items)} Order Items were marked as received") return self.redirect(self.get_index_url()) @@ -1793,12 +1980,14 @@ class ReceivingView(OrderItemView): :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + note = self.request.POST.get("note", "").strip() or None self.order_handler.process_reorder(items, self.request.user, note=note) - self.request.session.flash(f"{len(items)} Order Items were marked as ready for placement") + self.request.session.flash( + f"{len(items)} Order Items were marked as ready for placement" + ) return self.redirect(self.get_index_url()) @classmethod @@ -1815,26 +2004,40 @@ class ReceivingView(OrderItemView): model_title_plural = cls.get_model_title_plural() # process receiving - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_receiving', - f"Process receiving for {model_title_plural}") - config.add_route(f'{route_prefix}.process_receiving', - f'{url_prefix}/process-receiving', - request_method='POST') - config.add_view(cls, attr='process_receiving', - route_name=f'{route_prefix}.process_receiving', - permission=f'{permission_prefix}.process_receiving') + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_receiving", + f"Process receiving for {model_title_plural}", + ) + config.add_route( + f"{route_prefix}.process_receiving", + f"{url_prefix}/process-receiving", + request_method="POST", + ) + config.add_view( + cls, + attr="process_receiving", + route_name=f"{route_prefix}.process_receiving", + permission=f"{permission_prefix}.process_receiving", + ) # process reorder - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_reorder', - f"Process re-order for {model_title_plural}") - config.add_route(f'{route_prefix}.process_reorder', - f'{url_prefix}/process-reorder', - request_method='POST') - config.add_view(cls, attr='process_reorder', - route_name=f'{route_prefix}.process_reorder', - permission=f'{permission_prefix}.process_reorder') + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_reorder", + f"Process re-order for {model_title_plural}", + ) + config.add_route( + f"{route_prefix}.process_reorder", + f"{url_prefix}/process-reorder", + request_method="POST", + ) + config.add_view( + cls, + attr="process_reorder", + route_name=f"{route_prefix}.process_reorder", + permission=f"{permission_prefix}.process_reorder", + ) class ContactView(OrderItemView): @@ -1854,43 +2057,56 @@ class ContactView(OrderItemView): * ``/contact/`` * ``/contact/XXX`` """ + model_title = "Order Item (Contact)" model_title_plural = "Order Items (Contact)" - route_prefix = 'order_items_contact' - url_prefix = '/contact' + route_prefix = "order_items_contact" + url_prefix = "/contact" def get_query(self, session=None): """ """ query = super().get_query(session=session) model = self.app.model enum = self.app.enum - return query.filter(model.OrderItem.status_code.in_(( - enum.ORDER_ITEM_STATUS_RECEIVED, - enum.ORDER_ITEM_STATUS_CONTACT_FAILED))) + return query.filter( + model.OrderItem.status_code.in_( + (enum.ORDER_ITEM_STATUS_RECEIVED, enum.ORDER_ITEM_STATUS_CONTACT_FAILED) + ) + ) def configure_grid(self, g): """ """ super().configure_grid(g) # checkable - if self.has_perm('process_contact'): + if self.has_perm("process_contact"): g.checkable = True # tool button: Contact Success - if self.has_perm('process_contact'): - button = self.make_button("Contact Success", primary=True, - icon_left='phone', - **{'@click': "$emit('process-contact-success', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_contact_success') + if self.has_perm("process_contact"): + button = self.make_button( + "Contact Success", + primary=True, + icon_left="phone", + **{ + "@click": "$emit('process-contact-success', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_contact_success") # tool button: Contact Failure - if self.has_perm('process_contact'): - button = self.make_button("Contact Failure", variant='is-warning', - icon_left='phone', - **{'@click': "$emit('process-contact-failure', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_contact_failure') + if self.has_perm("process_contact"): + button = self.make_button( + "Contact Failure", + variant="is-warning", + icon_left="phone", + **{ + "@click": "$emit('process-contact-failure', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_contact_failure") def process_contact_success(self): """ @@ -1909,8 +2125,8 @@ class ContactView(OrderItemView): on the :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + note = self.request.POST.get("note", "").strip() or None self.order_handler.process_contact_success(items, self.request.user, note=note) @@ -1934,12 +2150,14 @@ class ContactView(OrderItemView): on the :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + note = self.request.POST.get("note", "").strip() or None self.order_handler.process_contact_failure(items, self.request.user, note=note) - self.request.session.flash(f"{len(items)} Order Items were marked as contact failed") + self.request.session.flash( + f"{len(items)} Order Items were marked as contact failed" + ) return self.redirect(self.get_index_url()) @classmethod @@ -1956,25 +2174,37 @@ class ContactView(OrderItemView): model_title_plural = cls.get_model_title_plural() # common perm for processing contact success + failure - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_contact', - f"Process contact success/failure for {model_title_plural}") + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_contact", + f"Process contact success/failure for {model_title_plural}", + ) # process contact success - config.add_route(f'{route_prefix}.process_contact_success', - f'{url_prefix}/process-contact-success', - request_method='POST') - config.add_view(cls, attr='process_contact_success', - route_name=f'{route_prefix}.process_contact_success', - permission=f'{permission_prefix}.process_contact') + config.add_route( + f"{route_prefix}.process_contact_success", + f"{url_prefix}/process-contact-success", + request_method="POST", + ) + config.add_view( + cls, + attr="process_contact_success", + route_name=f"{route_prefix}.process_contact_success", + permission=f"{permission_prefix}.process_contact", + ) # process contact failure - config.add_route(f'{route_prefix}.process_contact_failure', - f'{url_prefix}/process-contact-failure', - request_method='POST') - config.add_view(cls, attr='process_contact_failure', - route_name=f'{route_prefix}.process_contact_failure', - permission=f'{permission_prefix}.process_contact') + config.add_route( + f"{route_prefix}.process_contact_failure", + f"{url_prefix}/process-contact-failure", + request_method="POST", + ) + config.add_view( + cls, + attr="process_contact_failure", + route_name=f"{route_prefix}.process_contact_failure", + permission=f"{permission_prefix}.process_contact", + ) class DeliveryView(OrderItemView): @@ -1994,43 +2224,55 @@ class DeliveryView(OrderItemView): * ``/delivery/`` * ``/delivery/XXX`` """ + model_title = "Order Item (Delivery)" model_title_plural = "Order Items (Delivery)" - route_prefix = 'order_items_delivery' - url_prefix = '/delivery' + route_prefix = "order_items_delivery" + url_prefix = "/delivery" def get_query(self, session=None): """ """ query = super().get_query(session=session) model = self.app.model enum = self.app.enum - return query.filter(model.OrderItem.status_code.in_(( - enum.ORDER_ITEM_STATUS_RECEIVED, - enum.ORDER_ITEM_STATUS_CONTACTED))) + return query.filter( + model.OrderItem.status_code.in_( + (enum.ORDER_ITEM_STATUS_RECEIVED, enum.ORDER_ITEM_STATUS_CONTACTED) + ) + ) def configure_grid(self, g): """ """ super().configure_grid(g) # checkable - if self.has_any_perm('process_delivery', 'process_restock'): + if self.has_any_perm("process_delivery", "process_restock"): g.checkable = True # tool button: Delivered - if self.has_perm('process_delivery'): - button = self.make_button("Delivered", primary=True, - icon_left='check', - **{'@click': "$emit('process-delivery', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_delivery') + if self.has_perm("process_delivery"): + button = self.make_button( + "Delivered", + primary=True, + icon_left="check", + **{ + "@click": "$emit('process-delivery', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_delivery") # tool button: Restocked - if self.has_perm('process_restock'): - button = self.make_button("Restocked", - icon_left='redo', - **{'@click': "$emit('process-restock', checkedRows)", - ':disabled': '!checkedRows.length'}) - g.add_tool(button, key='process_restock') + if self.has_perm("process_restock"): + button = self.make_button( + "Restocked", + icon_left="redo", + **{ + "@click": "$emit('process-restock', checkedRows)", + ":disabled": "!checkedRows.length", + }, + ) + g.add_tool(button, key="process_restock") def process_delivery(self): """ @@ -2048,8 +2290,8 @@ class DeliveryView(OrderItemView): the :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + note = self.request.POST.get("note", "").strip() or None self.order_handler.process_delivery(items, self.request.user, note=note) @@ -2072,8 +2314,8 @@ class DeliveryView(OrderItemView): :attr:`~OrderItemView.order_handler`, then redirects user back to the index page. """ - items = self.get_order_items(self.request.POST.get('item_uuids', '')) - note = self.request.POST.get('note', '').strip() or None + items = self.get_order_items(self.request.POST.get("item_uuids", "")) + note = self.request.POST.get("note", "").strip() or None self.order_handler.process_restock(items, self.request.user, note=note) @@ -2094,47 +2336,61 @@ class DeliveryView(OrderItemView): model_title_plural = cls.get_model_title_plural() # process delivery - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_delivery', - f"Process delivery for {model_title_plural}") - config.add_route(f'{route_prefix}.process_delivery', - f'{url_prefix}/process-delivery', - request_method='POST') - config.add_view(cls, attr='process_delivery', - route_name=f'{route_prefix}.process_delivery', - permission=f'{permission_prefix}.process_delivery') + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_delivery", + f"Process delivery for {model_title_plural}", + ) + config.add_route( + f"{route_prefix}.process_delivery", + f"{url_prefix}/process-delivery", + request_method="POST", + ) + config.add_view( + cls, + attr="process_delivery", + route_name=f"{route_prefix}.process_delivery", + permission=f"{permission_prefix}.process_delivery", + ) # process restock - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.process_restock', - f"Process restock for {model_title_plural}") - config.add_route(f'{route_prefix}.process_restock', - f'{url_prefix}/process-restock', - request_method='POST') - config.add_view(cls, attr='process_restock', - route_name=f'{route_prefix}.process_restock', - permission=f'{permission_prefix}.process_restock') + config.add_wutta_permission( + permission_prefix, + f"{permission_prefix}.process_restock", + f"Process restock for {model_title_plural}", + ) + config.add_route( + f"{route_prefix}.process_restock", + f"{url_prefix}/process-restock", + request_method="POST", + ) + config.add_view( + cls, + attr="process_restock", + route_name=f"{route_prefix}.process_restock", + permission=f"{permission_prefix}.process_restock", + ) def defaults(config, **kwargs): base = globals() - OrderView = kwargs.get('OrderView', base['OrderView']) + OrderView = kwargs.get("OrderView", base["OrderView"]) OrderView.defaults(config) - OrderItemView = kwargs.get('OrderItemView', base['OrderItemView']) + OrderItemView = kwargs.get("OrderItemView", base["OrderItemView"]) OrderItemView.defaults(config) - PlacementView = kwargs.get('PlacementView', base['PlacementView']) + PlacementView = kwargs.get("PlacementView", base["PlacementView"]) PlacementView.defaults(config) - ReceivingView = kwargs.get('ReceivingView', base['ReceivingView']) + ReceivingView = kwargs.get("ReceivingView", base["ReceivingView"]) ReceivingView.defaults(config) - ContactView = kwargs.get('ContactView', base['ContactView']) + ContactView = kwargs.get("ContactView", base["ContactView"]) ContactView.defaults(config) - DeliveryView = kwargs.get('DeliveryView', base['DeliveryView']) + DeliveryView = kwargs.get("DeliveryView", base["DeliveryView"]) DeliveryView.defaults(config) diff --git a/src/sideshow/web/views/products.py b/src/sideshow/web/views/products.py index d9ce266..a205977 100644 --- a/src/sideshow/web/views/products.py +++ b/src/sideshow/web/views/products.py @@ -44,47 +44,48 @@ class LocalProductView(MasterView): * ``/local/products/XXX/edit`` * ``/local/products/XXX/delete`` """ + model_class = LocalProduct model_title = "Local Product" - route_prefix = 'local_products' - url_prefix = '/local/products' + route_prefix = "local_products" + url_prefix = "/local/products" labels = { - 'external_id': "External ID", - 'department_id': "Department ID", + "external_id": "External ID", + "department_id": "Department ID", } grid_columns = [ - 'scancode', - 'brand_name', - 'description', - 'size', - 'department_name', - 'special_order', - 'case_size', - 'unit_cost', - 'unit_price_reg', + "scancode", + "brand_name", + "description", + "size", + "department_name", + "special_order", + "case_size", + "unit_cost", + "unit_price_reg", ] - sort_defaults = 'scancode' + sort_defaults = "scancode" form_fields = [ - 'external_id', - 'scancode', - 'brand_name', - 'description', - 'size', - 'department_id', - 'department_name', - 'special_order', - 'vendor_name', - 'vendor_item_code', - 'case_size', - 'unit_cost', - 'unit_price_reg', - 'notes', - 'orders', - 'new_order_batches', + "external_id", + "scancode", + "brand_name", + "description", + "size", + "department_id", + "department_name", + "special_order", + "vendor_name", + "vendor_item_code", + "case_size", + "unit_cost", + "unit_price_reg", + "notes", + "orders", + "new_order_batches", ] def configure_grid(self, g): @@ -92,17 +93,17 @@ class LocalProductView(MasterView): super().configure_grid(g) # unit_cost - g.set_renderer('unit_cost', 'currency', scale=4) + g.set_renderer("unit_cost", "currency", scale=4) # unit_price_reg - g.set_label('unit_price_reg', "Reg. Price", column_only=True) - g.set_renderer('unit_price_reg', 'currency') + g.set_label("unit_price_reg", "Reg. Price", column_only=True) + g.set_renderer("unit_price_reg", "currency") # links - g.set_link('scancode') - g.set_link('brand_name') - g.set_link('description') - g.set_link('size') + g.set_link("scancode") + g.set_link("brand_name") + g.set_link("description") + g.set_link("size") def configure_form(self, f): """ """ @@ -112,40 +113,40 @@ class LocalProductView(MasterView): # external_id if self.creating: - f.remove('external_id') + f.remove("external_id") else: - f.set_readonly('external_id') + f.set_readonly("external_id") # TODO: should not have to explicitly mark these nodes # as required=False.. i guess i do for now b/c i am # totally overriding the node from colanderlachemy # case_size - f.set_node('case_size', WuttaQuantity(self.request)) - f.set_required('case_size', False) + f.set_node("case_size", WuttaQuantity(self.request)) + f.set_required("case_size", False) # unit_cost - f.set_node('unit_cost', WuttaMoney(self.request, scale=4)) - f.set_required('unit_cost', False) + f.set_node("unit_cost", WuttaMoney(self.request, scale=4)) + f.set_required("unit_cost", False) # unit_price_reg - f.set_node('unit_price_reg', WuttaMoney(self.request)) - f.set_required('unit_price_reg', False) + f.set_node("unit_price_reg", WuttaMoney(self.request)) + f.set_required("unit_price_reg", False) # notes - f.set_widget('notes', 'notes') + f.set_widget("notes", "notes") # orders if self.creating or self.editing: - f.remove('orders') + f.remove("orders") else: - f.set_grid('orders', self.make_orders_grid(product)) + f.set_grid("orders", self.make_orders_grid(product)) # new_order_batches if self.creating or self.editing: - f.remove('new_order_batches') + f.remove("new_order_batches") else: - f.set_grid('new_order_batches', self.make_new_order_batches_grid(product)) + f.set_grid("new_order_batches", self.make_new_order_batches_grid(product)) def make_orders_grid(self, product): """ @@ -157,26 +158,30 @@ class LocalProductView(MasterView): orders = set([item.order for item in product.order_items]) orders = sorted(orders, key=lambda order: order.order_id) - grid = self.make_grid(key=f'{route_prefix}.view.orders', - model_class=model.Order, - data=orders, - columns=[ - 'order_id', - 'total_price', - 'created', - 'created_by', - ], - labels={ - 'order_id': "Order ID", - }, - renderers={ - 'total_price': 'currency', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.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'): - url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('order_id') + if self.request.has_perm("orders.view"): + url = lambda order, i: self.request.route_url( + "orders.view", uuid=order.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("order_id") return grid @@ -190,28 +195,32 @@ class LocalProductView(MasterView): batches = set([row.batch for row in product.new_order_batch_rows]) batches = sorted(batches, key=lambda batch: batch.id) - grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', - model_class=model.NewOrderBatch, - data=batches, - columns=[ - 'id', - 'total_price', - 'created', - 'created_by', - 'executed', - ], - labels={ - 'id': "Batch ID", - 'status_code': "Status", - }, - renderers={ - 'id': 'batch_id', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.new_order_batches", + model_class=model.NewOrderBatch, + 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'): - url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('id') + if self.request.has_perm("neworder_batches.view"): + url = lambda batch, i: self.request.route_url( + "neworder_batches.view", uuid=batch.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("id") return grid @@ -230,57 +239,57 @@ class PendingProductView(MasterView): * ``/pending/products/XXX/edit`` * ``/pending/products/XXX/delete`` """ + model_class = PendingProduct model_title = "Pending Product" - route_prefix = 'pending_products' - url_prefix = '/pending/products' + route_prefix = "pending_products" + url_prefix = "/pending/products" labels = { - 'department_id': "Department ID", - 'product_id': "Product ID", + "department_id": "Department ID", + "product_id": "Product ID", } grid_columns = [ - 'scancode', - 'department_name', - 'brand_name', - 'description', - 'size', - 'unit_cost', - 'case_size', - 'unit_price_reg', - 'special_order', - 'status', - 'created', - 'created_by', + "scancode", + "department_name", + "brand_name", + "description", + "size", + "unit_cost", + "case_size", + "unit_price_reg", + "special_order", + "status", + "created", + "created_by", ] - sort_defaults = ('created', 'desc') + sort_defaults = ("created", "desc") filter_defaults = { - 'status': {'active': True, - 'value': PendingProductStatus.READY.name}, + "status": {"active": True, "value": PendingProductStatus.READY.name}, } form_fields = [ - 'product_id', - 'scancode', - 'department_id', - 'department_name', - 'brand_name', - 'description', - 'size', - 'vendor_name', - 'vendor_item_code', - 'unit_cost', - 'case_size', - 'unit_price_reg', - 'special_order', - 'notes', - 'created', - 'created_by', - 'orders', - 'new_order_batches', + "product_id", + "scancode", + "department_id", + "department_name", + "brand_name", + "description", + "size", + "vendor_name", + "vendor_item_code", + "unit_cost", + "case_size", + "unit_price_reg", + "special_order", + "notes", + "created", + "created_by", + "orders", + "new_order_batches", ] def configure_grid(self, g): @@ -289,26 +298,26 @@ class PendingProductView(MasterView): enum = self.app.enum # unit_cost - g.set_renderer('unit_cost', 'currency', scale=4) + g.set_renderer("unit_cost", "currency", scale=4) # unit_price_reg - g.set_label('unit_price_reg', "Reg. Price", column_only=True) - g.set_renderer('unit_price_reg', 'currency') + g.set_label("unit_price_reg", "Reg. Price", column_only=True) + g.set_renderer("unit_price_reg", "currency") # status - g.set_enum('status', enum.PendingProductStatus) + g.set_enum("status", enum.PendingProductStatus) # links - g.set_link('scancode') - g.set_link('brand_name') - g.set_link('description') - g.set_link('size') + g.set_link("scancode") + g.set_link("brand_name") + g.set_link("description") + g.set_link("size") def grid_row_class(self, product, data, i): """ """ enum = self.app.enum if product.status == enum.PendingProductStatus.IGNORED: - return 'has-background-warning' + return "has-background-warning" def configure_form(self, f): """ """ @@ -318,40 +327,40 @@ class PendingProductView(MasterView): # product_id if self.creating: - f.remove('product_id') + f.remove("product_id") else: - f.set_readonly('product_id') + f.set_readonly("product_id") # unit_price_reg - f.set_node('unit_price_reg', WuttaMoney(self.request)) + f.set_node("unit_price_reg", WuttaMoney(self.request)) # notes - f.set_widget('notes', 'notes') + f.set_widget("notes", "notes") # created if self.creating: - f.remove('created') + f.remove("created") else: - f.set_readonly('created') + f.set_readonly("created") # created_by if self.creating: - f.remove('created_by') + f.remove("created_by") else: - f.set_node('created_by', UserRef(self.request)) - f.set_readonly('created_by') + f.set_node("created_by", UserRef(self.request)) + f.set_readonly("created_by") # orders if self.creating or self.editing: - f.remove('orders') + f.remove("orders") else: - f.set_grid('orders', self.make_orders_grid(product)) + f.set_grid("orders", self.make_orders_grid(product)) # new_order_batches if self.creating or self.editing: - f.remove('new_order_batches') + f.remove("new_order_batches") else: - f.set_grid('new_order_batches', self.make_new_order_batches_grid(product)) + f.set_grid("new_order_batches", self.make_new_order_batches_grid(product)) def make_orders_grid(self, product): """ @@ -363,26 +372,30 @@ class PendingProductView(MasterView): orders = set([item.order for item in product.order_items]) orders = sorted(orders, key=lambda order: order.order_id) - grid = self.make_grid(key=f'{route_prefix}.view.orders', - model_class=model.Order, - data=orders, - columns=[ - 'order_id', - 'total_price', - 'created', - 'created_by', - ], - labels={ - 'order_id': "Order ID", - }, - renderers={ - 'total_price': 'currency', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.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'): - url = lambda order, i: self.request.route_url('orders.view', uuid=order.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('order_id') + if self.request.has_perm("orders.view"): + url = lambda order, i: self.request.route_url( + "orders.view", uuid=order.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("order_id") return grid @@ -396,28 +409,32 @@ class PendingProductView(MasterView): batches = set([row.batch for row in product.new_order_batch_rows]) batches = sorted(batches, key=lambda batch: batch.id) - grid = self.make_grid(key=f'{route_prefix}.view.new_order_batches', - model_class=model.NewOrderBatch, - data=batches, - columns=[ - 'id', - 'total_price', - 'created', - 'created_by', - 'executed', - ], - labels={ - 'id': "Batch ID", - 'status_code': "Status", - }, - renderers={ - 'id': 'batch_id', - }) + grid = self.make_grid( + key=f"{route_prefix}.view.new_order_batches", + model_class=model.NewOrderBatch, + 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'): - url = lambda batch, i: self.request.route_url('neworder_batches.view', uuid=batch.uuid) - grid.add_action('view', icon='eye', url=url) - grid.set_link('id') + if self.request.has_perm("neworder_batches.view"): + url = lambda batch, i: self.request.route_url( + "neworder_batches.view", uuid=batch.uuid + ) + grid.add_action("view", icon="eye", url=url) + grid.set_link("id") return grid @@ -426,11 +443,12 @@ class PendingProductView(MasterView): enum = self.app.enum if self.viewing: - product = context['instance'] - if (product.status == enum.PendingProductStatus.READY - and self.has_any_perm('resolve', 'ignore')): - handler = self.app.get_batch_handler('neworder') - context['use_local_products'] = handler.use_local_products() + product = context["instance"] + if product.status == enum.PendingProductStatus.READY and self.has_any_perm( + "resolve", "ignore" + ): + handler = self.app.get_batch_handler("neworder") + context["use_local_products"] = handler.use_local_products() return context @@ -441,9 +459,12 @@ class PendingProductView(MasterView): for row in product.new_order_batch_rows: if not row.batch.executed: model_title = self.get_model_title() - self.request.session.flash(f"Cannot delete {model_title} still attached " - "to New Order Batch(es)", 'warning') - raise self.redirect(self.get_action_url('view', product)) + self.request.session.flash( + f"Cannot delete {model_title} still attached " + "to New Order Batch(es)", + "warning", + ) + raise self.redirect(self.get_action_url("view", product)) # go ahead and delete per usual super().delete_instance(product) @@ -469,21 +490,23 @@ class PendingProductView(MasterView): product = self.get_instance() if product.status != enum.PendingProductStatus.READY: - self.request.session.flash("pending product does not have 'ready' status!", 'error') - return self.redirect(self.get_action_url('view', product)) + self.request.session.flash( + "pending product does not have 'ready' status!", "error" + ) + return self.redirect(self.get_action_url("view", product)) - product_id = self.request.POST.get('product_id') + product_id = self.request.POST.get("product_id") if not product_id: - self.request.session.flash("must specify valid product_id", 'error') - return self.redirect(self.get_action_url('view', product)) + self.request.session.flash("must specify valid product_id", "error") + return self.redirect(self.get_action_url("view", product)) - batch_handler = self.app.get_batch_handler('neworder') + batch_handler = self.app.get_batch_handler("neworder") order_handler = self.app.get_order_handler() info = batch_handler.get_product_info_external(session, product_id) order_handler.resolve_pending_product(product, info, self.request.user) - return self.redirect(self.get_action_url('view', product)) + return self.redirect(self.get_action_url("view", product)) def ignore(self): """ @@ -499,11 +522,13 @@ class PendingProductView(MasterView): product = self.get_instance() if product.status != enum.PendingProductStatus.READY: - self.request.session.flash("pending product does not have 'ready' status!", 'error') - return self.redirect(self.get_action_url('view', product)) + self.request.session.flash( + "pending product does not have 'ready' status!", "error" + ) + return self.redirect(self.get_action_url("view", product)) product.status = enum.PendingProductStatus.IGNORED - return self.redirect(self.get_action_url('view', product)) + return self.redirect(self.get_action_url("view", product)) @classmethod def defaults(cls, config): @@ -519,35 +544,45 @@ class PendingProductView(MasterView): model_title = cls.get_model_title() # resolve - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.resolve', - f"Resolve {model_title}") - config.add_route(f'{route_prefix}.resolve', - f'{instance_url_prefix}/resolve', - request_method='POST') - config.add_view(cls, attr='resolve', - route_name=f'{route_prefix}.resolve', - permission=f'{permission_prefix}.resolve') + config.add_wutta_permission( + permission_prefix, f"{permission_prefix}.resolve", f"Resolve {model_title}" + ) + config.add_route( + f"{route_prefix}.resolve", + f"{instance_url_prefix}/resolve", + request_method="POST", + ) + config.add_view( + cls, + attr="resolve", + route_name=f"{route_prefix}.resolve", + permission=f"{permission_prefix}.resolve", + ) # ignore - config.add_wutta_permission(permission_prefix, - f'{permission_prefix}.ignore', - f"Ignore {model_title}") - config.add_route(f'{route_prefix}.ignore', - f'{instance_url_prefix}/ignore', - request_method='POST') - config.add_view(cls, attr='ignore', - route_name=f'{route_prefix}.ignore', - permission=f'{permission_prefix}.ignore') + config.add_wutta_permission( + permission_prefix, f"{permission_prefix}.ignore", f"Ignore {model_title}" + ) + config.add_route( + f"{route_prefix}.ignore", + f"{instance_url_prefix}/ignore", + request_method="POST", + ) + config.add_view( + cls, + attr="ignore", + route_name=f"{route_prefix}.ignore", + permission=f"{permission_prefix}.ignore", + ) def defaults(config, **kwargs): base = globals() - LocalProductView = kwargs.get('LocalProductView', base['LocalProductView']) + LocalProductView = kwargs.get("LocalProductView", base["LocalProductView"]) LocalProductView.defaults(config) - PendingProductView = kwargs.get('PendingProductView', base['PendingProductView']) + PendingProductView = kwargs.get("PendingProductView", base["PendingProductView"]) PendingProductView.defaults(config) diff --git a/src/sideshow/web/views/stores.py b/src/sideshow/web/views/stores.py index 0eaf41d..13282c9 100644 --- a/src/sideshow/web/views/stores.py +++ b/src/sideshow/web/views/stores.py @@ -43,51 +43,51 @@ class StoreView(MasterView): * ``/stores/XXX/edit`` * ``/stores/XXX/delete`` """ + model_class = Store labels = { - 'store_id': "Store ID", + "store_id": "Store ID", } filter_defaults = { - 'archived': {'active': True, 'verb': 'is_false'}, + "archived": {"active": True, "verb": "is_false"}, } - sort_defaults = 'store_id' + sort_defaults = "store_id" def configure_grid(self, g): """ """ super().configure_grid(g) # links - g.set_link('store_id') - g.set_link('name') + g.set_link("store_id") + g.set_link("name") def grid_row_class(self, store, data, i): """ """ if store.archived: - return 'has-background-warning' + return "has-background-warning" def configure_form(self, f): """ """ super().configure_form(f) # store_id - f.set_validator('store_id', self.unique_store_id) + f.set_validator("store_id", self.unique_store_id) # name - f.set_validator('name', self.unique_name) + f.set_validator("name", self.unique_name) def unique_store_id(self, node, value): """ """ model = self.app.model session = self.Session() - query = session.query(model.Store)\ - .filter(model.Store.store_id == value) + query = session.query(model.Store).filter(model.Store.store_id == value) if self.editing: - uuid = self.request.matchdict['uuid'] + uuid = self.request.matchdict["uuid"] query = query.filter(model.Store.uuid != uuid) if query.count(): @@ -98,11 +98,10 @@ class StoreView(MasterView): model = self.app.model session = self.Session() - query = session.query(model.Store)\ - .filter(model.Store.name == value) + query = session.query(model.Store).filter(model.Store.name == value) if self.editing: - uuid = self.request.matchdict['uuid'] + uuid = self.request.matchdict["uuid"] query = query.filter(model.Store.uuid != uuid) if query.count(): @@ -112,7 +111,7 @@ class StoreView(MasterView): def defaults(config, **kwargs): base = globals() - StoreView = kwargs.get('StoreView', base['StoreView']) + StoreView = kwargs.get("StoreView", base["StoreView"]) StoreView.defaults(config) diff --git a/tasks.py b/tasks.py index 6cdbab2..394e573 100644 --- a/tasks.py +++ b/tasks.py @@ -15,14 +15,14 @@ def release(c, skip_tests=False): Release a new version of Sideshow """ if not skip_tests: - c.run('pytest') + c.run("pytest") # rebuild pkg - if os.path.exists('dist'): - shutil.rmtree('dist') - if os.path.exists('Sideshow.egg-info'): - shutil.rmtree('Sideshow.egg-info') - c.run('python -m build --sdist') + if os.path.exists("dist"): + shutil.rmtree("dist") + if os.path.exists("Sideshow.egg-info"): + shutil.rmtree("Sideshow.egg-info") + c.run("python -m build --sdist") # upload - c.run('twine upload dist/*') + c.run("twine upload dist/*") diff --git a/tests/batch/test_neworder.py b/tests/batch/test_neworder.py index 85ffa79..7bbc081 100644 --- a/tests/batch/test_neworder.py +++ b/tests/batch/test_neworder.py @@ -15,8 +15,8 @@ class TestNewOrderBatchHandler(DataTestCase): def make_config(self, **kwargs): config = super().make_config(**kwargs) - config.setdefault('wutta.model_spec', 'sideshow.db.model') - config.setdefault('wutta.enum_spec', 'sideshow.enum') + config.setdefault("wutta.model_spec", "sideshow.db.model") + config.setdefault("wutta.enum_spec", "sideshow.enum") return config def make_handler(self): @@ -29,8 +29,8 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(handler.get_default_store_id()) # whatever is configured - self.config.setdefault('sideshow.orders.default_store_id', '042') - self.assertEqual(handler.get_default_store_id(), '042') + self.config.setdefault("sideshow.orders.default_store_id", "042") + self.assertEqual(handler.get_default_store_id(), "042") def test_use_local_customers(self): handler = self.make_handler() @@ -39,7 +39,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertTrue(handler.use_local_customers()) # config can disable - self.config.setdefault('sideshow.orders.use_local_customers', 'false') + self.config.setdefault("sideshow.orders.use_local_customers", "false") self.assertFalse(handler.use_local_customers()) def test_use_local_products(self): @@ -49,7 +49,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertTrue(handler.use_local_products()) # config can disable - self.config.setdefault('sideshow.orders.use_local_products', 'false') + self.config.setdefault("sideshow.orders.use_local_products", "false") self.assertFalse(handler.use_local_products()) def test_allow_unknown_products(self): @@ -59,7 +59,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertTrue(handler.allow_unknown_products()) # config can disable - self.config.setdefault('sideshow.orders.allow_unknown_products', 'false') + self.config.setdefault("sideshow.orders.allow_unknown_products", "false") self.assertFalse(handler.allow_unknown_products()) def test_allow_item_discounts(self): @@ -69,7 +69,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertFalse(handler.allow_item_discounts()) # config can enable - self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') + self.config.setdefault("sideshow.orders.allow_item_discounts", "true") self.assertTrue(handler.allow_item_discounts()) def test_allow_item_discounts_if_on_sale(self): @@ -79,7 +79,9 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertFalse(handler.allow_item_discounts_if_on_sale()) # config can enable - self.config.setdefault('sideshow.orders.allow_item_discounts_if_on_sale', 'true') + self.config.setdefault( + "sideshow.orders.allow_item_discounts_if_on_sale", "true" + ) self.assertTrue(handler.allow_item_discounts_if_on_sale()) def test_get_default_item_discount(self): @@ -89,20 +91,24 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(handler.get_default_item_discount()) # config can define - self.config.setdefault('sideshow.orders.default_item_discount', '15') - self.assertEqual(handler.get_default_item_discount(), decimal.Decimal('15.00')) + self.config.setdefault("sideshow.orders.default_item_discount", "15") + self.assertEqual(handler.get_default_item_discount(), decimal.Decimal("15.00")) def test_autocomplete_customers_external(self): handler = self.make_handler() - self.assertRaises(NotImplementedError, handler.autocomplete_customers_external, - self.session, 'jack') + self.assertRaises( + NotImplementedError, + handler.autocomplete_customers_external, + self.session, + "jack", + ) def test_autocomplete_cutomers_local(self): model = self.app.model handler = self.make_handler() # empty results by default - self.assertEqual(handler.autocomplete_customers_local(self.session, 'foo'), []) + self.assertEqual(handler.autocomplete_customers_local(self.session, "foo"), []) # add a customer customer = model.LocalCustomer(full_name="Chuck Norris") @@ -110,15 +116,20 @@ class TestNewOrderBatchHandler(DataTestCase): self.session.flush() # search for chuck finds chuck - results = handler.autocomplete_customers_local(self.session, 'chuck') + results = handler.autocomplete_customers_local(self.session, "chuck") self.assertEqual(len(results), 1) - self.assertEqual(results[0], { - 'value': customer.uuid.hex, - 'label': "Chuck Norris", - }) + self.assertEqual( + results[0], + { + "value": customer.uuid.hex, + "label": "Chuck Norris", + }, + ) # search for sally finds nothing - self.assertEqual(handler.autocomplete_customers_local(self.session, 'sally'), []) + self.assertEqual( + handler.autocomplete_customers_local(self.session, "sally"), [] + ) def test_init_batch(self): model = self.app.model @@ -131,17 +142,17 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(batch.store_id) # but default can be configured - self.config.setdefault('sideshow.orders.default_store_id', '042') + self.config.setdefault("sideshow.orders.default_store_id", "042") batch = handler.model_class() self.assertIsNone(batch.store_id) handler.init_batch(batch) - self.assertEqual(batch.store_id, '042') + self.assertEqual(batch.store_id, "042") def test_set_customer(self): model = self.app.model handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # customer starts blank @@ -156,12 +167,15 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(batch.email_address) # pending, typical (nb. full name is automatic) - handler.set_customer(batch, { - 'first_name': "Fred", - 'last_name': "Flintstone", - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', - }) + handler.set_customer( + batch, + { + "first_name": "Fred", + "last_name": "Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + }, + ) self.assertIsNone(batch.customer_id) self.assertIsNone(batch.local_customer) self.assertIsInstance(batch.pending_customer, model.PendingCustomer) @@ -169,15 +183,15 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(customer.first_name, "Fred") self.assertEqual(customer.last_name, "Flintstone") self.assertEqual(customer.full_name, "Fred Flintstone") - self.assertEqual(customer.phone_number, '555-1234') - self.assertEqual(customer.email_address, 'fred@mailinator.com') + self.assertEqual(customer.phone_number, "555-1234") + self.assertEqual(customer.email_address, "fred@mailinator.com") self.assertEqual(batch.customer_name, "Fred Flintstone") - self.assertEqual(batch.phone_number, '555-1234') - self.assertEqual(batch.email_address, 'fred@mailinator.com') + self.assertEqual(batch.phone_number, "555-1234") + self.assertEqual(batch.email_address, "fred@mailinator.com") # pending, minimal - last_customer = customer # save ref to prev record - handler.set_customer(batch, {'full_name': "Wilma Flintstone"}) + last_customer = customer # save ref to prev record + handler.set_customer(batch, {"full_name": "Wilma Flintstone"}) self.assertIsNone(batch.customer_id) self.assertIsNone(batch.local_customer) self.assertIsInstance(batch.pending_customer, model.PendingCustomer) @@ -193,9 +207,12 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(batch.email_address) # local customer - local = model.LocalCustomer(full_name="Bam Bam", - first_name="Bam", last_name="Bam", - phone_number='555-4321') + local = model.LocalCustomer( + full_name="Bam Bam", + first_name="Bam", + last_name="Bam", + phone_number="555-4321", + ) self.session.add(local) self.session.flush() handler.set_customer(batch, local.uuid.hex) @@ -208,10 +225,10 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(customer.full_name, "Bam Bam") self.assertEqual(customer.first_name, "Bam") self.assertEqual(customer.last_name, "Bam") - self.assertEqual(customer.phone_number, '555-4321') + self.assertEqual(customer.phone_number, "555-4321") self.assertIsNone(customer.email_address) self.assertEqual(batch.customer_name, "Bam Bam") - self.assertEqual(batch.phone_number, '555-4321') + self.assertEqual(batch.phone_number, "555-4321") self.assertIsNone(batch.email_address) # local customer, not found @@ -219,8 +236,8 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertRaises(ValueError, handler.set_customer, batch, mock_uuid.hex) # external lookup not implemented - self.config.setdefault('sideshow.orders.use_local_customers', 'false') - self.assertRaises(NotImplementedError, handler.set_customer, batch, '42') + self.config.setdefault("sideshow.orders.use_local_customers", "false") + self.assertRaises(NotImplementedError, handler.set_customer, batch, "42") # null handler.set_customer(batch, None) @@ -235,15 +252,19 @@ class TestNewOrderBatchHandler(DataTestCase): def test_autocomplete_products_external(self): handler = self.make_handler() - self.assertRaises(NotImplementedError, handler.autocomplete_products_external, - self.session, 'cheese') + self.assertRaises( + NotImplementedError, + handler.autocomplete_products_external, + self.session, + "cheese", + ) def test_autocomplete_products_local(self): model = self.app.model handler = self.make_handler() # empty results by default - self.assertEqual(handler.autocomplete_products_local(self.session, 'foo'), []) + self.assertEqual(handler.autocomplete_products_local(self.session, "foo"), []) # add a product product = model.LocalProduct(brand_name="Bragg's", description="Vinegar") @@ -251,49 +272,62 @@ class TestNewOrderBatchHandler(DataTestCase): self.session.flush() # search for vinegar finds product - results = handler.autocomplete_products_local(self.session, 'vinegar') + results = handler.autocomplete_products_local(self.session, "vinegar") self.assertEqual(len(results), 1) - self.assertEqual(results[0], { - 'value': product.uuid.hex, - 'label': "Bragg's Vinegar", - }) + self.assertEqual( + results[0], + { + "value": product.uuid.hex, + "label": "Bragg's Vinegar", + }, + ) # search for brag finds product - results = handler.autocomplete_products_local(self.session, 'brag') + results = handler.autocomplete_products_local(self.session, "brag") self.assertEqual(len(results), 1) - self.assertEqual(results[0], { - 'value': product.uuid.hex, - 'label': "Bragg's Vinegar", - }) + self.assertEqual( + results[0], + { + "value": product.uuid.hex, + "label": "Bragg's Vinegar", + }, + ) # search for juice finds nothing - self.assertEqual(handler.autocomplete_products_local(self.session, 'juice'), []) + self.assertEqual(handler.autocomplete_products_local(self.session, "juice"), []) def test_get_default_uom_choices(self): enum = self.app.enum handler = self.make_handler() uoms = handler.get_default_uom_choices() - self.assertEqual(uoms, [{'key': key, 'value': val} - for key, val in enum.ORDER_UOM.items()]) + self.assertEqual( + uoms, [{"key": key, "value": val} for key, val in enum.ORDER_UOM.items()] + ) def test_get_product_info_external(self): handler = self.make_handler() - self.assertRaises(NotImplementedError, handler.get_product_info_external, - self.session, '07430500132') + self.assertRaises( + NotImplementedError, + handler.get_product_info_external, + self.session, + "07430500132", + ) def test_get_product_info_local(self): model = self.app.model handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - local = model.LocalProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - case_size=12, - unit_price_reg=decimal.Decimal('5.99')) + local = model.LocalProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + case_size=12, + unit_price_reg=decimal.Decimal("5.99"), + ) self.session.add(local) self.session.flush() @@ -302,43 +336,49 @@ class TestNewOrderBatchHandler(DataTestCase): # typical, for local product info = handler.get_product_info_local(self.session, local.uuid.hex) - self.assertEqual(info['product_id'], local.uuid.hex) - self.assertEqual(info['scancode'], '07430500132') - self.assertEqual(info['brand_name'], 'Bragg') - self.assertEqual(info['description'], 'Vinegar') - self.assertEqual(info['size'], '32oz') - self.assertEqual(info['full_description'], 'Bragg Vinegar 32oz') - self.assertEqual(info['case_size'], 12) - self.assertEqual(info['unit_price_reg'], decimal.Decimal('5.99')) + self.assertEqual(info["product_id"], local.uuid.hex) + self.assertEqual(info["scancode"], "07430500132") + self.assertEqual(info["brand_name"], "Bragg") + self.assertEqual(info["description"], "Vinegar") + self.assertEqual(info["size"], "32oz") + self.assertEqual(info["full_description"], "Bragg Vinegar 32oz") + self.assertEqual(info["case_size"], 12) + self.assertEqual(info["unit_price_reg"], decimal.Decimal("5.99")) # error if no product_id - self.assertRaises(ValueError, handler.get_product_info_local, self.session, None) + self.assertRaises( + ValueError, handler.get_product_info_local, self.session, None + ) # error if product not found mock_uuid = self.app.make_true_uuid() - self.assertRaises(ValueError, handler.get_product_info_local, self.session, mock_uuid.hex) + self.assertRaises( + ValueError, handler.get_product_info_local, self.session, mock_uuid.hex + ) def test_normalize_local_product(self): model = self.app.model handler = self.make_handler() - product = model.LocalProduct(scancode='07430500132', - brand_name="Bragg's", - description="Apple Cider Vinegar", - size="32oz", - department_name="Grocery", - case_size=12, - unit_price_reg=5.99, - vendor_name="UNFI", - vendor_item_code='1234') + product = model.LocalProduct( + scancode="07430500132", + brand_name="Bragg's", + description="Apple Cider Vinegar", + size="32oz", + department_name="Grocery", + case_size=12, + unit_price_reg=5.99, + vendor_name="UNFI", + vendor_item_code="1234", + ) self.session.add(product) self.session.flush() info = handler.normalize_local_product(product) self.assertIsInstance(info, dict) - self.assertEqual(info['product_id'], product.uuid.hex) + self.assertEqual(info["product_id"], product.uuid.hex) for prop in sa.inspect(model.LocalProduct).column_attrs: - if prop.key == 'uuid': + if prop.key == "uuid": continue if prop.key not in info: continue @@ -348,7 +388,7 @@ class TestNewOrderBatchHandler(DataTestCase): model = self.app.model handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) @@ -360,7 +400,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertRaises(ValueError, handler.get_past_orders, batch) # empty history for customer - customer = model.LocalCustomer(full_name='Fred Flintstone') + customer = model.LocalCustomer(full_name="Fred Flintstone") batch.local_customer = customer self.session.flush() orders = handler.get_past_orders(batch) @@ -377,7 +417,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIs(orders[0], order) # ..now we test external customers, w/ new batch - with patch.object(handler, 'use_local_customers', return_value=False): + with patch.object(handler, "use_local_customers", return_value=False): batch2 = handler.make_batch(self.session, created_by=user) self.session.add(batch2) self.session.flush() @@ -386,13 +426,13 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertRaises(ValueError, handler.get_past_orders, batch2) # empty history for customer - batch2.customer_id = '123' + batch2.customer_id = "123" self.session.flush() orders = handler.get_past_orders(batch2) self.assertEqual(len(orders), 0) # mock historical order - order2 = model.Order(order_id=42, customer_id='123', created_by=user) + order2 = model.Order(order_id=42, customer_id="123", created_by=user) self.session.add(order2) self.session.flush() @@ -406,7 +446,7 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) @@ -420,7 +460,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertRaises(ValueError, handler.get_past_products, batch) # empty history for customer - customer = model.LocalCustomer(full_name='Fred Flintstone') + customer = model.LocalCustomer(full_name="Fred Flintstone") batch.local_customer = customer self.session.flush() products = handler.get_past_products(batch) @@ -428,10 +468,18 @@ class TestNewOrderBatchHandler(DataTestCase): # mock historical order order = model.Order(order_id=42, local_customer=customer, created_by=user) - product = model.LocalProduct(scancode='07430500132', description='Vinegar', - unit_price_reg=5.99, case_size=12) - item = model.OrderItem(local_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + product = model.LocalProduct( + scancode="07430500132", + description="Vinegar", + unit_price_reg=5.99, + case_size=12, + ) + item = model.OrderItem( + local_product=product, + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item) self.session.add(order) self.session.flush() @@ -440,14 +488,14 @@ class TestNewOrderBatchHandler(DataTestCase): # that should now be returned products = handler.get_past_products(batch) self.assertEqual(len(products), 1) - self.assertEqual(products[0]['product_id'], product.uuid.hex) - self.assertEqual(products[0]['scancode'], '07430500132') - self.assertEqual(products[0]['description'], 'Vinegar') - self.assertEqual(products[0]['case_price_quoted'], decimal.Decimal('71.88')) - self.assertEqual(products[0]['case_price_quoted_display'], '$71.88') + self.assertEqual(products[0]["product_id"], product.uuid.hex) + self.assertEqual(products[0]["scancode"], "07430500132") + self.assertEqual(products[0]["description"], "Vinegar") + self.assertEqual(products[0]["case_price_quoted"], decimal.Decimal("71.88")) + self.assertEqual(products[0]["case_price_quoted_display"], "$71.88") # ..now we test external products, w/ new batch - with patch.object(handler, 'use_local_products', return_value=False): + with patch.object(handler, "use_local_products", return_value=False): batch2 = handler.make_batch(self.session, created_by=user) self.session.add(batch2) self.session.flush() @@ -464,34 +512,41 @@ class TestNewOrderBatchHandler(DataTestCase): # mock historical order order2 = model.Order(order_id=44, local_customer=customer, created_by=user) self.session.add(order2) - item2 = model.OrderItem(product_id='07430500116', - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + item2 = model.OrderItem( + product_id="07430500116", + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order2.items.append(item2) self.session.flush() # its product should now be returned - with patch.object(handler, 'get_product_info_external', return_value={ - 'product_id': '07430500116', - 'scancode': '07430500116', - 'description': 'VINEGAR', - 'unit_price_reg': decimal.Decimal('3.99'), - 'case_size': 12, - }): + with patch.object( + handler, + "get_product_info_external", + return_value={ + "product_id": "07430500116", + "scancode": "07430500116", + "description": "VINEGAR", + "unit_price_reg": decimal.Decimal("3.99"), + "case_size": 12, + }, + ): products = handler.get_past_products(batch2) self.assertEqual(len(products), 1) - self.assertEqual(products[0]['product_id'], '07430500116') - self.assertEqual(products[0]['scancode'], '07430500116') - self.assertEqual(products[0]['description'], 'VINEGAR') - self.assertEqual(products[0]['case_price_quoted'], decimal.Decimal('47.88')) - self.assertEqual(products[0]['case_price_quoted_display'], '$47.88') + self.assertEqual(products[0]["product_id"], "07430500116") + self.assertEqual(products[0]["scancode"], "07430500116") + self.assertEqual(products[0]["description"], "VINEGAR") + self.assertEqual(products[0]["case_price_quoted"], decimal.Decimal("47.88")) + self.assertEqual(products[0]["case_price_quoted_display"], "$47.88") def test_add_item(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) @@ -500,13 +555,13 @@ class TestNewOrderBatchHandler(DataTestCase): # pending, typical kw = dict( - scancode='07430500001', - brand_name='Bragg', - description='Vinegar', - size='1oz', + scancode="07430500001", + brand_name="Bragg", + description="Vinegar", + size="1oz", case_size=12, - unit_cost=decimal.Decimal('1.99'), - unit_price_reg=decimal.Decimal('2.99'), + unit_cost=decimal.Decimal("1.99"), + unit_price_reg=decimal.Decimal("2.99"), ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_UNIT) # nb. this is the first row in batch @@ -516,40 +571,42 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.local_product) product = row.pending_product self.assertIsInstance(product, model.PendingProduct) - self.assertEqual(product.scancode, '07430500001') - self.assertEqual(product.brand_name, 'Bragg') - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '1oz') + self.assertEqual(product.scancode, "07430500001") + self.assertEqual(product.brand_name, "Bragg") + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "1oz") self.assertEqual(product.case_size, 12) - self.assertEqual(product.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(product.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.product_scancode, '07430500001') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '1oz') + self.assertEqual(product.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(product.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.product_scancode, "07430500001") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "1oz") self.assertEqual(row.case_size, 12) - self.assertEqual(row.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(row.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('2.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('35.88')) - self.assertEqual(row.total_price, decimal.Decimal('2.99')) + self.assertEqual(row.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(row.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("2.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("35.88")) + self.assertEqual(row.total_price, decimal.Decimal("2.99")) # pending, minimal - row = handler.add_item(batch, {'description': "Tangerines"}, 1, enum.ORDER_UOM_UNIT) + row = handler.add_item( + batch, {"description": "Tangerines"}, 1, enum.ORDER_UOM_UNIT + ) self.assertIsNone(row.product_id) self.assertIsNone(row.local_product) product = row.pending_product self.assertIsInstance(product, model.PendingProduct) self.assertIsNone(product.scancode) self.assertIsNone(product.brand_name) - self.assertEqual(product.description, 'Tangerines') + self.assertEqual(product.description, "Tangerines") self.assertIsNone(product.size) self.assertIsNone(product.case_size) self.assertIsNone(product.unit_cost) self.assertIsNone(product.unit_price_reg) self.assertIsNone(row.product_scancode) self.assertIsNone(row.product_brand) - self.assertEqual(row.product_description, 'Tangerines') + self.assertEqual(row.product_description, "Tangerines") self.assertIsNone(row.product_size) self.assertIsNone(row.case_size) self.assertIsNone(row.unit_cost) @@ -559,20 +616,25 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.total_price) # error if unknown products not allowed - self.config.setdefault('sideshow.orders.allow_unknown_products', 'false') - self.assertRaises(TypeError, handler.add_item, batch, kw, 1, enum.ORDER_UOM_UNIT) + self.config.setdefault("sideshow.orders.allow_unknown_products", "false") + self.assertRaises( + TypeError, handler.add_item, batch, kw, 1, enum.ORDER_UOM_UNIT + ) # local product w/ discount - local = model.LocalProduct(scancode='07430500002', - description='Vinegar', - size='2oz', - unit_price_reg=2.99, - case_size=12) + local = model.LocalProduct( + scancode="07430500002", + description="Vinegar", + size="2oz", + unit_price_reg=2.99, + case_size=12, + ) self.session.add(local) self.session.flush() - with patch.object(handler, 'allow_item_discounts', return_value=True): - row = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_CASE, - discount_percent=15) + with patch.object(handler, "allow_item_discounts", return_value=True): + row = handler.add_item( + batch, local.uuid.hex, 1, enum.ORDER_UOM_CASE, discount_percent=15 + ) self.session.flush() self.session.refresh(row) self.session.refresh(local) @@ -580,41 +642,43 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.pending_product) product = row.local_product self.assertIsInstance(product, model.LocalProduct) - self.assertEqual(product.scancode, '07430500002') + self.assertEqual(product.scancode, "07430500002") self.assertIsNone(product.brand_name) - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '2oz') + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "2oz") self.assertEqual(product.case_size, 12) self.assertIsNone(product.unit_cost) - self.assertEqual(product.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.product_scancode, '07430500002') + self.assertEqual(product.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.product_scancode, "07430500002") self.assertIsNone(row.product_brand) - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '2oz') + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "2oz") self.assertEqual(row.case_size, 12) self.assertIsNone(row.unit_cost) - self.assertEqual(row.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('2.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('35.88')) - self.assertEqual(row.discount_percent, decimal.Decimal('15.00')) - self.assertEqual(row.total_price, decimal.Decimal('30.50')) + self.assertEqual(row.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("2.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("35.88")) + self.assertEqual(row.discount_percent, decimal.Decimal("15.00")) + self.assertEqual(row.total_price, decimal.Decimal("30.50")) # local product, not found mock_uuid = self.app.make_true_uuid() - self.assertRaises(ValueError, handler.add_item, - batch, mock_uuid.hex, 1, enum.ORDER_UOM_CASE) + self.assertRaises( + ValueError, handler.add_item, batch, mock_uuid.hex, 1, enum.ORDER_UOM_CASE + ) # external lookup not implemented - self.config.setdefault('sideshow.orders.use_local_products', 'false') - self.assertRaises(NotImplementedError, handler.add_item, - batch, '42', 1, enum.ORDER_UOM_CASE) + self.config.setdefault("sideshow.orders.use_local_products", "false") + self.assertRaises( + NotImplementedError, handler.add_item, batch, "42", 1, enum.ORDER_UOM_CASE + ) def test_update_item(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) @@ -623,41 +687,41 @@ class TestNewOrderBatchHandler(DataTestCase): # start with typical pending product kw = dict( - scancode='07430500001', - brand_name='Bragg', - description='Vinegar', - size='1oz', + scancode="07430500001", + brand_name="Bragg", + description="Vinegar", + size="1oz", case_size=12, - unit_cost=decimal.Decimal('1.99'), - unit_price_reg=decimal.Decimal('2.99'), + unit_cost=decimal.Decimal("1.99"), + unit_price_reg=decimal.Decimal("2.99"), ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_CASE) self.assertIsNone(row.product_id) self.assertIsNone(row.local_product) product = row.pending_product self.assertIsInstance(product, model.PendingProduct) - self.assertEqual(product.scancode, '07430500001') - self.assertEqual(product.brand_name, 'Bragg') - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '1oz') + self.assertEqual(product.scancode, "07430500001") + self.assertEqual(product.brand_name, "Bragg") + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "1oz") self.assertEqual(product.case_size, 12) - self.assertEqual(product.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(product.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.product_scancode, '07430500001') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '1oz') + self.assertEqual(product.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(product.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.product_scancode, "07430500001") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "1oz") self.assertEqual(row.case_size, 12) - self.assertEqual(row.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(row.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('2.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('35.88')) + self.assertEqual(row.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(row.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("2.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("35.88")) self.assertEqual(row.order_qty, 1) self.assertEqual(row.order_uom, enum.ORDER_UOM_CASE) - self.assertEqual(row.total_price, decimal.Decimal('35.88')) + self.assertEqual(row.total_price, decimal.Decimal("35.88")) # set pending, minimal - handler.update_item(row, {'description': 'Vinegar'}, 1, enum.ORDER_UOM_UNIT) + handler.update_item(row, {"description": "Vinegar"}, 1, enum.ORDER_UOM_UNIT) # self.session.flush() self.assertIsNone(row.product_id) self.assertIsNone(row.local_product) @@ -665,14 +729,14 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsInstance(product, model.PendingProduct) self.assertIsNone(product.scancode) self.assertIsNone(product.brand_name) - self.assertEqual(product.description, 'Vinegar') + self.assertEqual(product.description, "Vinegar") self.assertIsNone(product.size) self.assertIsNone(product.case_size) self.assertIsNone(product.unit_cost) self.assertIsNone(product.unit_price_reg) self.assertIsNone(row.product_scancode) self.assertIsNone(row.product_brand) - self.assertEqual(row.product_description, 'Vinegar') + self.assertEqual(row.product_description, "Vinegar") self.assertIsNone(row.product_size) self.assertIsNone(row.case_size) self.assertIsNone(row.unit_cost) @@ -684,11 +748,13 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.total_price) # start over, new row w/ local product - local = model.LocalProduct(scancode='07430500002', - description='Vinegar', - size='2oz', - unit_price_reg=3.99, - case_size=12) + local = model.LocalProduct( + scancode="07430500002", + description="Vinegar", + size="2oz", + unit_price_reg=3.99, + case_size=12, + ) self.session.add(local) self.session.flush() row = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_CASE) @@ -699,25 +765,25 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.pending_product) product = row.local_product self.assertIsInstance(product, model.LocalProduct) - self.assertEqual(product.scancode, '07430500002') + self.assertEqual(product.scancode, "07430500002") self.assertIsNone(product.brand_name) - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '2oz') + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "2oz") self.assertEqual(product.case_size, 12) self.assertIsNone(product.unit_cost) - self.assertEqual(product.unit_price_reg, decimal.Decimal('3.99')) - self.assertEqual(row.product_scancode, '07430500002') + self.assertEqual(product.unit_price_reg, decimal.Decimal("3.99")) + self.assertEqual(row.product_scancode, "07430500002") self.assertIsNone(row.product_brand) - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '2oz') + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "2oz") self.assertEqual(row.case_size, 12) self.assertIsNone(row.unit_cost) - self.assertEqual(row.unit_price_reg, decimal.Decimal('3.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('3.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('47.88')) + self.assertEqual(row.unit_price_reg, decimal.Decimal("3.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("3.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("47.88")) self.assertEqual(row.order_qty, 1) self.assertEqual(row.order_uom, enum.ORDER_UOM_CASE) - self.assertEqual(row.total_price, decimal.Decimal('47.88')) + self.assertEqual(row.total_price, decimal.Decimal("47.88")) # update w/ pending product handler.update_item(row, kw, 2, enum.ORDER_UOM_CASE) @@ -725,76 +791,86 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.local_product) product = row.pending_product self.assertIsInstance(product, model.PendingProduct) - self.assertEqual(product.scancode, '07430500001') - self.assertEqual(product.brand_name, 'Bragg') - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '1oz') + self.assertEqual(product.scancode, "07430500001") + self.assertEqual(product.brand_name, "Bragg") + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "1oz") self.assertEqual(product.case_size, 12) - self.assertEqual(product.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(product.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.product_scancode, '07430500001') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '1oz') + self.assertEqual(product.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(product.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.product_scancode, "07430500001") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "1oz") self.assertEqual(row.case_size, 12) - self.assertEqual(row.unit_cost, decimal.Decimal('1.99')) - self.assertEqual(row.unit_price_reg, decimal.Decimal('2.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('2.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('35.88')) + self.assertEqual(row.unit_cost, decimal.Decimal("1.99")) + self.assertEqual(row.unit_price_reg, decimal.Decimal("2.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("2.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("35.88")) self.assertEqual(row.order_qty, 2) self.assertEqual(row.order_uom, enum.ORDER_UOM_CASE) - self.assertEqual(row.total_price, decimal.Decimal('71.76')) + self.assertEqual(row.total_price, decimal.Decimal("71.76")) # update w/ pending, error if not allowed - self.config.setdefault('sideshow.orders.allow_unknown_products', 'false') - self.assertRaises(TypeError, handler.update_item, row, kw, 1, enum.ORDER_UOM_UNIT) + self.config.setdefault("sideshow.orders.allow_unknown_products", "false") + self.assertRaises( + TypeError, handler.update_item, row, kw, 1, enum.ORDER_UOM_UNIT + ) # update w/ local product and discount percent - with patch.object(handler, 'allow_item_discounts', return_value=True): - handler.update_item(row, local.uuid.hex, 1, enum.ORDER_UOM_CASE, - discount_percent=15) + with patch.object(handler, "allow_item_discounts", return_value=True): + handler.update_item( + row, local.uuid.hex, 1, enum.ORDER_UOM_CASE, discount_percent=15 + ) self.assertIsNone(row.product_id) # nb. pending remains intact here self.assertIsNotNone(row.pending_product) product = row.local_product self.assertIsInstance(product, model.LocalProduct) - self.assertEqual(product.scancode, '07430500002') + self.assertEqual(product.scancode, "07430500002") self.assertIsNone(product.brand_name) - self.assertEqual(product.description, 'Vinegar') - self.assertEqual(product.size, '2oz') + self.assertEqual(product.description, "Vinegar") + self.assertEqual(product.size, "2oz") self.assertEqual(product.case_size, 12) self.assertIsNone(product.unit_cost) - self.assertEqual(product.unit_price_reg, decimal.Decimal('3.99')) - self.assertEqual(row.product_scancode, '07430500002') + self.assertEqual(product.unit_price_reg, decimal.Decimal("3.99")) + self.assertEqual(row.product_scancode, "07430500002") self.assertIsNone(row.product_brand) - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '2oz') + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "2oz") self.assertEqual(row.case_size, 12) self.assertIsNone(row.unit_cost) - self.assertEqual(row.unit_price_reg, decimal.Decimal('3.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('3.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('47.88')) + self.assertEqual(row.unit_price_reg, decimal.Decimal("3.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("3.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("47.88")) self.assertEqual(row.order_qty, 1) self.assertEqual(row.order_uom, enum.ORDER_UOM_CASE) - self.assertEqual(row.discount_percent, decimal.Decimal('15.00')) - self.assertEqual(row.total_price, decimal.Decimal('40.70')) + self.assertEqual(row.discount_percent, decimal.Decimal("15.00")) + self.assertEqual(row.total_price, decimal.Decimal("40.70")) # update w/ local, not found mock_uuid = self.app.make_true_uuid() - self.assertRaises(ValueError, handler.update_item, - batch, mock_uuid.hex, 1, enum.ORDER_UOM_CASE) + self.assertRaises( + ValueError, + handler.update_item, + batch, + mock_uuid.hex, + 1, + enum.ORDER_UOM_CASE, + ) # external lookup not implemented - self.config.setdefault('sideshow.orders.use_local_products', 'false') - self.assertRaises(NotImplementedError, handler.update_item, - row, '42', 1, enum.ORDER_UOM_CASE) + self.config.setdefault("sideshow.orders.use_local_products", "false") + self.assertRaises( + NotImplementedError, handler.update_item, row, "42", 1, enum.ORDER_UOM_CASE + ) def test_refresh_row(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) @@ -814,26 +890,30 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(row.status_code, row.STATUS_MISSING_ORDER_QTY) # refreshed from pending product (null price) - product = model.PendingProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - vendor_name='Acme Distributors', - vendor_item_code='1234', - created_by=user, - status=enum.PendingProductStatus.PENDING) - row = handler.make_row(pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT) + product = model.PendingProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + vendor_name="Acme Distributors", + vendor_item_code="1234", + created_by=user, + status=enum.PendingProductStatus.PENDING, + ) + row = handler.make_row( + pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT + ) self.assertIsNone(row.status_code) handler.add_row(batch, row) self.assertEqual(row.status_code, row.STATUS_OK) self.assertIsNone(row.product_id) self.assertIs(row.pending_product, product) - self.assertEqual(row.product_scancode, '07430500132') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '32oz') - self.assertEqual(row.vendor_name, 'Acme Distributors') - self.assertEqual(row.vendor_item_code, '1234') + self.assertEqual(row.product_scancode, "07430500132") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "32oz") + self.assertEqual(row.vendor_name, "Acme Distributors") + self.assertEqual(row.vendor_item_code, "1234") self.assertIsNone(row.case_size) self.assertIsNone(row.unit_cost) self.assertIsNone(row.unit_price_reg) @@ -842,23 +922,27 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.total_price) # refreshed from pending product (zero price) - product = model.PendingProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - unit_price_reg=0, - created_by=user, - status=enum.PendingProductStatus.PENDING) - row = handler.make_row(pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT) + product = model.PendingProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + unit_price_reg=0, + created_by=user, + status=enum.PendingProductStatus.PENDING, + ) + row = handler.make_row( + pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT + ) self.assertIsNone(row.status_code) handler.add_row(batch, row) self.assertEqual(row.status_code, row.STATUS_OK) self.assertIsNone(row.product_id) self.assertIs(row.pending_product, product) - self.assertEqual(row.product_scancode, '07430500132') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '32oz') + self.assertEqual(row.product_scancode, "07430500132") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "32oz") self.assertIsNone(row.case_size) self.assertIsNone(row.unit_cost) self.assertEqual(row.unit_price_reg, 0) @@ -867,69 +951,79 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(row.total_price, 0) # refreshed from pending product (normal, case) - product = model.PendingProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), - created_by=user, - status=enum.PendingProductStatus.PENDING) - row = handler.make_row(pending_product=product, order_qty=2, order_uom=enum.ORDER_UOM_CASE) + product = model.PendingProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + case_size=12, + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), + created_by=user, + status=enum.PendingProductStatus.PENDING, + ) + row = handler.make_row( + pending_product=product, order_qty=2, order_uom=enum.ORDER_UOM_CASE + ) self.assertIsNone(row.status_code) handler.add_row(batch, row) self.assertEqual(row.status_code, row.STATUS_OK) self.assertIsNone(row.product_id) self.assertIs(row.pending_product, product) - self.assertEqual(row.product_scancode, '07430500132') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '32oz') + self.assertEqual(row.product_scancode, "07430500132") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "32oz") self.assertEqual(row.case_size, 12) - self.assertEqual(row.unit_cost, decimal.Decimal('3.99')) - self.assertEqual(row.unit_price_reg, decimal.Decimal('5.99')) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('5.99')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('71.88')) - self.assertEqual(row.total_price, decimal.Decimal('143.76')) + self.assertEqual(row.unit_cost, decimal.Decimal("3.99")) + self.assertEqual(row.unit_price_reg, decimal.Decimal("5.99")) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("5.99")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("71.88")) + self.assertEqual(row.total_price, decimal.Decimal("143.76")) # refreshed from pending product (sale price) - product = model.PendingProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), - created_by=user, - status=enum.PendingProductStatus.PENDING) - row = handler.make_row(pending_product=product, order_qty=2, order_uom=enum.ORDER_UOM_CASE, - unit_price_sale=decimal.Decimal('5.19'), - sale_ends=datetime.datetime(2099, 1, 1)) + product = model.PendingProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + case_size=12, + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), + created_by=user, + status=enum.PendingProductStatus.PENDING, + ) + row = handler.make_row( + pending_product=product, + order_qty=2, + order_uom=enum.ORDER_UOM_CASE, + unit_price_sale=decimal.Decimal("5.19"), + sale_ends=datetime.datetime(2099, 1, 1), + ) self.assertIsNone(row.status_code) handler.add_row(batch, row) self.assertEqual(row.status_code, row.STATUS_OK) self.assertIsNone(row.product_id) self.assertIs(row.pending_product, product) - self.assertEqual(row.product_scancode, '07430500132') - self.assertEqual(row.product_brand, 'Bragg') - self.assertEqual(row.product_description, 'Vinegar') - self.assertEqual(row.product_size, '32oz') + self.assertEqual(row.product_scancode, "07430500132") + self.assertEqual(row.product_brand, "Bragg") + self.assertEqual(row.product_description, "Vinegar") + self.assertEqual(row.product_size, "32oz") self.assertEqual(row.case_size, 12) - self.assertEqual(row.unit_cost, decimal.Decimal('3.99')) - self.assertEqual(row.unit_price_reg, decimal.Decimal('5.99')) - self.assertEqual(row.unit_price_sale, decimal.Decimal('5.19')) + self.assertEqual(row.unit_cost, decimal.Decimal("3.99")) + self.assertEqual(row.unit_price_reg, decimal.Decimal("5.99")) + self.assertEqual(row.unit_price_sale, decimal.Decimal("5.19")) self.assertEqual(row.sale_ends, datetime.datetime(2099, 1, 1)) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('5.19')) - self.assertEqual(row.case_price_quoted, decimal.Decimal('62.28')) - self.assertEqual(row.total_price, decimal.Decimal('124.56')) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("5.19")) + self.assertEqual(row.case_price_quoted, decimal.Decimal("62.28")) + self.assertEqual(row.total_price, decimal.Decimal("124.56")) def test_remove_row(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) @@ -937,21 +1031,21 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(len(batch.rows), 0) kw = dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), created_by=user, ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_CASE) self.session.add(row) self.session.flush() self.assertEqual(batch.row_count, 1) - self.assertEqual(row.total_price, decimal.Decimal('71.88')) - self.assertEqual(batch.total_price, decimal.Decimal('71.88')) + self.assertEqual(row.total_price, decimal.Decimal("71.88")) + self.assertEqual(batch.total_price, decimal.Decimal("71.88")) handler.do_remove_row(row) self.assertEqual(batch.row_count, 0) @@ -962,15 +1056,19 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # make batch w/ pending customer - customer = model.PendingCustomer(full_name="Fred Flintstone", - status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + full_name="Fred Flintstone", + status=enum.PendingCustomerStatus.PENDING, + created_by=user, + ) self.session.add(customer) - batch = handler.make_batch(self.session, created_by=user, pending_customer=customer) + batch = handler.make_batch( + self.session, created_by=user, pending_customer=customer + ) self.session.add(batch) self.session.commit() @@ -983,11 +1081,15 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(self.session.query(model.PendingCustomer).count(), 0) # make new pending customer, assigned to batch + order - customer = model.PendingCustomer(full_name="Wilma Flintstone", - status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + full_name="Wilma Flintstone", + status=enum.PendingCustomerStatus.PENDING, + created_by=user, + ) self.session.add(customer) - batch = handler.make_batch(self.session, created_by=user, pending_customer=customer) + batch = handler.make_batch( + self.session, created_by=user, pending_customer=customer + ) self.session.add(batch) order = model.Order(order_id=77, created_by=user, pending_customer=customer) self.session.add(order) @@ -1004,18 +1106,27 @@ class TestNewOrderBatchHandler(DataTestCase): # make new pending product, associate w/ batch + order batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, {'full_name': "Jack Black"}) - row = handler.add_item(batch, dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - unit_price_reg=5.99, - ), 1, enum.ORDER_UOM_UNIT) + handler.set_customer(batch, {"full_name": "Jack Black"}) + row = handler.add_item( + batch, + dict( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + unit_price_reg=5.99, + ), + 1, + enum.ORDER_UOM_UNIT, + ) product = row.pending_product order = model.Order(order_id=33, created_by=user) - item = model.OrderItem(pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_INITIATED) + item = model.OrderItem( + pending_product=product, + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_INITIATED, + ) order.items.append(item) self.session.add(order) self.session.flush() @@ -1031,8 +1142,9 @@ class TestNewOrderBatchHandler(DataTestCase): # make another batch w/ same pending product batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - row = handler.make_row(pending_product=product, - order_qty=1, order_uom=enum.ORDER_UOM_UNIT) + row = handler.make_row( + pending_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT + ) handler.add_row(batch, row) # also delete the associated order @@ -1052,7 +1164,7 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # make batch w/ different status rows @@ -1064,12 +1176,16 @@ class TestNewOrderBatchHandler(DataTestCase): self.session.add(row) self.session.flush() # STATUS_MISSING_ORDER_QTY - row = handler.make_row(product_id=42, order_qty=0, order_uom=enum.ORDER_UOM_UNIT) + row = handler.make_row( + product_id=42, order_qty=0, order_uom=enum.ORDER_UOM_UNIT + ) handler.add_row(batch, row) self.session.add(row) self.session.flush() # STATUS_OK - row = handler.add_item(batch, {'scancode': '07430500132'}, 1, enum.ORDER_UOM_UNIT) + row = handler.add_item( + batch, {"scancode": "07430500132"}, 1, enum.ORDER_UOM_UNIT + ) self.session.flush() # only 1 effective row @@ -1083,7 +1199,7 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) @@ -1099,19 +1215,19 @@ class TestNewOrderBatchHandler(DataTestCase): reason = handler.why_not_execute(batch) self.assertEqual(reason, "Customer phone number is required") - batch.phone_number = '555-1234' + batch.phone_number = "555-1234" reason = handler.why_not_execute(batch) self.assertEqual(reason, "Must add at least one valid item") kw = dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), created_by=user, ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_CASE) @@ -1123,7 +1239,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(reason) # unless we also require store - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") reason = handler.why_not_execute(batch) self.assertEqual(reason, "Must assign the store") @@ -1132,21 +1248,28 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # make a typical batch batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, {'first_name': "John", 'last_name': "Doe", - 'phone_number': '555-1234'}) - row = handler.add_item(batch, dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - unit_price_reg=5.99, - ), 1, enum.ORDER_UOM_UNIT) + handler.set_customer( + batch, + {"first_name": "John", "last_name": "Doe", "phone_number": "555-1234"}, + ) + row = handler.add_item( + batch, + dict( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + unit_price_reg=5.99, + ), + 1, + enum.ORDER_UOM_UNIT, + ) self.session.flush() # making local customer removes pending customer @@ -1163,7 +1286,7 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(local.first_name, "John") self.assertEqual(local.last_name, "Doe") self.assertEqual(local.full_name, "John Doe") - self.assertEqual(local.phone_number, '555-1234') + self.assertEqual(local.phone_number, "555-1234") # trying again does nothing handler.make_local_customer(batch) @@ -1175,24 +1298,31 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(local.first_name, "John") self.assertEqual(local.last_name, "Doe") self.assertEqual(local.full_name, "John Doe") - self.assertEqual(local.phone_number, '555-1234') + self.assertEqual(local.phone_number, "555-1234") # make another typical batch batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, {'first_name': "Chuck", 'last_name': "Norris", - 'phone_number': '555-1234'}) - row = handler.add_item(batch, dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - unit_price_reg=5.99, - ), 1, enum.ORDER_UOM_UNIT) + handler.set_customer( + batch, + {"first_name": "Chuck", "last_name": "Norris", "phone_number": "555-1234"}, + ) + row = handler.add_item( + batch, + dict( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + unit_price_reg=5.99, + ), + 1, + enum.ORDER_UOM_UNIT, + ) self.session.flush() # should do nothing if local customers disabled - with patch.object(handler, 'use_local_customers', return_value=False): + with patch.object(handler, "use_local_customers", return_value=False): self.assertEqual(self.session.query(model.PendingCustomer).count(), 1) self.assertEqual(self.session.query(model.LocalCustomer).count(), 1) self.assertIsNotNone(batch.pending_customer) @@ -1217,35 +1347,42 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(local.first_name, "Chuck") self.assertEqual(local.last_name, "Norris") self.assertEqual(local.full_name, "Chuck Norris") - self.assertEqual(local.phone_number, '555-1234') + self.assertEqual(local.phone_number, "555-1234") def test_process_pending_products(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # make a batch w/ one each local + pending products batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, {'full_name': "John Doe"}) - local = model.LocalProduct(scancode='07430500116', - brand_name='Bragg', - description='Vinegar', - size='16oz', - unit_price_reg=3.59) + handler.set_customer(batch, {"full_name": "John Doe"}) + local = model.LocalProduct( + scancode="07430500116", + brand_name="Bragg", + description="Vinegar", + size="16oz", + unit_price_reg=3.59, + ) self.session.add(local) self.session.flush() row1 = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_UNIT) - row2 = handler.add_item(batch, dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - unit_price_reg=5.99, - ), 1, enum.ORDER_UOM_UNIT) + row2 = handler.add_item( + batch, + dict( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + unit_price_reg=5.99, + ), + 1, + enum.ORDER_UOM_UNIT, + ) self.session.flush() # making local product removes pending product @@ -1259,11 +1396,11 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row2.pending_product) local = row2.local_product self.assertIsNotNone(local) - self.assertEqual(local.scancode, '07430500132') - self.assertEqual(local.brand_name, 'Bragg') - self.assertEqual(local.description, 'Vinegar') - self.assertEqual(local.size, '32oz') - self.assertEqual(local.unit_price_reg, decimal.Decimal('5.99')) + self.assertEqual(local.scancode, "07430500132") + self.assertEqual(local.brand_name, "Bragg") + self.assertEqual(local.description, "Vinegar") + self.assertEqual(local.size, "32oz") + self.assertEqual(local.unit_price_reg, decimal.Decimal("5.99")) # trying again does nothing handler.process_pending_products(batch, batch.rows) @@ -1272,37 +1409,46 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row2.pending_product) local = row2.local_product self.assertIsNotNone(local) - self.assertEqual(local.scancode, '07430500132') - self.assertEqual(local.brand_name, 'Bragg') - self.assertEqual(local.description, 'Vinegar') - self.assertEqual(local.size, '32oz') - self.assertEqual(local.unit_price_reg, decimal.Decimal('5.99')) + self.assertEqual(local.scancode, "07430500132") + self.assertEqual(local.brand_name, "Bragg") + self.assertEqual(local.description, "Vinegar") + self.assertEqual(local.size, "32oz") + self.assertEqual(local.unit_price_reg, decimal.Decimal("5.99")) # make another typical batch batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, {'full_name': "Chuck Norris"}) - row = handler.add_item(batch, dict( - scancode='07430500164', - brand_name='Bragg', - description='Vinegar', - size='64oz', - unit_price_reg=9.99, - ), 1, enum.ORDER_UOM_UNIT) + handler.set_customer(batch, {"full_name": "Chuck Norris"}) + row = handler.add_item( + batch, + dict( + scancode="07430500164", + brand_name="Bragg", + description="Vinegar", + size="64oz", + unit_price_reg=9.99, + ), + 1, + enum.ORDER_UOM_UNIT, + ) self.session.flush() # should update status if using external products - with patch.object(handler, 'use_local_products', return_value=False): + with patch.object(handler, "use_local_products", return_value=False): self.assertEqual(self.session.query(model.PendingProduct).count(), 1) self.assertEqual(self.session.query(model.LocalProduct).count(), 2) self.assertIsNotNone(row.pending_product) - self.assertEqual(row.pending_product.status, enum.PendingProductStatus.PENDING) + self.assertEqual( + row.pending_product.status, enum.PendingProductStatus.PENDING + ) self.assertIsNone(row.local_product) handler.process_pending_products(batch, batch.rows) self.assertEqual(self.session.query(model.PendingProduct).count(), 1) self.assertEqual(self.session.query(model.LocalProduct).count(), 2) self.assertIsNotNone(row.pending_product) - self.assertEqual(row.pending_product.status, enum.PendingProductStatus.READY) + self.assertEqual( + row.pending_product.status, enum.PendingProductStatus.READY + ) self.assertIsNone(row.local_product) # but if using local products (the default), pending is converted to local @@ -1316,31 +1462,32 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertIsNone(row.pending_product) local = row.local_product self.assertIsNotNone(local) - self.assertEqual(local.scancode, '07430500164') - self.assertEqual(local.brand_name, 'Bragg') - self.assertEqual(local.description, 'Vinegar') - self.assertEqual(local.size, '64oz') - self.assertEqual(local.unit_price_reg, decimal.Decimal('9.99')) + self.assertEqual(local.scancode, "07430500164") + self.assertEqual(local.brand_name, "Bragg") + self.assertEqual(local.description, "Vinegar") + self.assertEqual(local.size, "64oz") + self.assertEqual(local.unit_price_reg, decimal.Decimal("9.99")) def test_make_new_order(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - batch = handler.make_batch(self.session, created_by=user, - customer_id=42, customer_name="John Doe") + batch = handler.make_batch( + self.session, created_by=user, customer_id=42, customer_name="John Doe" + ) self.session.add(batch) kw = dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), created_by=user, ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_CASE) @@ -1354,19 +1501,19 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(order.customer_name, "John Doe") self.assertEqual(len(order.items), 1) item = order.items[0] - self.assertEqual(item.product_scancode, '07430500132') - self.assertEqual(item.product_brand, 'Bragg') - self.assertEqual(item.product_description, 'Vinegar') - self.assertEqual(item.product_size, '32oz') + self.assertEqual(item.product_scancode, "07430500132") + self.assertEqual(item.product_brand, "Bragg") + self.assertEqual(item.product_description, "Vinegar") + self.assertEqual(item.product_size, "32oz") self.assertEqual(item.case_size, 12) - self.assertEqual(item.unit_cost, decimal.Decimal('3.99')) - self.assertEqual(item.unit_price_reg, decimal.Decimal('5.99')) + self.assertEqual(item.unit_cost, decimal.Decimal("3.99")) + self.assertEqual(item.unit_price_reg, decimal.Decimal("5.99")) def test_set_initial_item_status(self): model = self.app.model enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") item = model.OrderItem() self.assertIsNone(item.status_code) self.assertEqual(len(item.events), 0) @@ -1381,20 +1528,21 @@ class TestNewOrderBatchHandler(DataTestCase): enum = self.app.enum handler = self.make_handler() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - batch = handler.make_batch(self.session, created_by=user, - customer_id=42, customer_name="John Doe") + batch = handler.make_batch( + self.session, created_by=user, customer_id=42, customer_name="John Doe" + ) self.session.add(batch) kw = dict( - scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", case_size=12, - unit_cost=decimal.Decimal('3.99'), - unit_price_reg=decimal.Decimal('5.99'), + unit_cost=decimal.Decimal("3.99"), + unit_price_reg=decimal.Decimal("5.99"), created_by=user, ) row = handler.add_item(batch, kw, 1, enum.ORDER_UOM_CASE) @@ -1408,10 +1556,10 @@ class TestNewOrderBatchHandler(DataTestCase): self.assertEqual(order.customer_name, "John Doe") self.assertEqual(len(order.items), 1) item = order.items[0] - self.assertEqual(item.product_scancode, '07430500132') - self.assertEqual(item.product_brand, 'Bragg') - self.assertEqual(item.product_description, 'Vinegar') - self.assertEqual(item.product_size, '32oz') + self.assertEqual(item.product_scancode, "07430500132") + self.assertEqual(item.product_brand, "Bragg") + self.assertEqual(item.product_description, "Vinegar") + self.assertEqual(item.product_size, "32oz") self.assertEqual(item.case_size, 12) - self.assertEqual(item.unit_cost, decimal.Decimal('3.99')) - self.assertEqual(item.unit_price_reg, decimal.Decimal('5.99')) + self.assertEqual(item.unit_cost, decimal.Decimal("3.99")) + self.assertEqual(item.unit_price_reg, decimal.Decimal("5.99")) diff --git a/tests/cli/test_install.py b/tests/cli/test_install.py index 25effc1..a9c7d3b 100644 --- a/tests/cli/test_install.py +++ b/tests/cli/test_install.py @@ -13,6 +13,6 @@ class TestInstall(ConfigTestCase): def test_run(self): ctx = MagicMock(params={}) ctx.parent.wutta_config = self.config - with patch.object(InstallHandler, 'run') as run: + with patch.object(InstallHandler, "run") as run: mod.install(ctx) run.assert_called_once_with() diff --git a/tests/db/model/batch/test_neworder.py b/tests/db/model/batch/test_neworder.py index 04992dc..78b82be 100644 --- a/tests/db/model/batch/test_neworder.py +++ b/tests/db/model/batch/test_neworder.py @@ -16,8 +16,6 @@ class TestNewOrderBatchRow(DataTestCase): row = mod.NewOrderBatchRow(product_description="Vinegar") self.assertEqual(str(row), "Vinegar") - product = PendingProduct(brand_name="Bragg", - description="Vinegar", - size="32oz") + product = PendingProduct(brand_name="Bragg", description="Vinegar", size="32oz") row = mod.NewOrderBatchRow(pending_product=product) self.assertEqual(str(row), "Bragg Vinegar 32oz") diff --git a/tests/db/model/test_orders.py b/tests/db/model/test_orders.py index 21ee153..6768bf9 100644 --- a/tests/db/model/test_orders.py +++ b/tests/db/model/test_orders.py @@ -21,7 +21,7 @@ class TestOrderItem(DataTestCase): def make_config(self, **kw): config = super().make_config(**kw) - config.setdefault('wutta.enum_spec', 'sideshow.enum') + config.setdefault("wutta.enum_spec", "sideshow.enum") return config def test_full_description(self): @@ -32,9 +32,9 @@ class TestOrderItem(DataTestCase): item = mod.OrderItem(product_description="Vinegar") self.assertEqual(item.full_description, "Vinegar") - item = mod.OrderItem(product_brand='Bragg', - product_description='Vinegar', - product_size='32oz') + item = mod.OrderItem( + product_brand="Bragg", product_description="Vinegar", product_size="32oz" + ) self.assertEqual(item.full_description, "Bragg Vinegar 32oz") def test_str(self): @@ -45,15 +45,15 @@ class TestOrderItem(DataTestCase): item = mod.OrderItem(product_description="Vinegar") self.assertEqual(str(item), "Vinegar") - item = mod.OrderItem(product_brand='Bragg', - product_description='Vinegar', - product_size='32oz') + item = mod.OrderItem( + product_brand="Bragg", product_description="Vinegar", product_size="32oz" + ) self.assertEqual(str(item), "Bragg Vinegar 32oz") def test_add_event(self): model = self.app.model enum = self.app.enum - user = model.User(username='barney') + user = model.User(username="barney") item = mod.OrderItem() self.assertEqual(item.events, []) item.add_event(enum.ORDER_ITEM_EVENT_INITIATED, user) diff --git a/tests/db/model/test_products.py b/tests/db/model/test_products.py index 17ffbc3..c986004 100644 --- a/tests/db/model/test_products.py +++ b/tests/db/model/test_products.py @@ -20,9 +20,9 @@ class TestPendingProduct(DataTestCase): product = mod.PendingProduct(size="32oz") self.assertEqual(str(product), "32oz") - product = mod.PendingProduct(brand_name="Bragg", - description="Vinegar", - size="32oz") + product = mod.PendingProduct( + brand_name="Bragg", description="Vinegar", size="32oz" + ) self.assertEqual(str(product), "Bragg Vinegar 32oz") def test_full_description(self): @@ -38,7 +38,7 @@ class TestPendingProduct(DataTestCase): product = mod.PendingProduct(size="32oz") self.assertEqual(product.full_description, "32oz") - product = mod.PendingProduct(brand_name="Bragg", - description="Vinegar", - size="32oz") + product = mod.PendingProduct( + brand_name="Bragg", description="Vinegar", size="32oz" + ) self.assertEqual(product.full_description, "Bragg Vinegar 32oz") diff --git a/tests/test_config.py b/tests/test_config.py index 403793f..816987f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -13,5 +13,5 @@ class TestSideshowConfig(TestCase): config = WuttaConfig(files=[]) ext = mod.SideshowConfig() ext.configure(config) - self.assertEqual(config.get('wutta.app_title'), "Sideshow") - self.assertEqual(config.get('wutta.app_dist'), "Sideshow") + self.assertEqual(config.get("wutta.app_title"), "Sideshow") + self.assertEqual(config.get("wutta.app_dist"), "Sideshow") diff --git a/tests/test_orders.py b/tests/test_orders.py index 6dca146..457bae9 100644 --- a/tests/test_orders.py +++ b/tests/test_orders.py @@ -9,8 +9,8 @@ class TestOrderHandler(DataTestCase): def make_config(self, **kwargs): config = super().make_config(**kwargs) - config.setdefault('wutta.model_spec', 'sideshow.db.model') - config.setdefault('wutta.enum_spec', 'sideshow.enum') + config.setdefault("wutta.model_spec", "sideshow.db.model") + config.setdefault("wutta.enum_spec", "sideshow.enum") return config def make_handler(self): @@ -23,7 +23,7 @@ class TestOrderHandler(DataTestCase): self.assertFalse(handler.expose_store_id()) # config can enable - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") self.assertTrue(handler.expose_store_id()) def test_get_order_qty_uom_text(self): @@ -35,7 +35,9 @@ class TestOrderHandler(DataTestCase): self.assertEqual(text, "2 Cases (x 12 = 24 Units)") # typical w/ html - text = handler.get_order_qty_uom_text(2, enum.ORDER_UOM_CASE, case_size=12, html=True) + text = handler.get_order_qty_uom_text( + 2, enum.ORDER_UOM_CASE, case_size=12, html=True + ) self.assertEqual(text, "2 Cases (× 12 = 24 Units)") # unknown case size @@ -55,20 +57,39 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # typical - self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INITIATED)) + self.assertIsNone( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INITIATED) + ) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_READY)) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PLACED)) - self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RECEIVED)) - self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CONTACTED)) + self.assertIsNone( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RECEIVED) + ) + self.assertIsNone( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CONTACTED) + ) self.assertIsNone(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_PAID)) # warning - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CANCELED), 'warning') - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUND_PENDING), 'warning') - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUNDED), 'warning') - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RESTOCKED), 'warning') - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_EXPIRED), 'warning') - self.assertEqual(handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INACTIVE), 'warning') + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_CANCELED), "warning" + ) + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUND_PENDING), + "warning", + ) + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_REFUNDED), "warning" + ) + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_RESTOCKED), "warning" + ) + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_EXPIRED), "warning" + ) + self.assertEqual( + handler.item_status_to_variant(enum.ORDER_ITEM_STATUS_INACTIVE), "warning" + ) def test_resolve_pending_product(self): model = self.app.model @@ -76,65 +97,87 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - pending = model.PendingProduct(description='vinegar', unit_price_reg=5.99, - status=enum.PendingProductStatus.PENDING, - created_by=user) + pending = model.PendingProduct( + description="vinegar", + unit_price_reg=5.99, + status=enum.PendingProductStatus.PENDING, + created_by=user, + ) self.session.add(pending) - order = model.Order(order_id=100, customer_name="Fred Flintstone", created_by=user) - item = model.OrderItem(pending_product=pending, - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + order = model.Order( + order_id=100, customer_name="Fred Flintstone", created_by=user + ) + item = model.OrderItem( + pending_product=pending, + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item) self.session.add(order) self.session.flush() info = { - 'product_id': '07430500132', - 'scancode': '07430500132', - 'brand_name': "Bragg's", - 'description': "Apple Cider Vinegar", - 'size': "32oz", - 'weighed': False, - 'department_id': None, - 'department_name': None, - 'special_order': False, - 'vendor_name': None, - 'vendor_item_code': None, - 'case_size': 12, - 'unit_cost': 2.99, - 'unit_price_reg': 5.99, + "product_id": "07430500132", + "scancode": "07430500132", + "brand_name": "Bragg's", + "description": "Apple Cider Vinegar", + "size": "32oz", + "weighed": False, + "department_id": None, + "department_name": None, + "special_order": False, + "vendor_name": None, + "vendor_item_code": None, + "case_size": 12, + "unit_cost": 2.99, + "unit_price_reg": 5.99, } # first try fails b/c pending status self.assertEqual(len(item.events), 0) - self.assertRaises(ValueError, handler.resolve_pending_product, pending, info, user) + self.assertRaises( + ValueError, handler.resolve_pending_product, pending, info, user + ) # resolves okay if ready status pending.status = enum.PendingProductStatus.READY handler.resolve_pending_product(pending, info, user) self.assertEqual(len(item.events), 1) - self.assertEqual(item.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED) + self.assertEqual( + item.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED + ) self.assertIsNone(item.events[0].note) # more sample data - pending2 = model.PendingProduct(description='vinegar', unit_price_reg=5.99, - status=enum.PendingProductStatus.READY, - created_by=user) + pending2 = model.PendingProduct( + description="vinegar", + unit_price_reg=5.99, + status=enum.PendingProductStatus.READY, + created_by=user, + ) self.session.add(pending2) - order2 = model.Order(order_id=101, customer_name="Wilma Flintstone", created_by=user) - item2 = model.OrderItem(pending_product=pending2, - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + order2 = model.Order( + order_id=101, customer_name="Wilma Flintstone", created_by=user + ) + item2 = model.OrderItem( + pending_product=pending2, + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order2.items.append(item2) self.session.add(order2) self.session.flush() # resolve with extra note - handler.resolve_pending_product(pending2, info, user, note='hello world') + handler.resolve_pending_product(pending2, info, user, note="hello world") self.assertEqual(len(item2.events), 2) - self.assertEqual(item2.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED) + self.assertEqual( + item2.events[0].type_code, enum.ORDER_ITEM_EVENT_PRODUCT_RESOLVED + ) self.assertIsNone(item2.events[0].note) self.assertEqual(item2.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED) self.assertEqual(item2.events[1].note, "hello world") @@ -145,17 +188,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item3) self.session.add(order) self.session.flush() @@ -165,8 +219,9 @@ class TestOrderHandler(DataTestCase): self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_READY) self.assertEqual(len(item1.events), 0) self.assertEqual(len(item2.events), 0) - handler.process_placement([item1, item2], user, - vendor_name="Acme Dist", po_number='ACME123') + handler.process_placement( + [item1, item2], user, vendor_name="Acme Dist", po_number="ACME123" + ) self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(len(item1.events), 1) @@ -193,23 +248,40 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item3) - item4 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item4 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item4) - item5 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item5 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item5) self.session.add(order) self.session.flush() @@ -217,17 +289,26 @@ class TestOrderHandler(DataTestCase): # all info provided self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(len(item1.events), 0) - handler.process_receiving([item1], user, vendor_name="Acme Dist", - invoice_number='INV123', po_number='123') + handler.process_receiving( + [item1], + user, + vendor_name="Acme Dist", + invoice_number="INV123", + po_number="123", + ) self.assertEqual(item1.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(len(item1.events), 1) - self.assertEqual(item1.events[0].note, "invoice INV123 (PO 123) from vendor Acme Dist") + self.assertEqual( + item1.events[0].note, "invoice INV123 (PO 123) from vendor Acme Dist" + ) self.assertEqual(item1.events[0].type_code, enum.ORDER_ITEM_EVENT_RECEIVED) # missing PO number self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(len(item2.events), 0) - handler.process_receiving([item2], user, vendor_name="Acme Dist", invoice_number='INV123') + handler.process_receiving( + [item2], user, vendor_name="Acme Dist", invoice_number="INV123" + ) self.assertEqual(item2.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(len(item2.events), 1) self.assertEqual(item2.events[0].note, "invoice INV123 from vendor Acme Dist") @@ -236,7 +317,9 @@ class TestOrderHandler(DataTestCase): # missing invoice number self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_PLACED) self.assertEqual(len(item3.events), 0) - handler.process_receiving([item3], user, vendor_name="Acme Dist", po_number='123') + handler.process_receiving( + [item3], user, vendor_name="Acme Dist", po_number="123" + ) self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) self.assertEqual(len(item3.events), 1) self.assertEqual(item3.events[0].note, "PO 123 from vendor Acme Dist") @@ -268,17 +351,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_PLACED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_PLACED, + ) order.items.append(item3) self.session.add(order) self.session.flush() @@ -315,17 +409,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item3) self.session.add(order) self.session.flush() @@ -362,17 +467,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item3) self.session.add(order) self.session.flush() @@ -389,8 +505,12 @@ class TestOrderHandler(DataTestCase): self.assertEqual(len(item2.events), 1) self.assertIsNone(item1.events[0].note) self.assertIsNone(item2.events[0].note) - self.assertEqual(item1.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) - self.assertEqual(item2.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) + self.assertEqual( + item1.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED + ) + self.assertEqual( + item2.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED + ) # update last item, with extra note self.assertEqual(item3.status_code, enum.ORDER_ITEM_STATUS_RECEIVED) @@ -400,7 +520,9 @@ class TestOrderHandler(DataTestCase): self.assertEqual(len(item3.events), 2) self.assertIsNone(item3.events[0].note) self.assertEqual(item3.events[1].note, "extra note") - self.assertEqual(item3.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED) + self.assertEqual( + item3.events[0].type_code, enum.ORDER_ITEM_EVENT_CONTACT_FAILED + ) self.assertEqual(item3.events[1].type_code, enum.ORDER_ITEM_EVENT_NOTE_ADDED) def test_process_delivery(self): @@ -409,17 +531,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item3) self.session.add(order) self.session.flush() @@ -456,17 +589,28 @@ class TestOrderHandler(DataTestCase): handler = self.make_handler() # sample data - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) - item1 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) + item1 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item1) - item2 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item2 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item2) - item3 = model.OrderItem(order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_RECEIVED) + item3 = model.OrderItem( + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_RECEIVED, + ) order.items.append(item3) self.session.add(order) self.session.flush() diff --git a/tests/web/forms/test_schema.py b/tests/web/forms/test_schema.py index 3dd838a..8abffbc 100644 --- a/tests/web/forms/test_schema.py +++ b/tests/web/forms/test_schema.py @@ -17,10 +17,10 @@ class TestOrderRef(WebTestCase): self.assertIsNot(sorted_query, query) def test_get_object_url(self): - self.pyramid_config.add_route('orders.view', '/orders/{uuid}') + self.pyramid_config.add_route("orders.view", "/orders/{uuid}") model = self.app.model - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) @@ -29,7 +29,7 @@ class TestOrderRef(WebTestCase): typ = mod.OrderRef(self.request, session=self.session) url = typ.get_object_url(order) self.assertIsNotNone(url) - self.assertIn(f'/orders/{order.uuid}', url) + self.assertIn(f"/orders/{order.uuid}", url) class TestLocalCustomerRef(WebTestCase): @@ -43,7 +43,7 @@ class TestLocalCustomerRef(WebTestCase): self.assertIsNot(sorted_query, query) def test_get_object_url(self): - self.pyramid_config.add_route('local_customers.view', '/local/customers/{uuid}') + self.pyramid_config.add_route("local_customers.view", "/local/customers/{uuid}") model = self.app.model enum = self.app.enum @@ -54,7 +54,7 @@ class TestLocalCustomerRef(WebTestCase): typ = mod.LocalCustomerRef(self.request, session=self.session) url = typ.get_object_url(customer) self.assertIsNotNone(url) - self.assertIn(f'/local/customers/{customer.uuid}', url) + self.assertIn(f"/local/customers/{customer.uuid}", url) class TestPendingCustomerRef(WebTestCase): @@ -68,21 +68,24 @@ class TestPendingCustomerRef(WebTestCase): self.assertIsNot(sorted_query, query) def test_get_object_url(self): - self.pyramid_config.add_route('pending_customers.view', '/pending/customers/{uuid}') + self.pyramid_config.add_route( + "pending_customers.view", "/pending/customers/{uuid}" + ) model = self.app.model enum = self.app.enum - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) self.session.commit() typ = mod.PendingCustomerRef(self.request, session=self.session) url = typ.get_object_url(customer) self.assertIsNotNone(url) - self.assertIn(f'/pending/customers/{customer.uuid}', url) + self.assertIn(f"/pending/customers/{customer.uuid}", url) class TestLocalProductRef(WebTestCase): @@ -96,7 +99,7 @@ class TestLocalProductRef(WebTestCase): self.assertIsNot(sorted_query, query) def test_get_object_url(self): - self.pyramid_config.add_route('local_products.view', '/local/products/{uuid}') + self.pyramid_config.add_route("local_products.view", "/local/products/{uuid}") model = self.app.model enum = self.app.enum @@ -107,7 +110,7 @@ class TestLocalProductRef(WebTestCase): typ = mod.LocalProductRef(self.request, session=self.session) url = typ.get_object_url(product) self.assertIsNotNone(url) - self.assertIn(f'/local/products/{product.uuid}', url) + self.assertIn(f"/local/products/{product.uuid}", url) class TestPendingProductRef(WebTestCase): @@ -121,18 +124,21 @@ class TestPendingProductRef(WebTestCase): self.assertIsNot(sorted_query, query) def test_get_object_url(self): - self.pyramid_config.add_route('pending_products.view', '/pending/products/{uuid}') + self.pyramid_config.add_route( + "pending_products.view", "/pending/products/{uuid}" + ) model = self.app.model enum = self.app.enum - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - product = model.PendingProduct(status=enum.PendingProductStatus.PENDING, - created_by=user) + product = model.PendingProduct( + status=enum.PendingProductStatus.PENDING, created_by=user + ) self.session.add(product) self.session.commit() typ = mod.PendingProductRef(self.request, session=self.session) url = typ.get_object_url(product) self.assertIsNotNone(url) - self.assertIn(f'/pending/products/{product.uuid}', url) + self.assertIn(f"/pending/products/{product.uuid}", url) diff --git a/tests/web/test_app.py b/tests/web/test_app.py index 84cddfc..f98a9a8 100644 --- a/tests/web/test_app.py +++ b/tests/web/test_app.py @@ -13,7 +13,7 @@ from sideshow.web import app as mod class TestMain(DataTestCase): def test_coverage(self): - app = mod.main({}, **{'wutta_config': self.config}) + app = mod.main({}, **{"wutta_config": self.config}) self.assertIsInstance(app, Router) diff --git a/tests/web/test_menus.py b/tests/web/test_menus.py index ed64232..4616a3e 100644 --- a/tests/web/test_menus.py +++ b/tests/web/test_menus.py @@ -9,12 +9,15 @@ class TestSideshowMenuHandler(WebTestCase): def test_make_menus(self): handler = mod.SideshowMenuHandler(self.config) menus = handler.make_menus(self.request) - titles = [menu['title'] for menu in menus] - self.assertEqual(titles, [ - 'Orders', - 'Customers', - 'Products', - 'Batches', - 'Other', - 'Admin', - ]) + titles = [menu["title"] for menu in menus] + self.assertEqual( + titles, + [ + "Orders", + "Customers", + "Products", + "Batches", + "Other", + "Admin", + ], + ) diff --git a/tests/web/views/batch/test_neworder.py b/tests/web/views/batch/test_neworder.py index b832986..8a34b26 100644 --- a/tests/web/views/batch/test_neworder.py +++ b/tests/web/views/batch/test_neworder.py @@ -33,16 +33,16 @@ class TestNewOrderBatchView(WebTestCase): # store_id not exposed by default grid = view.make_grid(model_class=model.NewOrderBatch) - self.assertIn('store_id', grid.columns) + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertNotIn('store_id', grid.columns) + self.assertNotIn("store_id", grid.columns) # store_id is exposed if configured - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") grid = view.make_grid(model_class=model.NewOrderBatch) - self.assertIn('store_id', grid.columns) + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertIn('store_id', grid.columns) + self.assertIn("store_id", grid.columns) def test_configure_form(self): model = self.app.model @@ -50,74 +50,85 @@ class TestNewOrderBatchView(WebTestCase): view = self.make_view() handler = view.batch_handler - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) - batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) + batch = handler.make_batch( + self.session, pending_customer=customer, created_by=user + ) self.session.add(batch) self.session.commit() # viewing - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=batch) view.configure_form(form) schema = form.get_schema() - self.assertIsInstance(schema['pending_customer'].typ, PendingCustomerRef) - self.assertIsInstance(schema['total_price'].typ, WuttaMoney) + self.assertIsInstance(schema["pending_customer"].typ, PendingCustomerRef) + self.assertIsInstance(schema["total_price"].typ, WuttaMoney) # store_id not exposed by default form = view.make_form(model_instance=batch) - self.assertIn('store_id', form) + self.assertIn("store_id", form) view.configure_form(form) - self.assertNotIn('store_id', form) + self.assertNotIn("store_id", form) # store_id is exposed if configured - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") form = view.make_form(model_instance=batch) - self.assertIn('store_id', form) + self.assertIn("store_id", form) view.configure_form(form) - self.assertIn('store_id', form) + self.assertIn("store_id", form) def test_configure_row_grid(self): model = self.app.model view = self.make_view() grid = view.make_grid(model_class=model.NewOrderBatchRow) - self.assertNotIn('total_price', grid.renderers) + self.assertNotIn("total_price", grid.renderers) view.configure_row_grid(grid) - self.assertIn('total_price', grid.renderers) + self.assertIn("total_price", grid.renderers) def test_get_xref_buttons(self): - self.pyramid_config.add_route('orders.view', '/orders/{uuid}') + self.pyramid_config.add_route("orders.view", "/orders/{uuid}") model = self.app.model enum = self.app.enum view = self.make_view() handler = view.batch_handler - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) # 1st batch has no order - batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) + batch = handler.make_batch( + self.session, pending_customer=customer, created_by=user + ) self.session.add(batch) self.session.flush() buttons = view.get_xref_buttons(batch) self.assertEqual(len(buttons), 0) # 2nd batch is executed; has order - batch = handler.make_batch(self.session, pending_customer=customer, created_by=user, - executed=datetime.datetime.now(), executed_by=user) + batch = handler.make_batch( + self.session, + pending_customer=customer, + created_by=user, + executed=datetime.datetime.now(), + executed_by=user, + ) self.session.add(batch) self.session.flush() order = model.Order(order_id=batch.id, created_by=user) self.session.add(order) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): # nb. this also requires perm - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): buttons = view.get_xref_buttons(batch) self.assertEqual(len(buttons), 1) diff --git a/tests/web/views/test_common.py b/tests/web/views/test_common.py index f00a314..b3ac82e 100644 --- a/tests/web/views/test_common.py +++ b/tests/web/views/test_common.py @@ -19,11 +19,11 @@ class TestCommonView(WebTestCase): model = self.app.model view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.flush() self.assertEqual(len(user.roles), 0) view.setup_enhance_admin_user(user) self.assertEqual(len(user.roles), 1) - self.assertEqual(user.roles[0].name, 'Order Admin') + self.assertEqual(user.roles[0].name, "Order Admin") diff --git a/tests/web/views/test_customers.py b/tests/web/views/test_customers.py index d68e48f..e5bd05d 100644 --- a/tests/web/views/test_customers.py +++ b/tests/web/views/test_customers.py @@ -25,43 +25,43 @@ class TestLocalCustomerView(WebTestCase): model = self.app.model view = self.make_view() grid = view.make_grid(model_class=model.LocalCustomer) - self.assertNotIn('full_name', grid.linked_columns) + self.assertNotIn("full_name", grid.linked_columns) view.configure_grid(grid) - self.assertIn('full_name', grid.linked_columns) + self.assertIn("full_name", grid.linked_columns) def test_configure_form(self): model = self.app.model view = self.make_view() # creating - with patch.object(view, 'creating', new=True): + with patch.object(view, "creating", new=True): form = view.make_form(model_class=model.LocalCustomer) view.configure_form(form) - self.assertNotIn('external_id', form) - self.assertNotIn('full_name', form) - self.assertNotIn('orders', form) - self.assertNotIn('new_order_batches', form) + self.assertNotIn("external_id", form) + self.assertNotIn("full_name", form) + self.assertNotIn("orders", form) + self.assertNotIn("new_order_batches", form) - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) customer = model.LocalCustomer() self.session.add(customer) self.session.commit() # viewing - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=customer) view.configure_form(form) - self.assertIn('external_id', form) - self.assertIn('full_name', form) - self.assertIn('orders', form) - self.assertIn('new_order_batches', form) + self.assertIn("external_id", form) + self.assertIn("full_name", form) + self.assertIn("orders", form) + self.assertIn("new_order_batches", form) def test_make_orders_grid(self): model = self.app.model view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) customer = model.LocalCustomer() self.session.add(customer) @@ -74,21 +74,23 @@ class TestLocalCustomerView(WebTestCase): self.assertEqual(len(grid.actions), 0) # with view perm - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): grid = view.make_orders_grid(customer) self.assertEqual(len(grid.actions), 1) - self.assertEqual(grid.actions[0].key, 'view') + self.assertEqual(grid.actions[0].key, "view") def test_make_new_order_batches_grid(self): model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) customer = model.LocalCustomer() self.session.add(customer) - batch = handler.make_batch(self.session, local_customer=customer, created_by=user) + batch = handler.make_batch( + self.session, local_customer=customer, created_by=user + ) self.session.add(batch) self.session.commit() @@ -97,31 +99,36 @@ class TestLocalCustomerView(WebTestCase): self.assertEqual(len(grid.actions), 0) # with view perm - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): grid = view.make_new_order_batches_grid(customer) self.assertEqual(len(grid.actions), 1) - self.assertEqual(grid.actions[0].key, 'view') + self.assertEqual(grid.actions[0].key, "view") def test_objectify(self): model = self.app.model view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() - with patch.object(view, 'creating', new=True): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "creating", new=True): + with patch.object(self.request, "user", new=user): form = view.make_model_form() - with patch.object(form, 'validated', create=True, new={ - 'first_name': 'Chuck', - 'last_name': 'Norris', - }): + with patch.object( + form, + "validated", + create=True, + new={ + "first_name": "Chuck", + "last_name": "Norris", + }, + ): customer = view.objectify(form) self.assertIsInstance(customer, model.LocalCustomer) - self.assertEqual(customer.first_name, 'Chuck') - self.assertEqual(customer.last_name, 'Norris') - self.assertEqual(customer.full_name, 'Chuck Norris') + self.assertEqual(customer.first_name, "Chuck") + self.assertEqual(customer.last_name, "Norris") + self.assertEqual(customer.full_name, "Chuck Norris") class TestPendingCustomerView(WebTestCase): @@ -135,7 +142,7 @@ class TestPendingCustomerView(WebTestCase): # nb. mostly just getting coverage here grid = view.make_grid(model_class=model.PendingCustomer) view.configure_grid(grid) - self.assertIn('full_name', grid.linked_columns) + self.assertIn("full_name", grid.linked_columns) def test_configure_form(self): model = self.app.model @@ -143,41 +150,43 @@ class TestPendingCustomerView(WebTestCase): view = self.make_view() # creating - with patch.object(view, 'creating', new=True): + with patch.object(view, "creating", new=True): form = view.make_form(model_class=model.PendingCustomer) view.configure_form(form) - self.assertNotIn('status', form) - self.assertNotIn('created', form) - self.assertNotIn('created_by', form) - self.assertNotIn('orders', form) - self.assertNotIn('new_order_batches', form) + self.assertNotIn("status", form) + self.assertNotIn("created", form) + self.assertNotIn("created_by", form) + self.assertNotIn("orders", form) + self.assertNotIn("new_order_batches", form) - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) self.session.commit() # viewing - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=customer) view.configure_form(form) - self.assertIn('status', form) - self.assertIn('created', form) - self.assertIn('created_by', form) - self.assertIn('orders', form) - self.assertIn('new_order_batches', form) + self.assertIn("status", form) + self.assertIn("created", form) + self.assertIn("created_by", form) + self.assertIn("orders", form) + self.assertIn("new_order_batches", form) def test_make_orders_grid(self): model = self.app.model enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) order = model.Order(order_id=42, pending_customer=customer, created_by=user) self.session.add(order) @@ -188,10 +197,10 @@ class TestPendingCustomerView(WebTestCase): self.assertEqual(len(grid.actions), 0) # with view perm - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): grid = view.make_orders_grid(customer) self.assertEqual(len(grid.actions), 1) - self.assertEqual(grid.actions[0].key, 'view') + self.assertEqual(grid.actions[0].key, "view") def test_make_new_order_batches_grid(self): model = self.app.model @@ -199,12 +208,15 @@ class TestPendingCustomerView(WebTestCase): handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) - batch = handler.make_batch(self.session, pending_customer=customer, created_by=user) + batch = handler.make_batch( + self.session, pending_customer=customer, created_by=user + ) self.session.add(batch) self.session.commit() @@ -213,44 +225,54 @@ class TestPendingCustomerView(WebTestCase): self.assertEqual(len(grid.actions), 0) # with view perm - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): grid = view.make_new_order_batches_grid(customer) self.assertEqual(len(grid.actions), 1) - self.assertEqual(grid.actions[0].key, 'view') + self.assertEqual(grid.actions[0].key, "view") def test_objectify(self): model = self.app.model enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() - with patch.object(view, 'creating', new=True): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "creating", new=True): + with patch.object(self.request, "user", new=user): form = view.make_model_form() - with patch.object(form, 'validated', create=True, new={ - 'full_name': "Fred Flinstone", - }): + with patch.object( + form, + "validated", + create=True, + new={ + "full_name": "Fred Flinstone", + }, + ): customer = view.objectify(form) self.assertIsInstance(customer, model.PendingCustomer) self.assertIs(customer.created_by, user) - self.assertEqual(customer.status, enum.PendingCustomerStatus.PENDING) + self.assertEqual( + customer.status, enum.PendingCustomerStatus.PENDING + ) def test_delete_instance(self): - self.pyramid_config.add_route('pending_customers.view', '/pending/customers/{uuid}') + self.pyramid_config.add_route( + "pending_customers.view", "/pending/customers/{uuid}" + ) model = self.app.model enum = self.app.enum handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # 1st customer is standalone, will be deleted - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) self.session.flush() self.assertEqual(self.session.query(model.PendingCustomer).count(), 1) @@ -259,10 +281,13 @@ class TestPendingCustomerView(WebTestCase): self.assertEqual(self.session.query(model.PendingCustomer).count(), 0) # 2nd customer is attached to new order batch, will not be deleted - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) - batch = handler.make_batch(self.session, created_by=user, pending_customer=customer) + batch = handler.make_batch( + self.session, created_by=user, pending_customer=customer + ) self.session.add(batch) self.session.flush() self.assertEqual(self.session.query(model.PendingCustomer).count(), 1) @@ -280,8 +305,9 @@ class TestPendingCustomerView(WebTestCase): self.assertEqual(self.session.query(model.PendingCustomer).count(), 0) # 3rd customer is attached to order, will not be deleted - customer = model.PendingCustomer(status=enum.PendingCustomerStatus.PENDING, - created_by=user) + customer = model.PendingCustomer( + status=enum.PendingCustomerStatus.PENDING, created_by=user + ) self.session.add(customer) order = model.Order(order_id=42, created_by=user, pending_customer=customer) self.session.add(order) diff --git a/tests/web/views/test_orders.py b/tests/web/views/test_orders.py index affb2d2..7e01411 100644 --- a/tests/web/views/test_orders.py +++ b/tests/web/views/test_orders.py @@ -43,42 +43,45 @@ class TestOrderView(WebTestCase): view = self.make_view() # store_id hidden by default - grid = view.make_grid(model_class=model.Order, columns=['store_id', 'order_id']) - self.assertIn('store_id', grid.columns) + grid = view.make_grid(model_class=model.Order, columns=["store_id", "order_id"]) + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertNotIn('store_id', grid.columns) + self.assertNotIn("store_id", grid.columns) # store_id is shown if configured - self.config.setdefault('sideshow.orders.expose_store_id', 'true') - grid = view.make_grid(model_class=model.Order, columns=['store_id', 'order_id']) - self.assertIn('store_id', grid.columns) + self.config.setdefault("sideshow.orders.expose_store_id", "true") + grid = view.make_grid(model_class=model.Order, columns=["store_id", "order_id"]) + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertIn('store_id', grid.columns) + self.assertIn("store_id", grid.columns) def test_create(self): - self.pyramid_config.include('sideshow.web.views') - self.config.setdefault('wutta.batch.neworder.handler.spec', - 'sideshow.batch.neworder:NewOrderBatchHandler') - self.config.setdefault('sideshow.orders.expose_store_id', 'true') - self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') + self.pyramid_config.include("sideshow.web.views") + self.config.setdefault( + "wutta.batch.neworder.handler.spec", + "sideshow.batch.neworder:NewOrderBatchHandler", + ) + self.config.setdefault("sideshow.orders.expose_store_id", "true") + self.config.setdefault("sideshow.orders.allow_item_discounts", "true") model = self.app.model enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - store = model.Store(store_id='001', name='Acme Goods') + store = model.Store(store_id="001", name="Acme Goods") self.session.add(store) - store = model.Store(store_id='002', name='Acme Services') + store = model.Store(store_id="002", name="Acme Services") self.session.add(store) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'current_route_url', return_value='/orders/new'): + with patch.object(view, "Session", return_value=self.session): + with patch.object( + self.request, "current_route_url", return_value="/orders/new" + ): # this will require some perms - with patch.multiple(self.request, create=True, - user=user, is_root=True): + with patch.multiple(self.request, create=True, user=user, is_root=True): # fetch page to start things off self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) @@ -87,13 +90,18 @@ class TestOrderView(WebTestCase): batch1 = self.session.query(model.NewOrderBatch).one() # start over; deletes current batch - with patch.multiple(self.request, create=True, - method='POST', - POST={'action': 'start_over'}): + with patch.multiple( + self.request, + create=True, + method="POST", + POST={"action": "start_over"}, + ): response = view.create() self.assertIsInstance(response, HTTPFound) - self.assertIn('/orders/new', response.location) - self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) + self.assertIn("/orders/new", response.location) + self.assertEqual( + self.session.query(model.NewOrderBatch).count(), 0 + ) # fetch again to get new batch response = view.create() @@ -102,73 +110,100 @@ class TestOrderView(WebTestCase): self.assertIsNot(batch2, batch1) # set pending customer - with patch.multiple(self.request, create=True, - method='POST', - json_body={'action': 'set_pending_customer', - 'first_name': 'Fred', - 'last_name': 'Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com'}): + with patch.multiple( + self.request, + create=True, + method="POST", + json_body={ + "action": "set_pending_customer", + "first_name": "Fred", + "last_name": "Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + }, + ): response = view.create() self.assertIsInstance(response, Response) - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json_body, { - 'store_id': None, - 'customer_is_known': False, - 'customer_id': None, - 'customer_name': 'Fred Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', - 'new_customer_full_name': 'Fred Flintstone', - 'new_customer_first_name': 'Fred', - 'new_customer_last_name': 'Flintstone', - 'new_customer_phone': '555-1234', - 'new_customer_email': 'fred@mailinator.com', - }) + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json_body, + { + "store_id": None, + "customer_is_known": False, + "customer_id": None, + "customer_name": "Fred Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + "new_customer_full_name": "Fred Flintstone", + "new_customer_first_name": "Fred", + "new_customer_last_name": "Flintstone", + "new_customer_phone": "555-1234", + "new_customer_email": "fred@mailinator.com", + }, + ) # invalid action - with patch.multiple(self.request, create=True, - method='POST', - POST={'action': 'bogus'}, - json_body={'action': 'bogus'}): + with patch.multiple( + self.request, + create=True, + method="POST", + POST={"action": "bogus"}, + json_body={"action": "bogus"}, + ): response = view.create() self.assertIsInstance(response, Response) - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json_body, {'error': 'unknown form action'}) + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json_body, {"error": "unknown form action"} + ) # add item - with patch.multiple(self.request, create=True, - method='POST', - json_body={'action': 'add_item', - 'product_info': { - 'scancode': '07430500132', - 'description': 'Vinegar', - 'unit_price_reg': 5.99, - }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_UNIT}): + with patch.multiple( + self.request, + create=True, + method="POST", + json_body={ + "action": "add_item", + "product_info": { + "scancode": "07430500132", + "description": "Vinegar", + "unit_price_reg": 5.99, + }, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_UNIT, + }, + ): response = view.create() self.assertIsInstance(response, Response) - self.assertEqual(response.content_type, 'application/json') + self.assertEqual(response.content_type, "application/json") data = response.json_body - self.assertEqual(sorted(data), ['batch', 'row']) + self.assertEqual(sorted(data), ["batch", "row"]) # add item, w/ error - with patch.object(NewOrderBatchHandler, 'add_item', side_effect=RuntimeError): - with patch.multiple(self.request, create=True, - method='POST', - json_body={'action': 'add_item', - 'product_info': { - 'scancode': '07430500116', - 'description': 'Vinegar', - 'unit_price_reg': 3.59, - }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_UNIT}): + with patch.object( + NewOrderBatchHandler, "add_item", side_effect=RuntimeError + ): + with patch.multiple( + self.request, + create=True, + method="POST", + json_body={ + "action": "add_item", + "product_info": { + "scancode": "07430500116", + "description": "Vinegar", + "unit_price_reg": 3.59, + }, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_UNIT, + }, + ): response = view.create() self.assertIsInstance(response, Response) - self.assertEqual(response.content_type, 'application/json') - self.assertEqual(response.json_body, {'error': 'RuntimeError'}) + self.assertEqual(response.content_type, "application/json") + self.assertEqual( + response.json_body, {"error": "RuntimeError"} + ) def test_get_current_batch(self): model = self.app.model @@ -178,13 +213,13 @@ class TestOrderView(WebTestCase): # user is required self.assertRaises(HTTPForbidden, view.get_current_batch) - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): # batch is auto-created self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) @@ -205,11 +240,11 @@ class TestOrderView(WebTestCase): view = self.make_view() view.batch_handler = handler - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): # empty results by default self.assertEqual(view.customer_autocomplete(), []) - with patch.object(self.request, 'GET', new={'term': 'foo'}, create=True): + with patch.object(self.request, "GET", new={"term": "foo"}, create=True): self.assertEqual(view.customer_autocomplete(), []) # add a customer @@ -218,22 +253,27 @@ class TestOrderView(WebTestCase): self.session.flush() # search for chuck finds chuck - with patch.object(self.request, 'GET', new={'term': 'chuck'}, create=True): + with patch.object(self.request, "GET", new={"term": "chuck"}, create=True): result = view.customer_autocomplete() self.assertEqual(len(result), 1) - self.assertEqual(result[0], { - 'value': customer.uuid.hex, - 'label': "Chuck Norris", - }) + self.assertEqual( + result[0], + { + "value": customer.uuid.hex, + "label": "Chuck Norris", + }, + ) # search for sally finds nothing - with patch.object(self.request, 'GET', new={'term': 'sally'}, create=True): + with patch.object(self.request, "GET", new={"term": "sally"}, create=True): result = view.customer_autocomplete() self.assertEqual(result, []) # external lookup not implemented by default - with patch.object(handler, 'use_local_customers', return_value=False): - with patch.object(self.request, 'GET', new={'term': 'sally'}, create=True): + with patch.object(handler, "use_local_customers", return_value=False): + with patch.object( + self.request, "GET", new={"term": "sally"}, create=True + ): self.assertRaises(NotImplementedError, view.customer_autocomplete) def test_product_autocomplete(self): @@ -242,11 +282,11 @@ class TestOrderView(WebTestCase): view = self.make_view() view.batch_handler = handler - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): # empty results by default self.assertEqual(view.product_autocomplete(), []) - with patch.object(self.request, 'GET', new={'term': 'foo'}, create=True): + with patch.object(self.request, "GET", new={"term": "foo"}, create=True): self.assertEqual(view.product_autocomplete(), []) # add a product @@ -255,31 +295,41 @@ class TestOrderView(WebTestCase): self.session.flush() # search for vinegar finds product - with patch.object(self.request, 'GET', new={'term': 'vinegar'}, create=True): + with patch.object( + self.request, "GET", new={"term": "vinegar"}, create=True + ): result = view.product_autocomplete() self.assertEqual(len(result), 1) - self.assertEqual(result[0], { - 'value': product.uuid.hex, - 'label': "Bragg's Vinegar", - }) + self.assertEqual( + result[0], + { + "value": product.uuid.hex, + "label": "Bragg's Vinegar", + }, + ) # search for brag finds product - with patch.object(self.request, 'GET', new={'term': 'brag'}, create=True): + with patch.object(self.request, "GET", new={"term": "brag"}, create=True): result = view.product_autocomplete() self.assertEqual(len(result), 1) - self.assertEqual(result[0], { - 'value': product.uuid.hex, - 'label': "Bragg's Vinegar", - }) + self.assertEqual( + result[0], + { + "value": product.uuid.hex, + "label": "Bragg's Vinegar", + }, + ) # search for juice finds nothing - with patch.object(self.request, 'GET', new={'term': 'juice'}, create=True): + with patch.object(self.request, "GET", new={"term": "juice"}, create=True): result = view.product_autocomplete() self.assertEqual(result, []) # external lookup not implemented by default - with patch.object(handler, 'use_local_products', return_value=False): - with patch.object(self.request, 'GET', new={'term': 'juice'}, create=True): + with patch.object(handler, "use_local_products", return_value=False): + with patch.object( + self.request, "GET", new={"term": "juice"}, create=True + ): self.assertRaises(NotImplementedError, view.product_autocomplete) def test_get_pending_product_required_fields(self): @@ -288,156 +338,220 @@ class TestOrderView(WebTestCase): # only description is required by default fields = view.get_pending_product_required_fields() - self.assertEqual(fields, ['description']) + self.assertEqual(fields, ["description"]) # but config can specify otherwise - self.config.setdefault('sideshow.orders.unknown_product.fields.brand_name.required', 'true') - self.config.setdefault('sideshow.orders.unknown_product.fields.description.required', 'false') - self.config.setdefault('sideshow.orders.unknown_product.fields.size.required', 'true') - self.config.setdefault('sideshow.orders.unknown_product.fields.unit_price_reg.required', 'true') + self.config.setdefault( + "sideshow.orders.unknown_product.fields.brand_name.required", "true" + ) + self.config.setdefault( + "sideshow.orders.unknown_product.fields.description.required", "false" + ) + self.config.setdefault( + "sideshow.orders.unknown_product.fields.size.required", "true" + ) + self.config.setdefault( + "sideshow.orders.unknown_product.fields.unit_price_reg.required", "true" + ) fields = view.get_pending_product_required_fields() - self.assertEqual(fields, ['brand_name', 'size', 'unit_price_reg']) + self.assertEqual(fields, ["brand_name", "size", "unit_price_reg"]) def test_get_dept_item_discounts(self): model = self.app.model view = self.make_view() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): # empty list by default discounts = view.get_dept_item_discounts() self.assertEqual(discounts, []) # mock settings - self.app.save_setting(self.session, 'sideshow.orders.departments.5.name', 'Bulk') - self.app.save_setting(self.session, 'sideshow.orders.departments.5.default_item_discount', '15') - self.app.save_setting(self.session, 'sideshow.orders.departments.6.name', 'Produce') - self.app.save_setting(self.session, 'sideshow.orders.departments.6.default_item_discount', '5') + self.app.save_setting( + self.session, "sideshow.orders.departments.5.name", "Bulk" + ) + self.app.save_setting( + self.session, + "sideshow.orders.departments.5.default_item_discount", + "15", + ) + self.app.save_setting( + self.session, "sideshow.orders.departments.6.name", "Produce" + ) + self.app.save_setting( + self.session, "sideshow.orders.departments.6.default_item_discount", "5" + ) discounts = view.get_dept_item_discounts() self.assertEqual(len(discounts), 2) - self.assertEqual(discounts[0], { - 'department_id': '5', - 'department_name': 'Bulk', - 'default_item_discount': '15', - }) - self.assertEqual(discounts[1], { - 'department_id': '6', - 'department_name': 'Produce', - 'default_item_discount': '5', - }) + self.assertEqual( + discounts[0], + { + "department_id": "5", + "department_name": "Bulk", + "default_item_discount": "15", + }, + ) + self.assertEqual( + discounts[1], + { + "department_id": "6", + "department_name": "Produce", + "default_item_discount": "5", + }, + ) # invalid setting - self.app.save_setting(self.session, 'sideshow.orders.departments.I.N.V.A.L.I.D.name', 'Bad News') - self.app.save_setting(self.session, 'sideshow.orders.departments.I.N.V.A.L.I.D.default_item_discount', '42') + self.app.save_setting( + self.session, + "sideshow.orders.departments.I.N.V.A.L.I.D.name", + "Bad News", + ) + self.app.save_setting( + self.session, + "sideshow.orders.departments.I.N.V.A.L.I.D.default_item_discount", + "42", + ) discounts = view.get_dept_item_discounts() self.assertEqual(len(discounts), 2) - self.assertEqual(discounts[0], { - 'department_id': '5', - 'department_name': 'Bulk', - 'default_item_discount': '15', - }) - self.assertEqual(discounts[1], { - 'department_id': '6', - 'department_name': 'Produce', - 'default_item_discount': '5', - }) + self.assertEqual( + discounts[0], + { + "department_id": "5", + "department_name": "Bulk", + "default_item_discount": "15", + }, + ) + self.assertEqual( + discounts[1], + { + "department_id": "6", + "department_name": "Produce", + "default_item_discount": "5", + }, + ) def test_get_context_customer(self): - self.pyramid_config.add_route('orders', '/orders/') + self.pyramid_config.add_route("orders", "/orders/") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() view.batch_handler = handler - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) # with external customer - with patch.object(handler, 'use_local_customers', return_value=False): - batch = handler.make_batch(self.session, created_by=user, - customer_id=42, customer_name='Fred Flintstone', - phone_number='555-1234', email_address='fred@mailinator.com') + with patch.object(handler, "use_local_customers", return_value=False): + batch = handler.make_batch( + self.session, + created_by=user, + customer_id=42, + customer_name="Fred Flintstone", + phone_number="555-1234", + email_address="fred@mailinator.com", + ) self.session.add(batch) self.session.flush() context = view.get_context_customer(batch) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': True, - 'customer_id': 42, - 'customer_name': 'Fred Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": True, + "customer_id": 42, + "customer_name": "Fred Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + }, + ) # with local customer local = model.LocalCustomer(full_name="Betty Boop") self.session.add(local) - batch = handler.make_batch(self.session, created_by=user, - local_customer=local, customer_name='Betty Boop', - phone_number='555-8888') + batch = handler.make_batch( + self.session, + created_by=user, + local_customer=local, + customer_name="Betty Boop", + phone_number="555-8888", + ) self.session.add(batch) self.session.flush() context = view.get_context_customer(batch) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': True, - 'customer_id': local.uuid.hex, - 'customer_name': 'Betty Boop', - 'phone_number': '555-8888', - 'email_address': None, - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": True, + "customer_id": local.uuid.hex, + "customer_name": "Betty Boop", + "phone_number": "555-8888", + "email_address": None, + }, + ) # with pending customer batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) - handler.set_customer(batch, dict( - full_name="Fred Flintstone", - first_name="Fred", last_name="Flintstone", - phone_number='555-1234', email_address='fred@mailinator.com', - )) + handler.set_customer( + batch, + dict( + full_name="Fred Flintstone", + first_name="Fred", + last_name="Flintstone", + phone_number="555-1234", + email_address="fred@mailinator.com", + ), + ) self.session.flush() context = view.get_context_customer(batch) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': False, - 'customer_id': None, - 'customer_name': 'Fred Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', - 'new_customer_full_name': 'Fred Flintstone', - 'new_customer_first_name': 'Fred', - 'new_customer_last_name': 'Flintstone', - 'new_customer_phone': '555-1234', - 'new_customer_email': 'fred@mailinator.com', - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": False, + "customer_id": None, + "customer_name": "Fred Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + "new_customer_full_name": "Fred Flintstone", + "new_customer_first_name": "Fred", + "new_customer_last_name": "Flintstone", + "new_customer_phone": "555-1234", + "new_customer_email": "fred@mailinator.com", + }, + ) # with no customer batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) self.session.flush() context = view.get_context_customer(batch) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': True, # nb. this is for UI default - 'customer_id': None, - 'customer_name': None, - 'phone_number': None, - 'email_address': None, - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": True, # nb. this is for UI default + "customer_id": None, + "customer_name": None, + "phone_number": None, + "email_address": None, + }, + ) def test_start_over(self): - self.pyramid_config.add_route('orders.create', '/orders/new') + self.pyramid_config.add_route("orders.create", "/orders/new") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() @@ -448,18 +562,18 @@ class TestOrderView(WebTestCase): self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) def test_cancel_order(self): - self.pyramid_config.add_route('orders', '/orders/') + self.pyramid_config.add_route("orders", "/orders/") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() @@ -474,125 +588,134 @@ class TestOrderView(WebTestCase): view = self.make_view() handler = NewOrderBatchHandler(self.config) - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.flush() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.assertIsNone(batch.store_id) # store_id is required result = view.set_store(batch, {}) - self.assertEqual(result, {'error': "Must provide store_id"}) - result = view.set_store(batch, {'store_id': ''}) - self.assertEqual(result, {'error': "Must provide store_id"}) + self.assertEqual(result, {"error": "Must provide store_id"}) + result = view.set_store(batch, {"store_id": ""}) + self.assertEqual(result, {"error": "Must provide store_id"}) # store_id is set on batch - result = view.set_store(batch, {'store_id': '042'}) - self.assertEqual(batch.store_id, '042') - self.assertIn('store_id', result) - self.assertEqual(result['store_id'], '042') + result = view.set_store(batch, {"store_id": "042"}) + self.assertEqual(batch.store_id, "042") + self.assertIn("store_id", result) + self.assertEqual(result["store_id"], "042") def test_assign_customer(self): - self.pyramid_config.add_route('orders.create', '/orders/new') + self.pyramid_config.add_route("orders.create", "/orders/new") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) weirdal = model.LocalCustomer(full_name="Weird Al") self.session.add(weirdal) self.session.flush() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() # normal self.assertIsNone(batch.local_customer) self.assertIsNone(batch.pending_customer) - context = view.assign_customer(batch, {'customer_id': weirdal.uuid.hex}) + context = view.assign_customer( + batch, {"customer_id": weirdal.uuid.hex} + ) self.assertIsNone(batch.pending_customer) self.assertIs(batch.local_customer, weirdal) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': True, - 'customer_id': weirdal.uuid.hex, - 'customer_name': 'Weird Al', - 'phone_number': None, - 'email_address': None, - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": True, + "customer_id": weirdal.uuid.hex, + "customer_name": "Weird Al", + "phone_number": None, + "email_address": None, + }, + ) # missing customer_id context = view.assign_customer(batch, {}) - self.assertEqual(context, {'error': "Must provide customer_id"}) + self.assertEqual(context, {"error": "Must provide customer_id"}) def test_unassign_customer(self): - self.pyramid_config.add_route('orders.create', '/orders/new') + self.pyramid_config.add_route("orders.create", "/orders/new") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.flush() - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() - view.set_pending_customer(batch, {'first_name': 'Jack', - 'last_name': 'Black'}) + view.set_pending_customer( + batch, {"first_name": "Jack", "last_name": "Black"} + ) # normal self.assertIsNone(batch.local_customer) self.assertIsNotNone(batch.pending_customer) - self.assertEqual(batch.customer_name, 'Jack Black') + self.assertEqual(batch.customer_name, "Jack Black") context = view.unassign_customer(batch, {}) # nb. pending record remains, but not used self.assertIsNotNone(batch.pending_customer) self.assertIsNone(batch.customer_name) self.assertIsNone(batch.local_customer) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': True, - 'customer_id': None, - 'customer_name': None, - 'phone_number': None, - 'email_address': None, - 'new_customer_full_name': 'Jack Black', - 'new_customer_first_name': 'Jack', - 'new_customer_last_name': 'Black', - 'new_customer_phone': None, - 'new_customer_email': None, - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": True, + "customer_id": None, + "customer_name": None, + "phone_number": None, + "email_address": None, + "new_customer_full_name": "Jack Black", + "new_customer_first_name": "Jack", + "new_customer_last_name": "Black", + "new_customer_phone": None, + "new_customer_email": None, + }, + ) def test_set_pending_customer(self): - self.pyramid_config.add_route('orders.create', '/orders/new') + self.pyramid_config.add_route("orders.create", "/orders/new") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() data = { - 'first_name': 'Fred', - 'last_name': 'Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', + "first_name": "Fred", + "last_name": "Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", } - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() @@ -600,72 +723,94 @@ class TestOrderView(WebTestCase): self.assertIsNone(batch.pending_customer) context = view.set_pending_customer(batch, data) self.assertIsInstance(batch.pending_customer, model.PendingCustomer) - self.assertEqual(context, { - 'store_id': None, - 'customer_is_known': False, - 'customer_id': None, - 'customer_name': 'Fred Flintstone', - 'phone_number': '555-1234', - 'email_address': 'fred@mailinator.com', - 'new_customer_full_name': 'Fred Flintstone', - 'new_customer_first_name': 'Fred', - 'new_customer_last_name': 'Flintstone', - 'new_customer_phone': '555-1234', - 'new_customer_email': 'fred@mailinator.com', - }) + self.assertEqual( + context, + { + "store_id": None, + "customer_is_known": False, + "customer_id": None, + "customer_name": "Fred Flintstone", + "phone_number": "555-1234", + "email_address": "fred@mailinator.com", + "new_customer_full_name": "Fred Flintstone", + "new_customer_first_name": "Fred", + "new_customer_last_name": "Flintstone", + "new_customer_phone": "555-1234", + "new_customer_email": "fred@mailinator.com", + }, + ) def test_get_product_info(self): model = self.app.model handler = self.make_handler() view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - local = model.LocalProduct(scancode='07430500132', - brand_name='Bragg', - description='Vinegar', - size='32oz', - case_size=12, - unit_price_reg=decimal.Decimal('5.99')) + local = model.LocalProduct( + scancode="07430500132", + brand_name="Bragg", + description="Vinegar", + size="32oz", + case_size=12, + unit_price_reg=decimal.Decimal("5.99"), + ) self.session.add(local) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "Session", return_value=self.session): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() # typical, for local product - context = view.get_product_info(batch, {'product_id': local.uuid.hex}) - self.assertEqual(context['product_id'], local.uuid.hex) - self.assertEqual(context['scancode'], '07430500132') - self.assertEqual(context['brand_name'], 'Bragg') - self.assertEqual(context['description'], 'Vinegar') - self.assertEqual(context['size'], '32oz') - self.assertEqual(context['full_description'], 'Bragg Vinegar 32oz') - self.assertEqual(context['case_size'], 12) - self.assertEqual(context['unit_price_reg'], 5.99) + context = view.get_product_info( + batch, {"product_id": local.uuid.hex} + ) + self.assertEqual(context["product_id"], local.uuid.hex) + self.assertEqual(context["scancode"], "07430500132") + self.assertEqual(context["brand_name"], "Bragg") + self.assertEqual(context["description"], "Vinegar") + self.assertEqual(context["size"], "32oz") + self.assertEqual(context["full_description"], "Bragg Vinegar 32oz") + self.assertEqual(context["case_size"], 12) + self.assertEqual(context["unit_price_reg"], 5.99) # error if no product_id context = view.get_product_info(batch, {}) - self.assertEqual(context, {'error': "Must specify a product ID"}) + self.assertEqual(context, {"error": "Must specify a product ID"}) # error if product not found mock_uuid = self.app.make_true_uuid() - self.assertRaises(ValueError, view.get_product_info, - batch, {'product_id': mock_uuid.hex}) + self.assertRaises( + ValueError, + view.get_product_info, + batch, + {"product_id": mock_uuid.hex}, + ) - with patch.object(handler, 'use_local_products', return_value=False): + with patch.object( + handler, "use_local_products", return_value=False + ): # external lookup not implemented by default - self.assertRaises(NotImplementedError, view.get_product_info, - batch, {'product_id': '42'}) + self.assertRaises( + NotImplementedError, + view.get_product_info, + batch, + {"product_id": "42"}, + ) # external lookup may return its own error - with patch.object(handler, 'get_product_info_external', - return_value={'error': "something smells fishy"}): - context = view.get_product_info(batch, {'product_id': '42'}) - self.assertEqual(context, {'error': "something smells fishy"}) + with patch.object( + handler, + "get_product_info_external", + return_value={"error": "something smells fishy"}, + ): + context = view.get_product_info(batch, {"product_id": "42"}) + self.assertEqual( + context, {"error": "something smells fishy"} + ) def test_get_past_products(self): model = self.app.model @@ -673,7 +818,7 @@ class TestOrderView(WebTestCase): view = self.make_view() handler = view.batch_handler - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) @@ -685,7 +830,7 @@ class TestOrderView(WebTestCase): self.assertRaises(ValueError, view.get_past_products, batch, {}) # empty history for customer - customer = model.LocalCustomer(full_name='Fred Flintstone') + customer = model.LocalCustomer(full_name="Fred Flintstone") batch.local_customer = customer self.session.flush() products = view.get_past_products(batch, {}) @@ -693,10 +838,18 @@ class TestOrderView(WebTestCase): # mock historical order order = model.Order(order_id=42, local_customer=customer, created_by=user) - product = model.LocalProduct(scancode='07430500132', description='Vinegar', - unit_price_reg=5.99, case_size=12) - item = model.OrderItem(local_product=product, order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_READY) + product = model.LocalProduct( + scancode="07430500132", + description="Vinegar", + unit_price_reg=5.99, + case_size=12, + ) + item = model.OrderItem( + local_product=product, + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_READY, + ) order.items.append(item) self.session.add(order) self.session.flush() @@ -705,86 +858,90 @@ class TestOrderView(WebTestCase): # that should now be returned products = view.get_past_products(batch, {}) self.assertEqual(len(products), 1) - self.assertEqual(products[0]['product_id'], product.uuid.hex) - self.assertEqual(products[0]['scancode'], '07430500132') - self.assertEqual(products[0]['description'], 'Vinegar') + self.assertEqual(products[0]["product_id"], product.uuid.hex) + self.assertEqual(products[0]["scancode"], "07430500132") + self.assertEqual(products[0]["description"], "Vinegar") # nb. this is a float, since result is JSON-safe - self.assertEqual(products[0]['case_price_quoted'], 71.88) - self.assertEqual(products[0]['case_price_quoted_display'], '$71.88') + self.assertEqual(products[0]["case_price_quoted"], 71.88) + self.assertEqual(products[0]["case_price_quoted_display"], "$71.88") def test_add_item(self): model = self.app.model enum = self.app.enum - self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') + self.config.setdefault("sideshow.orders.allow_item_discounts", "true") handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() data = { - 'product_info': { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, + "product_info": { + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_UNIT, - 'discount_percent': 10, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_UNIT, + "discount_percent": 10, } - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() self.assertEqual(len(batch.rows), 0) # normal pending product result = view.add_item(batch, data) - self.assertIn('batch', result) - self.assertIn('row', result) + self.assertIn("batch", result) + self.assertIn("row", result) self.session.flush() self.assertEqual(len(batch.rows), 1) row = batch.rows[0] self.assertIsInstance(row.pending_product, model.PendingProduct) # external product not yet supported - with patch.object(handler, 'use_local_products', return_value=False): - with patch.dict(data, product_info='42'): - self.assertRaises(NotImplementedError, view.add_item, batch, data) + with patch.object( + handler, "use_local_products", return_value=False + ): + with patch.dict(data, product_info="42"): + self.assertRaises( + NotImplementedError, view.add_item, batch, data + ) def test_update_item(self): model = self.app.model enum = self.app.enum - self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') + self.config.setdefault("sideshow.orders.allow_item_discounts", "true") handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() data = { - 'product_info': { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, - 'case_size': 12, + "product_info": { + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, + "case_size": 12, }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_CASE, - 'discount_percent': 15, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_CASE, + "discount_percent": 15, } - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() self.assertEqual(len(batch.rows), 0) @@ -794,16 +951,16 @@ class TestOrderView(WebTestCase): self.session.flush() row = batch.rows[0] self.assertIsInstance(row.pending_product, model.PendingProduct) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('5.99')) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("5.99")) # missing row uuid result = view.update_item(batch, data) - self.assertEqual(result, {'error': "Must specify row UUID"}) + self.assertEqual(result, {"error": "Must specify row UUID"}) # row not found with patch.dict(data, uuid=self.app.make_true_uuid()): result = view.update_item(batch, data) - self.assertEqual(result, {'error': "Row not found"}) + self.assertEqual(result, {"error": "Row not found"}) # row for wrong batch batch2 = handler.make_batch(self.session, created_by=user) @@ -813,29 +970,40 @@ class TestOrderView(WebTestCase): self.session.flush() with patch.dict(data, uuid=row2.uuid): result = view.update_item(batch, data) - self.assertEqual(result, {'error': "Row is for wrong batch"}) + self.assertEqual(result, {"error": "Row is for wrong batch"}) # true product not yet supported - with patch.object(handler, 'use_local_products', return_value=False): - self.assertRaises(NotImplementedError, view.update_item, batch, { - 'uuid': row.uuid, - 'product_info': '42', - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_UNIT, - }) + with patch.object( + handler, "use_local_products", return_value=False + ): + self.assertRaises( + NotImplementedError, + view.update_item, + batch, + { + "uuid": row.uuid, + "product_info": "42", + "order_qty": 1, + "order_uom": enum.ORDER_UOM_UNIT, + }, + ) # update row, pending product with patch.dict(data, uuid=row.uuid, order_qty=2): - with patch.dict(data['product_info'], scancode='07430500116'): - self.assertEqual(row.product_scancode, '07430500132') + with patch.dict(data["product_info"], scancode="07430500116"): + self.assertEqual(row.product_scancode, "07430500132") self.assertEqual(row.order_qty, 1) result = view.update_item(batch, data) - self.assertEqual(sorted(result), ['batch', 'row']) - self.assertEqual(row.product_scancode, '07430500116') + self.assertEqual(sorted(result), ["batch", "row"]) + self.assertEqual(row.product_scancode, "07430500116") self.assertEqual(row.order_qty, 2) - self.assertEqual(row.pending_product.scancode, '07430500116') - self.assertEqual(result['row']['product_scancode'], '07430500116') - self.assertEqual(result['row']['order_qty'], 2) + self.assertEqual( + row.pending_product.scancode, "07430500116" + ) + self.assertEqual( + result["row"]["product_scancode"], "07430500116" + ) + self.assertEqual(result["row"]["order_qty"], 2) def test_delete_item(self): model = self.app.model @@ -843,26 +1011,26 @@ class TestOrderView(WebTestCase): handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() data = { - 'product_info': { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, - 'case_size': 12, + "product_info": { + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, + "case_size": 12, }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_CASE, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_CASE, } - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.session.flush() self.assertEqual(len(batch.rows), 0) @@ -872,16 +1040,16 @@ class TestOrderView(WebTestCase): self.session.flush() row = batch.rows[0] self.assertIsInstance(row.pending_product, model.PendingProduct) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('5.99')) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("5.99")) # missing row uuid result = view.delete_item(batch, data) - self.assertEqual(result, {'error': "Must specify a row UUID"}) + self.assertEqual(result, {"error": "Must specify a row UUID"}) # row not found with patch.dict(data, uuid=self.app.make_true_uuid()): result = view.delete_item(batch, data) - self.assertEqual(result, {'error': "Row not found"}) + self.assertEqual(result, {"error": "Row not found"}) # row for wrong batch batch2 = handler.make_batch(self.session, created_by=user) @@ -891,45 +1059,45 @@ class TestOrderView(WebTestCase): self.session.flush() with patch.dict(data, uuid=row2.uuid): result = view.delete_item(batch, data) - self.assertEqual(result, {'error': "Row is for wrong batch"}) + self.assertEqual(result, {"error": "Row is for wrong batch"}) # row is deleted - data['uuid'] = row.uuid + data["uuid"] = row.uuid self.assertEqual(len(batch.rows), 1) self.assertEqual(batch.row_count, 1) result = view.delete_item(batch, data) - self.assertEqual(sorted(result), ['batch']) + self.assertEqual(sorted(result), ["batch"]) self.session.refresh(batch) self.assertEqual(len(batch.rows), 0) self.assertEqual(batch.row_count, 0) def test_submit_order(self): - self.pyramid_config.add_route('orders.view', '/orders/{uuid}') + self.pyramid_config.add_route("orders.view", "/orders/{uuid}") model = self.app.model enum = self.app.enum handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) self.session.commit() data = { - 'product_info': { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, - 'case_size': 12, + "product_info": { + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, + "case_size": 12, }, - 'order_qty': 1, - 'order_uom': enum.ORDER_UOM_CASE, + "order_qty": 1, + "order_uom": enum.ORDER_UOM_CASE, } - with patch.object(view, 'batch_handler', create=True, new=handler): - with patch.object(view, 'Session', return_value=self.session): - with patch.object(self.request, 'user', new=user): + with patch.object(view, "batch_handler", create=True, new=handler): + with patch.object(view, "Session", return_value=self.session): + with patch.object(self.request, "user", new=user): batch = view.get_current_batch() self.assertEqual(len(batch.rows), 0) @@ -938,29 +1106,35 @@ class TestOrderView(WebTestCase): self.assertEqual(len(batch.rows), 1) row = batch.rows[0] self.assertIsInstance(row.pending_product, model.PendingProduct) - self.assertEqual(row.unit_price_quoted, decimal.Decimal('5.99')) + self.assertEqual(row.unit_price_quoted, decimal.Decimal("5.99")) # execute not allowed yet (no customer) result = view.submit_order(batch, {}) - self.assertEqual(result, {'error': "Must assign the customer"}) + self.assertEqual(result, {"error": "Must assign the customer"}) # execute not allowed yet (no phone number) - view.set_pending_customer(batch, {'full_name': 'John Doe'}) + view.set_pending_customer(batch, {"full_name": "John Doe"}) result = view.submit_order(batch, {}) - self.assertEqual(result, {'error': "Customer phone number is required"}) + self.assertEqual( + result, {"error": "Customer phone number is required"} + ) # submit/execute ok - view.set_pending_customer(batch, {'full_name': 'John Doe', - 'phone_number': '555-1234'}) + view.set_pending_customer( + batch, {"full_name": "John Doe", "phone_number": "555-1234"} + ) result = view.submit_order(batch, {}) - self.assertEqual(sorted(result), ['next_url']) - self.assertIn('/orders/', result['next_url']) + self.assertEqual(sorted(result), ["next_url"]) + self.assertIn("/orders/", result["next_url"]) # error (already executed) result = view.submit_order(batch, {}) - self.assertEqual(result, { - 'error': f"ValueError: batch has already been executed: {batch}", - }) + self.assertEqual( + result, + { + "error": f"ValueError: batch has already been executed: {batch}", + }, + ) def test_normalize_batch(self): model = self.app.model @@ -968,29 +1142,32 @@ class TestOrderView(WebTestCase): handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) pending = { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, - 'case_size': 12, + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, + "case_size": 12, } row = handler.add_item(batch, pending, 1, enum.ORDER_UOM_CASE) self.session.commit() data = view.normalize_batch(batch) - self.assertEqual(data, { - 'uuid': batch.uuid.hex, - 'total_price': '71.880', - 'total_price_display': '$71.88', - 'status_code': None, - 'status_text': None, - }) + self.assertEqual( + data, + { + "uuid": batch.uuid.hex, + "total_price": "71.880", + "total_price_display": "$71.88", + "status_code": None, + "status_text": None, + }, + ) def test_normalize_row(self): model = self.app.model @@ -999,7 +1176,7 @@ class TestOrderView(WebTestCase): view = self.make_view() view.batch_handler = handler - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) batch = handler.make_batch(self.session, created_by=user) self.session.add(batch) @@ -1007,60 +1184,63 @@ class TestOrderView(WebTestCase): # add 1st row w/ pending product pending = { - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'unit_price_reg': 5.99, - 'case_size': 12, - 'vendor_name': 'Acme Warehouse', - 'vendor_item_code': '1234', + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "unit_price_reg": 5.99, + "case_size": 12, + "vendor_name": "Acme Warehouse", + "vendor_item_code": "1234", } row1 = handler.add_item(batch, pending, 2, enum.ORDER_UOM_CASE) # typical, pending product data = view.normalize_row(row1) self.assertIsInstance(data, dict) - self.assertEqual(data['uuid'], row1.uuid.hex) - self.assertEqual(data['sequence'], 1) - self.assertIsNone(data['product_id']) - self.assertEqual(data['product_scancode'], '07430500132') - self.assertEqual(data['product_full_description'], 'Bragg Vinegar 32oz') - self.assertEqual(data['case_size'], 12) - self.assertEqual(data['vendor_name'], 'Acme Warehouse') - self.assertEqual(data['order_qty'], 2) - self.assertEqual(data['order_uom'], 'CS') - self.assertEqual(data['order_qty_display'], '2 Cases (× 12 = 24 Units)') - self.assertEqual(data['unit_price_reg'], 5.99) - self.assertEqual(data['unit_price_reg_display'], '$5.99') - self.assertNotIn('unit_price_sale', data) - self.assertNotIn('unit_price_sale_display', data) - self.assertNotIn('sale_ends', data) - self.assertNotIn('sale_ends_display', data) - self.assertEqual(data['unit_price_quoted'], 5.99) - self.assertEqual(data['unit_price_quoted_display'], '$5.99') - self.assertEqual(data['case_price_quoted'], 71.88) - self.assertEqual(data['case_price_quoted_display'], '$71.88') - self.assertEqual(data['total_price'], 143.76) - self.assertEqual(data['total_price_display'], '$143.76') - self.assertIsNone(data['special_order']) - self.assertEqual(data['status_code'], row1.STATUS_OK) - self.assertEqual(data['pending_product'], { - 'uuid': row1.pending_product_uuid.hex, - 'scancode': '07430500132', - 'brand_name': 'Bragg', - 'description': 'Vinegar', - 'size': '32oz', - 'department_id': None, - 'department_name': None, - 'unit_price_reg': 5.99, - 'vendor_name': 'Acme Warehouse', - 'vendor_item_code': '1234', - 'unit_cost': None, - 'case_size': 12.0, - 'notes': None, - 'special_order': None, - }) + self.assertEqual(data["uuid"], row1.uuid.hex) + self.assertEqual(data["sequence"], 1) + self.assertIsNone(data["product_id"]) + self.assertEqual(data["product_scancode"], "07430500132") + self.assertEqual(data["product_full_description"], "Bragg Vinegar 32oz") + self.assertEqual(data["case_size"], 12) + self.assertEqual(data["vendor_name"], "Acme Warehouse") + self.assertEqual(data["order_qty"], 2) + self.assertEqual(data["order_uom"], "CS") + self.assertEqual(data["order_qty_display"], "2 Cases (× 12 = 24 Units)") + self.assertEqual(data["unit_price_reg"], 5.99) + self.assertEqual(data["unit_price_reg_display"], "$5.99") + self.assertNotIn("unit_price_sale", data) + self.assertNotIn("unit_price_sale_display", data) + self.assertNotIn("sale_ends", data) + self.assertNotIn("sale_ends_display", data) + self.assertEqual(data["unit_price_quoted"], 5.99) + self.assertEqual(data["unit_price_quoted_display"], "$5.99") + self.assertEqual(data["case_price_quoted"], 71.88) + self.assertEqual(data["case_price_quoted_display"], "$71.88") + self.assertEqual(data["total_price"], 143.76) + self.assertEqual(data["total_price_display"], "$143.76") + self.assertIsNone(data["special_order"]) + self.assertEqual(data["status_code"], row1.STATUS_OK) + self.assertEqual( + data["pending_product"], + { + "uuid": row1.pending_product_uuid.hex, + "scancode": "07430500132", + "brand_name": "Bragg", + "description": "Vinegar", + "size": "32oz", + "department_id": None, + "department_name": None, + "unit_price_reg": 5.99, + "vendor_name": "Acme Warehouse", + "vendor_item_code": "1234", + "unit_cost": None, + "case_size": 12.0, + "notes": None, + "special_order": None, + }, + ) # the next few tests will morph 1st row.. @@ -1069,126 +1249,132 @@ class TestOrderView(WebTestCase): handler.refresh_row(row1) self.session.flush() data = view.normalize_row(row1) - self.assertIsNone(data['case_size']) - self.assertEqual(data['order_qty_display'], '2 Cases (× ?? = ?? Units)') + self.assertIsNone(data["case_size"]) + self.assertEqual(data["order_qty_display"], "2 Cases (× ?? = ?? Units)") # order by unit row1.order_uom = enum.ORDER_UOM_UNIT handler.refresh_row(row1) self.session.flush() data = view.normalize_row(row1) - self.assertEqual(data['order_uom'], enum.ORDER_UOM_UNIT) - self.assertEqual(data['order_qty_display'], '2 Units') + self.assertEqual(data["order_uom"], enum.ORDER_UOM_UNIT) + self.assertEqual(data["order_qty_display"], "2 Units") # item on sale row1.pending_product.case_size = 12 - row1.unit_price_sale = decimal.Decimal('5.19') + row1.unit_price_sale = decimal.Decimal("5.19") row1.sale_ends = datetime.datetime(2099, 1, 5, 20, 32) handler.refresh_row(row1) self.session.flush() data = view.normalize_row(row1) - self.assertEqual(data['unit_price_sale'], 5.19) - self.assertEqual(data['unit_price_sale_display'], '$5.19') - self.assertEqual(data['sale_ends'], '2099-01-05 20:32:00') - self.assertEqual(data['sale_ends_display'], '2099-01-05') - self.assertEqual(data['unit_price_quoted'], 5.19) - self.assertEqual(data['unit_price_quoted_display'], '$5.19') - self.assertEqual(data['case_price_quoted'], 62.28) - self.assertEqual(data['case_price_quoted_display'], '$62.28') + self.assertEqual(data["unit_price_sale"], 5.19) + self.assertEqual(data["unit_price_sale_display"], "$5.19") + self.assertEqual(data["sale_ends"], "2099-01-05 20:32:00") + self.assertEqual(data["sale_ends_display"], "2099-01-05") + self.assertEqual(data["unit_price_quoted"], 5.19) + self.assertEqual(data["unit_price_quoted_display"], "$5.19") + self.assertEqual(data["case_price_quoted"], 62.28) + self.assertEqual(data["case_price_quoted_display"], "$62.28") # add 2nd row w/ local product - local = model.LocalProduct(brand_name="Lay's", - description="Potato Chips", - vendor_name='Acme Distribution', - unit_price_reg=3.29) + local = model.LocalProduct( + brand_name="Lay's", + description="Potato Chips", + vendor_name="Acme Distribution", + unit_price_reg=3.29, + ) self.session.add(local) self.session.flush() row2 = handler.add_item(batch, local.uuid.hex, 1, enum.ORDER_UOM_UNIT) # typical, local product data = view.normalize_row(row2) - self.assertEqual(data['uuid'], row2.uuid.hex) - self.assertEqual(data['sequence'], 2) - self.assertEqual(data['product_id'], local.uuid.hex) - self.assertIsNone(data['product_scancode']) - self.assertEqual(data['product_full_description'], "Lay's Potato Chips") - self.assertIsNone(data['case_size']) - self.assertEqual(data['vendor_name'], 'Acme Distribution') - self.assertEqual(data['order_qty'], 1) - self.assertEqual(data['order_uom'], 'EA') - self.assertEqual(data['order_qty_display'], '1 Units') - self.assertEqual(data['unit_price_reg'], 3.29) - self.assertEqual(data['unit_price_reg_display'], '$3.29') - self.assertNotIn('unit_price_sale', data) - self.assertNotIn('unit_price_sale_display', data) - self.assertNotIn('sale_ends', data) - self.assertNotIn('sale_ends_display', data) - self.assertEqual(data['unit_price_quoted'], 3.29) - self.assertEqual(data['unit_price_quoted_display'], '$3.29') - self.assertIsNone(data['case_price_quoted']) - self.assertEqual(data['case_price_quoted_display'], '') - self.assertEqual(data['total_price'], 3.29) - self.assertEqual(data['total_price_display'], '$3.29') - self.assertIsNone(data['special_order']) - self.assertEqual(data['status_code'], row2.STATUS_OK) - self.assertNotIn('pending_product', data) + self.assertEqual(data["uuid"], row2.uuid.hex) + self.assertEqual(data["sequence"], 2) + self.assertEqual(data["product_id"], local.uuid.hex) + self.assertIsNone(data["product_scancode"]) + self.assertEqual(data["product_full_description"], "Lay's Potato Chips") + self.assertIsNone(data["case_size"]) + self.assertEqual(data["vendor_name"], "Acme Distribution") + self.assertEqual(data["order_qty"], 1) + self.assertEqual(data["order_uom"], "EA") + self.assertEqual(data["order_qty_display"], "1 Units") + self.assertEqual(data["unit_price_reg"], 3.29) + self.assertEqual(data["unit_price_reg_display"], "$3.29") + self.assertNotIn("unit_price_sale", data) + self.assertNotIn("unit_price_sale_display", data) + self.assertNotIn("sale_ends", data) + self.assertNotIn("sale_ends_display", data) + self.assertEqual(data["unit_price_quoted"], 3.29) + self.assertEqual(data["unit_price_quoted_display"], "$3.29") + self.assertIsNone(data["case_price_quoted"]) + self.assertEqual(data["case_price_quoted_display"], "") + self.assertEqual(data["total_price"], 3.29) + self.assertEqual(data["total_price_display"], "$3.29") + self.assertIsNone(data["special_order"]) + self.assertEqual(data["status_code"], row2.STATUS_OK) + self.assertNotIn("pending_product", data) # the next few tests will morph 2nd row.. def refresh_external(row): - row.product_scancode = '012345' - row.product_brand = 'Acme' - row.product_description = 'Bricks' - row.product_size = '1 ton' + row.product_scancode = "012345" + row.product_brand = "Acme" + row.product_description = "Bricks" + row.product_size = "1 ton" row.product_weighed = True row.department_id = 1 row.department_name = "Bricks & Mortar" row.special_order = False - row.vendor_name = 'Acme Distributors' - row.vendor_item_code = '1234' + row.vendor_name = "Acme Distributors" + row.vendor_item_code = "1234" row.case_size = None - row.unit_cost = decimal.Decimal('599.99') - row.unit_price_reg = decimal.Decimal('999.99') + row.unit_cost = decimal.Decimal("599.99") + row.unit_price_reg = decimal.Decimal("999.99") # typical, external product - with patch.object(handler, 'use_local_products', return_value=False): - with patch.object(handler, 'refresh_row_from_external_product', new=refresh_external): - handler.update_item(row2, '42', 1, enum.ORDER_UOM_UNIT) + with patch.object(handler, "use_local_products", return_value=False): + with patch.object( + handler, "refresh_row_from_external_product", new=refresh_external + ): + handler.update_item(row2, "42", 1, enum.ORDER_UOM_UNIT) data = view.normalize_row(row2) - self.assertEqual(data['uuid'], row2.uuid.hex) - self.assertEqual(data['sequence'], 2) - self.assertEqual(data['product_id'], '42') - self.assertEqual(data['product_scancode'], '012345') - self.assertEqual(data['product_full_description'], 'Acme Bricks 1 ton') - self.assertIsNone(data['case_size']) - self.assertEqual(data['vendor_name'], 'Acme Distributors') - self.assertEqual(data['vendor_item_code'], '1234') - self.assertEqual(data['order_qty'], 1) - self.assertEqual(data['order_uom'], 'EA') - self.assertEqual(data['order_qty_display'], '1 Units') - self.assertEqual(data['unit_price_reg'], 999.99) - self.assertEqual(data['unit_price_reg_display'], '$999.99') - self.assertNotIn('unit_price_sale', data) - self.assertNotIn('unit_price_sale_display', data) - self.assertNotIn('sale_ends', data) - self.assertNotIn('sale_ends_display', data) - self.assertEqual(data['unit_price_quoted'], 999.99) - self.assertEqual(data['unit_price_quoted_display'], '$999.99') - self.assertIsNone(data['case_price_quoted']) - self.assertEqual(data['case_price_quoted_display'], '') - self.assertEqual(data['total_price'], 999.99) - self.assertEqual(data['total_price_display'], '$999.99') - self.assertFalse(data['special_order']) - self.assertEqual(data['status_code'], row2.STATUS_OK) - self.assertNotIn('pending_product', data) + self.assertEqual(data["uuid"], row2.uuid.hex) + self.assertEqual(data["sequence"], 2) + self.assertEqual(data["product_id"], "42") + self.assertEqual(data["product_scancode"], "012345") + self.assertEqual(data["product_full_description"], "Acme Bricks 1 ton") + self.assertIsNone(data["case_size"]) + self.assertEqual(data["vendor_name"], "Acme Distributors") + self.assertEqual(data["vendor_item_code"], "1234") + self.assertEqual(data["order_qty"], 1) + self.assertEqual(data["order_uom"], "EA") + self.assertEqual(data["order_qty_display"], "1 Units") + self.assertEqual(data["unit_price_reg"], 999.99) + self.assertEqual(data["unit_price_reg_display"], "$999.99") + self.assertNotIn("unit_price_sale", data) + self.assertNotIn("unit_price_sale_display", data) + self.assertNotIn("sale_ends", data) + self.assertNotIn("sale_ends_display", data) + self.assertEqual(data["unit_price_quoted"], 999.99) + self.assertEqual(data["unit_price_quoted_display"], "$999.99") + self.assertIsNone(data["case_price_quoted"]) + self.assertEqual(data["case_price_quoted_display"], "") + self.assertEqual(data["total_price"], 999.99) + self.assertEqual(data["total_price_display"], "$999.99") + self.assertFalse(data["special_order"]) + self.assertEqual(data["status_code"], row2.STATUS_OK) + self.assertNotIn("pending_product", data) def test_get_instance_title(self): model = self.app.model view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) - order = model.Order(order_id=42, customer_name="Fred Flintstone", created_by=user) + order = model.Order( + order_id=42, customer_name="Fred Flintstone", created_by=user + ) self.session.add(order) self.session.flush() @@ -1199,86 +1385,89 @@ class TestOrderView(WebTestCase): model = self.app.model view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) self.session.commit() # viewing (no customer) - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=order) # nb. this is to avoid include/exclude ambiguity - form.remove('items') + form.remove("items") # nb. store_id gets hidden by default - form.append('store_id') - self.assertIn('store_id', form) + form.append("store_id") + self.assertIn("store_id", form) view.configure_form(form) - self.assertNotIn('store_id', form) + self.assertNotIn("store_id", form) schema = form.get_schema() - self.assertIn('pending_customer', form) - self.assertIsInstance(schema['total_price'].typ, WuttaMoney) + self.assertIn("pending_customer", form) + self.assertIsInstance(schema["total_price"].typ, WuttaMoney) # assign local customer - local = model.LocalCustomer(first_name='Jack', last_name='Black', - phone_number='555-1234') + local = model.LocalCustomer( + first_name="Jack", last_name="Black", phone_number="555-1234" + ) self.session.add(local) self.session.flush() # nb. from now on we include store_id - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") # viewing (local customer) - with patch.object(view, 'viewing', new=True): - with patch.object(order, 'local_customer', new=local): + with patch.object(view, "viewing", new=True): + with patch.object(order, "local_customer", new=local): form = view.make_form(model_instance=order) # nb. this is to avoid include/exclude ambiguity - form.remove('items') + form.remove("items") # nb. store_id will now remain - form.append('store_id') - self.assertIn('store_id', form) + form.append("store_id") + self.assertIn("store_id", form) view.configure_form(form) - self.assertIn('store_id', form) - self.assertNotIn('pending_customer', form) + self.assertIn("store_id", form) + self.assertNotIn("pending_customer", form) schema = form.get_schema() - self.assertIsInstance(schema['total_price'].typ, WuttaMoney) + self.assertIsInstance(schema["total_price"].typ, WuttaMoney) # local customer is hidden if missing when customer_id is set - with patch.object(view, 'viewing', new=True): - with patch.object(order, 'customer_id', new='42'): + with patch.object(view, "viewing", new=True): + with patch.object(order, "customer_id", new="42"): form = view.make_form(model_instance=order) # nb. this is to avoid include/exclude ambiguity - form.remove('items') + form.remove("items") view.configure_form(form) - self.assertNotIn('local_customer', form) + self.assertNotIn("local_customer", form) def test_get_xref_buttons(self): - self.pyramid_config.add_route('neworder_batches.view', '/batch/neworder/{uuid}') + self.pyramid_config.add_route("neworder_batches.view", "/batch/neworder/{uuid}") model = self.app.model handler = NewOrderBatchHandler(self.config) view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): # nb. this requires perm to view batch - with patch.object(self.request, 'is_root', new=True): + with patch.object(self.request, "is_root", new=True): # order has no batch, so no buttons buttons = view.get_xref_buttons(order) self.assertEqual(buttons, []) # mock up a batch to get a button - batch = handler.make_batch(self.session, - id=order.order_id, - created_by=user, - executed=datetime.datetime.now(), - executed_by=user) + batch = handler.make_batch( + self.session, + id=order.order_id, + created_by=user, + executed=datetime.datetime.now(), + executed_by=user, + ) self.session.add(batch) self.session.flush() buttons = view.get_xref_buttons(order) @@ -1291,45 +1480,55 @@ class TestOrderView(WebTestCase): enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) self.session.flush() - order.items.append(model.OrderItem(product_id='07430500132', - product_scancode='07430500132', - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_INITIATED)) + order.items.append( + model.OrderItem( + product_id="07430500132", + product_scancode="07430500132", + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_INITIATED, + ) + ) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): query = view.get_row_grid_data(order) self.assertIsInstance(query, orm.Query) items = query.all() self.assertEqual(len(items), 1) - self.assertEqual(items[0].product_scancode, '07430500132') + self.assertEqual(items[0].product_scancode, "07430500132") def test_configure_row_grid(self): model = self.app.model enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) self.session.flush() - order.items.append(model.OrderItem(product_id='07430500132', - product_scancode='07430500132', - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_INITIATED)) + order.items.append( + model.OrderItem( + product_id="07430500132", + product_scancode="07430500132", + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_INITIATED, + ) + ) self.session.flush() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): grid = view.make_grid(model_class=model.OrderItem, data=order.items) - self.assertNotIn('product_scancode', grid.linked_columns) + self.assertNotIn("product_scancode", grid.linked_columns) view.configure_row_grid(grid) - self.assertIn('product_scancode', grid.linked_columns) + self.assertIn("product_scancode", grid.linked_columns) def test_row_grid_row_class(self): model = self.app.model @@ -1342,105 +1541,137 @@ class TestOrderView(WebTestCase): # warning item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_CANCELED) - self.assertEqual(view.row_grid_row_class(item, {}, 1), 'has-background-warning') + self.assertEqual(view.row_grid_row_class(item, {}, 1), "has-background-warning") def test_render_status_code(self): enum = self.app.enum view = self.make_view() result = view.render_status_code(None, None, enum.ORDER_ITEM_STATUS_INITIATED) self.assertEqual(result, "initiated") - self.assertEqual(result, enum.ORDER_ITEM_STATUS[enum.ORDER_ITEM_STATUS_INITIATED]) + self.assertEqual( + result, enum.ORDER_ITEM_STATUS[enum.ORDER_ITEM_STATUS_INITIATED] + ) def test_get_row_action_url_view(self): - self.pyramid_config.add_route('order_items.view', '/order-items/{uuid}') + self.pyramid_config.add_route("order_items.view", "/order-items/{uuid}") model = self.app.model enum = self.app.enum view = self.make_view() - user = model.User(username='barney') + user = model.User(username="barney") self.session.add(user) order = model.Order(order_id=42, created_by=user) self.session.add(order) self.session.flush() - item = model.OrderItem(product_id='07430500132', - product_scancode='07430500132', - order_qty=1, order_uom=enum.ORDER_UOM_UNIT, - status_code=enum.ORDER_ITEM_STATUS_INITIATED) + item = model.OrderItem( + product_id="07430500132", + product_scancode="07430500132", + order_qty=1, + order_uom=enum.ORDER_UOM_UNIT, + status_code=enum.ORDER_ITEM_STATUS_INITIATED, + ) order.items.append(item) self.session.flush() url = view.get_row_action_url_view(item, 0) - self.assertIn(f'/order-items/{item.uuid}', url) + self.assertIn(f"/order-items/{item.uuid}", url) def test_configure(self): - self.pyramid_config.add_route('home', '/') - self.pyramid_config.add_route('login', '/auth/login') - self.pyramid_config.add_route('orders', '/orders/') + self.pyramid_config.add_route("home", "/") + self.pyramid_config.add_route("login", "/auth/login") + self.pyramid_config.add_route("orders", "/orders/") model = self.app.model view = self.make_view() - self.app.save_setting(self.session, 'sideshow.orders.departments.5.name', 'Bulk') - self.app.save_setting(self.session, 'sideshow.orders.departments.5.default_item_discount', '15') - self.app.save_setting(self.session, 'sideshow.orders.departments.6.name', 'Produce') - self.app.save_setting(self.session, 'sideshow.orders.departments.6.default_item_discount', '5') + self.app.save_setting( + self.session, "sideshow.orders.departments.5.name", "Bulk" + ) + self.app.save_setting( + self.session, "sideshow.orders.departments.5.default_item_discount", "15" + ) + self.app.save_setting( + self.session, "sideshow.orders.departments.6.name", "Produce" + ) + self.app.save_setting( + self.session, "sideshow.orders.departments.6.default_item_discount", "5" + ) self.session.commit() - with patch.object(view, 'Session', return_value=self.session): + with patch.object(view, "Session", return_value=self.session): with patch.multiple(self.config, usedb=True, preferdb=True): # sanity check - allowed = self.config.get_bool('sideshow.orders.allow_unknown_products', - session=self.session) + allowed = self.config.get_bool( + "sideshow.orders.allow_unknown_products", session=self.session + ) self.assertIsNone(allowed) self.assertEqual(self.session.query(model.Setting).count(), 4) discounts = view.get_dept_item_discounts() self.assertEqual(len(discounts), 2) - self.assertEqual(discounts[0], { - 'department_id': '5', - 'department_name': 'Bulk', - 'default_item_discount': '15', - }) - self.assertEqual(discounts[1], { - 'department_id': '6', - 'department_name': 'Produce', - 'default_item_discount': '5', - }) + self.assertEqual( + discounts[0], + { + "department_id": "5", + "department_name": "Bulk", + "default_item_discount": "15", + }, + ) + self.assertEqual( + discounts[1], + { + "department_id": "6", + "department_name": "Produce", + "default_item_discount": "5", + }, + ) # fetch initial page response = view.configure() self.assertIsInstance(response, Response) self.assertNotIsInstance(response, HTTPFound) self.session.flush() - allowed = self.config.get_bool('sideshow.orders.allow_unknown_products', - session=self.session) + allowed = self.config.get_bool( + "sideshow.orders.allow_unknown_products", session=self.session + ) self.assertIsNone(allowed) self.assertEqual(self.session.query(model.Setting).count(), 4) # post new settings - with patch.multiple(self.request, create=True, - method='POST', - POST={ - 'sideshow.orders.allow_unknown_products': 'true', - 'dept_item_discounts': json.dumps([{ - 'department_id': '5', - 'department_name': 'Grocery', - 'default_item_discount': 10, - }]) - }): + with patch.multiple( + self.request, + create=True, + method="POST", + POST={ + "sideshow.orders.allow_unknown_products": "true", + "dept_item_discounts": json.dumps( + [ + { + "department_id": "5", + "department_name": "Grocery", + "default_item_discount": 10, + } + ] + ), + }, + ): response = view.configure() self.assertIsInstance(response, HTTPFound) self.session.flush() - allowed = self.config.get_bool('sideshow.orders.allow_unknown_products', - session=self.session) + allowed = self.config.get_bool( + "sideshow.orders.allow_unknown_products", session=self.session + ) self.assertTrue(allowed) self.assertTrue(self.session.query(model.Setting).count() > 1) discounts = view.get_dept_item_discounts() self.assertEqual(len(discounts), 1) - self.assertEqual(discounts[0], { - 'department_id': '5', - 'department_name': 'Grocery', - 'default_item_discount': '10', - }) + self.assertEqual( + discounts[0], + { + "department_id": "5", + "department_name": "Grocery", + "default_item_discount": "10", + }, + ) class OrderItemViewTestMixin: @@ -1448,9 +1679,8 @@ class OrderItemViewTestMixin: def test_common_get_fallback_templates(self): view = self.make_view() - templates = view.get_fallback_templates('view') - self.assertEqual(templates, ['/order-items/view.mako', - '/master/view.mako']) + templates = view.get_fallback_templates("view") + self.assertEqual(templates, ["/order-items/view.mako", "/master/view.mako"]) def test_common_get_query(self): view = self.make_view() @@ -1463,18 +1693,18 @@ class OrderItemViewTestMixin: # store_id is removed by default grid = view.make_grid(model_class=model.OrderItem) - grid.append('store_id') - self.assertIn('store_id', grid.columns) + grid.append("store_id") + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertNotIn('store_id', grid.columns) + self.assertNotIn("store_id", grid.columns) # store_id is shown if configured - self.config.setdefault('sideshow.orders.expose_store_id', 'true') + self.config.setdefault("sideshow.orders.expose_store_id", "true") grid = view.make_grid(model_class=model.OrderItem) - grid.append('store_id') - self.assertIn('store_id', grid.columns) + grid.append("store_id") + self.assertIn("store_id", grid.columns) view.configure_grid(grid) - self.assertIn('store_id', grid.columns) + self.assertIn("store_id", grid.columns) def test_common_render_order_attr(self): model = self.app.model @@ -1482,13 +1712,15 @@ class OrderItemViewTestMixin: order = model.Order(order_id=42) item = model.OrderItem() order.items.append(item) - self.assertEqual(view.render_order_attr(item, 'order_id', None), 42) + self.assertEqual(view.render_order_attr(item, "order_id", None), 42) def test_common_render_status_code(self): enum = self.app.enum view = self.make_view() - self.assertEqual(view.render_status_code(None, None, enum.ORDER_ITEM_STATUS_INITIATED), - 'initiated') + self.assertEqual( + view.render_status_code(None, None, enum.ORDER_ITEM_STATUS_INITIATED), + "initiated", + ) def test_common_grid_row_class(self): model = self.app.model @@ -1501,7 +1733,7 @@ class OrderItemViewTestMixin: # warning item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_CANCELED) - self.assertEqual(view.grid_row_class(item, {}, 1), 'has-background-warning') + self.assertEqual(view.grid_row_class(item, {}, 1), "has-background-warning") def test_common_configure_form(self): model = self.app.model @@ -1511,23 +1743,23 @@ class OrderItemViewTestMixin: item = model.OrderItem(status_code=enum.ORDER_ITEM_STATUS_INITIATED) # viewing, w/ pending product - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=item) view.configure_form(form) schema = form.get_schema() - self.assertIsInstance(schema['order'].typ, OrderRef) - self.assertIn('pending_product', form) - self.assertIsInstance(schema['pending_product'].typ, PendingProductRef) + self.assertIsInstance(schema["order"].typ, OrderRef) + self.assertIn("pending_product", form) + self.assertIsInstance(schema["pending_product"].typ, PendingProductRef) # viewing, w/ local product local = model.LocalProduct() item.local_product = local - with patch.object(view, 'viewing', new=True): + with patch.object(view, "viewing", new=True): form = view.make_form(model_instance=item) view.configure_form(form) schema = form.get_schema() - self.assertIsInstance(schema['order'].typ, OrderRef) - self.assertNotIn('pending_product', form) + self.assertIsInstance(schema["order"].typ, OrderRef) + self.assertNotIn("pending_product", form) def test_common_get_template_context(self): model = self.app.model @@ -1538,16 +1770,18 @@ class OrderItemViewTestMixin: item = model.OrderItem(order_qty=2, order_uom=enum.ORDER_UOM_CASE, case_size=8) order.items.append(item) - with patch.object(self.request, 'is_root', new=True): - with patch.object(view, 'viewing', new=True): + with patch.object(self.request, "is_root", new=True): + with patch.object(view, "viewing", new=True): form = view.make_model_form(model_instance=item) - context = view.get_template_context({'instance': item, 'form': form}) - self.assertIn('item', context) - self.assertIs(context['item'], item) - self.assertIn('order', context) - self.assertIs(context['order'], order) - self.assertIn('order_qty_uom_text', context) - self.assertEqual(context['order_qty_uom_text'], "2 Cases (× 8 = 16 Units)") + context = view.get_template_context({"instance": item, "form": form}) + self.assertIn("item", context) + self.assertIs(context["item"], item) + self.assertIn("order", context) + self.assertIs(context["order"], order) + self.assertIn("order_qty_uom_text", context) + self.assertEqual( + context["order_qty_uom_text"], "2 Cases (× 8 = 16 Units)" + ) def test_common_render_event_note(self): model = self.app.model @@ -1555,35 +1789,42 @@ class OrderItemViewTestMixin: view = self.make_view() # typical - event = model.OrderItemEvent(type_code=enum.ORDER_ITEM_EVENT_READY, note='testing') - result = view.render_event_note(event, 'note', 'testing') - self.assertEqual(result, 'testing') + event = model.OrderItemEvent( + type_code=enum.ORDER_ITEM_EVENT_READY, note="testing" + ) + result = view.render_event_note(event, "note", "testing") + self.assertEqual(result, "testing") # user note - event = model.OrderItemEvent(type_code=enum.ORDER_ITEM_EVENT_NOTE_ADDED, note='testing2') - result = view.render_event_note(event, 'note', 'testing2') - self.assertNotEqual(result, 'testing2') - self.assertIn('