Changeset - d720f0f0abd7
[Not reviewed]
0 2 0
Lance Edgar (lance) - 3 years ago 2021-11-03 20:19:23
lance@edbob.org
Add setting for "product price may be questionable" for custorders
2 files changed with 24 insertions and 0 deletions:
0 comments (0 inline, 0 general)
rattail/batch/custorder.py
Show inline comments
 
# -*- coding: utf-8; -*-
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2021 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
#  Rattail 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.
 
#
 
#  Rattail 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
 
#  Rattail.  If not, see <http://www.gnu.org/licenses/>.
 
#
 
################################################################################
 
"""
 
Handler for "customer order" batches
 
"""
 

	
 
from __future__ import unicode_literals, absolute_import, division
 

	
 
import re
 

	
 
import six
 
import sqlalchemy as sa
 
from sqlalchemy import orm
 

	
 
from rattail.db import model
 
from rattail.batch import BatchHandler
 

	
 

	
 
class CustomerOrderBatchHandler(BatchHandler):
 
    """
 
    Handler for all "customer order" batches, regardless of "mode".  The
 
    handler must inspect the
 
    :attr:`~rattail.db.model.batch.custorder.CustomerOrderBatch.mode` attribute
 
    of each batch it deals with, in order to determine which logic to apply.
 

	
 
    .. attribute:: has_custom_product_autocomplete
 

	
 
       If true, this flag indicates that the handler provides custom
 
       autocomplete logic for use when selecting a product while
 
       creating a new order.
 
    """
 
    batch_model_class = model.CustomerOrderBatch
 
    has_custom_product_autocomplete = False
 
    nondigits_pattern = re.compile(r'\D')
 

	
 
    def init_batch(self, batch, progress=None, **kwargs):
 
        """
 
        Assign the "local" store to the batch, if applicable.
 
        """
 
        session = self.app.get_session(batch)
 
        batch.store = self.config.get_store(session)
 

	
 
    def new_order_requires_customer(self):
 
        """
 
        Returns a boolean indicating whether a *new* "customer order"
 
        in fact requires a proper customer account, or not.  Note that
 
        in all cases a new order requires a *person* to associate
 
        with, but technically the customer is optional, unless this
 
        returns true.
 
        """
 
        return self.config.getbool('rattail.custorders',
 
                                   'new_order_requires_customer',
 
                                   default=False)
 

	
 
    def allow_contact_info_choice(self):
 
        """
 
        Returns a boolean indicating whether the user is allowed at
 
        all, to choose from existing contact info options for the
 
        customer, vs. they just have to go with whatever the handler
 
        auto-provides.
 
        """
 
        return self.config.getbool('rattail.custorders',
 
                                   'new_orders.allow_contact_info_choice',
 
                                   default=True)
 

	
 
    def should_restrict_contact_info(self):
 
        """
 
        Returns a boolean indicating whether contact info should be
 
        "restricted" - i.e. user can only choose from existing contact
 
        info and cannot override by e.g. entering a new phone number.
 
        """
 
        return self.config.getbool('rattail.custorders',
 
                                   'new_orders.restrict_contact_info',
 
                                   default=False)
 

	
 
    def product_price_may_be_questionable(self):
 
        """
 
        Returns a boolean indicating whether "any" product's price may
 
        be questionable.  So this isn't saying that a price *is*
 
        questionable but rather that it *may* be, if the user
 
        indicates it.  (That checkbox is only shown for the user if
 
        this flag is true.)
 
        """
 
        return self.config.getbool('rattail.custorders',
 
                                   'product_price_may_be_questionable',
 
                                   default=False)
 

	
 
    def assign_contact(self, batch, customer=None, person=None, **kwargs):
 
        """
 
        Assign the customer and/or person "contact" for the order.
 
        """
 
        clientele = self.app.get_clientele_handler()
 
        customer_required = self.new_order_requires_customer()
 

	
 
        # nb. person is always required
 
        if customer and not person:
 
            person = clientele.get_person(customer)
 
        if not person:
 
            raise ValueError("Must specify a person")
 

	
 
        # customer may or may not be optional
 
        if person and not customer:
 
            customer = clientele.get_customer(person)
 
        if customer_required and not customer:
 
            raise ValueError("Must specify a customer account")
 

	
 
        # assign contact
 
        batch.customer = customer
 
        batch.person = person
 

	
 
        # cache contact name
 
        batch.contact_name = self.get_contact_display(batch)
 

	
 
        # update phone/email per new contact
 
        batch.phone_number = None
 
        batch.email_address = None
 
        if customer_required:
 
            batch.phone_number = clientele.get_first_phone_number(customer)
 
            batch.email_address = clientele.get_first_email_address(customer)
 
        else:
 
            batch.phone_number = person.first_phone_number()
 
            batch.email_address = person.first_email_address()
 

	
 
        # always reset "add to customer" flags
 
        batch.clear_param('add_phone_number')
 
        batch.clear_param('add_email_address')
 

	
 
        session = self.app.get_session(batch)
 
        session.flush()
 

	
 
    def get_contact(self, batch):
 
        """
 
        Should return the contact record (i.e. Customer or Person) for
 
        the batch.
 
        """
 
        customer_required = self.new_order_requires_customer()
 

	
 
        if customer_required:
 
            return batch.customer
 
        else:
 
            return batch.person
 

	
 
    def get_contact_id(self, batch):
 
        """
 
        Should return contact ID for the batch, i.e. customer ID.
 
        """
 
        contact = self.get_contact(batch)
 
        if isinstance(contact, model.Customer):
 
            return contact.id
 

	
 
    def get_contact_display(self, batch):
 
        """
 
        Should return contact display text for the batch,
 
        i.e. customer name.
 
        """
 
        contact = self.get_contact(batch)
 
        if contact:
 
            return six.text_type(contact)
 

	
 
        pending = batch.pending_customer
 
        if pending:
 
            return six.text_type(pending)
 

	
 
    def get_contact_phones(self, batch):
 
        """
 
        Retrieve all phone records on file for the batch contact, to
 
        be presented as options for user to choose from when making a
 
        new order.
 
        """
 
        phones = []
 
        contact = self.get_contact(batch)
 
        if contact:
 
            phones = contact.phones
 

	
 
        return [self.normalize_phone(phone)
 
                for phone in phones]
 

	
 
    def normalize_phone(self, phone):
 
        """
 
        Normalize the given phone record to simple data dict, for
 
        passing around via JSON etc.
 
        """
 
        return {
rattail/settings.py
Show inline comments
 
@@ -56,192 +56,204 @@ class rattail_node_title(Setting):
 
    Official display title for the app node.
 
    """
 
    namespace = 'rattail'
 
    name = 'node_title'
 

	
 

	
 
class rattail_production(Setting):
 
    """
 
    If set, the app is considered to be running in "production" mode, whereas
 
    if disabled, the app is considered to be running in development / testing /
 
    staging mode.
 
    """
 
    namespace = 'rattail'
 
    name = 'production'
 
    data_type = bool
 

	
 

	
 
class tailbone_background_color(Setting):
 
    """
 
    Background color for this app node.  If unset, default color is white.
 
    """
 
    namespace = 'tailbone'
 
    name = 'background_color'
 

	
 

	
 
class rattail_single_store(Setting):
 
    """
 
    If set, the app should assume there is only one Store record, and that all
 
    purchases etc. will pertain to it.
 
    """
 
    namespace = 'rattail'
 
    name = 'single_store'
 
    data_type = bool
 

	
 

	
 
class rattail_demo(Setting):
 
    """
 
    If set, the app is considered to be running in "demo" mode.
 
    """
 
    namespace = 'rattail'
 
    name = 'demo'
 
    data_type = bool
 

	
 

	
 
class rattail_appdir(Setting):
 
    """
 
    Path to the "app" dir for the running instance.
 
    """
 
    namespace = 'rattail'
 
    name = 'appdir'
 

	
 

	
 
class rattail_workdir(Setting):
 
    """
 
    Path to the "work" dir for the running instance.
 
    """
 
    namespace = 'rattail'
 
    name = 'workdir'
 

	
 

	
 
##############################
 
# Customer Orders
 
##############################
 

	
 
class rattail_custorders_new_order_requires_customer(Setting):
 
    """
 
    If set, then all new orders require a proper customer account.  If
 
    *not* set then just a "person" will suffice.
 
    """
 
    group = "Customer Orders"
 
    namespace = 'rattail.custorders'
 
    name = 'new_order_requires_customer'
 
    data_type = bool
 

	
 
class rattail_custorders_new_orders_allow_contact_info_choice(Setting):
 
    """
 
    If set, then user can choose from contact info options, when
 
    creating new order.  If *not* set then they cannot choose, and
 
    must use whatever the batch handler provides.
 
    """
 
    group = "Customer Orders"
 
    namespace = 'rattail.custorders'
 
    name = 'new_orders.allow_contact_info_choice'
 
    data_type = bool
 

	
 
class rattail_custorders_new_orders_restrict_contact_info(Setting):
 
    """
 
    If set, then user can only choose from existing contact info options,
 
    for the customer/order.  If *not* set, then user is allowed to enter
 
    new/different contact info.
 
    """
 
    group = "Customer Orders"
 
    namespace = 'rattail.custorders'
 
    name = 'new_orders.restrict_contact_info'
 
    data_type = bool
 

	
 
class rattail_custorders_product_price_may_be_questionable(Setting):
 
    """
 
    If set, then user may indicate that the price for a given product
 
    is "questionable" - which normally would cause a new step in the
 
    workflow, for someone to update and/or confirm the price.  If
 
    *not* set then user cannot mark any price as questionable.
 
    """
 
    group = "Customer Orders"
 
    namespace = 'rattail.custorders'
 
    name = 'product_price_may_be_questionable'
 
    data_type = bool
 

	
 

	
 
##############################
 
# DataSync
 
##############################
 

	
 
class rattail_datasync_url(Setting):
 
    """
 
    URL for datasync change queue.
 
    """
 
    group = "DataSync"
 
    namespace = 'rattail.datasync'
 
    name = 'url'
 

	
 

	
 
class tailbone_datasync_restart(Setting):
 
    """
 
    Command used when restarting the datasync daemon.
 
    """
 
    group = "DataSync"
 
    namespace = 'tailbone'
 
    name = 'datasync.restart'
 

	
 

	
 
##############################
 
# Email
 
##############################
 

	
 
class rattail_mail_record_attempts(Setting):
 
    """
 
    If enabled, this flag will cause Email Attempts to be recorded in the
 
    database, for "most" attempts to send email.
 
    """
 
    group = "Email"
 
    namespace = 'rattail.mail'
 
    name = 'record_attempts'
 
    data_type = bool
 

	
 

	
 
##############################
 
# FileMon
 
##############################
 

	
 
class tailbone_filemon_restart(Setting):
 
    """
 
    Command used when restarting the filemon daemon.
 
    """
 
    group = "FileMon"
 
    namespace = 'tailbone'
 
    name = 'filemon.restart'
 

	
 

	
 
##############################
 
# Inventory
 
##############################
 

	
 
class tailbone_inventory_force_unit_item(Setting):
 
    """
 
    Defines which of the possible "product key" fields should be effectively
 
    treated as the product key.
 
    """
 
    group = "Inventory"
 
    namespace = 'tailbone'
 
    name = 'inventory.force_unit_item'
 
    data_type = bool
 

	
 

	
 
##############################
 
# Products
 
##############################
 

	
 
class rattail_product_key(Setting):
 
    """
 
    Defines which of the possible "product key" fields should be effectively
 
    treated as the product key.
 
    """
 
    group = "Products"
 
    namespace = 'rattail'
 
    name = 'product.key'
 
    choices = [
 
        'upc',
 
        'item_id',
 
        'scancode',
 
    ]
 

	
 

	
 
class rattail_product_key_title(Setting):
 
    """
 
    Defines the official "title" (display name) for the product key field.
 
    """
 
    group = "Products"
 
    namespace = 'rattail'
 
    name = 'product.key_title'
 

	
 

	
 
class rattail_products_mobile_quick_lookup(Setting):
 
    """
0 comments (0 inline, 0 general)