feat: add config option to show/hide Store ID; default value
This commit is contained in:
		
							parent
							
								
									3ef84ff706
								
							
						
					
					
						commit
						89e3445ace
					
				
					 19 changed files with 445 additions and 64 deletions
				
			
		
							
								
								
									
										6
									
								
								docs/api/sideshow.app.rst
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/api/sideshow.app.rst
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | 
 | ||||||
|  | ``sideshow.app`` | ||||||
|  | ================ | ||||||
|  | 
 | ||||||
|  | .. automodule:: sideshow.app | ||||||
|  |    :members: | ||||||
|  | @ -30,6 +30,7 @@ For an online demo see https://demo.wuttaproject.org/ | ||||||
|    :caption: Package API: |    :caption: Package API: | ||||||
| 
 | 
 | ||||||
|    api/sideshow |    api/sideshow | ||||||
|  |    api/sideshow.app | ||||||
|    api/sideshow.batch |    api/sideshow.batch | ||||||
|    api/sideshow.batch.neworder |    api/sideshow.batch.neworder | ||||||
|    api/sideshow.cli |    api/sideshow.cli | ||||||
|  |  | ||||||
|  | @ -51,6 +51,9 @@ sideshow_libcache = "sideshow.web.static:libcache" | ||||||
| [project.entry-points."paste.app_factory"] | [project.entry-points."paste.app_factory"] | ||||||
| "main" = "sideshow.web.app:main" | "main" = "sideshow.web.app:main" | ||||||
| 
 | 
 | ||||||
|  | [project.entry-points."wutta.app.providers"] | ||||||
|  | sideshow = "sideshow.app:SideshowAppProvider" | ||||||
|  | 
 | ||||||
| [project.entry-points."wutta.batch.neworder"] | [project.entry-points."wutta.batch.neworder"] | ||||||
| "sideshow" = "sideshow.batch.neworder:NewOrderBatchHandler" | "sideshow" = "sideshow.batch.neworder:NewOrderBatchHandler" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										56
									
								
								src/sideshow/app.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/sideshow/app.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | # -*- coding: utf-8; -*- | ||||||
|  | ################################################################################ | ||||||
|  | # | ||||||
|  | #  Sideshow -- Case/Special Order Tracker | ||||||
|  | #  Copyright © 2024 Lance Edgar | ||||||
|  | # | ||||||
|  | #  This file is part of Sideshow. | ||||||
|  | # | ||||||
|  | #  Sideshow is free software: you can redistribute it and/or modify it | ||||||
|  | #  under the terms of the GNU General Public License as published by | ||||||
|  | #  the Free Software Foundation, either version 3 of the License, or | ||||||
|  | #  (at your option) any later version. | ||||||
|  | # | ||||||
|  | #  Sideshow is distributed in the hope that it will be useful, but | ||||||
|  | #  WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  | #  General Public License for more details. | ||||||
|  | # | ||||||
|  | #  You should have received a copy of the GNU General Public License | ||||||
|  | #  along with Sideshow.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | # | ||||||
|  | ################################################################################ | ||||||
|  | """ | ||||||
|  | Sideshow app provider | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | from wuttjamaican import app as base | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SideshowAppProvider(base.AppProvider): | ||||||
|  |     """ | ||||||
|  |     The :term:`app provider` for Sideshow. | ||||||
|  | 
 | ||||||
|  |     This adds the :meth:`get_order_handler()` method to the :term:`app | ||||||
|  |     handler`. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def get_order_handler(self, **kwargs): | ||||||
|  |         """ | ||||||
|  |         Get the configured :term:`order handler` for the app. | ||||||
|  | 
 | ||||||
|  |         You can specify a custom handler in your :term:`config file` | ||||||
|  |         like: | ||||||
|  | 
 | ||||||
|  |         .. code-block:: ini | ||||||
|  | 
 | ||||||
|  |            [sideshow] | ||||||
|  |            orders.handler_spec = poser.orders:PoserOrderHandler | ||||||
|  | 
 | ||||||
|  |         :returns: Instance of :class:`~sideshow.orders.OrderHandler`. | ||||||
|  |         """ | ||||||
|  |         if 'order_handler' not in self.__dict__: | ||||||
|  |             spec = self.config.get('sideshow.orders.handler_spec', | ||||||
|  |                                    default='sideshow.orders:OrderHandler') | ||||||
|  |             self.order_handler = self.app.load_object(spec)(self.config) | ||||||
|  |         return self.order_handler | ||||||
|  | @ -50,6 +50,14 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|     """ |     """ | ||||||
|     model_class = NewOrderBatch |     model_class = NewOrderBatch | ||||||
| 
 | 
 | ||||||
|  |     def get_default_store_id(self): | ||||||
|  |         """ | ||||||
|  |         Returns the configured default value for | ||||||
|  |         :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`, | ||||||
|  |         or ``None``. | ||||||
|  |         """ | ||||||
|  |         return self.config.get('sideshow.orders.default_store_id') | ||||||
|  | 
 | ||||||
|     def use_local_customers(self): |     def use_local_customers(self): | ||||||
|         """ |         """ | ||||||
|         Returns boolean indicating whether :term:`local customer` |         Returns boolean indicating whether :term:`local customer` | ||||||
|  | @ -165,6 +173,18 @@ class NewOrderBatchHandler(BatchHandler): | ||||||
|                     'label': customer.full_name} |                     'label': customer.full_name} | ||||||
|         return [result(c) for c in customers] |         return [result(c) for c in customers] | ||||||
| 
 | 
 | ||||||
|  |     def init_batch(self, batch, session=None, progress=None, **kwargs): | ||||||
|  |         """ | ||||||
|  |         Initialize a new batch. | ||||||
|  | 
 | ||||||
|  |         This sets the | ||||||
|  |         :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id`, | ||||||
|  |         if the batch does not yet have one and a default is | ||||||
|  |         configured. | ||||||
|  |         """ | ||||||
|  |         if not batch.store_id: | ||||||
|  |             batch.store_id = self.get_default_store_id() | ||||||
|  | 
 | ||||||
|     def set_customer(self, batch, customer_info, user=None): |     def set_customer(self, batch, customer_info, user=None): | ||||||
|         """ |         """ | ||||||
|         Set/update customer info for the batch. |         Set/update customer info for the batch. | ||||||
|  |  | ||||||
|  | @ -62,6 +62,15 @@ class Order(model.Base): | ||||||
|     ID of the store to which the order pertains, if applicable. |     ID of the store to which the order pertains, if applicable. | ||||||
|     """) |     """) | ||||||
| 
 | 
 | ||||||
