Changeset - 37b8de91180d
[Not reviewed]
0 4 1
Lance Edgar (lance) - 3 years ago 2021-10-18 18:28:07
lance@edbob.org
Add basic "price needs confirmation" support for custorder
5 files changed with 117 insertions and 9 deletions:
0 comments (0 inline, 0 general)
rattail/batch/custorder.py
Show inline comments
 
@@ -219,372 +219,406 @@ class CustomerOrderBatchHandler(BatchHandler):
 
    def normalize_email(self, email):
 
        """
 
        Normalize the given email record to simple data dict, for
 
        passing around via JSON etc.
 
        """
 
        return {
 
            'uuid': email.uuid,
 
            'type': email.type,
 
            'address': email.address,
 
            'invalid': email.invalid,
 
            'preference': email.preference,
 
            'preferred': email.preference == 1,
 
        }
 

	
 
    def get_contact_notes(self, batch):
 
        """
 
        Get extra "contact notes" which should be made visible to the
 
        user who is entering the new order.
 
        """
 
        notes = []
 

	
 
        invalid = False
 
        contact = self.get_contact(batch)
 
        if contact:
 
            invalid = [email for email in contact.emails
 
                       if email.invalid]
 
        if invalid:
 
            notes.append("Customer has one or more invalid email addresses on file.")
 

	
 
        return notes
 

	
 
    def unassign_contact(self, batch, **kwargs):
 
        """
 
        Unassign the customer and/or person "contact" for the order.
 
        """
 
        batch.customer = None
 
        batch.person = None
 

	
 
        # note that if batch already has a "pending" customer on file,
 
        # we will "restore" it as the contact info for the batch
 
        pending = batch.pending_customer
 
        if pending:
 
            batch.contact_name = pending.display_name
 
            batch.phone_number = pending.phone_number
 
            batch.email_address = pending.email_address
 
        else:
 
            batch.contact_name = None
 
            batch.phone_number = None
 
            batch.email_address = None
 

	
 
        # 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()
 
        session.refresh(batch)
 

	
 
    def validate_pending_customer_data(self, batch, user, data):
 
        pass
 

	
 
    def update_pending_customer(self, batch, user, data):
 
        model = self.model
 
        people = self.app.get_people_handler()
 

	
 
        # first validate all data
 
        self.validate_pending_customer_data(batch, user, data)
 

	
 
        # clear out any contact it may have
 
        self.unassign_contact(batch)
 

	
 
        # create pending customer if needed
 
        pending = batch.pending_customer
 
        if not pending:
 
            pending = model.PendingCustomer()
 
            pending.user = user
 
            pending.status_code = self.enum.PENDING_CUSTOMER_STATUS_PENDING
 
            batch.pending_customer = pending
 

	
 
        # update pending customer info
 
        if 'first_name' in data:
 
            pending.first_name = data['first_name']
 
        if 'last_name' in data:
 
            pending.last_name = data['last_name']
 
        if 'display_name' in data:
 
            pending.display_name = data['display_name']
 
        else:
 
            pending.display_name = people.normalize_full_name(pending.first_name,
 
                                                              pending.last_name)
 
        if 'phone_number' in data:
 
            pending.phone_number = self.app.format_phone_number(data['phone_number'])
 
        if 'email_address' in data:
 
            pending.email_address = data['email_address']
 

	
 
        # also update the batch w/ contact info
 
        batch.contact_name = pending.display_name
 
        batch.phone_number = pending.phone_number
 
        batch.email_address = pending.email_address
 

	
 
    def get_case_size_for_product(self, product):
 
        if product.case_size:
 
            return product.case_size
 

	
 
        cost = product.cost
 
        if cost:
 
            return cost.case_size
 

	
 
    # TODO: this method should maybe not exist?  and caller just
 
    # invokes the handler directly instead?
 
    def customer_autocomplete(self, session, term, **kwargs):
 
        """
 
        Override the Customer autocomplete, to search by phone number
 
        as well as name.
 
        """
 
        autocompleter = self.app.get_autocompleter('customers.neworder')
 
        return autocompleter.autocomplete(session, term, **kwargs)
 

	
 
    # TODO: this method should maybe not exist?  and caller just
 
    # invokes the handler directly instead?
 
    def person_autocomplete(self, session, term, **kwargs):
 
        """
 
        Override the Person autocomplete, to search by phone number as
 
        well as name.
 
        """
 
        autocompleter = self.app.get_autocompleter('people.neworder')
 
        return autocompleter.autocomplete(session, term, **kwargs)
 

	
 
    def get_customer_info(self, batch, **kwargs):
 
        """
 
        Return a data dict containing misc. info pertaining to the
 
        customer/person for the order batch.
 
        """
 
        info = {
 
            'customer_uuid': None,
 
            'person_uuid': None,
 
            'phone_number': None,
 
            'email_address': None,
 
        }
 

	
 
        if batch.customer:
 
            info['customer_uuid'] = batch.customer.uuid
 
            phone = batch.customer.first_phone()
 
            if phone:
 
                info['phone_number'] = phone.number
 
            email = batch.customer.first_email()
 
            if email:
 
                info['email_address'] = email.address
 

	
 
        if batch.person:
 
            info['person_uuid'] = batch.person.uuid
 
            if not info['phone_number']:
 
                phone = batch.person.first_phone()
 
                if phone:
 
                    info['phone_number'] = phone.number
 
                email = batch.person.first_email()
 
                if email:
 
                    info['email_address'] = email.address
 

	
 
        return info
 

	
 
    def custom_product_autocomplete(self, session, term, **kwargs):
 
        """
 
        For the given term, this should return a (possibly empty) list
 
        of products which "match" the term.  Each element in the list
 
        should be a dict with "label" and "value" keys.
 
        """
 
        raise NotImplementedError("Please define the "
 
                                  "{}.custom_product_autocomplete() "
 
                                  "method.".format(__class__.__name__))
 

	
 
    def why_not_add_product(self, product, batch):
 
        """
 
        This method can inspect the given product, and batch, to
 
        determine if the product may be added to the batch as a new
 
        row.  Useful to e.g. prevent one customer from ordering too
 
        many things, etc.
 

	
 
        :returns: If there is a reason not to add the product, should
 
           return that reason as a string; otherwise ``None``.
 
        """
 

	
 
    def add_product(self, batch, product, order_quantity, order_uom,
 
                    **kwargs):
 
        """
 
        Add a new row to the batch, for the given product and order
 
        quantity.
 
        """
 
        row = self.make_row()
 
        row.item_entry = product.uuid
 
        row.product = product
 
        row.order_quantity = order_quantity
 
        row.order_uom = order_uom
 
        if 'price_needs_confirmation' in kwargs:
 
            row.price_needs_confirmation = kwargs['price_needs_confirmation']
 
        self.add_row(batch, row)
 
        return row
 

	
 
    def refresh_row(self, row):
 
        if not row.product:
 
            if row.item_entry:
 
                session = orm.object_session(row)
 
                # TODO: should do more than just query for uuid here
 
                product = session.query(model.Product).get(row.item_entry)
 
                if product:
 
                    row.product = product
 
            if not row.product:
 
                row.status_code = row.STATUS_PRODUCT_NOT_FOUND
 
                return
 

	
 
        product = row.product
 
        row.product_upc = product.upc
 
        row.product_brand = six.text_type(product.brand or "")
 
        row.product_description = product.description
 
        row.product_size = product.size
 
        row.product_weighed = product.weighed
 
        row.case_quantity = self.get_case_size_for_product(product)
 

	
 
        department = product.department
 
        row.department_number = department.number if department else None
 
        row.department_name = department.name if department else None
 

	
 
        cost = product.cost
 
        row.product_unit_cost = cost.unit_cost if cost else None
 

	
 
        regprice = product.regular_price
 
        row.unit_price = regprice.price if regprice else None
 

	
 
        # we need to know if total price is updated
 
        old_total = row.total_price
 

	
 
        # maybe update total price
 
        if row.unit_price is None:
 
            row.total_price = None
 
        elif not row.unit_price:
 
            row.total_price = 0
 
        else:
 
            row.total_price = row.unit_price * row.order_quantity
 
            if row.order_uom == self.enum.UNIT_OF_MEASURE_CASE:
 
                row.total_price *= (row.case_quantity or 1)
 

	
 
        # update total price for batch too, if it 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))
 

	
 
        row.status_code = row.STATUS_OK
 

	
 
    def remove_row(self, row):
 
        batch = row.batch
 

	
 
        if not row.removed:
 
            row.removed = True
 

	
 
            if row.total_price:
 
                batch.total_price = (batch.total_price or 0) - row.total_price
 

	
 
        self.refresh_batch_status(batch)
 

	
 
    def execute(self, batch, user=None, progress=None, **kwargs):
 
        """
 
        Default behavior here will create and return a new rattail
 
        Customer Order.  It also may "add contact info" e.g. to the
 
        customer record.  Override as needed.
 
        """
 
        order = self.make_new_order(batch, user=user, progress=progress, **kwargs)
 
        self.update_contact_info(batch, user)
 
        return order
 

	
 
    def update_contact_info(self, batch, user, **kwargs):
 
        """
 
        Update contact info from the batch, onto the customer record.
 
        """
 
        if batch.get_param('add_phone_number'):
 
            self.add_phone_number(batch, user)
 
        if batch.get_param('add_email_address'):
 
            self.add_email_address(batch, user)
 

	
 
    def add_phone_number(self, batch, user, **kwargs):
 
        """
 
        Add phone number from the batch to the customer record.
 

	
 
        Note that the default behavior does *not* do that, but instead
 
        will send an email alert to configured recipient(s) with the
 
        update request.
 
        """
 
        self.app.send_email('new_phone_requested', {
 
            'user': user,
 
            'user_display': user.display_name if user else "(unknown user)",
 
            'contact': self.get_contact(batch),
 
            'contact_id': self.get_contact_id(batch),
 
            'phone_number': batch.phone_number,
 
        })
 

	
 
    def add_email_address(self, batch, user, **kwargs):
 
        """
 
        Add email address from the batch to the customer record.
 

	
 
        Note that the default behavior does *not* do that, but instead
 
        will send an email alert to configured recipient(s) with the
 
        update request.
 
        """
 
        self.app.send_email('new_email_requested', {
 
            'user': user,
 
            'user_display': user.display_name if user else "(unknown user)",
 
            'contact': self.get_contact(batch),
 
            'contact_id': self.get_contact_id(batch),
 
            'email_address': batch.email_address,
 
        })
 

	
 
    def make_new_order(self, batch, user=None, progress=None, **kwargs):
 
        """
 
        Create and return a new rattail Customer Order based on the
 
        batch contents.
 
        """
 
        batch_fields = [
 
            'store',
 
            'id',
 
            'customer',
 
            'person',
 
            'pending_customer',
 
            'phone_number',
 
            'email_address',
 
            'total_price',
 
        ]
 

	
 
        order = model.CustomerOrder()
 
        order.created_by = user
 
        order.status_code = self.enum.CUSTORDER_STATUS_ORDERED
 
        for field in batch_fields:
 
            setattr(order, field, getattr(batch, field))
 

	
 
        row_fields = [
 
            'product',
 
            'product_upc',
 
            'product_brand',
 
            'product_description',
 
            'product_size',
 
            'product_weighed',
 
            'department_number',
 
            'department_name',
 
            'case_quantity',
 
            'order_quantity',
 
            'order_uom',
 
            'product_unit_cost',
 
            'unit_price',
 
            'discount_percent',
 
            'total_price',
 
            'price_needs_confirmation',
 
            'paid_amount',
 
            'payment_transaction_number',
 
        ]
 

	
 
        def convert(row, i):
 

	
 
            # add new order item
 
            item = model.CustomerOrderItem()
 
            item.sequence = i
 
            item.status_code = self.enum.CUSTORDER_ITEM_STATUS_INITIATED
 
            for field in row_fields:
 
                setattr(item, field, getattr(row, field))
 
            order.items.append(item)
 

	
 
            # attach event
 
            item.events.append(model.CustomerOrderItemEvent(
 
                type_code=self.enum.CUSTORDER_ITEM_EVENT_INITIATED,
 
                user=user))
 
            # set initial status and attach events
 
            self.set_initial_item_status(item, user)
 

	
 
        self.progress_loop(convert, batch.active_rows(), progress,
 
                           message="Converting batch rows to order items")
 

	
 
        session = orm.object_session(batch)
 
        session.add(order)
 
        session.flush()
 

	
 
        return order
 

	
 
    def set_initial_item_status(self, item, user, **kwargs):
 
        """
 
        Set the initial status for the given order item, and attach
 
        any events.
 

	
 
        The first logical status is ``CUSTORDER_ITEM_STATUS_INITIATED``
 
        and an item may stay there if there is some other step(s)
 
        which must occur before the item is ready to proceed.  For
 
        instance the default logic will leave it there if the price
 
        needs to be confirmed, but you can override as needed, for
 
        instance if you require payment up-front.
 

	
 
        The second status is ``CUSTORDER_ITEM_STATUS_READY`` which
 
        indicates the item is ready to proceed.  The default logic
 
        will auto-advance the item to this status if the price does
 
        *not* need to be confirmed.  Again you may need to override
 
        e.g. to prevent this until up-front payment is received.
 
        """
 
        # set "initiated" status
 
        item.status_code = self.enum.CUSTORDER_ITEM_STATUS_INITIATED
 
        item.add_event(self.enum.CUSTORDER_ITEM_EVENT_INITIATED, user)
 

	
 
        # but if the price is good...
 
        if not item.price_needs_confirmation:
 

	
 
            # then we set "ready" status
 
            item.status_code = self.enum.CUSTORDER_ITEM_STATUS_READY
 
            item.status_text = "everything looks normal"
 

	
 
            item.add_event(self.enum.CUSTORDER_ITEM_EVENT_READY, user,
 
                           note=item.status_text)
