Source code for sideshow.db.model.products

# -*- 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/>.
#
################################################################################
"""
Data models for Products
"""

import datetime

import sqlalchemy as sa
from sqlalchemy import orm

from wuttjamaican.db import model

from sideshow.enum import PendingProductStatus


[docs] class ProductMixin: """ Base class for product tables. This has shared columns, used by e.g.: * :class:`LocalProduct` * :class:`PendingProduct` """ scancode = sa.Column(sa.String(length=14), nullable=True, doc=""" Scancode for the product, as string. .. note:: This column allows 14 chars, so can store a full GPC with check digit. However as of writing the actual format used here does not matter to Sideshow logic; "anything" should work. That may change eventually, depending on POS integration scenarios that come up. Maybe a config option to declare whether check digit should be included or not, etc. """) brand_name = sa.Column(sa.String(length=100), nullable=True, doc=""" Brand name for the product - up to 100 chars. """) description = sa.Column(sa.String(length=255), nullable=True, doc=""" Description for the product - up to 255 chars. """) size = sa.Column(sa.String(length=30), nullable=True, doc=""" Size of the product, as string - up to 30 chars. """) weighed = sa.Column(sa.Boolean(), nullable=True, doc=""" Flag indicating the product is sold by weight; default is null. """) department_id = sa.Column(sa.String(length=10), nullable=True, doc=""" ID of the department to which the product belongs, if known. """) department_name = sa.Column(sa.String(length=30), nullable=True, doc=""" Name of the department to which the product belongs, if known. """) special_order = sa.Column(sa.Boolean(), nullable=True, doc=""" Flag indicating the item is a "special order" - e.g. something not normally carried by the store. Default is null. """) vendor_name = sa.Column(sa.String(length=50), nullable=True, doc=""" Name of vendor from which product may be purchased, if known. See also :attr:`vendor_item_code`. """) vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc=""" Item code (SKU) to use when ordering this product from the vendor identified by :attr:`vendor_name`, if known. """) case_size = sa.Column(sa.Numeric(precision=9, scale=4), nullable=True, doc=""" Case pack count for the product, if known. """) unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc=""" Cost of goods amount for one "unit" (not "case") of the product, as decimal to 4 places. """) unit_price_reg = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc=""" Regular price for a "unit" of the product. """) notes = sa.Column(sa.Text(), nullable=True, doc=""" Arbitrary notes regarding the product, if applicable. """) @property def full_description(self): """ """ fields = [ self.brand_name or '', self.description or '', self.size or ''] fields = [f.strip() for f in fields if f.strip()] return ' '.join(fields) def __str__(self): return self.full_description
[docs] class LocalProduct(ProductMixin, model.Base): """ This table contains the :term:`local product` records. Sideshow will do customer lookups against this table by default, unless it's configured to use :term:`external products <external product>` instead. Also by default, when a :term:`new order batch` with :term:`pending product(s) <pending product>` is executed, new record(s) will be added to this local products table, for lookup next time. """ __tablename__ = 'sideshow_product_local' uuid = model.uuid_column() external_id = sa.Column(sa.String(length=20), nullable=True, doc=""" ID of the true external product associated with this record, if applicable. """) order_items = orm.relationship( 'OrderItem', back_populates='local_product', cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.orders.OrderItem` records associated with this product. """) new_order_batch_rows = orm.relationship( 'NewOrderBatchRow', back_populates='local_product', cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` records associated with this product. """)
[docs] class PendingProduct(ProductMixin, model.Base): """ This table contains the :term:`pending product` records, used when creating an :term:`order` for new/unknown product(s). Sideshow will automatically create and (hopefully) delete these records as needed. By default, when a :term:`new order batch` with pending product(s) is executed, new record(s) will be added to the :term:`local products <local product>` table, for lookup next time. """ __tablename__ = 'sideshow_product_pending' uuid = model.uuid_column() product_id = sa.Column(sa.String(length=20), nullable=True, doc=""" ID of the :term:`external product` associated with this record, if applicable/known. """) status = sa.Column(sa.Enum(PendingProductStatus), nullable=False, doc=""" Status code for the product record. """) created = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" Timestamp when the product record was created. """) created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False) created_by = orm.relationship( model.User, cascade_backrefs=False, doc=""" Reference to the :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who created the product record. """) order_items = orm.relationship( 'OrderItem', back_populates='pending_product', cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.orders.OrderItem` records associated with this product. """) new_order_batch_rows = orm.relationship( 'NewOrderBatchRow', back_populates='pending_product', cascade_backrefs=False, doc=""" List of :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow` records associated with this product. """)