|  |     store = orm.relationship( | ||||||
|  |         'Store', | ||||||
|  |         primaryjoin='Store.store_id == Order.store_id', | ||||||
|  |         foreign_keys='Order.store_id', | ||||||
|  |         doc=""" | ||||||
|  |         Reference to the :class:`~sideshow.db.model.stores.Store` | ||||||
|  |         record, if applicable. | ||||||
|  |         """) | ||||||
|  | 
 | ||||||
|     customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" |     customer_id = sa.Column(sa.String(length=20), nullable=True, doc=""" | ||||||
|     Proper account ID for the :term:`external customer` to which the |     Proper account ID for the :term:`external customer` to which the | ||||||
|     order pertains, if applicable. |     order pertains, if applicable. | ||||||
|  |  | ||||||
|  | @ -51,4 +51,12 @@ class Store(model.Base): | ||||||
|     """) |     """) | ||||||
| 
 | 
 | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return self.name or "" |         return self.get_display() | ||||||
|  | 
 | ||||||
|  |     def get_display(self): | ||||||
|  |         """ | ||||||
|  |         Returns the display string for the store, e.g. "001 Acme Goods". | ||||||
|  |         """ | ||||||
|  |         return ' '.join([(self.store_id or '').strip(), | ||||||
|  |                          (self.name or '').strip()])\ | ||||||
|  |                   .strip() | ||||||
|  |  | ||||||
|  | @ -37,6 +37,14 @@ class OrderHandler(GenericHandler): | ||||||
|     handler is responsible for creation logic.) |     handler is responsible for creation logic.) | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|  |     def expose_store_id(self): | ||||||
|  |         """ | ||||||
|  |         Returns boolean indicating whether the ``store_id`` field | ||||||
|  |         should be exposed at all.  This is false by default. | ||||||
|  |         """ | ||||||
|  |         return self.config.get_bool('sideshow.orders.expose_store_id', | ||||||
|  |                                     default=False) | ||||||
|  | 
 | ||||||
|     def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False): |     def get_order_qty_uom_text(self, order_qty, order_uom, case_size=None, html=False): | ||||||
|         """ |         """ | ||||||
|         Return the display text for a given order quantity. |         Return the display text for a given order quantity. | ||||||
|  |  | ||||||
|  | @ -29,6 +29,17 @@ | ||||||
|             <b-field horizontal label="ID"> |             <b-field horizontal label="ID"> | ||||||
|               <span>${h.link_to(f"Order ID {order.order_id}", url('orders.view', uuid=order.uuid))} — Item #${item.sequence}</span> |               <span>${h.link_to(f"Order ID {order.order_id}", url('orders.view', uuid=order.uuid))} — Item #${item.sequence}</span> | ||||||
|             </b-field> |             </b-field> | ||||||
|  |             % if expose_store_id: | ||||||
|  |                 <b-field horizontal label="Store"> | ||||||
|  |                   <span> | ||||||
|  |                     % if order.store: | ||||||
|  |                         ${h.link_to(order.store.get_display(), url('stores.view', uuid=order.store.uuid))} | ||||||
|  |                     % elif order.store_id: | ||||||
|  |                         ${order.store_id} | ||||||
|  |                     % endif | ||||||
|  |                   </span> | ||||||
|  |                 </b-field> | ||||||
|  |             % endif | ||||||
|             <b-field horizontal label="Order Qty"> |             <b-field horizontal label="Order Qty"> | ||||||
|               <span>${order_qty_uom_text|n}</span> |               <span>${order_qty_uom_text|n}</span> | ||||||
|             </b-field> |             </b-field> | ||||||
|  |  | ||||||
|  | @ -3,6 +3,28 @@ | ||||||
| 
 | 
 | ||||||
| <%def name="form_content()"> | <%def name="form_content()"> | ||||||
| 
 | 
 | ||||||
|  |   <h3 class="block is-size-3">Stores</h3> | ||||||
|  |   <div class="block" style="padding-left: 2rem;"> | ||||||
|  | 
 | ||||||
|  |     <b-field> | ||||||
|  |       <b-checkbox name="sideshow.orders.expose_store_id" | ||||||
|  |                   v-model="simpleSettings['sideshow.orders.expose_store_id']" | ||||||
|  |                   native-value="true" | ||||||
|  |                   @input="settingsNeedSaved = true"> | ||||||
|  |         Show/choose the Store ID for each order | ||||||
|  |       </b-checkbox> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |     <b-field v-show="simpleSettings['sideshow.orders.expose_store_id']" | ||||||
|  |              label="Default Store ID"> | ||||||
|  |       <b-input name="sideshow.orders.default_store_id" | ||||||
|  |                v-model="simpleSettings['sideshow.orders.default_store_id']" | ||||||
|  |                @input="settingsNeedSaved = true" | ||||||
|  |                style="width: 25rem;" /> | ||||||
|  |     </b-field> | ||||||
|  | 
 | ||||||
|  |   </div> | ||||||
|  | 
 | ||||||
|   <h3 class="block is-size-3">Customers</h3> |   <h3 class="block is-size-3">Customers</h3> | ||||||
|   <div class="block" style="padding-left: 2rem;"> |   <div class="block" style="padding-left: 2rem;"> | ||||||
| 
 | 
 | ||||||
|  | @ -14,6 +36,7 @@ | ||||||
|         <option value="false">External Customers (e.g. in POS)</option> |         <option value="false">External Customers (e.g. in POS)</option> | ||||||
|       </b-select> |       </b-select> | ||||||
|     </b-field> |     </b-field> | ||||||
|  | 
 | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <h3 class="block is-size-3">Products</h3> |   <h3 class="block is-size-3">Products</h3> | ||||||
|  |  | ||||||
|  | @ -42,7 +42,25 @@ | ||||||
|   <script type="text/x-template" id="order-creator-template"> |   <script type="text/x-template" id="order-creator-template"> | ||||||
|     <div> |     <div> | ||||||
| 
 | 
 | ||||||