rattail/db/alembic/versions/8856f697902d_add_custorder_item_status_text.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
"""add custorder_item.status_text
 

	
 
Revision ID: 8856f697902d
 
Revises: 8b78ef45a36c
 
Create Date: 2021-10-18 11:51:34.326750
 

	
 
"""
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
# revision identifiers, used by Alembic.
 
revision = '8856f697902d'
 
down_revision = '8b78ef45a36c'
 
branch_labels = None
 
depends_on = None
 

	
 
from alembic import op
 
import sqlalchemy as sa
 
import rattail.db.types
 

	
 

	
 

	
 
def upgrade():
 

	
 
    # custorder_item
 
    op.add_column('custorder_item', sa.Column('price_needs_confirmation', sa.Boolean(), nullable=True))
 
    op.add_column('custorder_item', sa.Column('status_text', sa.String(length=255), nullable=True))
 

	
 
    # batch_custorder_row
 
    op.add_column('batch_custorder_row', sa.Column('price_needs_confirmation', sa.Boolean(), nullable=True))
 

	
 

	
 
def downgrade():
 

	
 
    # batch_custorder_row
 
    op.drop_column('batch_custorder_row', 'price_needs_confirmation')
 

	
 
    # custorder_item
 
    op.drop_column('custorder_item', 'status_text')
 
    op.drop_column('custorder_item', 'price_needs_confirmation')
