feat(UI): Stock Items page added with getting, filtering, and saving data.

This commit is contained in:
2024-10-13 19:40:25 +01:00
parent 818d049702
commit f6a6966ce4
183 changed files with 5747 additions and 8607 deletions

View File

@@ -0,0 +1,97 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Address Business Object
Description:
Business object for address
"""
# internal
import lib.argument_validation as av
from business_objects.base import Base
from business_objects.region import Region
from extensions import db
# external
from typing import ClassVar
class Address(db.Model, Base):
FLAG_ADDRESS_LINE_1: ClassVar[str] = 'address_line_1'
FLAG_ADDRESS_LINE_2: ClassVar[str] = 'address_line_2'
FLAG_CITY: ClassVar[str] = 'city'
FLAG_COUNTY: ClassVar[str] = 'county'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_ADDRESS
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_POSTCODE
__tablename__ = 'Shop_Address'
id_address = db.Column(db.Integer, primary_key=True)
id_region = db.Column(db.Integer)
postcode = db.Column(db.String(20))
address_line_1 = db.Column(db.String(256))
address_line_2 = db.Column(db.String(256))
city = db.Column(db.String(256))
county = db.Column(db.String(256))
active = db.Column(db.Boolean)
# region = None
def __init__(self):
super().__init__()
Base.__init__(self)
self.region = None
@classmethod
def from_DB_storage_location(cls, query_row):
address = cls()
address.id_address = query_row[2]
address.id_region = query_row[3]
return address
@classmethod
def from_DB_plant(cls, query_row):
address = cls()
address.id_address = query_row[1]
address.id_region = query_row[2]
@classmethod
def from_DB_stock_item(cls, query_row):
address = cls()
address.id_address = query_row[6]
address.id_region = query_row[7]
return address
def __repr__(self):
return f'''
{self.ATTR_ID_ADDRESS}: {self.id_address}
{self.FLAG_REGION}: {self.region}
{self.FLAG_POSTCODE}: {self.postcode}
{self.FLAG_ADDRESS_LINE_1}: {self.address_line_1}
{self.FLAG_ADDRESS_LINE_2}: {self.address_line_2}
{self.FLAG_CITY}: {self.city}
{self.FLAG_COUNTY}: {self.county}
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_ADDRESS: self.id_address,
self.FLAG_REGION: self.region.to_json(),
self.FLAG_POSTCODE: self.postcode,
self.FLAG_ADDRESS_LINE_1: self.address_line_1,
self.FLAG_ADDRESS_LINE_2: self.address_line_2,
self.FLAG_CITY: self.city,
self.FLAG_COUNTY: self.county,
self.FLAG_ACTIVE: 1 if av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0
}
@classmethod
def from_json(cls, json):
print(f'{cls.__name__}.from_json: {json}')
address = cls()
address.id_address = json[cls.ATTR_ID_ADDRESS],
address.region = Region.from_json(json[cls.FLAG_REGION]),
address.postcode = json[cls.FLAG_POSTCODE],
address.address_line_1 = json[cls.FLAG_ADDRESS_LINE_1],
address.address_line_2 = json.get(cls.FLAG_ADDRESS_LINE_2, ''),
address.city = json[cls.FLAG_CITY],
address.county = json[cls.FLAG_COUNTY],
address.active = json[cls.FLAG_ACTIVE]
return address

View File

@@ -19,15 +19,20 @@ from typing import ClassVar
class Base():
ATTR_ID_ACCESS_LEVEL: ClassVar[str] = 'id_access_level'
ATTR_ID_ADDRESS: ClassVar[str] = 'id_address'
ATTR_ID_CURRENCY: ClassVar[str] = 'id_currency'
ATTR_ID_DELIVERY_REGION: ClassVar[str] = 'id_delivery_region'
ATTR_ID_REGION: ClassVar[str] = 'id_region'
ATTR_ID_USER: ClassVar[str] = 'id_user'
ATTR_ID_USER_MANAGER: ClassVar[str] = 'id_user_manager'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'access_level_required'
FLAG_ACTIVE: ClassVar[str] = 'active'
FLAG_ADDRESS: ClassVar[str] = 'address'
FLAG_CAN_ADMIN: ClassVar[str] = 'can_admin'
FLAG_CAN_EDIT: ClassVar[str] = 'can_edit'
FLAG_CAN_VIEW: ClassVar[str] = 'can_view'
FLAG_CODE: ClassVar[str] = 'code'
FLAG_CURRENCY: ClassVar[str] = 'currency'
FLAG_CURRENCY_COST: ClassVar[str] = 'currency_cost'
FLAG_DESCRIPTION: ClassVar[str] = 'description'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display_order'
FLAG_GUID: ClassVar[str] = 'guid'
@@ -38,10 +43,14 @@ class Base():
FLAG_NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'NAME_ATTR_OPTION_VALUE'
FLAG_NAME_SINGULAR: ClassVar[str] = 'name_singular'
FLAG_NAME_PLURAL: ClassVar[str] = 'name_plural'
FLAG_POSTCODE: ClassVar[str] = 'postcode'
FLAG_PRIORITY: ClassVar[str] = 'priority'
FLAG_REGION: ClassVar[str] = 'region'
FLAG_ROWS: ClassVar[str] = 'rows'
FLAG_SYMBOL: ClassVar[str] = 'symbol'
FLAG_URL: ClassVar[str] = 'url'
FLAG_VALUE_LOCAL_VAT_EXCL: ClassVar[str] = 'value_local_vat_excl'
FLAG_VALUE_LOCAL_VAT_INCL: ClassVar[str] = 'value_local_vat_incl'
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'name-attribute-option-text'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'name-attribute-option-value'

View File

@@ -81,6 +81,15 @@ class Currency(db.Model, Store_Base):
v_arg_type = 'class attribute'
currency = cls()
return currency
@classmethod
def from_DB_stock_item(cls, query_row):
_m = 'Currency.from_DB_get_many_stock_item'
v_arg_type = 'class attribute'
currency = cls()
currency.id_currency = query_row[12]
currency.code = query_row[13]
currency.symbol = query_row[14]
return currency
def __repr__(self):
return f'''
id: {self.id_currency}

View File

@@ -30,11 +30,8 @@ class Get_Many_Parameters_Base(BaseModel, metaclass=ABCMeta):
@abstractmethod
def get_default(cls): # , id_user
pass
"""
@abstractmethod
def to_json(self):
pass
"""
return self.dict()
"""
@classmethod
@abstractmethod

View File

@@ -0,0 +1,82 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Address Region Business Object
Description:
Business object for address region
"""
# internal
import lib.argument_validation as av
from business_objects.base import Base
from extensions import db
# external
from typing import ClassVar
class Region(db.Model, Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_REGION
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME
__tablename__ = 'Shop_Region'
id_region = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(200))
active = db.Column(db.Boolean)
# region = None
def __init__(self):
super().__init__()
Base.__init__(self)
self.region = None
@classmethod
def from_DB_stock_item(cls, query_row):
plant = cls()
plant.id_region = query_row[7]
return plant
def __repr__(self):
return f'''
{self.ATTR_ID_REGION}: {self.id_region}
{self.FLAG_CODE}: {self.code}
{self.FLAG_NAME}: {self.name}
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_REGION: self.id_region,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_ACTIVE: 1 if av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0
}
@classmethod
def from_json(cls, json):
print(f'{cls.__name__}.from_json: {json}')
plant = cls()
plant.id_region = json[cls.ATTR_ID_REGION]
plant.code = json[cls.FLAG_CODE]
plant.name = json[cls.FLAG_NAME]
plant.active = json[cls.FLAG_ACTIVE]
return plant
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
region = cls()
region.id_region = query_row[0]
region.name = query_row[1]
region.code = query_row[2]
# self.display_order = query_row[3]
return region
@classmethod
def from_DB_region(cls, query_row):
region = cls()
region.id_region = query_row[0]
region.code = query_row[1]
region.name = query_row[2]
region.active = av.input_bool(query_row[3], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_region')
region.display_order = query_row[4]
return region

View File

@@ -22,7 +22,7 @@ class Delivery_Option():
delay_max: int
quantity_min: float
quantity_max: float
regions: list # [Enum_Delivery_Region]
regions: list # [Enum_Region]
cost: float
def __new__(cls, name, delay_min, delay_max, quantity_min, quantity_max, regions, cost):
@@ -33,7 +33,7 @@ class Delivery_Option():
av.val_int(delay_max, 'delay_max', _m, 0, v_arg_type = v_arg_type)
av.val_float(quantity_min, 'quantity_min', _m, 0, v_arg_type = v_arg_type)
av.val_float(quantity_max, 'quantity_max', _m, 0, v_arg_type = v_arg_type)
av.val_list_instances(regions, 'regions', _m, Enum_Delivery_Region, v_arg_type = v_arg_type)
av.val_list_instances(regions, 'regions', _m, Enum_Region, v_arg_type = v_arg_type)
av.val_float(cost, 'cost', _m, 0, v_arg_type = v_arg_type)
return super(Delivery_Option, cls).__new__(cls)

View File

@@ -1,103 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Delivery Region Business Object
Description:
Business object for delivery region
"""
# internal
from business_objects.store.store_base import Store_Base
from extensions import db
from lib import argument_validation as av
# external
from enum import Enum
from typing import ClassVar
class Enum_Delivery_Region(Enum):
UK = 0
class Delivery_Region(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_DELIVERY_REGION
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
id_region = db.Column(db.Integer, primary_key=True)
"""
id_category = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_discount = db.Column(db.Integer)
"""
code = db.Column(db.String(50))
name = db.Column(db.String(200))
active = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
"""
def __new__(cls, id, id_category, id_product, id_discount, code, name, display_order):
_m = 'Delivery_Region.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type = v_arg_type)
av.val_int(id_category, 'id_category', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_product, 'id_product', _m, 0, v_arg_type = v_arg_type)
av.val_int(id_discount, 'id_discount', _m, v_arg_type = v_arg_type)
av.val_str(code, 'code', _m, max_len = 50, v_arg_type = v_arg_type)
av.val_str(name, 'name', _m, max_len = 100, v_arg_type = v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type = v_arg_type)
return super(Delivery_Region, cls).__new__(cls)
def __init__(self, id, id_category, id_product, id_discount, code, name, display_order):
self.id_region = id
self.id_category = id_category
self.id_product = id_product
self.id_discount = id_discount
self.name = name
self.code = code
self.display_order = display_order
"""
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
region = cls()
region.id_region = query_row[0]
region.name = query_row[1]
region.code = query_row[2]
# self.display_order = query_row[3]
return region
@classmethod
def from_DB_region(cls, query_row):
region = cls()
region.id_region = query_row[0]
region.code = query_row[1]
region.name = query_row[2]
region.active = av.input_bool(query_row[3], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_region')
region.display_order = query_row[4]
return region
def __repr__(self):
return f'''
id: {self.id_region}
name: {self.name}
code: {self.code}
active: {self.active}
display_order: {self.display_order}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_REGION: self.id_region,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_ACTIVE: self.active,
self.FLAG_DISPLAY_ORDER: self.display_order
}
@staticmethod
def from_json(json_region):
region = Delivery_Region()
region.id_region = json_region[Delivery_Region.ATTR_ID_REGION]
region.code = json_region[Delivery_Region.FLAG_CODE]
region.name = json_region[Delivery_Region.FLAG_NAME]
region.active = json_region[Delivery_Region.FLAG_ACTIVE]
region.display_order = json_region[Delivery_Region.FLAG_DISPLAY_ORDER]
return region

View File

@@ -0,0 +1,92 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Plant Business Object
Description:
Business object for plant
"""
# internal
import lib.argument_validation as av
from business_objects.address import Address
from business_objects.store.store_base import Store_Base
from extensions import db
# external
from typing import ClassVar
class Plant(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PLANT
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Plant'
id_plant = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
id_address = db.Column(db.Integer)
id_user_manager = db.Column(db.Integer)
active = db.Column(db.Boolean)
# address = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.address = None
@classmethod
def from_DB_storage_location(cls, query_row):
plant = cls()
plant.id_plant = query_row[1]
plant.id_address = query_row[2]
plant.address = Address.from_DB_storage_location(query_row)
return plant
@classmethod
def from_DB_plant(cls, query_row):
plant = cls()
plant.id_plant = query_row[0]
plant.id_address = query_row[1]
plant.address = Address.from_DB_plant(query_row)
plant.code = query_row[4]
plant.name = query_row[5]
plant.active = query_row[6]
return plant
@classmethod
def from_DB_stock_item(cls, query_row):
plant = cls()
plant.id_plant = query_row[5]
plant.code = query_row[10]
plant.name = query_row[11]
return plant
def __repr__(self):
return f'''
{self.ATTR_ID_PLANT}: {self.id_plant}
{self.FLAG_CODE}: {self.code}
{self.FLAG_NAME}: {self.name}
{self.FLAG_ADDRESS}: {self.address}
{self.ATTR_ID_USER_MANAGER}: {self.id_user_manager}
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_PLANT: self.id_plant,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_ADDRESS: self.address.to_json(),
self.ATTR_ID_USER_MANAGER: self.id_user_manager,
self.FLAG_ACTIVE: 1 if av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0
}
@classmethod
def from_json(cls, json):
print(f'{cls.__name__}.from_json: {json}')
plant = cls()
plant.id_plant = json[cls.ATTR_ID_PLANT],
plant.code = json[cls.FLAG_CODE],
plant.name = json[cls.FLAG_NAME],
plant.address = Address.from_json(json[cls.FLAG_ADDRESS]),
plant.id_user_manager = json[cls.ATTR_ID_USER_MANAGER],
plant.active = json[cls.FLAG_ACTIVE]
return plant

View File

@@ -175,7 +175,7 @@ class Product(SQLAlchemy_ABC, Store_Base):
if self.variation_trees[index_tree].is_equal(variation_tree):
found_variation_tree_match = True
break
if not found_variation_tree_match:
if not found_variation_tree_match and variation_tree is not None:
self.variation_trees.append(variation_tree)
def from_DB_Stripe_product(query_row):
permutation = Product_Permutation.from_DB_Stripe_product(query_row)
@@ -374,6 +374,13 @@ class Product(SQLAlchemy_ABC, Store_Base):
text_input += tree.get_text_input_types()
return text_input
"""
def get_csv_ids_permutation(self):
csv = ''
for permutation in self.permutations:
if csv != '':
csv += ','
csv += str(permutation.id_permutation)
return csv
class Parameters_Product(Get_Many_Parameters_Base):
@@ -482,7 +489,7 @@ class Parameters_Product(Get_Many_Parameters_Base):
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Parameters_Product.from_form', Filters_Product_Permutation)
# av.val_instance(form, 'form', 'Parameters_Product.from_form', Filters_Product_Permutation)
has_category_filter = not (form.id_category.data == '0' or form.id_category.data == '')
has_product_filter = not (form.id_product.data == '0' or form.id_product.data == '')
get_permutations_stock_below_min = av.input_bool(form.is_out_of_stock.data, "is_out_of_stock", "Parameters_Product.from_form")
@@ -491,11 +498,11 @@ class Parameters_Product(Get_Many_Parameters_Base):
get_all_product_category = not has_category_filter,
get_inactive_product_category = False,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data,
ids_product_category = form.id_category.data if form.id_category.data is not None else '',
get_all_product = not has_product_filter,
get_inactive_product = False,
# get_first_product_only = False,
ids_product = form.id_product.data,
ids_product = form.id_product.data if form.id_product.data is not None else '',
get_all_permutation = not get_permutations_stock_below_min,
get_inactive_permutation = False,
# get_first_permutation_only = False,
@@ -601,6 +608,9 @@ class Parameters_Product(Get_Many_Parameters_Base):
ids_image = '',
get_products_quantity_stock_below_min = False
)
@classmethod
def from_filters_stock_item(cls, filters_stock_item):
return cls.from_form_filters_product_permutation(filters_stock_item)
"""
class Parameters_Product(Get_Many_Parameters_Base):

View File

@@ -242,6 +242,11 @@ class Product_Category(SQLAlchemy_ABC, Store_Base):
'value': self.id_category,
'text': self.name
}
def get_csv_ids_permutation(self):
list_ids = []
for product in self.products:
list_ids += product.get_csv_ids_permutation()
return list_ids
"""
class Filters_Product_Category(BaseModel, Store_Base):
ids_product_category: str
@@ -433,6 +438,11 @@ class Product_Category_Container(Store_Base):
if column.name not in ['created_on', 'created_by']
}
return self.to_object_with_missing_attributes(excluded_attributes)
def get_csv_ids_permutation(self):
list_ids = []
for category in self.categories:
list_ids += category.get_csv_ids_permutation()
return ','.join(list_ids)
"""

View File

@@ -14,7 +14,7 @@ Business object for product permutation
import lib.argument_validation as av
from lib import data_types
from forms.forms import Form_Basket_Add, Form_Basket_Edit
from business_objects.store.currency import Currency
from business_objects.currency import Currency
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.discount import Discount
from business_objects.store.image import Image
@@ -309,6 +309,7 @@ class Product_Permutation(db.Model, Store_Base):
'value': self.id_permutation,
'text': self.get_name_variations()
}
def get_name(self):
return
def get_name_variations(self):
@@ -317,19 +318,14 @@ class Product_Permutation(db.Model, Store_Base):
return len(self.prices) > 0
def get_price(self):
return self.prices[0]
def get_price_local_VAT_incl(self):
price = self.get_price()
return price.value_local_VAT_incl
def get_price_local_VAT_excl(self):
price = self.get_price()
return price.value_local_VAT_excl
def output_lead_time(self):
return '1 day' if self.latency_manufacture_days == 1 else f'{self.latency_manufacture_days} days'
def output_delivery_date(self):
return (datetime.now() + timedelta(days=self.latency_manufacture_days)).strftime('%A, %d %B %Y')
"""
def output_lead_time(self):
return '1 day' if self.latency_manufacture_days == 1 else f'{self.latency_manufacture_days} days'
def output_price(self, is_included_VAT):
if self.is_unavailable_in_currency_or_region:
return 'Not available in currency and region'
@@ -341,33 +337,14 @@ class Product_Permutation(db.Model, Store_Base):
return f'{price.symbol_currency} {locale.format_string("%d", price.value_local_VAT_incl, grouping=True)}'
else:
return f'{price.symbol_currency} {locale.format_string("%d", price.value_local_VAT_excl, grouping=True)}'
def output_currency(self):
if not self.is_available:
return ''
"""
price = self.get_price()
return price.code_currency
"""
return self.code_currency_cost if self.symbol_currency_cost == '' else self.symbol_currency_cost
"""
def output_variations(self):
if not self.has_variations: return ''
return '\n'.join([f'{variation.name_variation_type}: {variation.name_variation}' for variation in self.variations])
def output_variations_jsonify(self):
if not self.has_variations: return ''
return ','.join([f'{variation.id_type}: {variation.id_variation}' for variation in self.variations])
"""
def output_price_VAT_incl(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.price_GBP_VAT_incl, grouping=True)
def output_price_VAT_excl(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.price_GBP_VAT_excl, grouping=True)
def add_form_basket_add(self):
self.form_basket_add = None
def add_form_basket_edit(self):
self.form_basket_edit = None
"""
def __repr__(self):
return f'''Product_Permutation
id_permutation: {self.id_permutation}

View File

@@ -11,8 +11,8 @@ Business object for product price
"""
# internal
from business_objects.store.currency import Currency
from business_objects.store.delivery_region import Delivery_Region
from business_objects.currency import Currency
from business_objects.region import Region
from business_objects.store.store_base import Store_Base
from extensions import db
# external
@@ -22,8 +22,6 @@ from typing import ClassVar
class Product_Price(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT_PRICE
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_TEXT
FLAG_VALUE_LOCAL_VAT_INCL: ClassVar[str] = 'value-local-vat-incl'
FLAG_VALUE_LOCAL_VAT_EXCL: ClassVar[str] = 'value-local-vat-excl'
id_price = db.Column(db.Integer, primary_key=True)
id_permutation = db.Column(db.Integer)
@@ -87,7 +85,7 @@ class Product_Price(db.Model, Store_Base):
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.FLAG_CURRENCY: self.currency.to_json(),
# Delivery_Region.ATTR_ID_DELIVERY_REGION: self.id_region,
# Region.ATTR_ID_REGION_DELIVERY: self.id_region,
self.FLAG_VALUE_LOCAL_VAT_INCL: self.value_local_VAT_incl,
self.FLAG_VALUE_LOCAL_VAT_EXCL: self.value_local_VAT_excl,
self.FLAG_DISPLAY_ORDER: self.display_order
@@ -101,7 +99,7 @@ class Product_Price(db.Model, Store_Base):
price.id_product = json[cls.ATTR_ID_PRODUCT]
price.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
price.currency = Currency.from_json(json)
# price.id_region = json[Delivery_Region.ATTR_ID_DELIVERY_REGION]
# price.id_region = json[Region.ATTR_ID_REGION_DELIVERY]
price.value_local_VAT_incl = json[cls.FLAG_VALUE_LOCAL_VAT_INCL]
price.value_local_VAT_excl = json[cls.FLAG_VALUE_LOCAL_VAT_EXCL]
price.display_order = json[cls.FLAG_DISPLAY_ORDER]

View File

@@ -82,6 +82,7 @@ class Product_Variation_Tree():
@classmethod
def from_json_str(cls, json_str):
variations = []
if json_str is None or json_str == '': return None
for json_variation in json_str.split(','):
parts = json_variation.split(':')
if len(parts) != 2: continue

View File

@@ -13,13 +13,15 @@ import lib.argument_validation as av
from lib import data_types
from forms.store.stock_item import Filters_Stock_Item
from business_objects.db_base import Get_Many_Parameters_Base
from business_objects.store.product_price import Product_Price
from business_objects.currency import Currency
# from business_objects.discount import Discount
from business_objects.store.product_variation_tree import Product_Variation_Tree
from business_objects.store.storage_location import Storage_Location
from business_objects.store.store_base import Store_Base
from extensions import db
# external
from dataclasses import dataclass
from typing import ClassVar
from typing import ClassVar, Optional
from datetime import datetime
@@ -34,21 +36,19 @@ class Stock_Item(db.Model, Store_Base):
FLAG_DATE_UNSEALED: ClassVar[str] = 'date_unsealed'
FLAG_IS_CONSUMED: ClassVar[str] = 'is_consumed'
FLAG_IS_SEALED: ClassVar[str] = 'is_sealed'
FLAG_VALUE_LOCAL_VAT_EXCL_COST: ClassVar[str] = f'{Product_Price.FLAG_VALUE_LOCAL_VAT_EXCL}_cost'
FLAG_VALUE_LOCAL_VAT_INCL_COST: ClassVar[str] = f'{Product_Price.FLAG_VALUE_LOCAL_VAT_INCL}_cost'
id_stock = db.Column(db.Integer, primary_key=True)
id_permutation = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
# name_category = db.Column(db.String(255))
# name_product = db.Column(db.String(255))
id_location_storage = db.Column(db.Integer)
id_plant = db.Column(db.Integer)
id_region = db.Column(db.Integer)
id_currency_cost = db.Column(db.Integer)
cost_local_VAT_excl = db.Column(db.Float)
cost_local_VAT_incl = db.Column(db.Float)
date_purchased = db.Column(db.DateTime)
date_received = db.Column(db.DateTime)
id_location_storage = db.Column(db.Integer)
id_currency_cost = db.Column(db.Integer)
cost_local_VAT_incl = db.Column(db.Float)
cost_local_VAT_excl = db.Column(db.Float)
is_sealed = db.Column(db.Boolean)
date_unsealed = db.Column(db.DateTime)
date_expiration = db.Column(db.DateTime)
@@ -60,10 +60,15 @@ class Stock_Item(db.Model, Store_Base):
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
"""
# variation_tree: Product_Variation_Tree = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.currency_cost = None
self.storage_location = None
self.variation_tree = None
self.has_variations = False
def from_DB_stock_item(query_row):
_m = 'Product.from_DB_stock_item'
@@ -73,21 +78,20 @@ class Stock_Item(db.Model, Store_Base):
stock_item.id_permutation = query_row[1]
stock_item.id_product = query_row[2]
stock_item.id_category = query_row[3]
# stock_item.name_category = query_row[4]
# stock_item.name_product = query_row[5]
stock_item.date_purchased = query_row[4]
stock_item.date_received = query_row[5]
stock_item.id_location_storage = query_row[6]
# stock_item.name_location_storage = query_row[9]
stock_item.id_currency_cost = query_row[7]
stock_item.cost_local_VAT_incl = query_row[8]
stock_item.cost_local_VAT_excl = query_row[9]
stock_item.is_sealed = av.input_bool(query_row[10], "is_sealed", _m, v_arg_type=v_arg_type)
stock_item.date_unsealed = query_row[11]
stock_item.date_expiration = query_row[12]
stock_item.is_consumed = av.input_bool(query_row[13], "is_consumed", _m, v_arg_type=v_arg_type)
stock_item.date_consumed = query_row[14]
stock_item.active = av.input_bool(query_row[15], "active", _m, v_arg_type=v_arg_type)
stock_item.id_location_storage = query_row[4]
stock_item.storage_location = Storage_Location.from_DB_stock_item(query_row)
stock_item.id_currency_cost = query_row[12]
stock_item.currency_cost = Currency.from_DB_stock_item(query_row)
stock_item.cost_local_VAT_excl = query_row[15]
stock_item.cost_local_VAT_incl = query_row[16]
stock_item.date_purchased = query_row[17]
stock_item.date_received = query_row[18]
stock_item.is_sealed = av.input_bool(query_row[19], "is_sealed", _m, v_arg_type=v_arg_type)
stock_item.date_unsealed = query_row[20]
stock_item.date_expiration = query_row[21]
stock_item.is_consumed = av.input_bool(query_row[22], "is_consumed", _m, v_arg_type=v_arg_type)
stock_item.date_consumed = query_row[23]
stock_item.active = av.input_bool(query_row[24], "active", _m, v_arg_type=v_arg_type)
"""
stock_item.can_view = av.input_bool(query_row[24], "can_view", _m, v_arg_type=v_arg_type)
stock_item.can_edit = av.input_bool(query_row[25], "can_edit", _m, v_arg_type=v_arg_type)
@@ -99,96 +103,55 @@ class Stock_Item(db.Model, Store_Base):
def from_json(cls, json):
stock_item = cls()
stock_item.id_stock = json[cls.ATTR_ID_STOCK_ITEM]
stock_item.id_permutation = json[cls.ATTR_ID_PRODUCT_PERMUTATION]
stock_item.id_permutation = json.get(cls.ATTR_ID_PRODUCT_PERMUTATION, 0)
stock_item.id_product = json[cls.ATTR_ID_PRODUCT]
stock_item.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
print(f'json: {json}\nhalf stock item: {stock_item}')
stock_item.variation_tree = Product_Variation_Tree.from_json_str(json[cls.FLAG_PRODUCT_VARIATIONS])
stock_item.date_purchased = json[cls.FLAG_DATE_PURCHASED]
stock_item.date_received = json[cls.FLAG_DATE_RECEIVED]
stock_item.id_location_storage = json[cls.ATTR_ID_LOCATION_STORAGE]
stock_item.id_location_storage = json[cls.ATTR_ID_STORAGE_LOCATION]
stock_item.id_currency_cost = json[cls.ATTR_ID_CURRENCY_COST]
stock_item.cost_local_VAT_incl = json[cls.FLAG_VALUE_LOCAL_VAT_INCL_COST]
stock_item.cost_local_VAT_excl = json[cls.FLAG_VALUE_LOCAL_VAT_EXCL_COST]
stock_item.cost_local_VAT_excl = json[cls.FLAG_COST_LOCAL_VAT_EXCL]
stock_item.cost_local_VAT_incl = json[cls.FLAG_COST_LOCAL_VAT_INCL]
stock_item.is_sealed = json[cls.FLAG_IS_SEALED]
stock_item.date_unsealed = json[cls.FLAG_DATE_UNSEALED]
stock_item.date_expiration = json[cls.FLAG_DATE_EXPIRATION]
stock_item.is_consumed = json[cls.FLAG_IS_CONSUMED]
stock_item.date_consumed = json[cls.FLAG_DATE_CONSUMED]
stock_item.active = json[cls.FLAG_ACTIVE]
"""
stock_item.can_view = json[cls.FLAG_CAN_VIEW]
stock_item.can_edit = json[cls.FLAG_CAN_EDIT]
stock_item.can_admin = json[cls.FLAG_CAN_ADMIN]
"""
return stock_item
"""
def from_json(json_basket_item, key_id_product, key_id_permutation):
permutation = Product_Permutation.from_json(json_basket_item, key_id_product, key_id_permutation)
product = Product.from_permutation(permutation)
return product
def output_lead_time(self):
return self.get_permutation_selected().output_lead_time()
def output_delivery_date(self):
return self.get_permutation_selected().output_delivery_date()
def output_price(self, is_included_VAT):
return self.get_permutation_selected().output_price(is_included_VAT)
def output_price_VAT_incl(self):
return self.get_permutation_selected().output_price(True)
def output_price_VAT_excl(self):
return self.get_permutation_selected().output_price(False)
def get_price_local(self, is_included_VAT):
if is_included_VAT:
return self.get_price_local_VAT_incl()
else:
return self.get_price_local_VAT_excl()
def get_price_local_VAT_incl(self):
return self.get_permutation_selected().get_price_local_VAT_incl()
def get_price_local_VAT_excl(self):
return self.get_permutation_selected().get_price_local_VAT_excl()
def get_quantity_min(self):
return self.get_permutation_selected().quantity_min
def get_id_permutation(self):
return self.get_permutation_selected().id_permutation
def get_image_from_index(self, index_image):
return self.get_permutation_selected().images[index_image]
def get_name(self):
return self.get_permutation_selected().name
def get_description(self):
return self.get_permutation_selected().description
def output_currency(self):
return self.get_permutation_selected().get_price().symbol_currency
def add_form_basket_add(self):
self.form_basket_add = None
def add_form_basket_edit(self):
self.form_basket_edit = None
"""
def __repr__(self):
return f'''Product
return f'''Stock Item
id_stock: {self.id_stock}
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
name: {self.name}
display_order: {self.display_order}
can_view: {self.can_view}
can_edit: {self.can_edit}
can_admin: {self.can_admin}
has_variations: {self.has_variations}
permutations: {self.permutations}
variation trees: {self.variation_trees}
variations: {self.variation_tree.to_preview_str() if self.variation_tree is not None else 'None'}
storage_location: {self.storage_location}
currency: {self.currency_cost}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
date_purchased: {self.date_purchased}
date_received: {self.date_received}
is_sealed: {self.is_sealed}
date_unsealed: {self.date_unsealed}
date_expiration: {self.date_expiration}
is_consumed: {self.is_consumed}
date_consumed: {self.date_consumed}
active: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT: {self.id_product},
self.ATTR_ID_PRODUCT_CATEGORY: {self.id_category},
self.FLAG_NAME: {self.name},
self.FLAG_DISPLAY_ORDER: {self.display_order},
self.FLAG_CAN_VIEW: {self.can_view},
self.FLAG_CAN_EDIT: {self.can_edit},
self.FLAG_CAN_ADMIN: {self.can_admin},
self.FLAG_HAS_VARIATIONS: {self.has_variations},
self.FLAG_PRODUCT_PERMUTATION: {self.permutations},
self.FLAG_PRODUCT_VARIATION_TREES: {self.variation_trees},
self.ATTR_ID_STOCK_ITEM: self.id_stock,
self.ATTR_ID_PRODUCT_PERMUTATION: self.id_permutation,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.FLAG_STORAGE_LOCATION: self.storage_location.to_json(),
self.FLAG_CURRENCY_COST: self.currency_cost.to_json(),
self.FLAG_COST_LOCAL_VAT_EXCL: self.cost_local_VAT_excl,
self.FLAG_COST_LOCAL_VAT_INCL: self.cost_local_VAT_incl,
}
def has_permutations(self):
return len(self.permutations) > 0
@@ -224,12 +187,11 @@ class Parameters_Stock_Item(Get_Many_Parameters_Base):
a_ids_region_storage: str
a_get_all_plant_storage: bool
a_get_inactive_plant_storage: bool
a_get_first_plant_storage_only: bool
a_ids_plant_storage: str
a_get_all_location_storage: bool
a_get_inactive_location_storage: bool
a_ids_location_storage: str
a_date_received_to: datetime
a_date_received_to: Optional[datetime] = None
a_get_sealed_stock_item_only: bool
a_get_unsealed_stock_item_only: bool
a_get_expired_stock_item_only: bool
@@ -244,7 +206,7 @@ class Parameters_Stock_Item(Get_Many_Parameters_Base):
def get_default(cls):
return cls(
# a_id_user = id_user,
a_get_all_product_permutation = True,
a_get_all_product_permutation = False,
a_get_inactive_product_permutation = False,
a_ids_product_permutation = '',
a_get_all_stock_item = True,
@@ -270,4 +232,71 @@ class Parameters_Stock_Item(Get_Many_Parameters_Base):
@classmethod
def from_form_stock_item(cls, form):
return cls.get_default()
return cls.get_default()
class Stock_Item_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Stock_Item_Temp'
__table_args__ = { 'extend_existing': True }
id_stock: int = db.Column(db.Integer, primary_key=True)
# id_category: int = db.Column(db.Integer)
id_product: int = db.Column(db.Integer)
# has_variations: bool = db.Column(db.Boolean)
id_permutation: int = db.Column(db.Integer)
id_pairs_variations: str = db.Column(db.String(4000))
date_purchased: datetime = db.Column(db.DateTime)
date_received: datetime = db.Column(db.DateTime, nullable=True)
id_location_storage: int = db.Column(db.Integer)
id_currency_cost: int = db.Column(db.Integer)
cost_local_VAT_excl: float = db.Column(db.Float)
cost_local_VAT_incl: float = db.Column(db.Float)
is_sealed: bool = db.Column(db.Boolean)
date_unsealed: datetime = db.Column(db.DateTime, nullable=True)
date_expiration: datetime = db.Column(db.DateTime, nullable=True)
is_consumed: bool = db.Column(db.Boolean)
date_consumed: datetime = db.Column(db.DateTime, nullable=True)
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
@classmethod
def from_stock_item(cls, stock_item):
row = cls()
row.id_stock = stock_item.id_stock
# row.id_category = stock_item.id_category
row.id_product = stock_item.id_product
row.id_permutation = stock_item.id_permutation
# row.has_variations = stock_item.has_variations
row.id_pairs_variations = stock_item.variation_tree.to_json_str() if stock_item.variation_tree is not None else ''
row.date_purchased = stock_item.date_purchased
row.date_received = stock_item.date_received if stock_item.date_received else None
row.id_location_storage = stock_item.id_location_storage
row.id_currency_cost = stock_item.id_currency_cost
row.cost_local_VAT_excl = stock_item.cost_local_VAT_excl
row.cost_local_VAT_incl = stock_item.cost_local_VAT_incl
row.is_sealed = 1 if stock_item.is_sealed else 0
row.date_unsealed = stock_item.date_unsealed if stock_item.date_unsealed else None
row.date_expiration = stock_item.date_expiration if stock_item.date_expiration else None
row.is_consumed = 1 if stock_item.is_consumed else 0
row.date_consumed = stock_item.date_consumed if stock_item.date_consumed else None
row.active = 1 if stock_item.active else 0
return row
def __repr__(self):
return f'''
id_stock: {self.id_stock}
id_product: {self.id_product}
id_permutation: {self.id_permutation}
id_pairs_variations: {self.id_pairs_variations}
date_purchased: {self.date_purchased}
date_received: {self.date_received}
id_location_storage: {self.id_location_storage}
id_currency_cost: {self.id_currency_cost}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
is_sealed: {self.is_sealed}
date_unsealed: {self.date_unsealed}
date_expiration: {self.date_expiration}
is_consumed: {self.is_consumed}
date_consumed: {self.date_consumed}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -12,6 +12,7 @@ Business object for product
# internal
import lib.argument_validation as av
from business_objects.store.plant import Plant
from business_objects.store.store_base import Store_Base
from extensions import db
# external
@@ -27,19 +28,33 @@ class Storage_Location(db.Model, Store_Base):
code = db.Column(db.String(50))
name = db.Column(db.String(255))
active = db.Column(db.Boolean)
# plant = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.plant = None
@classmethod
def from_DB_storage_location(cls, query_row):
location = cls()
location.id_location = query_row[0]
location.id_plant = query_row[1]
location.plant = Plant.from_DB_storage_location(query_row)
location.code = query_row[2]
location.name = query_row[3]
location.active = query_row[4]
return location
@classmethod
def from_DB_stock_item(cls, query_row):
location = cls()
location.id_location = query_row[4]
location.id_plant = query_row[5]
location.code = query_row[8]
location.name = query_row[9]
location.plant = Plant.from_DB_stock_item(query_row)
return location
def __repr__(self):
return f'''
{self.ATTR_ID_STORAGE_LOCATION}: {self.id_location}
@@ -67,3 +82,6 @@ class Storage_Location(db.Model, Store_Base):
location.name = json[cls.FLAG_NAME],
location.active = json[cls.FLAG_ACTIVE]
return location
def get_full_name(self):
return f'{self.plant.name} - {self.name}'

View File

@@ -58,9 +58,9 @@ class I_Store_Base():
class Store_Base(Base):
# ATTR_ID_CURRENCY_COST: ClassVar[str] = 'id_currency_cost'
ATTR_ID_DELIVERY_OPTION: ClassVar[str] = 'id_delivery_option'
ATTR_ID_DISCOUNT: ClassVar[str] = 'id_discount'
ATTR_ID_IMAGE: ClassVar[str] = 'id_image'
ATTR_ID_LOCATION_STORAGE: ClassVar[str] = 'id_location_storage'
ATTR_ID_PLANT: ClassVar[str] = 'id_plant'
ATTR_ID_PRODUCT: ClassVar[str] = 'id_product'
ATTR_ID_PRODUCT_CATEGORY: ClassVar[str] = 'id_category'
@@ -70,10 +70,15 @@ class Store_Base(Base):
ATTR_ID_PRODUCT_VARIATION: ClassVar[str] = 'id_variation'
ATTR_ID_PRODUCT_VARIATION_TYPE: ClassVar[str] = 'id_type'
ATTR_ID_STOCK_ITEM: ClassVar[str] = 'id_stock_item'
FLAG_CURRENCY: ClassVar[str] = 'currency'
ATTR_ID_STORAGE_LOCATION: ClassVar[str] = 'id_location'
FLAG_COST_LOCAL: ClassVar[str] = 'cost_local'
FLAG_COST_LOCAL_VAT_EXCL: ClassVar[str] = FLAG_COST_LOCAL + '_vat_excl'
FLAG_COST_LOCAL_VAT_INCL: ClassVar[str] = FLAG_COST_LOCAL + '_vat_incl'
FLAG_DELIVERY_OPTION: ClassVar[str] = 'delivery_option'
FLAG_HAS_VARIATIONS: ClassVar[str] = 'has_variations'
FLAG_IS_OUT_OF_STOCK: ClassVar[str] = 'is_out_of_stock'
FLAG_DISCOUNT: ClassVar[str] = 'discount'
FLAG_PLANT: ClassVar[str] = 'plant'
FLAG_PRODUCT: ClassVar[str] = 'product'
FLAG_PRODUCT_CATEGORY: ClassVar[str] = 'product_category'
FLAG_PRODUCT_IMAGE: ClassVar[str] = 'product_image'
@@ -85,6 +90,7 @@ class Store_Base(Base):
FLAG_QUANTITY_MIN: ClassVar[str] = 'quantity_min'
FLAG_QUANTITY_MAX: ClassVar[str] = 'quantity_max'
FLAG_STOCK_ITEM: ClassVar[str] = 'stock_item'
FLAG_STORAGE_LOCATION: ClassVar[str] = 'storage_location'
FLAG_TEXT: ClassVar[str] = 'text'
FLAG_VALUE_TEXT: ClassVar[str] = 'value_text'

View File

@@ -38,7 +38,9 @@ class User(db.Model, Base):
# is_logged_in: bool
def __init__(self):
self.id_user = 0
self.is_logged_in = False
super().__init__()
def from_DB_user(query_row):
_m = 'User.from_DB_user'
@@ -62,7 +64,7 @@ class User(db.Model, Base):
_m = 'User.from_json'
user = User()
if json is None: return user
print(f'json: {json}')
print(f'{_m}\njson: {json}')
user.id_user = json['id_user']
user.id_user_auth0 = json['id_user_auth0']
user.firstname = json['firstname']
@@ -85,7 +87,7 @@ class User(db.Model, Base):
_m = 'User.from_json_auth0'
user = User()
if json is None: return user
print(f'json: {json}')
print(f'{_m}\njson: {json}')
user_info = json['userinfo']
user.id_user = None
user.id_user_auth0 = user_info['sub']
@@ -136,11 +138,6 @@ class User(db.Model, Base):
can_admin_user: {self.can_admin_user}
'''
@staticmethod
def get_default():
user = User()
user.id_user = None
return user
@dataclass