|       ${self.order_form_buttons()} |       <div style="display: flex; justify-content: space-between; margin-bottom: 1.5rem;"> | ||||||
|  |         <div> | ||||||
|  |           % if expose_store_id: | ||||||
|  |               <b-loading v-model="storeLoading" is-full-page /> | ||||||
|  |               <b-field label="Store" horizontal | ||||||
|  |                        :type="storeID ? null : 'is-danger'"> | ||||||
|  |                 <b-select v-model="storeID" | ||||||
|  |                           @input="storeChanged"> | ||||||
|  |                   <option v-for="store in stores" | ||||||
|  |                           :key="store.store_id" | ||||||
|  |                           :value="store.store_id"> | ||||||
|  |                     {{ store.display }} | ||||||
|  |                   </option> | ||||||
|  |                 </b-select> | ||||||
|  |               </b-field> | ||||||
|  |           % endif | ||||||
|  |         </div> | ||||||
|  |         ${self.order_form_buttons()} | ||||||
|  |       </div> | ||||||
| 
 | 
 | ||||||
|       <${b}-collapse class="panel" |       <${b}-collapse class="panel" | ||||||
|                      :class="customerPanelType" |                      :class="customerPanelType" | ||||||
|  | @ -837,6 +855,12 @@ | ||||||
| 
 | 
 | ||||||
|                 batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n}, |                 batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n}, | ||||||
| 
 | 
 | ||||||
|  |                 % if expose_store_id: | ||||||
|  |                     stores: ${json.dumps(stores)|n}, | ||||||
|  |                     storeID: ${json.dumps(batch.store_id)|n}, | ||||||
|  |                     storeLoading: false, | ||||||
|  |                 % endif | ||||||
|  | 
 | ||||||
|                 customerPanelOpen: false, |                 customerPanelOpen: false, | ||||||
|                 customerLoading: false, |                 customerLoading: false, | ||||||
|                 customerIsKnown: ${json.dumps(customer_is_known)|n}, |                 customerIsKnown: ${json.dumps(customer_is_known)|n}, | ||||||
|  | @ -1160,6 +1184,28 @@ | ||||||
|                 }) |                 }) | ||||||
|             }, |             }, | ||||||
| 
 | 
 | ||||||
|  |             % if expose_store_id: | ||||||
|  | 
 | ||||||
|  |                 storeChanged(storeID) { | ||||||
|  |                     this.storeLoading = true | ||||||
|  |                     const params = { | ||||||
|  |                         action: 'set_store', | ||||||
|  |                         store_id: storeID, | ||||||
|  |                     } | ||||||
|  |                     this.submitBatchData(params, ({data}) => { | ||||||
|  |                         this.storeLoading = false | ||||||
|  |                     }, response => { | ||||||
|  |                         this.$buefy.toast.open({ | ||||||
|  |                             message: "Update failed: " + (response.data.error || "(unknown error)"), | ||||||
|  |                             type: 'is-danger', | ||||||
|  |                             duration: 2000, // 2 seconds | ||||||
|  |                         }) | ||||||
|  |                         this.storeLoading = false | ||||||
|  |                     }) | ||||||
|  |                 }, | ||||||
|  | 
 | ||||||
|  |             % endif | ||||||
|  | 
 | ||||||