rattail/db/model/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/>.
 
#
 
################################################################################
 
"""
 
Models for customer order batches
 
"""
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
import sqlalchemy as sa
 
from sqlalchemy import orm
 
from sqlalchemy.ext.declarative import declared_attr
 

	
 
from rattail.db.model import (Base, BatchMixin, BatchRowMixin,
 
                              CustomerOrderBase, CustomerOrderItemBase,
 
                              CustomerOrder, CustomerOrderItem)
 

	
 

	
 
class CustomerOrderBatch(BatchMixin, CustomerOrderBase, Base):
 
    """
 
    Hopefully generic batch used for entering new customer orders into the
 
    system, as well as fulfilling them along the way, etc.
 
    """
 
    batch_key = 'custorder'
 
    __tablename__ = 'batch_custorder'
 
    __batchrow_class__ = 'CustomerOrderBatchRow'
 
    model_title_plural = "Customer Order Batches"
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__batch_table_args__() + cls.__customer_order_table_args__() + (
 
            sa.ForeignKeyConstraint(['order_uuid'], ['custorder.uuid'],
 
                                    name='batch_custorder_fk_order'),
 
        )
 

	
 
    STATUS_OK                           = 1
 

	
 
    STATUS = {
 
        STATUS_OK                       : "ok",
 
    }
 

	
 
    order_uuid = sa.Column(sa.String(length=32), nullable=True)
 
    order = orm.relationship(
 
        CustomerOrder,
 
        doc="""
 
        Reference to the customer order with which the batch is associated.
 
        May be null, e.g. in the case of a "new order" batch.
 
        """,
 
        backref=orm.backref(
 
            'batches',
 
            order_by='CustomerOrderBatch.id',
 
            doc="""
 
            List of batches associated with the customer order.
 
            """))
 

	
 
    mode = sa.Column(sa.Integer(), nullable=False, doc="""
 
    Numeric "mode" for the customer order batch, to indicate new/fulfilling etc.
 
    """)
 

	
 

	
 
class CustomerOrderBatchRow(BatchRowMixin, CustomerOrderItemBase, Base):
 
    """
 
    Row of data within a customer order batch.
 
    """
 
    __tablename__ = 'batch_custorder_row'
 
    __batch_class__ = CustomerOrderBatch
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__batchrow_table_args__() + cls.__customer_order_item_table_args__() + (
 
            sa.ForeignKeyConstraint(['item_uuid'], ['custorder_item.uuid'],
 
                                    name='batch_custorder_row_fk_item'),
 
        )
 

	
 
    STATUS_OK                           = 1
 
    STATUS_PRODUCT_NOT_FOUND            = 2
 
    # STATUS_PRICE_NEEDS_CONFIRMATION     = 3
 

	
 
    STATUS = {
 
        STATUS_OK                       : "ok",
 
        STATUS_PRODUCT_NOT_FOUND        : "product not found",
 
        # STATUS_PRICE_NEEDS_CONFIRMATION : "price needs to be confirmed",
 
    }
 

	
 
    item_entry = sa.Column(sa.String(length=32), nullable=True, doc="""
 
    Raw entry value, as obtained from the initial data source, and which is
 
    used to locate the product within the system.  This raw value is preserved
 
    in case the initial lookup fails and a refresh must attempt further
 
    lookup(s) later.  Only used by certain batch handlers in practice.
 
    """)
 

	
 
    item_uuid = sa.Column(sa.String(length=32), nullable=True)
 
    item = orm.relationship(
 
        CustomerOrderItem,
 
        doc="""
 
        Reference to the customer order line item with which the batch row is
 
        associated.  May be null, e.g. in the case of a "new order" batch.
 
        """)
rattail/db/model/custorders.py
Show inline comments
 
@@ -93,295 +93,318 @@ class CustomerOrderBase(object):
 
            """)
 

	
 
    pending_customer_uuid = sa.Column(sa.String(length=32), nullable=True)
 

	
 
    @declared_attr
 
    def pending_customer(cls):
 
        return orm.relationship(
 
            PendingCustomer,
 
            doc="""
 
            Reference to the *pending* customer account for the order,
 
            if applicable.
 
            """)
 

	
 
    contact_name = sa.Column(sa.String(length=100), nullable=True, doc="""
 
    Cached display name for the contact (customer).
 
    """)
 

	
 
    phone_number = sa.Column(sa.String(length=20), nullable=True, doc="""
 
    Customer contact phone number for sake of this order.
 
    """)
 

	
 
    email_address = sa.Column(sa.String(length=255), nullable=True, doc="""
 
    Customer contact email address for sake of this order.
 
    """)
 

	
 
    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.
 
    """)
 

	
 

	
 
@six.python_2_unicode_compatible
 
class CustomerOrder(CustomerOrderBase, Base):
 
    """
 
    Represents an order placed by the customer.
 
    """
 
    __tablename__ = 'custorder'
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__customer_order_table_args__() + (
 
            sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'],
 
                                    name='custorder_fk_created_by'),
 
        )
 

	
 
    uuid = uuid_column()
 

	
 
    id = sa.Column(sa.Integer(), doc="""
 
    Numeric, auto-increment ID for the order.
 
    """)
 

	
 
    created = sa.Column(sa.DateTime(), nullable=False, default=datetime.datetime.utcnow, doc="""
 
    Date and time when the order/batch was first created.
 
    """)
 

	
 
    created_by_uuid = sa.Column(sa.String(length=32), nullable=True)
 
    created_by = orm.relationship(
 
        User,
 
        doc="""
 
        Reference to the user who initially created the order/batch.
 
        """)
 

	
 
    status_code = sa.Column(sa.Integer(), nullable=False)
 

	
 
    items = orm.relationship(
 
        'CustomerOrderItem',
 
        back_populates='order',
 
        collection_class=ordering_list('sequence', count_from=1),
 
        cascade='all, delete-orphan',
 
        doc="""
 
        Sequence of :class:`CustomerOrderItem` instances which belong to the order.
 
        """)
 

	
 
    def __str__(self):
 
        if self.id:
 
            return "#{}".format(self.id)
 
        return "(pending)"
 

	
 

	
 
@six.python_2_unicode_compatible
 
class CustomerOrderItemBase(object):
 
    """
 
    Base class for customer order line items.
 
    """
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__customer_order_item_table_args__()
 

	
 
    @classmethod
 
    def __customer_order_item_table_args__(cls):
 
        table_name = cls.__tablename__
 
        return (
 
            sa.ForeignKeyConstraint(['product_uuid'], ['product.uuid'],
 
                                    name='{}_fk_product'.format(table_name)),
 
        )
 

	
 
    product_uuid = sa.Column(sa.String(length=32), nullable=True)
 

	
 
    @declared_attr
 
    def product(cls):
 
        return orm.relationship(
 
            Product,
 
            doc="""
 
            Reference to the master product record for the line item.
 
            """)
 

	
 
    product_upc = sa.Column(GPCType(), nullable=True, doc="""
 
    UPC for the product associated with the row.
 
    """)
 

	
 
    product_brand = sa.Column(sa.String(length=100), nullable=True, doc="""
 
    Brand name for the product being ordered.  This should be a cache of the
 
    relevant :attr:`Brand.name`.
 
    """)
 

	
 
    product_description = sa.Column(sa.String(length=60), nullable=True, doc="""
 
    Primary description for the product being ordered.  This should be a cache
 
    of :attr:`Product.description`.
 
    """)
 

	
 
    product_size = sa.Column(sa.String(length=30), nullable=True, doc="""
 
    Size of the product being ordered.  This should be a cache of
 
    :attr:`Product.size`.
 
    """)
 

	
 
    product_weighed = sa.Column(sa.String(length=4), nullable=True, doc="""
 
    Flag indicating whether the product is sold by weight.  This should be a
 
    cache of :attr:`Product.weighed`.
 
    """)
 

	
 
    # TODO: probably should get rid of this, i can't think of why it's needed.
 
    # for now we just make sure it is nullable, since that wasn't the case.
 
    product_unit_of_measure = sa.Column(sa.String(length=4), nullable=True, doc="""
 
    Code indicating the unit of measure for the product.  This should be a
 
    cache of :attr:`Product.unit_of_measure`.
 
    """)
 

	
 
    department_number = sa.Column(sa.Integer(), nullable=True, doc="""
 
    Number of the department to which the product belongs.
 
    """)
 

	
 
    department_name = sa.Column(sa.String(length=30), nullable=True, doc="""
 
    Name of the department to which the product belongs.
 
    """)
 

	
 
    case_quantity = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
 
    Case pack count for the product being ordered.  This should be a cache of
 
    :attr:`Product.case_size`.
 
    """)
 

	
 
    # TODO: i now think that cases_ordered and units_ordered should go away.
 
    # but will wait until that idea has proven itself before removing.  am
 
    # pretty sure they are obviated by order_quantity and order_uom.
 

	
 
    cases_ordered = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
 
    Number of cases of product which were initially ordered.
 
    """)
 

	
 
    units_ordered = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
 
    Number of units of product which were initially ordered.
 
    """)
 

	
 
    order_quantity = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
 
    Quantity being ordered by the customer.
 
    """)
 

	
 
    order_uom = sa.Column(sa.String(length=4), nullable=True, doc="""
 
    Code indicating the unit of measure for the order itself.  Does not
 
    directly reflect the :attr:`~rattail.db.model.Product.unit_of_measure`.
 
    """)
 

	
 
    product_unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc="""
 
    Unit cost of the product being ordered.  This should be a cache of the
 
    relevant :attr:`rattail.db.model.ProductCost.unit_cost`.
 
    """)
 

	
 
    unit_price = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc="""
 
    Unit price for the product being ordered.  This is the price which is
 
    quoted to the customer and/or charged to the customer, but for a unit only
 
    and *before* any discounts are applied.  It generally will be a cache of
 
    the relevant :attr:`ProductPrice.price`.
 
    """)
 

	
 
    discount_percent = sa.Column(sa.Numeric(precision=5, scale=3), nullable=False, default=0, doc="""
 
    Discount percentage which will be applied to the product's price as part of
 
    calculating the :attr:`total_price` for the item.
 
    """)
 

	
 
    total_price = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc="""
 
    Full price (not including tax etc.) which the customer is asked to pay for the item.
 
    """)
 

	
 
    price_needs_confirmation = sa.Column(sa.Boolean(), nullable=True, doc="""
 
    Flag indicating that the price for this item should be confirmed
 
    by someone, before the order advances to the procurement phase.
 

	
 
    Items/rows with this flag set will probably indicate that also via
 
    their status.
 

	
 
    When the price is eventually confirmed by someone, this flag
 
    should be cleared and probably the status will update as well.
 
    """)
 

	
 
    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 theitem.
 
    """)
 

	
 
    payment_transaction_number = sa.Column(sa.String(length=8), nullable=True, doc="""
 
    Transaction number in which payment for the order was taken, if applicable.
 
    """)
 

	
 
    def __str__(self):
 
        return str(self.product or "(no product)")
 

	
 

	
 
class CustomerOrderItem(CustomerOrderItemBase, Base):
 
    """
 
    Represents a particular line item (product) within a customer order.
 
    """
 
    __tablename__ = 'custorder_item'
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__customer_order_item_table_args__() + (
 
            sa.ForeignKeyConstraint(['order_uuid'], ['custorder.uuid'],
 
                                    name='custorder_item_fk_order'),
 
        )
 

	
 
    uuid = uuid_column()
 

	
 
    order_uuid = sa.Column(sa.String(length=32), nullable=False)
 
    order = orm.relationship(CustomerOrder, back_populates='items', doc="""
 
    Reference to the :class:`CustomerOrder` instance to which the item belongs.
 
    """)
 

	
 
    sequence = sa.Column(sa.Integer(), nullable=False, doc="""
 
    Numeric sequence for the item, i.e. its "line number".  These values should
 
    obviously increment in sequence and be unique within the context of a
 
    single order.
 
    """)
 

	
 
    status_code = sa.Column(sa.Integer(), nullable=False)
 

	
 
    status_text = sa.Column(sa.String(length=255), nullable=True, doc="""
 
    Text which may briefly explain the batch status code, if needed.
 
    """)
 

	
 
    def add_event(self, type_code, user, **kwargs):
 
        """
 
        Convenience method to add an event for the order item.
 
        """
 
        self.events.append(CustomerOrderItemEvent(type_code=type_code,
 
                                                  user=user,
 
                                                  **kwargs))
 

	
 

	
 
class CustomerOrderItemEvent(Base):
 
    """
 
    An event in the life of a customer order item
 
    """
 
    __tablename__ = 'custorder_item_event'
 
    __table_args__ = (
 
        sa.ForeignKeyConstraint(['item_uuid'], ['custorder_item.uuid'], name='custorder_item_event_fk_item'),
 
        sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], name='custorder_item_event_fk_user'),
 
    )
 

	
 
    uuid = uuid_column()
 

	
 
    item_uuid = sa.Column(sa.String(length=32), nullable=False)
 

	
 
    item = orm.relationship(
 
        CustomerOrderItem,
 
        doc="""
 
        Reference to the :class:`CustomerOrder` instance to which the item belongs.
 
        """,
 
        backref=orm.backref(
 
            'events',
 
            order_by='CustomerOrderItemEvent.occurred'))
 

	
 
    type_code = sa.Column(sa.Integer, nullable=False)
 

	
 
    occurred = sa.Column(sa.DateTime(), nullable=False, default=datetime.datetime.utcnow, doc="""
 
    Date and time when the event occurred.
 
    """)
 

	
 
    user_uuid = sa.Column(sa.String(length=32), nullable=False)
 

	
 
    user = orm.relationship(
 
        User,
 
        doc="""
 
        User who was the "actor" for the event.
 
        """)
 

	
 
    note = sa.Column(sa.Text(), nullable=True, doc="""
 
    Optional note recorded for the event.
 
    """)
 

	
 

	
 
class CustomerOrderItemNote(Note):
 
    """
 
    Represents a note attached to an order item.
 
    """
 
    __mapper_args__ = {'polymorphic_identity': 'CustomerOrderItem'}
 

	
 
CustomerOrderItem.notes = orm.relationship(
 
    CustomerOrderItemNote,
 
    primaryjoin=CustomerOrderItemNote.parent_uuid == CustomerOrderItem.uuid,
 
    foreign_keys=[CustomerOrderItemNote.parent_uuid],
 
    order_by=CustomerOrderItemNote.created,
 
    cascade='all, delete-orphan',
 
    doc="""
 
    Sequence of notes attached to the order item.
 
    """,
 
    backref=orm.backref(
 
        'person',
 
        doc="""
 
        Reference to the order item to which the note is attached.
 
        """))
rattail/enum.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/>.
 
#
 
################################################################################
 
"""
 
**Enumerations**
 

	
 
The following enumerations are provided:
 

	
 
.. attribute:: BATCH_ACTION
 

	
 
   Action types for use with batches.  These are taken from the SIL
 
   specification.
 

	
 
.. attribute:: EMAIL_PREFERENCE
 

	
 
   Various options indicating a person's preferences on whether to receive
 
   email, and if so, in what format.
 

	
 
.. attribute:: EMPLOYEE_STATUS
 

	
 
   Status types for employees (e.g. current, former).
 

	
 
.. attribute:: PHONE_TYPE
 

	
 
   Various "types" of phone contact information (e.g. home, work).
 

	
 
.. attribute:: PRICE_TYPE
 

	
 
   Various types of prices which may exist for a product.  These are taken from
 
   the SIL specification.
 

	
 
.. attribute:: UNIT_OF_MEASURE
 

	
 
   Units of measure for use with products (e.g. each, pound).  These are taken
 
   from the SIL specification.
 
"""
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
from rattail.util import OrderedDict
 

	
 

	
 
BATCH_ACTION_ADD                = 'ADD'
 
BATCH_ACTION_ADD_REPLACE        = 'ADDRPL'
 
BATCH_ACTION_CHANGE             = 'CHANGE'
 
BATCH_ACTION_LOAD               = 'LOAD'
 
BATCH_ACTION_REMOVE             = 'REMOVE'
 

	
 
BATCH_ACTION = {
 
    BATCH_ACTION_ADD            : "Add",
 
    BATCH_ACTION_ADD_REPLACE    : "Add/Replace",
 
    BATCH_ACTION_CHANGE         : "Change",
 
    BATCH_ACTION_LOAD           : "Load",
 
    BATCH_ACTION_REMOVE         : "Remove",
 
    }
 

	
 

	
 
CUSTORDER_BATCH_MODE_CREATING            = 10
 
CUSTORDER_BATCH_MODE_GATHERING           = 20
 

	
 
CUSTORDER_BATCH_MODE = {
 
    CUSTORDER_BATCH_MODE_CREATING        : "creating",
 
    CUSTORDER_BATCH_MODE_GATHERING       : "gathering",
 
}
 

	
 

	
 
CUSTORDER_STATUS_ORDERED                = 10
 
# CUSTORDER_STATUS_PAID                   = 20
 

	
 
CUSTORDER_STATUS = {
 
    CUSTORDER_STATUS_ORDERED            : "ordered",
 
    # CUSTORDER_STATUS_PAID               : "paid",
 
}
 

	
 

	
 
CUSTORDER_ITEM_STATUS_INITIATED         = 10
 
# TODO: deprecate / remove this one
 
CUSTORDER_ITEM_STATUS_ORDERED           = CUSTORDER_ITEM_STATUS_INITIATED
 
CUSTORDER_ITEM_STATUS_PAID              = 20
 
CUSTORDER_ITEM_STATUS_READY             = 20
 
# TODO: deprecate / remove this one
 
CUSTORDER_ITEM_STATUS_PAID              = CUSTORDER_ITEM_STATUS_READY
 
CUSTORDER_ITEM_STATUS_PLACED            = 30
 
CUSTORDER_ITEM_STATUS_RECEIVED          = 40
 
CUSTORDER_ITEM_STATUS_CONTACTED         = 50
 
CUSTORDER_ITEM_STATUS_CONTACT_FAILED    = 60
 
CUSTORDER_ITEM_STATUS_DELIVERED         = 70
 
CUSTORDER_ITEM_STATUS_CANCELED          = 900
 
CUSTORDER_ITEM_STATUS_REFUND_PENDING    = 910
 
CUSTORDER_ITEM_STATUS_REFUNDED          = 920
 
CUSTORDER_ITEM_STATUS_RESTOCKED         = 930
 
CUSTORDER_ITEM_STATUS_EXPIRED           = 940
 
CUSTORDER_ITEM_STATUS_INACTIVE          = 950
 

	
 
CUSTORDER_ITEM_STATUS = OrderedDict([
 
    (CUSTORDER_ITEM_STATUS_INITIATED,           "customer order initiated"),
 
    (CUSTORDER_ITEM_STATUS_PAID,                "payment received"),
 
    # (CUSTORDER_ITEM_STATUS_PAID,                "payment received"),
 
    (CUSTORDER_ITEM_STATUS_READY,               "ready to proceed"),
 
    (CUSTORDER_ITEM_STATUS_PLACED,              "order placed with vendor"),
 
    (CUSTORDER_ITEM_STATUS_RECEIVED,            "received from vendor"),
 
    (CUSTORDER_ITEM_STATUS_CONTACTED,           "customer contacted"),
 
    (CUSTORDER_ITEM_STATUS_CONTACT_FAILED,      "unable to contact customer"),
 
    (CUSTORDER_ITEM_STATUS_DELIVERED,           "delivered to customer"),
 
    (CUSTORDER_ITEM_STATUS_CANCELED,            "canceled"),
 
    (CUSTORDER_ITEM_STATUS_REFUND_PENDING,      "refund pending"),
 
    (CUSTORDER_ITEM_STATUS_REFUNDED,            "refunded"),
 
    (CUSTORDER_ITEM_STATUS_RESTOCKED,           "restocked"),
 
    (CUSTORDER_ITEM_STATUS_EXPIRED,             "expired"),
 
    (CUSTORDER_ITEM_STATUS_INACTIVE,            "inactive"),
 
])
 

	
 

	
 
CUSTORDER_ITEM_EVENT_INITIATED          = 10
 
CUSTORDER_ITEM_EVENT_PAID               = 20
 
CUSTORDER_ITEM_EVENT_PRICE_CONFIRMED    = 15
 
CUSTORDER_ITEM_EVENT_READY              = 20
 
# TODO: deprecate / remove this one
 
CUSTORDER_ITEM_EVENT_PAID               = CUSTORDER_ITEM_EVENT_READY
 
CUSTORDER_ITEM_EVENT_PLACED             = 30
 
CUSTORDER_ITEM_EVENT_RECEIVED           = 40
 
CUSTORDER_ITEM_EVENT_CONTACTED          = 50
 
CUSTORDER_ITEM_EVENT_CONTACT_FAILED     = 60
 
CUSTORDER_ITEM_EVENT_DELIVERED          = 70
 
CUSTORDER_ITEM_EVENT_STATUS_CHANGE      = 500 # nb. this is not in STATUS enum
 
# CUSTORDER_ITEM_EVENT_ADDED_NOTE         = 510 # nb. this is not in STATUS enum
 
CUSTORDER_ITEM_EVENT_CANCELED           = 900
 
CUSTORDER_ITEM_EVENT_REFUND_PENDING     = 910
 
CUSTORDER_ITEM_EVENT_REFUNDED           = 920
 
CUSTORDER_ITEM_EVENT_RESTOCKED          = 930
 
CUSTORDER_ITEM_EVENT_EXPIRED            = 940
 
CUSTORDER_ITEM_EVENT_INACTIVE           = 950
 

	
 
CUSTORDER_ITEM_EVENT = OrderedDict([
 
    (CUSTORDER_ITEM_EVENT_INITIATED,            "customer order initiated"),
 
    (CUSTORDER_ITEM_EVENT_PAID,                 "payment received"),
 
    (CUSTORDER_ITEM_EVENT_PRICE_CONFIRMED,      "price confirmed"),
 
    (CUSTORDER_ITEM_EVENT_READY,                "order becomes ready to proceed"),
 
    # (CUSTORDER_ITEM_EVENT_PAID,                 "payment received"),
 
    (CUSTORDER_ITEM_EVENT_PLACED,               "order placed with vendor"),
 
    (CUSTORDER_ITEM_EVENT_RECEIVED,             "received from vendor"),
 
    (CUSTORDER_ITEM_EVENT_CONTACTED,            "customer contacted"),
 
    (CUSTORDER_ITEM_EVENT_CONTACT_FAILED,       "unable to contact customer"),
 
    (CUSTORDER_ITEM_EVENT_DELIVERED,            "delivered to customer"),
 
    (CUSTORDER_ITEM_EVENT_STATUS_CHANGE,        "manual status change"),
 
    # (CUSTORDER_ITEM_EVENT_ADDED_NOTE,           "added note"),
 
    (CUSTORDER_ITEM_EVENT_CANCELED,             "canceled"),
 
    (CUSTORDER_ITEM_EVENT_REFUND_PENDING,       "refund pending"),
 
    (CUSTORDER_ITEM_EVENT_REFUNDED,             "refunded"),
 
    (CUSTORDER_ITEM_EVENT_RESTOCKED,            "restocked"),
 
    (CUSTORDER_ITEM_EVENT_EXPIRED,              "expired"),
 
    (CUSTORDER_ITEM_EVENT_INACTIVE,             "inactive"),
 
])
 

	
 

	
 
EMAIL_ATTEMPT_CREATED           = 0
 
EMAIL_ATTEMPT_SUCCESS           = 1
 
EMAIL_ATTEMPT_FAILURE           = 2
 
# EMAIL_ATTEMPT_BOUNCED           = 3
 

	
 
EMAIL_ATTEMPT = {
 
    EMAIL_ATTEMPT_CREATED       : "created",
 
    EMAIL_ATTEMPT_SUCCESS       : "success",
 
    EMAIL_ATTEMPT_FAILURE       : "failure",
 
    # EMAIL_ATTEMPT_BOUNCED       : "bounced",
 
}
 

	
 

	
 
EMAIL_PREFERENCE_NONE           = 0
 
EMAIL_PREFERENCE_TEXT           = 1
 
EMAIL_PREFERENCE_HTML           = 2
 
EMAIL_PREFERENCE_MOBILE         = 3
 

	
 
EMAIL_PREFERENCE = {
 
    EMAIL_PREFERENCE_NONE       : "No Emails",
 
    EMAIL_PREFERENCE_TEXT       : "Text",
 
    EMAIL_PREFERENCE_HTML       : "HTML",
 
    EMAIL_PREFERENCE_MOBILE     : "Mobile",
 
    }
 

	
 

	
 
HANDHELD_DEVICE_TYPE_MOTOROLA           = 'motorola'
 
HANDHELD_DEVICE_TYPE_PALMOS             = 'palmos'
 

	
 
HANDHELD_DEVICE_TYPE = {
 
    HANDHELD_DEVICE_TYPE_MOTOROLA       : "Motorola",
 
    HANDHELD_DEVICE_TYPE_PALMOS         : "PalmOS",
 
}
 

	
 

	
 
IMPORTER_BATCH_ROW_STATUS_NOCHANGE      = 0
 
IMPORTER_BATCH_ROW_STATUS_CREATE        = 1
 
IMPORTER_BATCH_ROW_STATUS_UPDATE        = 2
 
IMPORTER_BATCH_ROW_STATUS_DELETE        = 3
 

	
 
IMPORTER_BATCH_ROW_STATUS = {
 
    IMPORTER_BATCH_ROW_STATUS_NOCHANGE  : "no change",
 
    IMPORTER_BATCH_ROW_STATUS_CREATE    : "create",
 
    IMPORTER_BATCH_ROW_STATUS_UPDATE    : "update",
 
    IMPORTER_BATCH_ROW_STATUS_DELETE    : "delete",
 
}
 

	
 

	
 
INVENTORY_MODE_REPLACE          = 1
 
INVENTORY_MODE_REPLACE_ADJUST   = 2
 
INVENTORY_MODE_ADJUST           = 3
 
INVENTORY_MODE_ZERO_ALL         = 4
 
INVENTORY_MODE_VARIANCE         = 5
 

	
 
INVENTORY_MODE = {
 
    INVENTORY_MODE_REPLACE              : "Replace only",
 
    INVENTORY_MODE_REPLACE_ADJUST       : "Replace then adjust",
 
    INVENTORY_MODE_ADJUST               : "Adjust only",
 
    INVENTORY_MODE_ZERO_ALL             : "Zero all",
 
    INVENTORY_MODE_VARIANCE             : "Variance Correction",
 
}
 

	
 

	
 
MESSAGE_STATUS_INBOX            = 1
 
MESSAGE_STATUS_ARCHIVE          = 2
 

	
 
MESSAGE_STATUS = {
 
    MESSAGE_STATUS_INBOX        : "Inbox",
 
    MESSAGE_STATUS_ARCHIVE      : "Archive",
 
}
 

	
 

	
 
PENDING_CUSTOMER_STATUS_PENDING         = 1
 
PENDING_CUSTOMER_STATUS_READY           = 2
 

	
 
PENDING_CUSTOMER_STATUS = OrderedDict([
 
    (PENDING_CUSTOMER_STATUS_PENDING,           "pending"),
 
    (PENDING_CUSTOMER_STATUS_READY,             "ready"),
 
])
 

	
 

	
 
PHONE_TYPE_HOME                 = 'home'
 
PHONE_TYPE_MOBILE               = 'mobile'
 
PHONE_TYPE_OTHER                = 'other'
 

	
 
PHONE_TYPE = {
 
    PHONE_TYPE_HOME             : "Home",
 
    PHONE_TYPE_MOBILE           : "Mobile",
 
    PHONE_TYPE_OTHER            : "Other",
 
    }
 

	
 

	
 
PRICE_TYPE_REGULAR              = 0
 
PRICE_TYPE_TPR                  = 1
 
PRICE_TYPE_SALE                 = 2
 
PRICE_TYPE_MANAGER_SPECIAL      = 3
 
PRICE_TYPE_ALTERNATE            = 4
 
PRICE_TYPE_FREQUENT_SHOPPER     = 5
 
PRICE_TYPE_MFR_SUGGESTED        = 901
 

	
 
PRICE_TYPE = {
 
    PRICE_TYPE_REGULAR          : "Regular Price",
 
    PRICE_TYPE_TPR              : "TPR",
 
    PRICE_TYPE_SALE             : "Sale",
 
    PRICE_TYPE_MANAGER_SPECIAL  : "Manager Special",
 
    PRICE_TYPE_ALTERNATE        : "Alternate Price",
 
    PRICE_TYPE_FREQUENT_SHOPPER : "Frequent Shopper",
 
    PRICE_TYPE_MFR_SUGGESTED    : "Manufacturer's Suggested",
 
    }
 

	
 

	
 
PURCHASE_BATCH_MODE_ORDERING            = 10
 
PURCHASE_BATCH_MODE_RECEIVING           = 20
 
PURCHASE_BATCH_MODE_COSTING             = 30
 

	
 
PURCHASE_BATCH_MODE = {
 
    PURCHASE_BATCH_MODE_ORDERING        : "ordering",
 
    PURCHASE_BATCH_MODE_RECEIVING       : "receiving",
 
    PURCHASE_BATCH_MODE_COSTING         : "invoicing",
 
}
 

	
 

	
 
PURCHASE_CREDIT_STATUS_NEW              = 10
 
PURCHASE_CREDIT_STATUS_SUBMITTED        = 20
 
PURCHASE_CREDIT_STATUS_SATISFIED        = 30
 
PURCHASE_CREDIT_STATUS_NONCREDITABLE    = 40
 

	
 
PURCHASE_CREDIT_STATUS = {
 
    PURCHASE_CREDIT_STATUS_NEW          : "new",
 
    PURCHASE_CREDIT_STATUS_SUBMITTED    : "submitted",
 
    PURCHASE_CREDIT_STATUS_SATISFIED    : "satisfied",
 
    PURCHASE_CREDIT_STATUS_NONCREDITABLE: "non-creditable",
 
}
 

	
 

	
 
PURCHASE_STATUS_NEW             = 1 # TODO: is this needed?
 
PURCHASE_STATUS_ORDERED         = 10
 
PURCHASE_STATUS_RECEIVED        = 20
 
PURCHASE_STATUS_COSTED          = 30
 
PURCHASE_STATUS_PAID            = 40
 

	
 
PURCHASE_STATUS = {
 
    PURCHASE_STATUS_NEW         : "new/pending",
 
    PURCHASE_STATUS_ORDERED     : "ordered",
 
    PURCHASE_STATUS_RECEIVED    : "received",
 
    PURCHASE_STATUS_COSTED      : "invoiced",
 
    PURCHASE_STATUS_PAID        : "paid",
 
}
 

	
 

	
 
TEMPMON_APPLIANCE_TYPE_COOLER           = 1
 
TEMPMON_APPLIANCE_TYPE_FREEZER          = 2
 

	
 
TEMPMON_APPLIANCE_TYPE = {
 
    TEMPMON_APPLIANCE_TYPE_COOLER       : "cooler",
 
    TEMPMON_APPLIANCE_TYPE_FREEZER      : "freezer",
 
}
 

	
 

	
 
TEMPMON_DISK_TYPE_SDCARD                = 1
 
TEMPMON_DISK_TYPE_USB                   = 2
 

	
 
TEMPMON_DISK_TYPE = {
 
    TEMPMON_DISK_TYPE_SDCARD            : "SD card",
 
    TEMPMON_DISK_TYPE_USB               : "USB",
 
}
 

	
 

	
 
TEMPMON_PROBE_STATUS_GOOD_TEMP          = 1
 
TEMPMON_PROBE_STATUS_LOW_TEMP           = 2
 
TEMPMON_PROBE_STATUS_HIGH_TEMP          = 3
 
TEMPMON_PROBE_STATUS_CRITICAL_HIGH_TEMP = 4
 
# TODO: deprecate / remove this one
 
TEMPMON_PROBE_STATUS_CRITICAL_TEMP      = TEMPMON_PROBE_STATUS_CRITICAL_HIGH_TEMP
 
TEMPMON_PROBE_STATUS_ERROR              = 5
 
TEMPMON_PROBE_STATUS_CRITICAL_LOW_TEMP  = 6
0 comments (0 inline, 0 general)