Coverage for src/sideshow/db/model/products.py: 100%
36 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-06 15:04 -0600
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-06 15:04 -0600
1# -*- coding: utf-8; -*-
2################################################################################
3#
4# Sideshow -- Case/Special Order Tracker
5# Copyright © 2024 Lance Edgar
6#
7# This file is part of Sideshow.
8#
9# Sideshow is free software: you can redistribute it and/or modify it
10# under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# Sideshow is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Sideshow. If not, see <http://www.gnu.org/licenses/>.
21#
22################################################################################
23"""
24Data models for Products
25"""
27import datetime
29import sqlalchemy as sa
30from sqlalchemy import orm
32from wuttjamaican.db import model
34from sideshow.enum import PendingProductStatus
37class PendingProduct(model.Base):
38 """
39 A "pending" product record, used when entering an :term:`order
40 item` for new/unknown product.
41 """
42 __tablename__ = 'sideshow_pending_product'
44 uuid = model.uuid_column()
46 product_id = sa.Column(sa.String(length=20), nullable=True, doc="""
47 ID of the true product associated with this record, if applicable.
48 """)
50 scancode = sa.Column(sa.String(length=14), nullable=True, doc="""
51 Scancode for the product, as string.
53 .. note::
55 This column allows 14 chars, so can store a full GPC with check
56 digit. However as of writing the actual format used here does
57 not matter to Sideshow logic; "anything" should work.
59 That may change eventually, depending on POS integration
60 scenarios that come up. Maybe a config option to declare
61 whether check digit should be included or not, etc.
62 """)
64 brand_name = sa.Column(sa.String(length=100), nullable=True, doc="""
65 Brand name for the product - up to 100 chars.
66 """)
68 description = sa.Column(sa.String(length=255), nullable=True, doc="""
69 Description for the product - up to 255 chars.
70 """)
72 size = sa.Column(sa.String(length=30), nullable=True, doc="""
73 Size of the product, as string - up to 30 chars.
74 """)
76 weighed = sa.Column(sa.Boolean(), nullable=True, doc="""
77 Flag indicating the product is sold by weight; default is null.
78 """)
80 department_id = sa.Column(sa.String(length=10), nullable=True, doc="""
81 ID of the department to which the product belongs, if known.
82 """)
84 department_name = sa.Column(sa.String(length=30), nullable=True, doc="""
85 Name of the department to which the product belongs, if known.
86 """)
88 special_order = sa.Column(sa.Boolean(), nullable=True, doc="""
89 Flag indicating the item is a "special order" - e.g. something not
90 normally carried by the store. Default is null.
91 """)
93 vendor_name = sa.Column(sa.String(length=50), nullable=True, doc="""
94 Name of vendor from which product may be purchased, if known. See
95 also :attr:`vendor_item_code`.
96 """)
98 vendor_item_code = sa.Column(sa.String(length=20), nullable=True, doc="""
99 Item code (SKU) to use when ordering this product from the vendor
100 identified by :attr:`vendor_name`, if known.
101 """)
103 case_size = sa.Column(sa.Numeric(precision=9, scale=4), nullable=True, doc="""
104 Case pack count for the product, if known.
105 """)
107 unit_cost = sa.Column(sa.Numeric(precision=9, scale=5), nullable=True, doc="""
108 Cost of goods amount for one "unit" (not "case") of the product,
109 as decimal to 4 places.
110 """)
112 unit_price_reg = sa.Column(sa.Numeric(precision=8, scale=3), nullable=True, doc="""
113 Regular price for a "unit" of the product.
114 """)
116 notes = sa.Column(sa.Text(), nullable=True, doc="""
117 Arbitrary notes regarding the product, if applicable.
118 """)
120 status = sa.Column(sa.Enum(PendingProductStatus), nullable=False, doc="""
121 Status code for the product record.
122 """)
124 created = sa.Column(sa.DateTime(timezone=True), nullable=False,
125 default=datetime.datetime.now, doc="""
126 Timestamp when the product record was created.
127 """)
129 created_by_uuid = model.uuid_fk_column('user.uuid', nullable=False)
130 created_by = orm.relationship(
131 model.User,
132 cascade_backrefs=False,
133 doc="""
134 Reference to the
135 :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` who
136 created the product record.
137 """)
139 order_items = orm.relationship(
140 'OrderItem',
141 # TODO
142 # order_by='NewOrderBatchRow.id.desc()',
143 cascade_backrefs=False,
144 back_populates='pending_product',
145 doc="""
146 List of :class:`~sideshow.db.model.orders.OrderItem` records
147 associated with this product.
148 """)
150 new_order_batch_rows = orm.relationship(
151 'NewOrderBatchRow',
152 # TODO
153 # order_by='NewOrderBatchRow.id.desc()',
154 cascade_backrefs=False,
155 back_populates='pending_product',
156 doc="""
157 List of
158 :class:`~sideshow.db.model.batch.neworder.NewOrderBatchRow`
159 records associated with this product.
160 """)
162 @property
163 def full_description(self):
164 """ """
165 fields = [
166 self.brand_name or '',
167 self.description or '',
168 self.size or '']
169 fields = [f.strip() for f in fields if f.strip()]
170 return ' '.join(fields)
172 def __str__(self):
173 return self.full_description