|             customerChanged(customerID, callback) { |             customerChanged(customerID, callback) { | ||||||
|                 this.customerLoading = true |                 this.customerLoading = true | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -126,6 +126,10 @@ class NewOrderBatchView(BatchMasterView): | ||||||
|         'status_code', |         'status_code', | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|  |     def __init__(self, request, context=None): | ||||||
|  |         super().__init__(request, context=context) | ||||||
|  |         self.order_handler = self.app.get_order_handler() | ||||||
|  | 
 | ||||||
|     def get_batch_handler(self): |     def get_batch_handler(self): | ||||||
|         """ """ |         """ """ | ||||||
|         # TODO: call self.app.get_batch_handler() |         # TODO: call self.app.get_batch_handler() | ||||||
|  | @ -135,6 +139,10 @@ class NewOrderBatchView(BatchMasterView): | ||||||
|         """ """ |         """ """ | ||||||
|         super().configure_grid(g) |         super().configure_grid(g) | ||||||
| 
 | 
 | ||||||
|  |         # store_id | ||||||
|  |         if not self.order_handler.expose_store_id(): | ||||||
|  |             g.remove('store_id') | ||||||
|  | 
 | ||||||
|         # total_price |         # total_price | ||||||
|         g.set_renderer('total_price', 'currency') |         g.set_renderer('total_price', 'currency') | ||||||
| 
 | 
 | ||||||
|  | @ -142,6 +150,10 @@ class NewOrderBatchView(BatchMasterView): | ||||||
|         """ """ |         """ """ | ||||||
|         super().configure_form(f) |         super().configure_form(f) | ||||||
| 
 | 
 | ||||||
|  |         # store_id | ||||||
|  |         if not self.order_handler.expose_store_id(): | ||||||
|  |             f.remove('store_id') | ||||||
|  | 
 | ||||||
|         # local_customer |         # local_customer | ||||||
|         f.set_node('local_customer', LocalCustomerRef(self.request)) |         f.set_node('local_customer', LocalCustomerRef(self.request)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -37,7 +37,6 @@ 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 sideshow.db.model import Order, OrderItem | from sideshow.db.model import Order, OrderItem | ||||||
| from sideshow.orders import OrderHandler |  | ||||||
| from sideshow.batch.neworder import NewOrderBatchHandler | from sideshow.batch.neworder import NewOrderBatchHandler | ||||||
| from sideshow.web.forms.schema import (OrderRef, | from sideshow.web.forms.schema import (OrderRef, | ||||||
|                                        LocalCustomerRef, LocalProductRef, |                                        LocalCustomerRef, LocalProductRef, | ||||||
|  | @ -155,20 +154,7 @@ class OrderView(MasterView): | ||||||
| 
 | 
 | ||||||
|     def __init__(self, request, context=None): |     def __init__(self, request, context=None): | ||||||
|         super().__init__(request, context=context) |         super().__init__(request, context=context) | ||||||
|         self.order_handler = self.get_order_handler() |         self.order_handler = self.app.get_order_handler() | ||||||
| 
 |  | ||||||
|     def get_order_handler(self): |  | ||||||
|         """ |  | ||||||
|         Returns the configured :term:`order handler`. |  | ||||||
| 
 |  | ||||||
|         You normally would not need to call this, and can use |  | ||||||
|         :attr:`order_handler` instead. |  | ||||||
| 
 |  | ||||||
|         :rtype: :class:`~sideshow.orders.OrderHandler` |  | ||||||
|         """ |  | ||||||
|         if hasattr(self, 'order_handler'): |  | ||||||
|             return self.order_handler |  | ||||||
|         return OrderHandler(self.config) |  | ||||||
| 
 | 
 | ||||||
|     def get_batch_handler(self): |     def get_batch_handler(self): | ||||||
|         """ |         """ | ||||||
|  | @ -190,6 +176,10 @@ class OrderView(MasterView): | ||||||
|         """ """ |         """ """ | ||||||
|         super().configure_grid(g) |         super().configure_grid(g) | ||||||
| 
 | 
 | ||||||
|  |         # store_id | ||||||
|  |         if not self.order_handler.expose_store_id(): | ||||||
|  |             g.remove('store_id') | ||||||
|  | 
 | ||||||
|         # order_id |         # order_id | ||||||
|         g.set_link('order_id') |         g.set_link('order_id') | ||||||
| 
 | 
 | ||||||
|  | @ -223,6 +213,7 @@ class OrderView(MasterView): | ||||||
| 
 | 
 | ||||||
|         * :meth:`start_over()` |         * :meth:`start_over()` | ||||||
|         * :meth:`cancel_order()` |         * :meth:`cancel_order()` | ||||||
|  |         * :meth:`set_store()` | ||||||
|         * :meth:`assign_customer()` |         * :meth:`assign_customer()` | ||||||
|         * :meth:`unassign_customer()` |         * :meth:`unassign_customer()` | ||||||
|         * :meth:`set_pending_customer()` |         * :meth:`set_pending_customer()` | ||||||
|  | @ -232,10 +223,12 @@ class OrderView(MasterView): | ||||||
|         * :meth:`delete_item()` |         * :meth:`delete_item()` | ||||||
|         * :meth:`submit_order()` |         * :meth:`submit_order()` | ||||||
|         """ |         """ | ||||||
|  |         model = self.app.model | ||||||
|         enum = self.app.enum |         enum = self.app.enum | ||||||
|         self.creating = True |         session = self.Session() | ||||||
|         self.batch_handler = self.get_batch_handler() |         self.batch_handler = self.get_batch_handler() | ||||||
|         batch = self.get_current_batch() |         batch = self.get_current_batch() | ||||||
|  |         self.creating = True | ||||||
| 
 | 
 | ||||||
|         context = self.get_context_customer(batch) |         context = self.get_context_customer(batch) | ||||||
| 
 | 
 | ||||||
|  | @ -254,6 +247,7 @@ class OrderView(MasterView): | ||||||
|             data = dict(self.request.json_body) |             data = dict(self.request.json_body) | ||||||
|             action = data.pop('action') |             action = data.pop('action') | ||||||
|             json_actions = [ |             json_actions = [ | ||||||
|  |                 'set_store', | ||||||
|                 'assign_customer', |                 'assign_customer', | ||||||
|                 'unassign_customer', |                 'unassign_customer', | ||||||
|                 # 'update_phone_number', |                 # 'update_phone_number', | ||||||
|  | @ -285,12 +279,25 @@ class OrderView(MasterView): | ||||||
|                             for row in batch.rows], |                             for row in batch.rows], | ||||||
|             'default_uom_choices': self.get_default_uom_choices(), |             'default_uom_choices': self.get_default_uom_choices(), | ||||||
|             'default_uom': None, # TODO? |             'default_uom': None, # TODO? | ||||||
|  |             'expose_store_id': self.order_handler.expose_store_id(), | ||||||
|             'allow_item_discounts': self.batch_handler.allow_item_discounts(), |             'allow_item_discounts': self.batch_handler.allow_item_discounts(), | ||||||
|             'allow_unknown_products': (self.batch_handler.allow_unknown_products() |             'allow_unknown_products': (self.batch_handler.allow_unknown_products() | ||||||
|                                        and self.has_perm('create_unknown_product')), |                                        and self.has_perm('create_unknown_product')), | ||||||
|             'pending_product_required_fields': self.get_pending_product_required_fields(), |             'pending_product_required_fields': self.get_pending_product_required_fields(), | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|  |         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']: |         if context['allow_item_discounts']: | ||||||
|             context['allow_item_discounts_if_on_sale'] = self.batch_handler\ |             context['allow_item_discounts_if_on_sale'] = self.batch_handler\ | ||||||
|                                                              .allow_item_discounts_if_on_sale() |                                                              .allow_item_discounts_if_on_sale() | ||||||
|  | @ -436,9 +443,26 @@ class OrderView(MasterView): | ||||||
|         url = self.get_index_url() |         url = self.get_index_url() | ||||||
|         return self.redirect(url) |         return self.redirect(url) | ||||||
| 
 | 
 | ||||||
|  |     def set_store(self, batch, data): | ||||||
|  |         """ | ||||||
|  |         Assign the | ||||||
|  |         :attr:`~sideshow.db.model.batch.neworder.NewOrderBatch.store_id` | ||||||
|  |         for a batch. | ||||||
|  | 
 | ||||||
|  |         This is a "batch action" method which may be called from | ||||||
|  |         :meth:`create()`. | ||||||
|  |         """ | ||||||
|  |         store_id = data.get('store_id') | ||||||
|  |         if not store_id: | ||||||
|  |             return {'error': "Must provide store_id"} | ||||||
|  | 
 | ||||||
|  |         batch.store_id = store_id | ||||||
|  |         return self.get_context_customer(batch) | ||||||
|  | 
 | ||||||
|     def get_context_customer(self, batch): |     def get_context_customer(self, batch): | ||||||
|         """ """ |         """ """ | ||||||
|         context = { |         context = { | ||||||
|  |             'store_id': batch.store_id, | ||||||
|             'customer_is_known': True, |             'customer_is_known': True, | ||||||
|             'customer_id': None, |             'customer_id': None, | ||||||
|             'customer_name': batch.customer_name, |             'customer_name': batch.customer_name, | ||||||
|  | @ -810,6 +834,10 @@ class OrderView(MasterView): | ||||||
|         super().configure_form(f) |         super().configure_form(f) | ||||||
|         order = f.model_instance |         order = f.model_instance | ||||||
| 
 | 
 | ||||||
|  |         # store_id | ||||||
|  |         if not self.order_handler.expose_store_id(): | ||||||
|  |             f.remove('store_id') | ||||||
|  | 
 | ||||||
|         # local_customer |         # local_customer | ||||||
|         if order.customer_id and not order.local_customer: |         if order.customer_id and not order.local_customer: | ||||||
|             f.remove('local_customer') |             f.remove('local_customer') | ||||||
|  | @ -910,8 +938,10 @@ class OrderView(MasterView): | ||||||
|         """ """ |         """ """ | ||||||
|         settings = [ |         settings = [ | ||||||
| 
 | 
 | ||||||
|             # batches |             # stores | ||||||
|             {'name': 'wutta.batch.neworder.handler.spec'}, |             {'name': 'sideshow.orders.expose_store_id', | ||||||
|  |              'type': bool}, | ||||||
|  |             {'name': 'sideshow.orders.default_store_id'}, | ||||||
| 
 | 
 | ||||||
|             # customers |             # customers | ||||||
|             {'name': 'sideshow.orders.use_local_customers', |             {'name': 'sideshow.orders.use_local_customers', | ||||||
|  | @ -933,6 +963,9 @@ class OrderView(MasterView): | ||||||
|             {'name': 'sideshow.orders.allow_unknown_products', |             {'name': 'sideshow.orders.allow_unknown_products', | ||||||
|              'type': bool, |              'type': bool, | ||||||
|              'default': True}, |              'default': True}, | ||||||
|  | 
 | ||||||
|  |             # batches | ||||||
|  |             {'name': 'wutta.batch.neworder.handler.spec'}, | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|         # required fields for new product entry |         # required fields for new product entry | ||||||
|  | @ -1037,6 +1070,7 @@ class OrderItemView(MasterView): | ||||||
| 
 | 
 | ||||||
|     labels = { |     labels = { | ||||||
|         'order_id': "Order ID", |         'order_id': "Order ID", | ||||||
|  |         'store_id': "Store ID", | ||||||
|         'product_id': "Product ID", |         'product_id': "Product ID", | ||||||
|         'product_scancode': "Scancode", |         'product_scancode': "Scancode", | ||||||
|         'product_brand': "Brand", |         'product_brand': "Brand", | ||||||
|  | @ -1050,6 +1084,7 @@ class OrderItemView(MasterView): | ||||||
| 
 | 
 | ||||||
|     grid_columns = [ |     grid_columns = [ | ||||||
|         'order_id', |         'order_id', | ||||||
|  |         'store_id', | ||||||
|         'customer_name', |         'customer_name', | ||||||
|         # 'sequence', |         # 'sequence', | ||||||
|         'product_scancode', |         'product_scancode', | ||||||
|  | @ -1099,20 +1134,7 @@ class OrderItemView(MasterView): | ||||||
| 
 | 
 | ||||||
|     def __init__(self, request, context=None): |     def __init__(self, request, context=None): | ||||||
|         super().__init__(request, context=context) |         super().__init__(request, context=context) | ||||||
|         self.order_handler = self.get_order_handler() |         self.order_handler = self.app.get_order_handler() | ||||||
| 
 |  | ||||||
|     def get_order_handler(self): |  | ||||||
|         """ |  | ||||||
|         Returns the configured :term:`order handler`. |  | ||||||
| 
 |  | ||||||
|         You normally would not need to call this, and can use |  | ||||||
|         :attr:`order_handler` instead. |  | ||||||
| 
 |  | ||||||
|         :rtype: :class:`~sideshow.orders.OrderHandler` |  | ||||||
|         """ |  | ||||||
|         if hasattr(self, 'order_handler'): |  | ||||||
|             return self.order_handler |  | ||||||
|         return OrderHandler(self.config) |  | ||||||
| 
 | 
 | ||||||
|     def get_fallback_templates(self, template): |     def get_fallback_templates(self, template): | ||||||
|         """ """ |         """ """ | ||||||
|  | @ -1132,11 +1154,19 @@ class OrderItemView(MasterView): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         # enum = self.app.enum |         # enum = self.app.enum | ||||||
| 
 | 
 | ||||||
|  |         # store_id | ||||||
|  |         if not self.order_handler.expose_store_id(): | ||||||
|  |             g.remove('store_id') | ||||||
|  | 
 | ||||||
|         # order_id |         # order_id | ||||||
|         g.set_sorter('order_id', model.Order.order_id) |         g.set_sorter('order_id', model.Order.order_id) | ||||||
|         g.set_renderer('order_id', self.render_order_id) |         g.set_renderer('order_id', self.render_order_attr) | ||||||
|         g.set_link('order_id') |         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) | ||||||
|  | 
 | ||||||
|         # customer_name |         # customer_name | ||||||
|         g.set_label('customer_name', "Customer", column_only=True) |         g.set_label('customer_name', "Customer", column_only=True) | ||||||
| 
 | 
 | ||||||
|  | @ -1165,9 +1195,10 @@ class OrderItemView(MasterView): | ||||||
|         # status_code |         # status_code | ||||||
|         g.set_renderer('status_code', self.render_status_code) |         g.set_renderer('status_code', self.render_status_code) | ||||||
| 
 | 
 | ||||||
|     def render_order_id(self, item, key, value): |     def render_order_attr(self, item, key, value): | ||||||
|         """ """ |         """ """ | ||||||
|         return item.order.order_id |         order = item.order | ||||||
|  |         return getattr(order, key) | ||||||
| 
 | 
 | ||||||
|     def render_status_code(self, item, key, value): |     def render_status_code(self, item, key, value): | ||||||
|         """ """ |         """ """ | ||||||
|  | @ -1237,6 +1268,8 @@ class OrderItemView(MasterView): | ||||||
|             item = context['instance'] |             item = context['instance'] | ||||||
|             form = context['form'] |             form = context['form'] | ||||||
| 
 | 
 | ||||||
|  |             context['expose_store_id'] = self.order_handler.expose_store_id() | ||||||
|  | 
 | ||||||
|             context['item'] = item |             context['item'] = item | ||||||
|             context['order'] = item.order |             context['order'] = item.order | ||||||
|             context['order_qty_uom_text'] = self.order_handler.get_order_qty_uom_text( |             context['order_qty_uom_text'] = self.order_handler.get_order_qty_uom_text( | ||||||
|  |  | ||||||
|  | @ -20,6 +20,16 @@ class TestNewOrderBatchHandler(DataTestCase): | ||||||
|     def make_handler(self): |     def make_handler(self): | ||||||
|         return mod.NewOrderBatchHandler(self.config) |         return mod.NewOrderBatchHandler(self.config) | ||||||
| 
 | 
 | ||||||
|  |     def test_get_default_store_id(self): | ||||||
|  |         handler = self.make_handler() | ||||||
|  | 
 | ||||||
|  |         # null by default | ||||||
|  |         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') | ||||||
|  | 
 | ||||||
|     def test_use_local_customers(self): |     def test_use_local_customers(self): | ||||||
|         handler = self.make_handler() |         handler = self.make_handler() | ||||||
| 
 | 
 | ||||||
|  | @ -108,6 +118,23 @@ class TestNewOrderBatchHandler(DataTestCase): | ||||||
|         # search for sally finds nothing |         # 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 | ||||||
|  |         handler = self.make_handler() | ||||||
|  | 
 | ||||||
|  |         # store_id is null by default | ||||||
|  |         batch = handler.model_class() | ||||||
|  |         self.assertIsNone(batch.store_id) | ||||||
|  |         handler.init_batch(batch) | ||||||
|  |         self.assertIsNone(batch.store_id) | ||||||
|  | 
 | ||||||
|  |         # but default can be configured | ||||||
|  |         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') | ||||||
|  | 
 | ||||||
|     def test_set_customer(self): |     def test_set_customer(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         handler = self.make_handler() |         handler = self.make_handler() | ||||||
|  |  | ||||||
|  | @ -13,3 +13,16 @@ class TestPendingCustomer(DataTestCase): | ||||||
| 
 | 
 | ||||||
|         store.name = "Acme Goods" |         store.name = "Acme Goods" | ||||||
|         self.assertEqual(str(store), "Acme Goods") |         self.assertEqual(str(store), "Acme Goods") | ||||||
|  | 
 | ||||||
|  |         store.store_id = "001" | ||||||
|  |         self.assertEqual(str(store), "001 Acme Goods") | ||||||
|  | 
 | ||||||
|  |     def test_get_display(self): | ||||||
|  |         store = mod.Store() | ||||||
|  |         self.assertEqual(store.get_display(), "") | ||||||
|  | 
 | ||||||
|  |         store.name = "Acme Goods" | ||||||
|  |         self.assertEqual(store.get_display(), "Acme Goods") | ||||||
|  | 
 | ||||||
|  |         store.store_id = "001" | ||||||
|  |         self.assertEqual(store.get_display(), "001 Acme Goods") | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								tests/test_app.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/test_app.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | # -*- coding: utf-8; -*- | ||||||
|  | 
 | ||||||
|  | from wuttjamaican.testing import ConfigTestCase | ||||||
|  | 
 | ||||||
|  | from sideshow import app as mod | ||||||
|  | from sideshow.orders import OrderHandler | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TestSideshowAppProvider(ConfigTestCase): | ||||||
|  | 
 | ||||||
|  |     def make_provider(self): | ||||||
|  |         return mod.SideshowAppProvider(self.config) | ||||||
|  | 
 | ||||||
|  |     def test_get_order_handler(self): | ||||||
|  |         provider = self.make_provider() | ||||||
|  |         handler = provider.get_order_handler() | ||||||
|  |         self.assertIsInstance(handler, OrderHandler) | ||||||
|  | @ -16,6 +16,16 @@ class TestOrderHandler(DataTestCase): | ||||||
|     def make_handler(self): |     def make_handler(self): | ||||||
|         return mod.OrderHandler(self.config) |         return mod.OrderHandler(self.config) | ||||||
| 
 | 
 | ||||||
|  |     def test_expose_store_id(self): | ||||||
|  |         handler = self.make_handler() | ||||||
|  | 
 | ||||||
|  |         # false by default | ||||||
|  |         self.assertFalse(handler.expose_store_id()) | ||||||
|  | 
 | ||||||
|  |         # config can enable | ||||||
|  |         self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|  |         self.assertTrue(handler.expose_store_id()) | ||||||
|  | 
 | ||||||
|     def test_get_order_qty_uom_text(self): |     def test_get_order_qty_uom_text(self): | ||||||
|         enum = self.app.enum |         enum = self.app.enum | ||||||
|         handler = self.make_handler() |         handler = self.make_handler() | ||||||
|  |  | ||||||
|  | @ -30,10 +30,19 @@ class TestNewOrderBatchView(WebTestCase): | ||||||
|     def test_configure_grid(self): |     def test_configure_grid(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
|  | 
 | ||||||
|  |         # store_id not exposed by default | ||||||
|         grid = view.make_grid(model_class=model.NewOrderBatch) |         grid = view.make_grid(model_class=model.NewOrderBatch) | ||||||
|         self.assertNotIn('total_price', grid.renderers) |         self.assertIn('store_id', grid.columns) | ||||||
|         view.configure_grid(grid) |         view.configure_grid(grid) | ||||||
|         self.assertIn('total_price', grid.renderers) |         self.assertNotIn('store_id', grid.columns) | ||||||
|  | 
 | ||||||
|  |         # store_id is exposed if configured | ||||||
|  |         self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|  |         grid = view.make_grid(model_class=model.NewOrderBatch) | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
|  |         view.configure_grid(grid) | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
| 
 | 
 | ||||||
|     def test_configure_form(self): |     def test_configure_form(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|  | @ -58,6 +67,19 @@ class TestNewOrderBatchView(WebTestCase): | ||||||
|             self.assertIsInstance(schema['pending_customer'].typ, PendingCustomerRef) |             self.assertIsInstance(schema['pending_customer'].typ, PendingCustomerRef) | ||||||
|             self.assertIsInstance(schema['total_price'].typ, WuttaMoney) |             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) | ||||||
|  |             view.configure_form(form) | ||||||
|  |             self.assertNotIn('store_id', form) | ||||||
|  | 
 | ||||||
|  |             # store_id is exposed if configured | ||||||
|  |             self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|  |             form = view.make_form(model_instance=batch) | ||||||
|  |             self.assertIn('store_id', form) | ||||||
|  |             view.configure_form(form) | ||||||
|  |             self.assertIn('store_id', form) | ||||||
|  | 
 | ||||||
|     def test_configure_row_grid(self): |     def test_configure_row_grid(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
|  |  | ||||||
|  | @ -31,27 +31,28 @@ class TestOrderView(WebTestCase): | ||||||
|     def make_handler(self): |     def make_handler(self): | ||||||
|         return NewOrderBatchHandler(self.config) |         return NewOrderBatchHandler(self.config) | ||||||
| 
 | 
 | ||||||
|     def test_order_handler(self): |  | ||||||
|         view = self.make_view() |  | ||||||
|         handler = view.order_handler |  | ||||||
|         self.assertIsInstance(handler, OrderHandler) |  | ||||||
|         handler2 = view.get_order_handler() |  | ||||||
|         self.assertIs(handler2, handler) |  | ||||||
| 
 |  | ||||||
|     def test_configure_grid(self): |     def test_configure_grid(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
|         grid = view.make_grid(model_class=model.PendingProduct) | 
 | ||||||
|         self.assertNotIn('order_id', grid.linked_columns) |         # store_id hidden by default | ||||||
|         self.assertNotIn('total_price', grid.renderers) |         grid = view.make_grid(model_class=model.Order, columns=['store_id', 'order_id']) | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
|         view.configure_grid(grid) |         view.configure_grid(grid) | ||||||
|         self.assertIn('order_id', grid.linked_columns) |         self.assertNotIn('store_id', grid.columns) | ||||||
|         self.assertIn('total_price', grid.renderers) | 
 | ||||||
|  |         # 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) | ||||||
|  |         view.configure_grid(grid) | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
| 
 | 
 | ||||||
|     def test_create(self): |     def test_create(self): | ||||||
|         self.pyramid_config.include('sideshow.web.views') |         self.pyramid_config.include('sideshow.web.views') | ||||||
|         self.config.setdefault('wutta.batch.neworder.handler.spec', |         self.config.setdefault('wutta.batch.neworder.handler.spec', | ||||||
|                                'sideshow.batch.neworder:NewOrderBatchHandler') |                                'sideshow.batch.neworder:NewOrderBatchHandler') | ||||||
|  |         self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|         self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') |         self.config.setdefault('sideshow.orders.allow_item_discounts', 'true') | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         enum = self.app.enum |         enum = self.app.enum | ||||||
|  | @ -59,6 +60,10 @@ class TestOrderView(WebTestCase): | ||||||
| 
 | 
 | ||||||
|         user = model.User(username='barney') |         user = model.User(username='barney') | ||||||
|         self.session.add(user) |         self.session.add(user) | ||||||
|  |         store = model.Store(store_id='001', name='Acme Goods') | ||||||
|  |         self.session.add(store) | ||||||
|  |         store = model.Store(store_id='002', name='Acme Services') | ||||||
|  |         self.session.add(store) | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
| 
 | 
 | ||||||
|         with patch.object(view, 'Session', return_value=self.session): |         with patch.object(view, 'Session', return_value=self.session): | ||||||
|  | @ -101,6 +106,7 @@ class TestOrderView(WebTestCase): | ||||||
|                         self.assertIsInstance(response, Response) |                         self.assertIsInstance(response, Response) | ||||||
|                         self.assertEqual(response.content_type, 'application/json') |                         self.assertEqual(response.content_type, 'application/json') | ||||||
|                         self.assertEqual(response.json_body, { |                         self.assertEqual(response.json_body, { | ||||||
|  |                             'store_id': None, | ||||||
|                             'customer_is_known': False, |                             'customer_is_known': False, | ||||||
|                             'customer_id': None, |                             'customer_id': None, | ||||||
|                             'customer_name': 'Fred Flintstone', |                             'customer_name': 'Fred Flintstone', | ||||||
|  | @ -304,6 +310,7 @@ class TestOrderView(WebTestCase): | ||||||
|             self.session.flush() |             self.session.flush() | ||||||
|             context = view.get_context_customer(batch) |             context = view.get_context_customer(batch) | ||||||
|             self.assertEqual(context, { |             self.assertEqual(context, { | ||||||
|  |                 'store_id': None, | ||||||
|                 'customer_is_known': True, |                 'customer_is_known': True, | ||||||
|                 'customer_id': 42, |                 'customer_id': 42, | ||||||
|                 'customer_name': 'Fred Flintstone', |                 'customer_name': 'Fred Flintstone', | ||||||
|  | @ -321,6 +328,7 @@ class TestOrderView(WebTestCase): | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
|         context = view.get_context_customer(batch) |         context = view.get_context_customer(batch) | ||||||
|         self.assertEqual(context, { |         self.assertEqual(context, { | ||||||
|  |             'store_id': None, | ||||||
|             'customer_is_known': True, |             'customer_is_known': True, | ||||||
|             'customer_id': local.uuid.hex, |             'customer_id': local.uuid.hex, | ||||||
|             'customer_name': 'Betty Boop', |             'customer_name': 'Betty Boop', | ||||||
|  | @ -339,6 +347,7 @@ class TestOrderView(WebTestCase): | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
|         context = view.get_context_customer(batch) |         context = view.get_context_customer(batch) | ||||||
|         self.assertEqual(context, { |         self.assertEqual(context, { | ||||||
|  |             'store_id': None, | ||||||
|             'customer_is_known': False, |             'customer_is_known': False, | ||||||
|             'customer_id': None, |             'customer_id': None, | ||||||
|             'customer_name': 'Fred Flintstone', |             'customer_name': 'Fred Flintstone', | ||||||
|  | @ -357,6 +366,7 @@ class TestOrderView(WebTestCase): | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
|         context = view.get_context_customer(batch) |         context = view.get_context_customer(batch) | ||||||
|         self.assertEqual(context, { |         self.assertEqual(context, { | ||||||
|  |             'store_id': None, | ||||||
|             'customer_is_known': True, # nb. this is for UI default |             'customer_is_known': True, # nb. this is for UI default | ||||||
|             'customer_id': None, |             'customer_id': None, | ||||||
|             'customer_name': None, |             'customer_name': None, | ||||||
|  | @ -408,6 +418,34 @@ class TestOrderView(WebTestCase): | ||||||
|                     self.session.flush() |                     self.session.flush() | ||||||
|                     self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) |                     self.assertEqual(self.session.query(model.NewOrderBatch).count(), 0) | ||||||
| 
 | 
 | ||||||
|  |     def test_set_store(self): | ||||||
|  |         model = self.app.model | ||||||
|  |         view = self.make_view() | ||||||
|  |         handler = NewOrderBatchHandler(self.config) | ||||||
|  | 
 | ||||||
|  |         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): | ||||||
|  | 
 | ||||||
|  |                     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"}) | ||||||
|  | 
 | ||||||
|  |                     # 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') | ||||||
|  | 
 | ||||||
|     def test_assign_customer(self): |     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 |         model = self.app.model | ||||||
|  | @ -432,6 +470,7 @@ class TestOrderView(WebTestCase): | ||||||
|                     self.assertIsNone(batch.pending_customer) |                     self.assertIsNone(batch.pending_customer) | ||||||
|                     self.assertIs(batch.local_customer, weirdal) |                     self.assertIs(batch.local_customer, weirdal) | ||||||
|                     self.assertEqual(context, { |                     self.assertEqual(context, { | ||||||
|  |                         'store_id': None, | ||||||
|                         'customer_is_known': True, |                         'customer_is_known': True, | ||||||
|                         'customer_id': weirdal.uuid.hex, |                         'customer_id': weirdal.uuid.hex, | ||||||
|                         'customer_name': 'Weird Al', |                         'customer_name': 'Weird Al', | ||||||
|  | @ -470,6 +509,7 @@ class TestOrderView(WebTestCase): | ||||||
|                     self.assertIsNone(batch.customer_name) |                     self.assertIsNone(batch.customer_name) | ||||||
|                     self.assertIsNone(batch.local_customer) |                     self.assertIsNone(batch.local_customer) | ||||||
|                     self.assertEqual(context, { |                     self.assertEqual(context, { | ||||||
|  |                         'store_id': None, | ||||||
|                         'customer_is_known': True, |                         'customer_is_known': True, | ||||||
|                         'customer_id': None, |                         'customer_id': None, | ||||||
|                         'customer_name': None, |                         'customer_name': None, | ||||||
|  | @ -510,6 +550,7 @@ class TestOrderView(WebTestCase): | ||||||
|                     context = view.set_pending_customer(batch, data) |                     context = view.set_pending_customer(batch, data) | ||||||
|                     self.assertIsInstance(batch.pending_customer, model.PendingCustomer) |                     self.assertIsInstance(batch.pending_customer, model.PendingCustomer) | ||||||
|                     self.assertEqual(context, { |                     self.assertEqual(context, { | ||||||
|  |                         'store_id': None, | ||||||
|                         'customer_is_known': False, |                         'customer_is_known': False, | ||||||
|                         'customer_id': None, |                         'customer_id': None, | ||||||
|                         'customer_name': 'Fred Flintstone', |                         'customer_name': 'Fred Flintstone', | ||||||
|  | @ -1078,7 +1119,11 @@ class TestOrderView(WebTestCase): | ||||||
|             form = view.make_form(model_instance=order) |             form = view.make_form(model_instance=order) | ||||||
|             # nb. this is to avoid include/exclude ambiguity |             # 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) | ||||||
|             view.configure_form(form) |             view.configure_form(form) | ||||||
|  |             self.assertNotIn('store_id', form) | ||||||
|             schema = form.get_schema() |             schema = form.get_schema() | ||||||
|             self.assertIn('pending_customer', form) |             self.assertIn('pending_customer', form) | ||||||
|             self.assertIsInstance(schema['total_price'].typ, WuttaMoney) |             self.assertIsInstance(schema['total_price'].typ, WuttaMoney) | ||||||
|  | @ -1089,13 +1134,20 @@ class TestOrderView(WebTestCase): | ||||||
|         self.session.add(local) |         self.session.add(local) | ||||||
|         self.session.flush() |         self.session.flush() | ||||||
| 
 | 
 | ||||||
|  |         # nb. from now on we include store_id | ||||||
|  |         self.config.setdefault('sideshow.orders.expose_store_id', 'true') | ||||||
|  | 
 | ||||||
|         # viewing (local customer) |         # viewing (local customer) | ||||||
|         with patch.object(view, 'viewing', new=True): |         with patch.object(view, 'viewing', new=True): | ||||||
|             with patch.object(order, 'local_customer', new=local): |             with patch.object(order, 'local_customer', new=local): | ||||||
|                 form = view.make_form(model_instance=order) |                 form = view.make_form(model_instance=order) | ||||||
|                 # nb. this is to avoid include/exclude ambiguity |                 # 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) | ||||||
|                 view.configure_form(form) |                 view.configure_form(form) | ||||||
|  |                 self.assertIn('store_id', form) | ||||||
|                 self.assertNotIn('pending_customer', form) |                 self.assertNotIn('pending_customer', form) | ||||||
|                 schema = form.get_schema() |                 schema = form.get_schema() | ||||||
|                 self.assertIsInstance(schema['total_price'].typ, WuttaMoney) |                 self.assertIsInstance(schema['total_price'].typ, WuttaMoney) | ||||||
|  | @ -1272,13 +1324,6 @@ class TestOrderView(WebTestCase): | ||||||
| 
 | 
 | ||||||
| class OrderItemViewTestMixin: | class OrderItemViewTestMixin: | ||||||
| 
 | 
 | ||||||
|     def test_common_order_handler(self): |  | ||||||
|         view = self.make_view() |  | ||||||
|         handler = view.order_handler |  | ||||||
|         self.assertIsInstance(handler, OrderHandler) |  | ||||||
|         handler2 = view.get_order_handler() |  | ||||||
|         self.assertIs(handler2, handler) |  | ||||||
| 
 |  | ||||||
|     def test_common_get_fallback_templates(self): |     def test_common_get_fallback_templates(self): | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
| 
 | 
 | ||||||
|  | @ -1294,18 +1339,29 @@ class OrderItemViewTestMixin: | ||||||
|     def test_common_configure_grid(self): |     def test_common_configure_grid(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
|         grid = view.make_grid(model_class=model.OrderItem) |  | ||||||
|         self.assertNotIn('order_id', grid.linked_columns) |  | ||||||
|         view.configure_grid(grid) |  | ||||||
|         self.assertIn('order_id', grid.linked_columns) |  | ||||||
| 
 | 
 | ||||||
|     def test_common_render_order_id(self): |         # store_id is removed by default | ||||||
|  |         grid = view.make_grid(model_class=model.OrderItem) | ||||||
|  |         grid.append('store_id') | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
|  |         view.configure_grid(grid) | ||||||
|  |         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.OrderItem) | ||||||
|  |         grid.append('store_id') | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
|  |         view.configure_grid(grid) | ||||||
|  |         self.assertIn('store_id', grid.columns) | ||||||
|  | 
 | ||||||
|  |     def test_common_render_order_attr(self): | ||||||
|         model = self.app.model |         model = self.app.model | ||||||
|         view = self.make_view() |         view = self.make_view() | ||||||
|         order = model.Order(order_id=42) |         order = model.Order(order_id=42) | ||||||
|         item = model.OrderItem() |         item = model.OrderItem() | ||||||
|         order.items.append(item) |         order.items.append(item) | ||||||
|         self.assertEqual(view.render_order_id(item, None, None), 42) |         self.assertEqual(view.render_order_attr(item, 'order_id', None), 42) | ||||||
| 
 | 
 | ||||||
|     def test_common_render_status_code(self): |     def test_common_render_status_code(self): | ||||||
|         enum = self.app.enum |         enum = self.app.enum | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue