Compare commits
	
		
			4 commits
		
	
	
		
			4bb4272341
			...
			edd1f17184
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| edd1f17184 | |||
| d8b37969c5 | |||
| 7ea83b2715 | |||
| f3cca2e370 | 
					 8 changed files with 135 additions and 2 deletions
				
			
		|  | @ -38,7 +38,8 @@ dependencies = [ | ||||||
| [project.optional-dependencies] | [project.optional-dependencies] | ||||||
| postgres = ["psycopg2"] | postgres = ["psycopg2"] | ||||||
| mysql = ["mysql-connector-python"] | mysql = ["mysql-connector-python"] | ||||||
| docs = ["Sphinx", "furo", "sphinxcontrib-programoutput", "enum-tools[sphinx]"] | # TODO: remove sphinx version cap after new sphinx-toolbox release? | ||||||
|  | docs = ["Sphinx<8.2", "furo", "sphinxcontrib-programoutput", "enum-tools[sphinx]"] | ||||||
| tests = ["pytest-cov", "tox"] | tests = ["pytest-cov", "tox"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -938,6 +938,8 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|         row.department_id = product.department_id |         row.department_id = product.department_id | ||||||
|         row.department_name = product.department_name |         row.department_name = product.department_name | ||||||
|         row.special_order = product.special_order |         row.special_order = product.special_order | ||||||
|  |         row.vendor_name = product.vendor_name | ||||||
|  |         row.vendor_item_code = product.vendor_item_code | ||||||
|         row.case_size = product.case_size |         row.case_size = product.case_size | ||||||
|         row.unit_cost = product.unit_cost |         row.unit_cost = product.unit_cost | ||||||
|         row.unit_price_reg = product.unit_price_reg |         row.unit_price_reg = product.unit_price_reg | ||||||
|  | @ -959,6 +961,8 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|         row.department_id = product.department_id |         row.department_id = product.department_id | ||||||
|         row.department_name = product.department_name |         row.department_name = product.department_name | ||||||
|         row.special_order = product.special_order |         row.special_order = product.special_order | ||||||
|  |         row.vendor_name = product.vendor_name | ||||||
|  |         row.vendor_item_code = product.vendor_item_code | ||||||
|         row.case_size = product.case_size |         row.case_size = product.case_size | ||||||
|         row.unit_cost = product.unit_cost |         row.unit_cost = product.unit_cost | ||||||
|         row.unit_price_reg = product.unit_price_reg |         row.unit_price_reg = product.unit_price_reg | ||||||
|  | @ -1017,8 +1021,14 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|     def why_not_execute(self, batch, **kwargs): |     def why_not_execute(self, batch, **kwargs): | ||||||
|         """ |         """ | ||||||
|         By default this checks to ensure the batch has a customer with |         By default this checks to ensure the batch has a customer with | ||||||
|         phone number, and at least one item. |         phone number, and at least one item.  It also may check to | ||||||
|  |         ensure the store is assigned, if applicable. | ||||||
|         """ |         """ | ||||||
|  |         if not batch.store_id: | ||||||
|  |             order_handler = self.app.get_order_handler() | ||||||
|  |             if order_handler.expose_store_id(): | ||||||
|  |                 return "Must assign the store" | ||||||
|  | 
 | ||||||
|         if not batch.customer_name: |         if not batch.customer_name: | ||||||
|             return "Must assign the customer" |             return "Must assign the customer" | ||||||
| 
 | 
 | ||||||
|  | @ -1190,6 +1200,8 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|             'product_weighed', |             'product_weighed', | ||||||
|             'department_id', |             'department_id', | ||||||
|             'department_name', |             'department_name', | ||||||
|  |             'vendor_name', | ||||||
|  |             'vendor_item_code', | ||||||
|             'case_size', |             'case_size', | ||||||
|             'order_qty', |             'order_qty', | ||||||
|             'order_uom', |             'order_uom', | ||||||
|  |  | ||||||
|  | @ -0,0 +1,41 @@ | ||||||
|  | """add order_item.vendor* | ||||||
|  | 
 | ||||||
|  | Revision ID: 13af2ffbc0e0 | ||||||
|  | Revises: a4273360d379 | ||||||
|  | Create Date: 2025-02-19 19:36:30.308840 | ||||||
|  | 
 | ||||||
|  | """ | ||||||
|  | from typing import Sequence, Union | ||||||
|  | 
 | ||||||
|  | from alembic import op | ||||||
|  | import sqlalchemy as sa | ||||||
|  | import wuttjamaican.db.util | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # revision identifiers, used by Alembic. | ||||||
|  | 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 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 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)) | ||||||
|  | 
 | ||||||
|  |     # 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)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def downgrade() -> None: | ||||||
|  | 
 | ||||||
|  |     # sideshow_order_item | ||||||
|  |     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') | ||||||
|  | @ -252,6 +252,16 @@ class NewOrderBatchRow(model.BatchRowMixin, model.Base): | ||||||
|     normally carried by the store.  Default is null. |     normally carried by the store.  Default is null. | ||||||
|     """) |     """) | ||||||
| 
 | 
 | ||||||
|  |     vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" | ||||||
|  |     Name of vendor from which product may be purchased, if known.  See | ||||||
|  |     also :attr:`vendor_item_code`. | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|  |     vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" | ||||||
|  |     Item code (SKU) to use when ordering this product from the vendor | ||||||
|  |     identified by :attr:`vendor_name`, if known. | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|     case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" |     case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" | ||||||
|     Case pack count for the product, if known. |     Case pack count for the product, if known. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -253,6 +253,16 @@ class OrderItem(model.Base): | ||||||
|     normally carried by the store.  Default is null. |     normally carried by the store.  Default is null. | ||||||
|     """) |     """) | ||||||
| 
 | 
 | ||||||
|  |     vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" | ||||||
|  |     Name of vendor from which product may be purchased, if known.  See | ||||||
|  |     also :attr:`vendor_item_code`. | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|  |     vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" | ||||||
|  |     Item code (SKU) to use when ordering this product from the vendor | ||||||
|  |     identified by :attr:`vendor_name`, if known. | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|     case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" |     case_size = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc=""" | ||||||
|     Case pack count for the product, if known. |     Case pack count for the product, if known. | ||||||
|     """) |     """) | ||||||
|  |  | ||||||
|  | @ -243,6 +243,12 @@ | ||||||
|             <b-field horizontal label="Special Order"> |             <b-field horizontal label="Special Order"> | ||||||
|               <span>${app.render_boolean(item.special_order)}</span> |               <span>${app.render_boolean(item.special_order)}</span> | ||||||
|             </b-field> |             </b-field> | ||||||
|  |             <b-field horizontal label="Vendor Name"> | ||||||
|  |               <span>${item.vendor_name}</span> | ||||||
|  |             </b-field> | ||||||
|  |             <b-field horizontal label="Vendor Item Code"> | ||||||
|  |               <span>${item.vendor_item_code}</span> | ||||||
|  |             </b-field> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </nav> |       </nav> | ||||||
|  |  | ||||||
|  | @ -1239,6 +1239,9 @@ class OrderItemView(MasterView): | ||||||
| 
 | 
 | ||||||
|         # customer_name |         # customer_name | ||||||
|         g.set_label('customer_name', "Customer", column_only=True) |         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 |         # # sequence | ||||||
|         # g.set_label('sequence', "Seq.", column_only=True) |         # g.set_label('sequence', "Seq.", column_only=True) | ||||||
|  | @ -1562,6 +1565,26 @@ class PlacementView(OrderItemView): | ||||||
|     route_prefix = 'order_items_placement' |     route_prefix = 'order_items_placement' | ||||||
|     url_prefix = '/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', | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     filter_defaults = { | ||||||
|  |         'vendor_name': {'active': True}, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     def get_query(self, session=None): |     def get_query(self, session=None): | ||||||
|         """ """ |         """ """ | ||||||
|         query = super().get_query(session=session) |         query = super().get_query(session=session) | ||||||
|  | @ -1664,6 +1687,26 @@ class ReceivingView(OrderItemView): | ||||||
|     route_prefix = 'order_items_receiving' |     route_prefix = 'order_items_receiving' | ||||||
|     url_prefix = '/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', | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     filter_defaults = { | ||||||
|  |         'vendor_name': {'active': True}, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     def get_query(self, session=None): |     def get_query(self, session=None): | ||||||
|         """ """ |         """ """ | ||||||
|         query = super().get_query(session=session) |         query = super().get_query(session=session) | ||||||
|  |  | ||||||
|  | @ -818,6 +818,8 @@ class TestNewOrderBatchHandler(DataTestCase): | ||||||
|                                        brand_name='Bragg', |                                        brand_name='Bragg', | ||||||
|                                        description='Vinegar', |                                        description='Vinegar', | ||||||
|                                        size='32oz', |                                        size='32oz', | ||||||
|  |                                        vendor_name='Acme Distributors', | ||||||
|  |                                        vendor_item_code='1234', | ||||||
|                                        created_by=user, |                                        created_by=user, | ||||||
|                                        status=enum.PendingProductStatus.PENDING) |                                        status=enum.PendingProductStatus.PENDING) | ||||||
|         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) | ||||||
|  | @ -830,6 +832,8 @@ class TestNewOrderBatchHandler(DataTestCase): | ||||||
|         self.assertEqual(row.product_brand, 'Bragg') |         self.assertEqual(row.product_brand, 'Bragg') | ||||||
|         self.assertEqual(row.product_description, 'Vinegar') |         self.assertEqual(row.product_description, 'Vinegar') | ||||||
|         self.assertEqual(row.product_size, '32oz') |         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.case_size) | ||||||
|         self.assertIsNone(row.unit_cost) |         self.assertIsNone(row.unit_cost) | ||||||
|         self.assertIsNone(row.unit_price_reg) |         self.assertIsNone(row.unit_price_reg) | ||||||
|  | @ -1114,9 +1118,15 @@ class TestNewOrderBatchHandler(DataTestCase): | ||||||
|         self.session.add(row) |         self.session.add(row) | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
| 
 | 
 | ||||||
|  |         # batch is okay to execute.. | ||||||
|         reason = handler.why_not_execute(batch) |         reason = handler.why_not_execute(batch) | ||||||
|         self.assertIsNone(reason) |         self.assertIsNone(reason) | ||||||
| 
 | 
 | ||||||
|  |         # unless we also require store | ||||||
|  |         self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|  |         reason = handler.why_not_execute(batch) | ||||||
|  |         self.assertEqual(reason, "Must assign the store") | ||||||
|  | 
 | ||||||
|     def test_make_local_customer(self): |     def test_make_local_customer(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         enum = self.app.enum |         enum = self.app.enum | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue