1. View, filter, and save Product Permutation. \n 2. Synchronised with Product Category page and all common functionality moved into base and base table css, js, and python files.

This commit is contained in:
2024-09-24 23:25:52 +01:00
parent d37f632c92
commit cf78e4b3bc
239 changed files with 6371 additions and 4336 deletions

Binary file not shown.

25
app.py
View File

@@ -37,15 +37,15 @@ from datastores.datastore_store_base import DataStore_Store
from helpers.helper_app import Helper_App
import lib.argument_validation as av
"""
from routing.core import routes_core
from routing.legal import routes_legal
from routing.store.store import routes_store
from routing.store.product import routes_store_product
from routing.store.product_category import routes_store_product_category
from routing.store.product_permutation import routes_store_product_permutation
from routing.store.stock_item import routes_store_stock_item
from routing.store.supplier import routes_store_supplier
from routing.user import routes_user
from controllers.core import routes_core
from controllers.legal import routes_legal
from controllers.store.store import routes_store
from controllers.store.product import routes_store_product
from controllers.store.product_category import routes_store_product_category
from controllers.store.product_permutation import routes_store_product_permutation
from controllers.store.stock_item import routes_store_stock_item
from controllers.store.supplier import routes_store_supplier
from controllers.user import routes_user
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session
# from flask_appconfig import AppConfig
@@ -131,3 +131,10 @@ app.register_blueprint(routes_store_product_permutation)
app.register_blueprint(routes_store_stock_item)
app.register_blueprint(routes_store_supplier)
app.register_blueprint(routes_user)
@app.template_filter('console_log')
def console_log(value):
print(value)
return value

59
business_objects/base.py Normal file
View File

@@ -0,0 +1,59 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Base Business Object
Description:
Abstract business object
"""
# internal
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
class Base():
ATTR_ID_ACCESS_LEVEL: ClassVar[str] = 'id_access_level'
ATTR_ID_CURRENCY: ClassVar[str] = 'id_currency'
ATTR_ID_DELIVERY_REGION: ClassVar[str] = 'id_delivery_region'
ATTR_ID_USER: ClassVar[str] = 'id_user'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'access_level_required'
FLAG_ACTIVE: ClassVar[str] = 'active'
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_DESCRIPTION: ClassVar[str] = 'description'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display_order'
FLAG_GUID: ClassVar[str] = 'guid'
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
# FLAG_KEY_PRIMARY: ClassVar[str] = 'key_primary'
FLAG_NAME: ClassVar[str] = 'name'
FLAG_NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'NAME_ATTR_OPTION_TEXT'
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_PRIORITY: ClassVar[str] = 'priority'
FLAG_ROWS: ClassVar[str] = 'rows'
FLAG_SYMBOL: ClassVar[str] = 'symbol'
FLAG_URL: ClassVar[str] = 'url'
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'name-attribute-option-text'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'name-attribute-option-value'
@classmethod
def output_bool(cls, value):
return av.input_bool(value, f'{cls.__name__} bool attribute', f'{cls.__name__}.output_bool')
@staticmethod
def convert_list_objects_to_list_options(objects):
return [object.to_json_option() for object in objects]
@classmethod
def get_shared_json_attributes(cls, object):
return {
cls.FLAG_NAME_ATTR_OPTION_TEXT: object.NAME_ATTR_OPTION_TEXT,
cls.FLAG_NAME_ATTR_OPTION_VALUE: object.NAME_ATTR_OPTION_VALUE
}

View File

@@ -61,8 +61,10 @@ class SQLAlchemy_ABC(db.Model, metaclass=SQLAlchemy_ABCMeta):
@classmethod
def from_json(cls, json):
pass
"""
def to_json_option(self):
pass
"""
def to_temporary_record(self):
pass
def to_object_with_missing_attributes(self, excluded_attributes):

View File

@@ -20,6 +20,8 @@ from typing import ClassVar
class Access_Level(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_ACCESS_LEVEL
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Access_Level_Temp'
id_access_level = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
@@ -33,8 +35,9 @@ class Access_Level(db.Model, Store_Base):
def __init__(self):
super().__init__()
Store_Base.__init__(self)
def from_DB_access_level(query_row):
access_level = Access_Level()
@classmethod
def from_DB_access_level(cls, query_row):
access_level = cls()
access_level.id_access_level = query_row[0]
access_level.code = query_row[1]
access_level.name = query_row[2]
@@ -54,13 +57,14 @@ class Access_Level(db.Model, Store_Base):
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level[0] if isinstance(self.id_access_level, tuple) else self.id_access_level,
self.FLAG_CODE: self.code[0] if isinstance(self.code, tuple) else self.code,
self.FLAG_NAME: self.name[0] if isinstance(self.name, tuple) else self.name,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.FLAG_PRIORITY: self.priority[0] if isinstance(self.priority, tuple) else self.priority,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json')
}
def to_json_option(self):
return {
@@ -79,17 +83,3 @@ class Access_Level(db.Model, Store_Base):
access_level.display_order = json[cls.FLAG_DISPLAY_ORDER]
access_level.active = json[cls.FLAG_ACTIVE]
return access_level
class Filters_Access_Level(BaseModel):
get_inactive_access_level: bool
def __init__(self, get_inactive_access_level = False):
super().__init__(get_inactive_access_level = get_inactive_access_level)
def to_json(self):
return {
'a_get_inactive_access_level': self.get_inactive_access_level
}
@classmethod
def from_json(cls, json):
filters = cls()
filters.get_inactive_access_level = json['a_get_inactive_access_level']
return filters

View File

@@ -22,6 +22,7 @@ import lib.argument_validation as av
from business_objects.store.product import Product #, Filters_Product
from business_objects.store.discount import Discount
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.store_base import Store_Base
# from forms import Form_Product
# from models.model_view_store import Model_View_Store # circular
# from datastores.datastore_store import DataStore_Store # circular
@@ -107,7 +108,7 @@ class Basket_Item():
subtotal: {self.get_subtotal()}
'''
class Basket():
class Basket(Store_Base):
KEY_BASKET: str = 'basket'
KEY_ID_CURRENCY: str = 'id_currency'
KEY_ID_REGION_DELIVERY: str = 'id_region_delivery'
@@ -147,6 +148,7 @@ class Basket():
return json_list
def to_json(self):
return {
**self.get_shared_json_attributes(self),
Basket.KEY_ITEMS: self.to_json_list(),
Basket.KEY_IS_INCLUDED_VAT: self.is_included_VAT,
Basket.KEY_ID_CURRENCY: self.id_currency,

View File

@@ -11,7 +11,9 @@ Business object for product
"""
# internal
from business_objects.store.store_base import Store_Base
from extensions import db
from lib import argument_validation as av
# external
from typing import ClassVar
@@ -39,13 +41,10 @@ class Currency_Enum(Enum):
# return Resolution_Level_Enum.HIGH
"""
class Currency(db.Model):
ATTR_ID_CURRENCY: ClassVar[str] = 'id-currency'
FLAG_CODE: ClassVar[str] = 'code-currency'
FLAG_NAME: ClassVar[str] = 'name-currency'
FLAG_SYMBOL: ClassVar[str] = 'symbol-currency'
FLAG_FACTOR_FROM_GBP: ClassVar[str] = 'factor-from-GBP-currency'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display-order-currency'
class Currency(db.Model, Store_Base):
FLAG_FACTOR_FROM_GBP: ClassVar[str] = 'factor-from-GBP'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_CURRENCY
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
id_currency = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
@@ -53,29 +52,35 @@ class Currency(db.Model):
symbol = db.Column(db.String(50))
factor_from_GBP = db.Column(db.Float)
display_order = db.Column(db.Integer)
def from_DB_currency(query_row):
# _m = 'Currency.from_DB_currency'
# v_arg_type = 'class attribute'
currency = Currency()
active = db.Column(db.Boolean)
@classmethod
def from_DB_currency(cls, query_row):
_m = 'Currency.from_DB_currency'
v_arg_type = 'class attribute'
currency = cls()
currency.id_currency = query_row[0]
currency.code = query_row[1]
currency.name = query_row[2]
currency.symbol = query_row[3]
currency.factor_from_GBP = query_row[4]
currency.display_order = query_row[5]
currency.active = av.input_bool(query_row[6], 'active', _m, v_arg_type=v_arg_type)
return currency
"""
def from_DB_get_many_product_catalogue(query_row):
_m = 'Currency.from_DB_get_many_product_catalogue'
@classmethod
def from_DB_get_many_product_catalogue_product_permutation(cls, query_row):
_m = 'Currency.from_DB_get_many_product_catalogue_product_permutation'
v_arg_type = 'class attribute'
currency = Currency()
currency.id_permutation = query_row[0]
currency.id_product = query_row[1]
currency.id_category = query_row[2]
currency.id_variation = query_row[3]
currency = cls()
currency.id_currency = query_row[5]
currency.code = query_row[6]
currency.symbol = query_row[7]
return currency
@classmethod
def from_DB_get_many_product_price_and_discount_and_delivery_region(cls, query_row):
_m = 'Currency.from_DB_get_many_product_price_and_discount_and_delivery_region'
v_arg_type = 'class attribute'
currency = cls()
return currency
"""
def __repr__(self):
return f'''
id: {self.id_currency}
@@ -84,23 +89,35 @@ class Currency(db.Model):
symbol: {self.symbol}
factor from GBP: {self.factor_from_GBP}
display_order: {self.display_order}
active: {self.active}
'''
def to_json(self):
return {
Currency.ATTR_ID_CURRENCY: self.id_currency,
Currency.FLAG_CODE: self.code,
Currency.FLAG_NAME: self.name,
Currency.FLAG_SYMBOL: self.symbol,
Currency.FLAG_FACTOR_FROM_GBP: self.factor_from_GBP,
Currency.FLAG_DISPLAY_ORDER: self.display_order
**self.get_shared_json_attributes(self),
self.NAME_ATTR_OPTION_TEXT: self.FLAG_NAME,
self.NAME_ATTR_OPTION_VALUE: self.ATTR_ID_CURRENCY,
self.ATTR_ID_CURRENCY: self.id_currency,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_SYMBOL: self.symbol,
self.FLAG_FACTOR_FROM_GBP: self.factor_from_GBP,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active,
}
@staticmethod
def from_json(json_currency):
currency = Currency()
currency.id_currency = json_currency[Currency.ATTR_ID_CURRENCY]
currency.code = json_currency[Currency.FLAG_CODE]
currency.name = json_currency[Currency.FLAG_NAME]
currency.symbol = json_currency[Currency.FLAG_SYMBOL]
currency.factor_from_GBP = json_currency[Currency.FLAG_FACTOR_FROM_GBP]
currency.display_order = json_currency[Currency.FLAG_DISPLAY_ORDER]
return currency
@classmethod
def from_json(cls, json_currency, key_suffix = ''):
currency = cls()
currency.id_currency = json_currency[f'{cls.ATTR_ID_CURRENCY}{key_suffix}']
currency.code = json_currency.get(f'{cls.FLAG_CODE}{key_suffix}')
currency.name = json_currency.get(f'{cls.FLAG_NAME}{key_suffix}')
currency.symbol = json_currency.get(f'{cls.FLAG_SYMBOL}{key_suffix}')
currency.factor_from_GBP = json_currency.get(f'{cls.FLAG_FACTOR_FROM_GBP}{key_suffix}')
currency.display_order = json_currency.get(f'{cls.FLAG_DISPLAY_ORDER}{key_suffix}')
currency.active = json_currency.get(f'{cls.FLAG_ACTIVE}{key_suffix}')
return currency
def to_json_option(self):
return {
'value': self.id_currency,
'text': self.name
}

View File

@@ -11,7 +11,9 @@ 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
@@ -20,12 +22,9 @@ from typing import ClassVar
class Enum_Delivery_Region(Enum):
UK = 0
class Delivery_Region(db.Model):
ATTR_ID_REGION: ClassVar[str] = 'id-region'
FLAG_CODE: ClassVar[str] = 'code-region'
FLAG_NAME: ClassVar[str] = 'name-region'
FLAG_ACTIVE: ClassVar[str] = 'active-region'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display-order-region'
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)
"""
@@ -59,19 +58,21 @@ class Delivery_Region(db.Model):
self.code = code
self.display_order = display_order
"""
def from_DB_get_many_product_catalogue(query_row):
region = Delivery_Region()
@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
def from_DB_region(query_row):
region = Delivery_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 = query_row[3]
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):
@@ -84,11 +85,12 @@ class Delivery_Region(db.Model):
'''
def to_json(self):
return {
Delivery_Region.ATTR_ID_REGION: self.id_region,
Delivery_Region.FLAG_CODE: self.code,
Delivery_Region.FLAG_NAME: self.name,
Delivery_Region.FLAG_ACTIVE: self.active,
Delivery_Region.FLAG_DISPLAY_ORDER: self.display_order
**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):

View File

@@ -11,8 +11,9 @@ Business object for product image
"""
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from extensions import db
import lib.argument_validation as av
# external
from enum import Enum
@@ -46,7 +47,7 @@ class Resolution_Level_Enum(Enum):
return Resolution_Level_Enum.HIGH
class Image(db.Model):
class Image(db.Model, Store_Base):
id_image = db.Column(db.Integer, primary_key=True)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
@@ -93,6 +94,15 @@ class Image(db.Model):
url: {self.url}
display_order: {self.display_order}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT_IMAGE: self.id_image,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.FLAG_URL: self.url,
self.FLAG_DISPLAY_ORDER: self.display_order
}
class Product_Image_Filters():

View File

@@ -15,6 +15,7 @@ import lib.argument_validation as av
# from lib import data_types
from business_objects.store.product import Product
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.store_base import Store_Base
# from forms import Form_Product
# from models.model_view_store import Model_View_Store # circular
# external
@@ -26,7 +27,7 @@ import locale
# VARIABLE INSTANTIATION
# CLASSES
class Order():
class Order(Store_Base):
category: str
product: Product
quantity: int
@@ -69,6 +70,7 @@ class Order():
def to_json(self):
return {
**self.get_shared_json_attributes(self),
'product_id': self.product.id_product,
'price': self.product.price_GBP_full,
'quantity': self.quantity

View File

@@ -13,7 +13,8 @@ Business object for product
# internal
import lib.argument_validation as av
from lib import data_types
from forms.forms import Form_Basket_Add, Form_Basket_Edit, Form_Filters_Permutation
from forms.forms import Form_Basket_Add, Form_Basket_Edit
from forms.store.product_permutation import Filters_Product_Permutation
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.discount import Discount
@@ -52,14 +53,11 @@ class Enum_Status_Stock(Enum):
"""
class Product(SQLAlchemy_ABC, Store_Base):
FLAG_NAME: ClassVar[str] = 'name-product'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display-order-product'
FLAG_CAN_VIEW: ClassVar[str] = 'can-view-product'
FLAG_CAN_EDIT: ClassVar[str] = 'can-edit-product'
FLAG_CAN_ADMIN: ClassVar[str] = 'can-admin-product'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_NAME
FLAG_HAS_VARIATIONS: ClassVar[str] = 'has-variations-product'
FLAG_INDEX_PERMUTATION_SELECTED: ClassVar[str] = 'index-permutation-selected'
FLAG_VARIATION_TREES: ClassVar[str] = 'variation-trees'
FLAG_PRODUCT_VARIATION_TREES: ClassVar[str] = 'variation-trees'
id_product = db.Column(db.Integer, primary_key=True)
id_category = db.Column(db.Integer)
@@ -305,11 +303,12 @@ class Product(SQLAlchemy_ABC, Store_Base):
for json_permutation in json[cls.ATTR_ID_PRODUCT_PERMUTATION]:
product.permutations.append(Product_Permutation.from_json(json_permutation))
product.variation_trees = []
for json_tree in json[cls.FLAG_VARIATION_TREES]:
for json_tree in json[cls.FLAG_PRODUCT_VARIATION_TREES]:
product.variation_trees.append(Product_Variation_Tree.from_json(json_tree))
return product
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,
@@ -320,7 +319,7 @@ class Product(SQLAlchemy_ABC, Store_Base):
self.FLAG_HAS_VARIATIONS: self.has_variations,
self.FLAG_INDEX_PERMUTATION_SELECTED: self.index_permutation_selected,
self.ATTR_ID_PRODUCT_PERMUTATION: [permutation.to_json() for permutation in self.permutations],
self.FLAG_VARIATION_TREES: [tree.to_json() for tree in self.variation_trees]
self.FLAG_PRODUCT_VARIATION_TREES: [tree.to_json() for tree in self.variation_trees]
}
def to_json_option(self):
return {
@@ -421,7 +420,7 @@ class Filters_Product():
@staticmethod
def from_form_filters_product(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Product)
has_filter_category = not (form.id_category.data == '0' or form.id_category.data == '')
is_not_empty = av.input_bool(form.is_not_empty.data, "is_not_empty", "Filters_Product.from_form_filters_product")
@@ -458,8 +457,8 @@ class Filters_Product():
)
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Permutation)
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_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", "Filters_Product.from_form")
@@ -651,7 +650,7 @@ class Filters_Product(Get_Many_Parameters_Base):
@staticmethod
def from_form_filters_product(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Product)
has_filter_category = not (form.id_category.data == '0' or form.id_category.data == '')
is_not_empty = av.input_bool(form.is_not_empty.data, "is_not_empty", "Filters_Product.from_form_filters_product")
@@ -660,7 +659,7 @@ class Filters_Product(Get_Many_Parameters_Base):
get_all_product_category = not has_filter_category,
get_inactive_product_category = not active,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data,
ids_product_category = str(form.id_category.data),
get_all_product = True,
get_inactive_product = not active,
# get_first_product_only = False,
@@ -686,23 +685,23 @@ class Filters_Product(Get_Many_Parameters_Base):
# ids_discount = '',
get_products_quantity_stock_below_min = False
)
@staticmethod
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_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 == '')
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Filters_Product_Permutation)
has_category_filter = not (form.id_category.data is None or form.id_category.data == '0' or form.id_category.data == '')
has_product_filter = not (form.id_product.data is None or 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", "Filters_Product.from_form")
print(f'form question: {type(form.is_out_of_stock)}\nbool interpretted: {get_permutations_stock_below_min}\type form: {type(form)}')
return Filters_Product(
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 = str(form.id_category.data) if has_category_filter 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 = str(form.id_product.data) if has_product_filter else '',
get_all_permutation = not get_permutations_stock_below_min,
get_inactive_permutation = False,
# get_first_permutation_only = False,

View File

@@ -28,6 +28,9 @@ from typing import ClassVar
class Product_Category(SQLAlchemy_ABC, Store_Base):
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'id_access_level_required'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT_CATEGORY
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Product_Category_Temp'
id_category = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
@@ -48,8 +51,9 @@ class Product_Category(SQLAlchemy_ABC, Store_Base):
super().__init__()
Store_Base.__init__(self)
self.name_access_level_required = None
def from_DB_get_many_product_catalogue(query_row):
category = Product_Category()
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
category = cls()
category.id_category = query_row[0]
category.code = query_row[1]
category.name = query_row[2]
@@ -57,10 +61,10 @@ class Product_Category(SQLAlchemy_ABC, Store_Base):
category.id_access_level_required = query_row[4]
category.name_access_level_required = query_row[5]
category.display_order = query_row[6]
category.active = query_row[7]
category.can_view = query_row[8]
category.can_edit = query_row[9]
category.can_admin = query_row[10]
category.active = av.input_bool(query_row[7], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_get_many_product_catalogue')
category.can_view = av.input_bool(query_row[8], cls.FLAG_CAN_VIEW, f'{cls.__name__}.from_DB_get_many_product_catalogue')
category.can_edit = av.input_bool(query_row[9], cls.FLAG_CAN_EDIT, f'{cls.__name__}.from_DB_get_many_product_catalogue')
category.can_admin = av.input_bool(query_row[10], cls.FLAG_CAN_ADMIN, f'{cls.__name__}.from_DB_get_many_product_catalogue')
return category
"""
def key_product_index_from_ids_product_permutation(id_product, id_permutation):
@@ -178,7 +182,7 @@ class Product_Category(SQLAlchemy_ABC, Store_Base):
return list_products
def to_json(self):
return {
self.FLAG_KEY_PRIMARY: self.ATTR_ID_PRODUCT_CATEGORY,
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category[0] if isinstance(self.id_category, tuple) else self.id_category,
self.FLAG_CODE: self.code[0] if isinstance(self.code, tuple) else self.code,
self.FLAG_NAME: self.name[0] if isinstance(self.name, tuple) else self.name,
@@ -195,12 +199,12 @@ class Product_Category(SQLAlchemy_ABC, Store_Base):
def from_json(cls, json):
print(f' Category.from_json: {json}')
category = cls()
category.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY],
category.code = json[cls.FLAG_CODE],
category.name = json[cls.FLAG_NAME],
category.description = json[cls.FLAG_DESCRIPTION],
category.id_access_level_required = json[cls.ATTR_ID_ACCESS_LEVEL],
category.name_access_level_required = json.get(cls.FLAG_ACCESS_LEVEL_REQUIRED, ''),
category.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
category.code = json[cls.FLAG_CODE]
category.name = json[cls.FLAG_NAME]
category.description = json[cls.FLAG_DESCRIPTION]
category.id_access_level_required = json[cls.ATTR_ID_ACCESS_LEVEL]
category.name_access_level_required = json.get(cls.FLAG_ACCESS_LEVEL_REQUIRED, '')
category.display_order = json[cls.FLAG_DISPLAY_ORDER]
category.active = json[cls.FLAG_ACTIVE]
category.can_view = json.get(cls.FLAG_CAN_VIEW, False)
@@ -258,8 +262,9 @@ class Filters_Product_Category(BaseModel, Store_Base):
ids_product_category = '',
ids_product = ''
)
def to_json(self):
def to_json(self):
return {
**self.get_shared_json_attributes(self),
'a_ids_product_category': self.ids_product_category,
'a_ids_product': self.ids_product
}
@@ -282,10 +287,11 @@ class Filters_Product_Category(Get_Many_Parameters_Base):
is_not_empty = False,
active = True
)
def to_json(self):
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.FLAG_IS_NOT_EMPTY: self.is_not_empty,
self.FLAG_ACTIVE: self.active
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json')
}
@classmethod
def from_json(cls, json):
@@ -379,13 +385,19 @@ class Product_Category_Container(Store_Base):
for category in self.categories:
list_categories.append({'value': category.id_category, 'text': category.name})
return list_categories
def to_product_option_list(self):
def get_list_products(self):
list_products = []
for category in self.categories:
# list_products.append(category.to_product_option_list())
"""
for product in category.products:
list_products.append({'value': product.id_product, 'text': product.name, Product.ATTR_ID_PRODUCT_CATEGORY: product.id_category})
"""
list_products += category.products
return list_products
def to_product_option_list(self):
list_products = self.get_list_products()
return [{'value': product.id_product, 'text': product.name, Product.ATTR_ID_PRODUCT_CATEGORY: product.id_category} for product in list_products]
def get_product_option_lists_by_category(self):
dict_lists_products = {}
for category in self.categories:
@@ -393,6 +405,7 @@ class Product_Category_Container(Store_Base):
return dict_lists_products
def to_json(self):
return {
**self.get_shared_json_attributes(self),
f'{self.FLAG_ROWS}': [category.to_json() for category in self.categories]
}
"""
@@ -412,4 +425,64 @@ class Product_Category_Container(Store_Base):
for column in self.__table__.columns
if column.name not in ['created_on', 'created_by']
}
return self.to_object_with_missing_attributes(excluded_attributes)
return self.to_object_with_missing_attributes(excluded_attributes)
"""
class Table_Shop_Product_Category(db.Model):
__tablename__ = 'Shop_Product_Category'
id_category: int = db.Column(db.Integer, primary_key=True)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
created_on: datetime = db.Column(db.DateTime)
created_by: int = db.Column(db.Integer)
id_change_set: int = db.Column(db.Integer)
"""
class Product_Category_Temp(db.Model):
__tablename__ = 'Shop_Product_Category_Temp'
__table_args__ = { 'extend_existing': True }
id_category: int = db.Column(db.Integer, primary_key=True)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
id_access_level_required: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.BINARY(36))
# created_on: datetime = db.Column(db.DateTime)
# created_by: int = db.Column(db.Integer)
@classmethod
def from_product_category(cls, product_category):
row = cls()
row.id_category = product_category.id_category[0] if isinstance(product_category.id_category, tuple) else product_category.id_category
row.code = product_category.code[0] if isinstance(product_category.code, tuple) else product_category.code
row.name = product_category.name[0] if isinstance(product_category.name, tuple) else product_category.name
row.description = product_category.description[0] if isinstance(product_category.description, tuple) else product_category.description
row.id_access_level_required = product_category.id_access_level_required[0] if isinstance(product_category.id_access_level_required, tuple) else product_category.id_access_level_required
row.active = product_category.active
row.display_order = product_category.display_order
"""
row.guid = product_category.guid
row.created_on = product_category.created_on
row.created_by = product_category.created_by
"""
return row
def to_json(self):
return {
'id_category': self.id_category,
'code': self.code,
'name': self.name,
'description': self.description,
'id_access_level_required': self.id_access_level_required,
'active': av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
'display_order': self.display_order,
'guid': self.guid,
}
"""
'created_on': self.created_on,
'created_by': self.created_by
"""

View File

@@ -13,7 +13,8 @@ Business object for product permutation
# internal
import lib.argument_validation as av
from lib import data_types
from forms.forms import Form_Basket_Add, Form_Basket_Edit, Form_Filters_Permutation
from forms.forms import Form_Basket_Add, Form_Basket_Edit
from business_objects.store.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
@@ -22,42 +23,91 @@ from business_objects.store.stock_item import Stock_Item
from business_objects.store.store_base import Store_Base
from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_tree import Product_Variation_Tree
from business_objects.unit_measurement import Unit_Measurement
from extensions import db
# external
from datetime import datetime, timedelta
import locale
from dataclasses import dataclass
from typing import ClassVar
class Product_Permutation(db.Model, Store_Base):
FLAG_QUANTITY_STOCK = 'quantity-stock'
FLAG_QUANTITY_MIN = 'quantity-min'
FLAG_QUANTITY_MAX = 'quantity-max'
FLAG_COST_LOCAL = 'cost-local'
NAME_ATTR_OPTION_VALUE = Store_Base.ATTR_ID_PRODUCT_PERMUTATION
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_NAME
FLAG_CURRENCY_COST = f'{Currency.ATTR_ID_CURRENCY}_cost'
FLAG_CODE_CURRENCY_COST = f'{Currency.FLAG_CODE}_cost'
FLAG_SYMBOL_CURRENCY_COST = f'{Currency.FLAG_SYMBOL}_cost'
FLAG_COST_LOCAL = 'cost_local'
FLAG_PROFIT_LOCAL_MIN = 'profit_local_min'
FLAG_HAS_VARIATIONS = 'has_variations'
FLAG_LATENCY_MANUFACTURE_DAYS = 'latency_manufacture_days'
FLAG_UNIT_MEASUREMENT_QUANTITY = f'{Unit_Measurement.ATTR_ID_UNIT_MEASUREMENT}_quantity'
FLAG_SYMBOL_UNIT_MEASUREMENT_QUANTITY = f'{Unit_Measurement.FLAG_SYMBOL}_quantity'
FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_QUANTITY = f'{Unit_Measurement.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX}_quantity'
FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_QUANTITY = f'{Unit_Measurement.FLAG_NAME_SINGULAR}_quantity'
FLAG_NAME_PLURAL_UNIT_MEASUREMENT_QUANTITY = f'{Unit_Measurement.FLAG_NAME_PLURAL}_quantity'
FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP = 'count_unit_measurement_per_quantity_step'
FLAG_QUANTITY_STOCK = 'quantity_stock'
FLAG_IS_SUBSCRIPTION = 'is_subscription'
FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = f'{Unit_Measurement.ATTR_ID_UNIT_MEASUREMENT}_interval_recurrence'
FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = f'{Unit_Measurement.FLAG_SYMBOL}_unit_measurement_interval_recurrence'
FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = f'{Unit_Measurement.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX}_unit_measurement_interval_recurrence'
FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = f'{Unit_Measurement.FLAG_NAME_SINGULAR}_unit_measurement_interval_recurrence'
FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = f'{Unit_Measurement.FLAG_NAME_PLURAL}_unit_measurement_interval_recurrence'
FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE = 'count_interval_recurrence'
FLAG_ID_STRIPE_PRODUCT = 'id_stripe_product'
FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED = 'does_expire_faster_once_unsealed'
FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = f'{Unit_Measurement.ATTR_ID_UNIT_MEASUREMENT}_interval_expiration_unsealed'
FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = f'{Unit_Measurement.FLAG_SYMBOL}_interval_expiration_unsealed'
FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = f'{Unit_Measurement.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX}_interval_expiration_unsealed'
FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = f'{Unit_Measurement.FLAG_NAME_SINGULAR}_interval_expiration_unsealed'
FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = f'{Unit_Measurement.FLAG_NAME_PLURAL}_interval_expiration_unsealed'
FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED = 'count_interval_expiration_unsealed'
id_product = db.Column(db.Integer, primary_key=True)
id_permutation = db.Column(db.Integer, primary_key=True)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
# name = db.Column(db.String(255))
description = db.Column(db.String(4000))
# price_GBP_full = db.Column(db.Float)
# price_GBP_min = db.Column(db.Float)
"""
id_currency_cost = db.Column(db.Integer)
code_currency_cost = db.Column(db.String(3))
symbol_currency_cost = db.Column(db.String(3))
"""
# currency_cost: Currency
cost_local = db.Column(db.Float)
profit_local_min = db.Column(db.Float)
has_variations = db.Column(db.Boolean)
id_category = db.Column(db.Integer)
latency_manufacture = db.Column(db.Integer)
latency_manufacture_days = db.Column(db.Integer)
id_unit_measurement_quantity = db.Column(db.Integer)
symbol_unit_measurement_quantity = db.Column(db.String(50))
symbol_is_suffix_not_prefix_unit_measurement_quantity = db.Column(db.Boolean)
name_singular_unit_measurement_quantity = db.Column(db.String(255))
name_plural_unit_measurement_quantity = db.Column(db.String(256))
count_unit_measurement_per_quantity_step = db.Column(db.Integer)
quantity_min = db.Column(db.Float)
quantity_max = db.Column(db.Float)
quantity_step = db.Column(db.Float)
quantity_stock = db.Column(db.Float)
id_stripe_product = db.Column(db.String(100))
is_subscription = db.Column(db.Boolean)
name_recurrence_interval = db.Column(db.String(255))
name_plural_recurrence_interval = db.Column(db.String(256))
count_recurrence_interval = db.Column(db.Integer)
id_unit_measurement_interval_recurrence = db.Column(db.Integer)
symbol_unit_measurement_interval_recurrence = db.Column(db.String(50))
symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence = db.Column(db.Boolean)
name_singular_unit_measurement_interval_recurrence = db.Column(db.String(255))
name_plural_unit_measurement_interval_recurrence = db.Column(db.String(256))
count_interval_recurrence = db.Column(db.Integer)
id_stripe_product = db.Column(db.String(100))
does_expire_faster_once_unsealed = db.Column(db.Boolean)
id_unit_measurement_interval_expiration_unsealed = db.Column(db.Integer)
symbol_unit_measurement_interval_expiration_unsealed = db.Column(db.String(50))
symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed = db.Column(db.Boolean)
name_singular_unit_measurement_interval_expiration_unsealed = db.Column(db.String(255))
name_plural_unit_measurement_interval_expiration_unsealed = db.Column(db.String(256))
count_interval_expiration_unsealed = db.Column(db.Integer)
has_variations = db.Column(db.Boolean)
active = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
# display_order = db.Column(db.Integer)
can_view = db.Column(db.Boolean)
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
@@ -82,41 +132,56 @@ class Product_Permutation(db.Model, Store_Base):
self.stock_item_index = {}
super().__init__()
Store_Base.__init__(self)
self.currency_cost = None
self.form_basket_add = Form_Basket_Add()
self.form_basket_edit = Form_Basket_Edit()
self.is_unavailable_in_currency_or_region = False
# self.is_available = False
self.variation_tree = None
def from_DB_get_many_product_catalogue(query_row):
_m = 'Product_Permutation.from_DB_get_many_product_catalogue'
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
_m = f'{cls.__name__}.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
print(f'query_row: {query_row}')
permutation = Product_Permutation()
permutation = cls()
permutation.id_permutation = query_row[0]
permutation.id_product = query_row[1]
permutation.id_category = query_row[2]
permutation.description = query_row[3]
permutation.cost_local = query_row[4]
permutation.id_currency_cost = query_row[5]
permutation.code_currency_cost = query_row[6]
permutation.symbol_currency_cost = query_row[7]
# permutation.profit_local_min = query_row[8]
permutation.latency_manufacture = query_row[9]
permutation.quantity_min = query_row[10]
permutation.quantity_max = query_row[11]
permutation.quantity_step = query_row[12]
permutation.quantity_stock = query_row[13]
permutation.id_stripe_product = query_row[14]
permutation.is_subscription = av.input_bool(query_row[15], "is_subscription", _m, v_arg_type=v_arg_type)
permutation.name_recurrence_interval = query_row[16]
permutation.name_plural_recurrence_interval = query_row[17]
permutation.count_recurrence_interval = query_row[18]
permutation.active = query_row[19]
permutation.display_order = query_row[20]
permutation.can_view = av.input_bool(query_row[21], "can_view", _m, v_arg_type=v_arg_type)
permutation.can_edit = av.input_bool(query_row[22], "can_edit", _m, v_arg_type=v_arg_type)
permutation.can_admin = av.input_bool(query_row[23], "can_admin", _m, v_arg_type=v_arg_type)
permutation.currency_cost = Currency.from_DB_get_many_product_catalogue_product_permutation(query_row)
permutation.profit_local_min = query_row[8]
permutation.latency_manufacture_days = query_row[9]
permutation.id_unit_measurement_quantity = query_row[10]
permutation.symbol_unit_measurement_quantity = query_row[11]
permutation.symbol_is_suffix_not_prefix_unit_measurement_quantity = av.input_bool(query_row[12], cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_QUANTITY, _m, v_arg_type=v_arg_type)
permutation.name_singular_unit_measurement_quantity = query_row[13]
permutation.name_plural_unit_measurement_quantity = query_row[14]
permutation.count_unit_measurement_per_quantity_step = query_row[15]
permutation.quantity_min = query_row[16]
permutation.quantity_max = query_row[17]
permutation.quantity_stock = query_row[18]
permutation.is_subscription = av.input_bool(query_row[19], "is_subscription", _m, v_arg_type=v_arg_type)
permutation.id_unit_measurement_interval_recurrence = query_row[20]
permutation.symbol_unit_measurement_interval_recurrence = query_row[21]
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence = av.input_bool(query_row[22], cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_RECURRENCE, _m, v_arg_type=v_arg_type)
permutation.name_singular_unit_measurement_interval_recurrence = query_row[23]
permutation.name_plural_unit_measurement_interval_recurrence = query_row[24]
permutation.count_interval_recurrence = query_row[25]
permutation.id_stripe_product = query_row[26]
permutation.does_expire_faster_once_unsealed = av.input_bool(query_row[27], "does_expire_faster_once_unsealed", _m, v_arg_type=v_arg_type)
permutation.id_unit_measurement_interval_expiration_unsealed = query_row[28]
permutation.symbol_unit_measurement_interval_expiration_unsealed = query_row[29]
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed = av.input_bool(query_row[30], cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED, _m, v_arg_type=v_arg_type)
permutation.name_singular_unit_measurement_interval_expiration_unsealed = query_row[31]
permutation.name_plural_unit_measurement_interval_expiration_unsealed = query_row[32]
permutation.count_interval_expiration_unsealed = query_row[33]
permutation.has_variations = av.input_bool(query_row[34], cls.FLAG_HAS_VARIATIONS, _m, v_arg_type=v_arg_type)
permutation.active = av.input_bool(query_row[35], cls.FLAG_ACTIVE, _m, v_arg_type=v_arg_type)
# permutation.display_order = query_row[27]
permutation.can_view = av.input_bool(query_row[36], "can_view", _m, v_arg_type=v_arg_type)
permutation.can_edit = av.input_bool(query_row[37], "can_edit", _m, v_arg_type=v_arg_type)
permutation.can_admin = av.input_bool(query_row[38], "can_admin", _m, v_arg_type=v_arg_type)
return permutation
def from_DB_Stripe_product(query_row):
@@ -136,8 +201,8 @@ class Product_Permutation(db.Model, Store_Base):
# permutation.price_GBP_full = query_row[1]
permutation.id_stripe_product = query_row[2]
permutation.is_subscription = av.input_bool(query_row[3], "is_subscription", _m, v_arg_type=v_arg_type)
permutation.name_recurrence_interval = query_row[4]
permutation.count_recurrence_interval = query_row[5]
permutation.name_singular_unit_measurement_interval_recurrence = query_row[4]
permutation.count_interval_recurrence = query_row[5]
return permutation
"""
def from_json(json_basket_item, key_id_product, key_id_permutation):
@@ -150,49 +215,102 @@ class Product_Permutation(db.Model, Store_Base):
"""
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
permutation = cls()
permutation.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
permutation.id_product = json[cls.ATTR_ID_PRODUCT]
permutation.id_permutation = json[cls.ATTR_ID_PRODUCT_PERMUTATION]
permutation.has_variations = len(json[cls.ATTR_ID_PRODUCT_VARIATION]) > 0
if permutation.has_variations:
for jsonProductVariation in json[cls.ATTR_ID_PRODUCT_VARIATION]:
variation = Product_Variation.from_json(jsonProductVariation)
permutation.add_product_variation(variation)
permutation.quantity_stock = json[cls.FLAG_QUANTITY_STOCK]
permutation.id_product = json[cls.ATTR_ID_PRODUCT]
permutation.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
permutation.description = json[cls.FLAG_DESCRIPTION]
permutation.cost_local = json[cls.FLAG_COST_LOCAL]
permutation.currency_cost = Currency.from_json(json, '_cost')
permutation.profit_local_min = json[cls.FLAG_PROFIT_LOCAL_MIN]
permutation.latency_manufacture_days = json[cls.FLAG_LATENCY_MANUFACTURE_DAYS]
permutation.id_unit_measurement_quantity = json[cls.FLAG_UNIT_MEASUREMENT_QUANTITY]
permutation.symbol_unit_measurement_quantity = json.get(cls.FLAG_SYMBOL_UNIT_MEASUREMENT_QUANTITY)
permutation.symbol_is_suffix_not_prefix_unit_measurement_quantity = json.get(cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_QUANTITY)
permutation.name_singular_unit_measurement_quantity = json.get(cls.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_QUANTITY)
permutation.name_plural_unit_measurement_quantity = json.get(cls.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_QUANTITY)
permutation.count_unit_measurement_per_quantity_step = json[cls.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP]
permutation.quantity_min = json[cls.FLAG_QUANTITY_MIN]
permutation.quantity_max = json[cls.FLAG_QUANTITY_MAX]
permutation.quantity_stock = json[cls.FLAG_QUANTITY_STOCK]
permutation.is_subscription = 1 if av.input_bool(json[cls.FLAG_IS_SUBSCRIPTION], cls.FLAG_IS_SUBSCRIPTION, _m) else 0
permutation.id_unit_measurement_interval_recurrence = json[cls.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE] if json[cls.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE] != '' else None
permutation.symbol_unit_measurement_interval_recurrence = json.get(cls.FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE)
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence = json.get(cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_RECURRENCE)
permutation.name_singular_unit_measurement_interval_recurrence = json.get(cls.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_RECURRENCE)
permutation.name_plural_unit_measurement_interval_recurrence = json.get(cls.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE)
permutation.count_interval_recurrence = json[cls.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE] if json[cls.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE] != '' else None
permutation.id_stripe_product = json[cls.FLAG_ID_STRIPE_PRODUCT]
permutation.does_expire_faster_once_unsealed = 1 if av.input_bool(json[cls.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED], cls.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED, _m) else 0
permutation.id_unit_measurement_interval_expiration_unsealed = json[cls.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED] if json[cls.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED] != '' else None
permutation.symbol_unit_measurement_interval_expiration_unsealed = json.get(cls.FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED)
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed = json.get(cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED)
permutation.name_singular_unit_measurement_interval_expiration_unsealed = json.get(cls.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED)
permutation.name_plural_unit_measurement_interval_expiration_unsealed = json.get(cls.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED)
permutation.count_interval_expiration_unsealed = json[cls.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED] if json[cls.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED] != '' else None
permutation.has_variations = json[cls.FLAG_HAS_VARIATIONS]
permutation.active = 1 if av.input_bool(json[cls.FLAG_ACTIVE], cls.FLAG_ACTIVE, _m) else 0
if permutation.has_variations:
permutation.variation_tree = Product_Variation_Tree.from_json_str(json[cls.FLAG_PRODUCT_VARIATIONS])
"""
for jsonProductVariation in json[cls.FLAG_PRODUCT_VARIATIONS]:
variation = Product_Variation.from_json(jsonProductVariation)
permutation.add_product_variation(variation)
"""
return permutation
def to_json(self):
return {
'id_product': {self.id_product},
'id_permutation': {self.id_permutation},
'description': {self.description},
'id_category': {self.id_category},
'latency_manufacture': {self.latency_manufacture},
'quantity_min': {self.quantity_min},
'quantity_max': {self.quantity_max},
'quantity_step': {self.quantity_step},
'quantity_stock': {self.quantity_stock},
'id_stripe_product': {self.id_stripe_product},
'is_subscription': {self.is_subscription},
'name_recurrence_interval': {self.name_recurrence_interval},
'name_plural_recurrence_interval': {self.name_plural_recurrence_interval},
'count_recurrence_interval': {self.count_recurrence_interval},
'display_order': {self.display_order},
'can_view': {self.can_view},
'can_edit': {self.can_edit},
'can_admin': {self.can_admin},
'variations': {self.variations},
'images': {self.images},
'delivery_options': {self.delivery_options},
'prices': {self.prices}
**self.get_shared_json_attributes(self),
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_DESCRIPTION: self.description,
self.FLAG_COST_LOCAL: self.cost_local,
self.FLAG_CURRENCY_COST: self.currency_cost.to_json(),
self.FLAG_PROFIT_LOCAL_MIN: self.profit_local_min,
self.FLAG_LATENCY_MANUFACTURE_DAYS: self.latency_manufacture_days,
self.FLAG_UNIT_MEASUREMENT_QUANTITY: self.id_unit_measurement_quantity,
self.FLAG_SYMBOL_UNIT_MEASUREMENT_QUANTITY: self.symbol_unit_measurement_quantity,
self.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_QUANTITY: self.symbol_is_suffix_not_prefix_unit_measurement_quantity,
self.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_QUANTITY: self.name_singular_unit_measurement_quantity,
self.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_QUANTITY: self.name_plural_unit_measurement_quantity,
self.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP: self.count_unit_measurement_per_quantity_step,
self.FLAG_QUANTITY_MIN: self.quantity_min,
self.FLAG_QUANTITY_MAX: self.quantity_max,
self.FLAG_QUANTITY_STOCK: self.quantity_stock,
self.FLAG_IS_SUBSCRIPTION: self.is_subscription,
self.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.id_unit_measurement_interval_recurrence,
self.FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.symbol_unit_measurement_interval_recurrence,
self.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence,
self.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.name_singular_unit_measurement_interval_recurrence,
self.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.name_plural_unit_measurement_interval_recurrence,
self.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: self.count_interval_recurrence,
self.FLAG_ID_STRIPE_PRODUCT: self.id_stripe_product,
self.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED: self.does_expire_faster_once_unsealed,
self.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.id_unit_measurement_interval_expiration_unsealed,
self.FLAG_SYMBOL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.symbol_unit_measurement_interval_expiration_unsealed,
self.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed,
self.FLAG_NAME_SINGULAR_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.name_singular_unit_measurement_interval_expiration_unsealed,
self.FLAG_NAME_PLURAL_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.name_plural_unit_measurement_interval_expiration_unsealed,
self.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: self.count_interval_expiration_unsealed,
self.FLAG_HAS_VARIATIONS: self.has_variations,
self.FLAG_ACTIVE: self.active,
self.FLAG_CAN_VIEW: self.can_view,
self.FLAG_CAN_EDIT: self.can_edit,
self.FLAG_CAN_ADMIN: self.can_admin,
self.FLAG_PRODUCT_VARIATION: [variation.to_json() for variation in self.variations],
self.FLAG_PRODUCT_IMAGE: [image.to_json() for image in self.images],
self.FLAG_DELIVERY_OPTION: [option.to_json() for option in self.delivery_options],
self.FLAG_PRODUCT_PRICE: [price.to_json() for price in self.prices],
}
def to_json_option(self):
return {
'value': self.id_permutation,
'text': self.get_name_variations()
}
def get_name(self):
return
def get_name_variations(self):
return self.variation_tree.get_name_variations()
def is_available(self):
@@ -207,10 +325,10 @@ class Product_Permutation(db.Model, Store_Base):
return price.value_local_VAT_excl
def output_lead_time(self):
return '1 day' if self.latency_manufacture == 1 else f'{self.latency_manufacture} days'
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)).strftime('%A, %d %B %Y')
return (datetime.now() + timedelta(days=self.latency_manufacture_days)).strftime('%A, %d %B %Y')
def output_price(self, is_included_VAT):
if self.is_unavailable_in_currency_or_region:
@@ -252,21 +370,38 @@ class Product_Permutation(db.Model, Store_Base):
"""
def __repr__(self):
return f'''Product_Permutation
id_product: {self.id_product}
id_permutation: {self.id_permutation}
description: {self.description}
id_product: {self.id_product}
id_category: {self.id_category}
latency_manufacture: {self.latency_manufacture}
description: {self.description}
cost_local: {self.cost_local}
currency_cost: {self.currency_cost}
latency_manufacture_days: {self.latency_manufacture_days}
id_unit_measurement_quantity: {self.id_unit_measurement_quantity}
symbol_unit_measurement_quantity: {self.symbol_unit_measurement_quantity}
symbol_is_suffix_not_prefix_unit_measurement_quantity: {self.symbol_is_suffix_not_prefix_unit_measurement_quantity}
name_singular_unit_measurement_quantity: {self.name_singular_unit_measurement_quantity}
name_plural_unit_measurement_quantity: {self.name_plural_unit_measurement_quantity}
count_unit_measurement_per_quantity_step: {self.count_unit_measurement_per_quantity_step}
quantity_min: {self.quantity_min}
quantity_max: {self.quantity_max}
quantity_step: {self.quantity_step}
quantity_stock: {self.quantity_stock}
id_stripe_product: {self.id_stripe_product}
is_subscription: {self.is_subscription}
name_recurrence_interval: {self.name_recurrence_interval}
name_plural_recurrence_interval: {self.name_plural_recurrence_interval}
count_recurrence_interval: {self.count_recurrence_interval}
display_order: {self.display_order}
id_unit_measurement_interval_recurrence: {self.id_unit_measurement_interval_recurrence}
symbol_unit_measurement_interval_recurrence: {self.symbol_unit_measurement_interval_recurrence}
symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence: {self.symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence}
name_singular_unit_measurement_interval_recurrence: {self.name_singular_unit_measurement_interval_recurrence}
name_plural_unit_measurement_interval_recurrence: {self.name_plural_unit_measurement_interval_recurrence}
count_interval_recurrence: {self.count_interval_recurrence}
id_stripe_product: {self.id_stripe_product}
does_expire_faster_once_unsealed: {self.does_expire_faster_once_unsealed}
id_unit_measurement_interval_expiration_unsealed: {self.id_unit_measurement_interval_expiration_unsealed}
symbol_unit_measurement_interval_expiration_unsealed: {self.symbol_unit_measurement_interval_expiration_unsealed}
symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed: {self.symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed}
name_singular_unit_measurement_interval_expiration_unsealed: {self.name_singular_unit_measurement_interval_expiration_unsealed}
name_plural_unit_measurement_interval_expiration_unsealed: {self.name_plural_unit_measurement_interval_expiration_unsealed}
count_interval_expiration_unsealed: {self.count_interval_expiration_unsealed}
has_variations: {self.has_variations}
can_view: {self.can_view}
can_edit: {self.can_edit}
can_admin: {self.can_admin}
@@ -378,3 +513,104 @@ class Permutation_Product_Variation_Link(db.Model):
link.id_variation = query_row[3]
return link
"""
class Product_Permutation_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Product_Permutation_Temp'
__table_args__ = { 'extend_existing': True }
id_permutation: int = db.Column(db.Integer, primary_key=True)
id_product: int = db.Column(db.Integer)
description: str = db.Column(db.String(4000))
cost_local: float = db.Column(db.Float)
id_currency_cost: int = db.Column(db.Integer)
profit_local_min: float = db.Column(db.Float)
latency_manufacture_days: int = db.Column(db.Integer)
id_unit_measurement_quantity: int = db.Column(db.Integer)
count_unit_measurement_per_quantity_step: int = db.Column(db.Float)
quantity_min: int = db.Column(db.Integer)
quantity_max: int = db.Column(db.Integer)
quantity_stock: int = db.Column(db.Integer)
is_subscription: bool = db.Column(db.Boolean)
id_unit_measurement_interval_recurrence: int = db.Column(db.Integer)
count_interval_recurrence: int = db.Column(db.Float)
id_stripe_product: str = db.Column(db.String(50))
does_expire_faster_once_unsealed: bool = db.Column(db.Boolean)
id_unit_measurement_interval_expiration_unsealed: int = db.Column(db.Integer)
count_interval_expiration_unsealed: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
@classmethod
def from_product_permutation(cls, product_permutation):
row = cls()
row.id_permutation = product_permutation.id_permutation
row.id_product = product_permutation.id_product
row.description = product_permutation.description
row.cost_local = product_permutation.cost_local
row.id_currency_cost = product_permutation.currency_cost.id_currency
row.profit_local_min = product_permutation.profit_local_min
row.latency_manufacture_days = product_permutation.latency_manufacture_days
row.id_unit_measurement_quantity = product_permutation.id_unit_measurement_quantity
row.count_unit_measurement_per_quantity_step = product_permutation.count_unit_measurement_per_quantity_step
row.quantity_min = product_permutation.quantity_min
row.quantity_max = product_permutation.quantity_max
row.quantity_stock = product_permutation.quantity_stock
row.is_subscription = product_permutation.is_subscription
row.id_unit_measurement_interval_recurrence = product_permutation.id_unit_measurement_interval_recurrence
row.count_interval_recurrence = product_permutation.count_interval_recurrence
row.id_stripe_product = product_permutation.id_stripe_product
row.does_expire_faster_once_unsealed = product_permutation.does_expire_faster_once_unsealed
row.id_unit_measurement_interval_expiration_unsealed = product_permutation.id_unit_measurement_interval_expiration_unsealed
row.count_interval_expiration_unsealed = product_permutation.count_interval_expiration_unsealed
row.active = product_permutation.active
return row
def __repr__(self):
return f'''
id_permutation: {self.id_permutation}
id_product: {self.id_product}
description: {self.description}
cost_local: {self.cost_local}
id_currency_cost: {self.id_currency_cost}
profit_local_min: {self.profit_local_min}
latency_manufacture_days: {self.latency_manufacture_days}
id_unit_measurement_quantity: {self.id_unit_measurement_quantity}
{Product_Permutation.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP}: {self.count_unit_measurement_per_quantity_step}
quantity_min: {self.quantity_min}
quantity_max: {self.quantity_max}
quantity_stock: {self.quantity_stock}
is_subscription: {self.is_subscription}
id_unit_measurement_interval_recurrence: {self.id_unit_measurement_interval_recurrence}
count_interval_recurrence: {self.count_interval_recurrence}
id_stripe_product: {self.id_stripe_product}
does_expire_faster_once_unsealed: {self.does_expire_faster_once_unsealed}
id_unit_measurement_interval_expiration_unsealed: {self.id_unit_measurement_interval_expiration_unsealed}
count_interval_expiration_unsealed: {self.count_interval_expiration_unsealed}
active: {self.active}
guid: {self.guid}
'''
"""
def to_json(self):
return {
self.ATTR_ID_PRODUCT_PERMUTATION: int(self.id_permutation),
self.ATTR_ID_PRODUCT: int(self.id_product),
self.FLAG_DESCRIPTION: self.description,
Product_Permutation.FLAG_COST_LOCAL: float(self.cost_local),
Product_Permutation.FLAG_CURRENCY_COST: int(self.id_currency_cost),
Product_Permutation.FLAG_PROFIT_LOCAL_MIN: float(self.profit_local_min),
Product_Permutation.FLAG_LATENCY_MANUFACTURE_DAYS: int(self.latency_manufacture_days),
Product_Permutation.FLAG_UNIT_MEASUREMENT_QUANTITY: int(self.id_unit_measurement_quantity),
Product_Permutation.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP: float(self.count_unit_measurement_per_quantity_step),
self.FLAG_QUANTITY_MIN: float(self.quantity_min),
self.FLAG_QUANTITY_MAX: float(self.quantity_max),
Product_Permutation.FLAG_QUANTITY_STOCK: float(self.quantity_stock),
Product_Permutation.FLAG_IS_SUBSCRIPTION: bool(self.is_subscription),
Product_Permutation.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: int(self.id_unit_measurement_interval_recurrence) if self.id_unit_measurement_interval_recurrence != '' else None,
Product_Permutation.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE: float(self.count_interval_recurrence) if self.count_interval_recurrence != '' else None,
Product_Permutation.FLAG_ID_STRIPE_PRODUCT: self.id_stripe_product,
Product_Permutation.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED: bool(self.does_expire_faster_once_unsealed),
Product_Permutation.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: int(self.id_unit_measurement_interval_expiration_unsealed) if self.id_unit_measurement_interval_expiration_unsealed != '' else None,
Product_Permutation.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED: float(self.count_interval_expiration_unsealed) if self.count_interval_expiration_unsealed != '' else None,
self.FLAG_ACTIVE: bool(self.active),
self.FLAG_GUID: self.guid
}
"""

View File

@@ -20,19 +20,21 @@ from dataclasses import dataclass
from typing import ClassVar
class Product_Price(db.Model, Store_Base):
ATTR_ID_PRODUCT_PRICE: ClassVar[str] = 'id-price'
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'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display-order-price'
id_price = 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)
"""
id_currency = db.Column(db.Integer)
code_currency = db.Column(db.String(50))
name_currency = db.Column(db.String(255))
symbol_currency = db.Column(db.String(50))
"""
id_region = db.Column(db.Integer)
value_local_VAT_incl = db.Column(db.Float)
value_local_VAT_excl = db.Column(db.Float)
@@ -41,18 +43,23 @@ class Product_Price(db.Model, Store_Base):
def __init__(self):
super().__init__()
Store_Base.__init__(self)
def from_DB_get_many_product_catalogue(query_row):
self.currency = None
self.delivery_region = None
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
# _m = 'Product_Price.from_DB_get_many_product_catalogue'
price = Product_Price()
price = cls()
price.id_price = query_row[0]
price.id_permutation = query_row[1]
price.id_product = query_row[2]
price.id_category = query_row[3]
price.currency = Currency.from_DB_get_many_product_price_and_discount_and_delivery_region(query_row)
"""
price.id_currency = query_row[4]
price.code_currency = query_row[5]
price.name_currency = query_row[6]
price.symbol_currency = query_row[7]
"""
price.id_region = query_row[8]
price.value_local_VAT_incl = query_row[9]
price.value_local_VAT_excl = query_row[10]
@@ -65,35 +72,26 @@ class Product_Price(db.Model, Store_Base):
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
id_currency: {self.id_currency}
code_currency: {self.code_currency}
name_currency: {self.name_currency}
symbol_currency: {self.symbol_currency}
currency: {self.currency}
id_region: {self.id_region}
value_local (VAT incl): {self.value_local_VAT_incl}
value_local (VAT excl): {self.value_local_VAT_excl}
display_order (UID): {self.display_order}
{self.FLAG_TEXT}: {self.currency.symbol} {self.value_local_VAT_incl}
'''
def to_json(self):
return {
self.ATTR_ID_PRODUCT_PRICE: {self.id_price},
self.ATTR_ID_PRODUCT_PERMUTATION: {self.id_permutation},
self.ATTR_ID_PRODUCT: {self.id_product},
self.ATTR_ID_PRODUCT_CATEGORY: {self.id_category},
Currency.ATTR_ID_CURRENCY: {self.id_currency},
Currency.FLAG_CODE: {self.code_currency},
Currency.FLAG_NAME: {self.name_currency},
Currency.FLAG_SYMBOL: {self.symbol_currency},
Delivery_Region.ATTR_ID_REGION: {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}
}
def to_json_option(self):
return {
'value': self.id_price,
'text': f'{self.symbol_currency} {self.value_local_VAT_incl}'
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT_PRICE: self.id_price,
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_CURRENCY: self.currency.to_json(),
Delivery_Region.ATTR_ID_DELIVERY_REGION: 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
}
@classmethod
@@ -103,11 +101,8 @@ class Product_Price(db.Model, Store_Base):
price.id_permutation = json[cls.ATTR_ID_PRODUCT_PERMUTATION]
price.id_product = json[cls.ATTR_ID_PRODUCT]
price.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
price.id_currency = json[Currency.ATTR_ID_CURRENCY]
price.code_currency = json[Currency.FLAG_CODE]
price.name_currency = json[Currency.FLAG_NAME]
price.symbol_currency = json[Currency.FLAG_SYMBOL]
price.id_region = json[Delivery_Region.ATTR_ID_REGION]
price.currency = Currency.from_json(json)
price.id_region = json[Delivery_Region.ATTR_ID_DELIVERY_REGION]
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

@@ -19,124 +19,103 @@ Business object for product variation
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from business_objects.store.product_variation_type import Product_Variation_Type
from extensions import db
# external
from dataclasses import dataclass
from typing import ClassVar
from pydantic import BaseModel
from itertools import filterfalse
from operator import attrgetter
# CLASSES
class Product_Variation(db.Model, Store_Base):
KEY_ACTIVE_VARIATION: ClassVar[str] = 'active_variation'
KEY_ACTIVE_VARIATION_TYPE: ClassVar[str] = 'active_variation_type'
KEY_CODE_VARIATION: ClassVar[str] = 'code_variation'
KEY_CODE_VARIATION_TYPE: ClassVar[str] = 'code_variation_type'
KEY_DISPLAY_ORDER_VARIATION: ClassVar[str] = 'display_order_variation'
KEY_DISPLAY_ORDER_VARIATION_TYPE: ClassVar[str] = 'display_order_variation_type'
KEY_NAME_VARIATION: ClassVar[str] = 'name_variation'
KEY_NAME_VARIATION_TYPE: ClassVar[str] = 'name_variation_type'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT_VARIATION
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_NAME
id_variation = db.Column(db.Integer, primary_key=True)
code_variation = db.Column(db.String(50))
name_variation = db.Column(db.String(255))
active_variation = db.Column(db.Boolean)
display_order_variation = db.Column(db.Integer)
id_type = db.Column(db.Integer)
code_variation_type = db.Column(db.String(50))
name_variation_type = db.Column(db.String(255))
name_plural_variation_type = db.Column(db.String(255))
active_variation_type = db.Column(db.Boolean)
display_order_variation_type = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
id_category = db.Column(db.Integer)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
id_permutation = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.variation_type = None
def from_DB_get_many_product_catalogue(query_row):
variation = Product_Variation.from_DB_variation(query_row)
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
variation = Product_Variation.from_DB_get_many_product_variation(query_row)
variation.id_product = query_row[11]
variation.id_permutation = query_row[12]
variation.id_category = query_row[13]
variation.variation_type = Product_Variation_Type.from_DB_get_many_product_catalogue(query_row)
return variation
def from_DB_variation(query_row):
_m = 'Product_Variation.from_DB_variation'
variation = Product_Variation()
@classmethod
def from_DB_get_many_product_variation(cls, query_row):
variation = cls()
variation.id_variation = query_row[0]
variation.code_variation = query_row[1]
variation.name_variation = query_row[2]
variation.active_variation = av.input_bool(query_row[3], 'active_variation', _m)
variation.display_order_variation = query_row[4]
variation.id_type = query_row[5]
variation.code_variation_type = query_row[6]
variation.name_variation_type = query_row[7]
variation.name_plural_variation_type = query_row[8]
variation.active_variation_type = av.input_bool(query_row[9], 'active_variation', _m)
variation.display_order_variation_type = query_row[10]
variation.id_type = query_row[1]
variation.code = query_row[2]
variation.name = query_row[3]
variation.display_order = query_row[4]
variation.active = av.input_bool(query_row[5], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_get_many_product_variation')
return variation
@classmethod
def from_json(cls, json):
variation = cls()
variation.id_variation = json[cls.ATTR_ID_VARIATION]
variation.id_variation = json[cls.ATTR_ID_PRODUCT_VARIATION]
variation.code = json[cls.FLAG_CODE]
variation.name = json[cls.FLAG_NAME]
variation.display_order = json[cls.FLAG_DISPLAY_ORDER]
variation.active = json[cls.FLAG_ACTIVE]
variation.id_permutation = json[cls.ATTR_ID_PRODUCT_PERMUTATION]
variation.id_product = json[cls.ATTR_ID_PRODUCT]
variation.id_permutation = json[cls.ATTR_ID_PERMUTATION]
variation.id_category = json[cls.ATTR_ID_CATEGORY]
variation.name_variation_type = json[cls.KEY_NAME_VARIATION_TYPE]
variation.name_variation = json[cls.KEY_NAME_VARIATION]
variation.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
return variation
def __repr__(self):
return f'''
id: {self.id_variation}
id_product: {self.id_product}
{self.__class__.__name__}
id_variation: {self.id_variation}
id_type: {self.id_type}
code: {self.code}
name: {self.name}
display_order: {self.display_order}
active: {self.active}
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
code_variation_type: {self.code_variation_type}
name_variation_type: {self.name_variation_type}
code_variation: {self.code_variation}
name_variation: {self.name_variation}
active_variation: {self.active_variation}
active_variation_type: {self.active_variation_type}
display_order_variation: {self.display_order_variation}
display_order_variation_type: {self.display_order_variation_type}
variation_type: {self.variation_type}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT_VARIATION: self.id_variation,
self.ATTR_ID_PRODUCT_VARIATION_TYPE: self.id_type,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_PERMUTATION: self.id_permutation,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.ATTR_ID_PRODUCT_VARIATION_TYPE: self.id_type,
self.KEY_CODE_VARIATION_TYPE: self.code_variation_type,
self.KEY_CODE_VARIATION: self.code_variation,
self.KEY_DISPLAY_ORDER_VARIATION_TYPE: self.display_order_variation_type,
self.KEY_DISPLAY_ORDER_VARIATION: self.display_order_variation,
self.KEY_NAME_VARIATION_TYPE: self.name_variation_type,
self.KEY_NAME_VARIATION: self.name_variation,
self.KEY_ACTIVE_VARIATION_TYPE: self.active_variation_type,
self.KEY_ACTIVE_VARIATION: self.active_variation,
}
def to_json_option(self):
return {
'value': self.id_variation,
'text': self.name_variation
}
def to_json_variation_type(self):
return {
self.ATTR_ID_PRODUCT_VARIATION_TYPE: self.id_type,
self.KEY_CODE_VARIATION_TYPE: self.code_variation_type,
self.KEY_DISPLAY_ORDER_VARIATION_TYPE: self.display_order_variation_type,
self.KEY_NAME_VARIATION_TYPE: self.name_variation_type,
self.KEY_ACTIVE_VARIATION_TYPE: self.active_variation_type,
'text': self.name
}
@dataclass
class Product_Variation_Filters():
get_all_variation_type: bool
@@ -198,30 +177,35 @@ class Product_Variation_Filters():
get_first_variation = False,
ids_variation = ''
)
class Product_Variation_List(BaseModel):
class Product_Variation_Container(BaseModel):
variation_types: list = []
variations: list = []
def add_product_variation_type(self, variation_type):
av.val_instance(variation_type, 'variation_type', 'Product_Variation_Container.add_product_variation_type', Product_Variation_Type)
self.variations.append(variation_type)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Product_Variation_List.add_product_variation', Product_Variation)
av.val_instance(variation, 'variation', 'Product_Variation_Container.add_product_variation', Product_Variation)
if variation.variation_type is None:
variation_type = next(filterfalse(lambda x: x.id_type != variation.id_type, self.variation_types), None)
if variation_type is not None:
variation.variation_type = variation_type
self.variations.append(variation)
def __repr__(self):
return f'variations: {self.variations}'
return f'Product_Variation_Container:\nvariations_types: {self.variation_types}\nvariations: {self.variations}'
def to_list_variations(self):
def to_list_variation_options(self):
list_variations = []
for variation in self.variations:
list_variations.append(variation.to_json())
list_variations.append(variation.to_json_option())
print(f'list_variations: {list_variations}')
return list_variations
def to_list_variation_types(self):
def to_list_variation_type_options(self):
list_variation_types = []
list_variation_ids = []
for variation in self.variations:
if variation.id_type not in list_variation_ids:
list_variation_ids.append(variation.id_type)
list_variation_types.append(variation.to_json_variation_type())
for variation_type in self.variation_types:
list_variation_types.append(variation_type.to_json_option())
return list_variation_types

View File

@@ -55,20 +55,42 @@ class Product_Variation_Tree():
is_equal = (sz_me == sz_other)
if is_equal:
for index_type in range(sz_me):
if sz_me[index_type] != sz_other[index_type]:
if my_type_list[index_type] != other_type_list[index_type]:
is_equal = False
break
return is_equal
def from_product_permutation(product_permutation):
@classmethod
def from_product_permutation(cls, product_permutation):
depth_max = len(product_permutation.variations)
node_root = Product_Variation_Tree_Node.from_variation_and_node_parent(product_permutation.variations[0], None)
node = node_root
for depth in range(depth_max - 1):
node = Product_Variation_Tree_Node.from_variation_and_node_parent(product_permutation.variations[depth + 1], node)
return Product_Variation_Tree.from_node_root(node_root)
def from_product_variation(product_variation):
return cls.from_node_root(node_root)
@classmethod
def from_product_variation(cls, product_variation):
node_root = Product_Variation_Tree_Node.from_variation_and_node_parent(product_variation, None)
return Product_Variation_Tree.from_node_root(node_root)
return cls.from_node_root(node_root)
@classmethod
def from_product_variations(cls, product_variations):
node_root = Product_Variation_Tree_Node.from_variation_and_node_parent(product_variations[0], None)
tree = cls.from_node_root(node_root)
if len(product_variations) > 1:
for variation in product_variations[1:]:
tree.add_product_variation(variation)
return tree
@classmethod
def from_json_str(cls, json_str):
variations = []
for json_variation in json_str.split(','):
parts = json_variation.split(':')
if len(parts) != 2: continue
variation = Product_Variation()
variation.id_type = parts[0]
variation.id_variation = parts[1]
variations.append(variation)
return cls.from_product_variations(variations)
"""
def get_name_variations(self):
node = self.node_root
name = node.variation.name_variation_type
@@ -78,6 +100,7 @@ class Product_Variation_Tree():
name += f', {node.variation.name_variation_type}'
at_leaf_node = node.is_leaf()
return name
"""
def get_node_leaf(self):
node = self.node_root
at_leaf_node = node.is_leaf()
@@ -102,8 +125,64 @@ class Product_Variation_Tree():
variations = []
node = self.node_root
at_leaf_node = node.is_leaf()
variations.append(node.variation)
while not at_leaf_node:
variations.append(node.variation)
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
return variations
variations.append(node.variation)
return variations
def to_preview_str(self):
print(f'Product_Variation_Tree.to_preview_str')
variations = self.get_product_variations()
print(f'variations: {variations}')
preview_str = ''
for variation in variations:
is_first = (preview_str == '')
preview_str += f'{variation.variation_type.name_singular}: {variation.name}'
if is_first:
preview_str += '\n'
print(f'preview_str: {preview_str}')
return preview_str
def to_json(self):
variations = self.get_product_variations()
json_variations = []
for variation in variations:
json_variations.append(variation.to_json())
return json_variations
def to_variation_id_pairs_str(self):
variations = self.get_product_variations()
pairs_str = ''
for variation in variations:
pairs_str += f'{variation.id_type}:{variation.id_variation},'
return pairs_str
"""
class Product_Variation_Container(BaseModel):
variation_types: list = []
variations: list = []
def add_product_variation_type(self, variation_type):
av.val_instance(variation_type, 'variation_type', 'Product_Variation_Container.add_product_variation_type', Product_Variation_Type)
self.variations.append(variation_type)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Product_Variation_Container.add_product_variation', Product_Variation)
if variation.variation_type is None:
variation_type = next(filterfalse(lambda x: x.id_type != variation.id_type, self.variation_types), None)
if variation_type is not None:
variation.variation_type = variation_type
self.variations.append(variation)
def __repr__(self):
return f'Product_Variation_Container:\nvariations_types: {self.variation_types}\nvariations: {self.variations}'
def to_list_variation_options(self):
list_variations = []
for variation in self.variations:
list_variations.append(variation.to_json_option())
print(f'list_variations: {list_variations}')
return list_variations
def to_list_variation_type_options(self):
list_variation_types = []
for variation_type in self.variation_types:
list_variation_types.append(variation_type.to_json_option())
return list_variation_types
"""

View File

@@ -0,0 +1,103 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Product_Variation Business Object
Description:
Business object for product variation
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from extensions import db
# external
from dataclasses import dataclass
from typing import ClassVar
from pydantic import BaseModel
from itertools import filterfalse
from operator import attrgetter
class Product_Variation_Type(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT_VARIATION_TYPE
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_NAME_SINGULAR
id_type = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name_singular = db.Column(db.String(255))
name_plural = db.Column(db.String(255))
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
def __init__(self):
super().__init__()
@classmethod
def from_DB_get_many_product_catalogue(cls, query_row):
variation_type = cls()
variation_type.id_type = query_row[1]
variation_type.code = query_row[6]
variation_type.name_singular = query_row[7]
variation_type.name_plural = query_row[8]
variation_type.display_order = query_row[9]
variation_type.active = av.input_bool(query_row[10], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_get_many_product_catalogue')
return variation_type
@classmethod
def from_DB_get_many_product_variation(cls, query_row):
variation_type = cls()
variation_type.id_type = query_row[0]
variation_type.code = query_row[1]
variation_type.name_singular = query_row[2]
variation_type.name_plural = query_row[3]
variation_type.display_order = query_row[4]
variation_type.active = av.input_bool(query_row[5], cls.FLAG_ACTIVE, f'{cls.__name__}.from_DB_get_many_product_variation')
return variation_type
@classmethod
def from_json(cls, json):
variation_type = cls()
variation_type.id_type = json[cls.ATTR_ID_PRODUCT_VARIATION_TYPE]
variation_type.code = json[cls.FLAG_CODE]
variation_type.name_singular = json[cls.FLAG_NAME_SINGULAR]
variation_type.name_plural = json[cls.FLAG_NAME_PLURAL]
variation_type.display_order = json[cls.FLAG_DISPLAY_ORDER]
variation_type.active = json[cls.FLAG_ACTIVE]
return variation_type
def __repr__(self):
return f'''
{self.__class__.__name__}
id_type: {self.id_type}
code: {self.code}
name_singular: {self.name_singular}
name_plural: {self.name_plural}
display_order: {self.display_order}
active: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_PRODUCT_VARIATION_TYPE: self.id_type,
self.FLAG_CODE: self.code,
self.FLAG_NAME_SINGULAR: self.name_singular,
self.FLAG_NAME_PLURAL: self.name_plural,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active,
}
def to_json_option(self):
return {
'value': self.id_type,
'text': self.name_singular
}

View File

@@ -24,6 +24,8 @@ from datetime import datetime
class Stock_Item(db.Model, Store_Base):
ATTR_ID_CURRENCY_COST: ClassVar[str] = f'{Store_Base.ATTR_ID_CURRENCY}-cost'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_CURRENCY
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_NAME
FLAG_DATE_CONSUMED: ClassVar[str] = 'date-consumed'
FLAG_DATE_EXPIRATION: ClassVar[str] = 'date-expiration'
FLAG_DATE_PURCHASED: ClassVar[str] = 'date-purchased'
@@ -175,6 +177,7 @@ class Stock_Item(db.Model, Store_Base):
'''
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},
@@ -183,8 +186,8 @@ class Stock_Item(db.Model, Store_Base):
self.FLAG_CAN_EDIT: {self.can_edit},
self.FLAG_CAN_ADMIN: {self.can_admin},
self.FLAG_HAS_VARIATIONS: {self.has_variations},
self.FLAG_PERMUTATIONS: {self.permutations},
self.FLAG_VARIATION_TREES: {self.variation_trees},
self.FLAG_PRODUCT_PERMUTATION: {self.permutations},
self.FLAG_PRODUCT_VARIATION_TREES: {self.variation_trees},
}
def has_permutations(self):
return len(self.permutations) > 0

View File

@@ -12,6 +12,7 @@ Abstract business object for store objects
# internal
# from helpers.DEPRECATED.helper_abc import Interface_ABC
from business_objects.base import Base
from extensions import db
import lib.argument_validation as av
# external
@@ -55,40 +56,33 @@ class I_Store_Base():
""
"""
class Store_Base():
ATTR_ID_ACCESS_LEVEL: ClassVar[str] = 'id_access_level'
ATTR_ID_CURRENCY: ClassVar[str] = 'id_currency'
class Store_Base(Base):
# ATTR_ID_CURRENCY_COST: ClassVar[str] = 'id_currency_cost'
ATTR_ID_DELIVERY_REGION: ClassVar[str] = 'id_delivery_region'
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_PRODUCT: ClassVar[str] = 'id_product'
ATTR_ID_PRODUCT_CATEGORY: ClassVar[str] = 'id_category'
ATTR_ID_PRODUCT_IMAGE: ClassVar[str] = 'id_image'
ATTR_ID_PRODUCT_PERMUTATION: ClassVar[str] = 'id_permutation'
ATTR_ID_PRODUCT_PRICE: ClassVar[str] = 'id_price'
ATTR_ID_PRODUCT_VARIATION: ClassVar[str] = 'id_variation'
ATTR_ID_PRODUCT_VARIATION_TYPE: ClassVar[str] = 'id_variation_type'
ATTR_ID_PRODUCT_VARIATION_TYPE: ClassVar[str] = 'id_type'
ATTR_ID_STOCK_ITEM: ClassVar[str] = 'id_stock_item'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'access_level_required'
FLAG_ACTIVE: ClassVar[str] = 'active'
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_DESCRIPTION: ClassVar[str] = 'description'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display_order'
FLAG_CURRENCY: ClassVar[str] = 'currency'
FLAG_DELIVERY_OPTION: ClassVar[str] = 'delivery_option'
FLAG_HAS_VARIATIONS: ClassVar[str] = 'has_variations'
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
FLAG_KEY_PRIMARY: ClassVar[str] = 'key_primary'
FLAG_NAME: ClassVar[str] = 'name'
FLAG_PERMUTATIONS: ClassVar[str] = 'permutations'
FLAG_PRIORITY: ClassVar[str] = 'priority'
FLAG_ROWS: ClassVar[str] = 'rows'
FLAG_VARIATION_TREES: ClassVar[str] = 'variation_trees'
@classmethod
def output_bool(cls, value):
return av.input_bool(value, f'{cls.__name__} bool attribute', f'{cls.__name__}.output_bool')
@staticmethod
def convert_list_objects_to_list_options(objects):
return [object.to_json_option() for object in objects]
FLAG_IS_OUT_OF_STOCK: ClassVar[str] = 'is_out_of_stock'
FLAG_PRODUCT: ClassVar[str] = 'product'
FLAG_PRODUCT_CATEGORY: ClassVar[str] = 'product_category'
FLAG_PRODUCT_IMAGE: ClassVar[str] = 'product_image'
FLAG_PRODUCT_PERMUTATION: ClassVar[str] = 'product_permutation'
FLAG_PRODUCT_PRICE: ClassVar[str] = 'product_price'
FLAG_PRODUCT_VARIATION: ClassVar[str] = 'product_variation'
FLAG_PRODUCT_VARIATIONS: ClassVar[str] = f'{FLAG_PRODUCT_VARIATION}s'
FLAG_PRODUCT_VARIATION_TYPE: ClassVar[str] = 'product_variation_type'
FLAG_QUANTITY_MIN: ClassVar[str] = 'quantity_min'
FLAG_QUANTITY_MAX: ClassVar[str] = 'quantity_max'
FLAG_TEXT: ClassVar[str] = 'text'
FLAG_VALUE_TEXT: ClassVar[str] = 'value_text'

View File

@@ -0,0 +1,160 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Unit of Measurement Business Object
"""
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from extensions import db
# from forms.forms import Form_Filters_User
import lib.argument_validation as av
# external
from dataclasses import dataclass
from typing import ClassVar
class Unit_Measurement(SQLAlchemy_ABC, Base):
ATTR_ID_UNIT_MEASUREMENT: ClassVar[str] = 'id_unit_measurement'
FLAG_IS_BASE_UNIT: ClassVar[str] = 'is_base_unit'
FLAG_IS_UNIT_OF_DISTANCE: ClassVar[str] = 'is_unit_of_distance'
FLAG_IS_UNIT_OF_MASS: ClassVar[str] = 'is_unit_of_mass'
FLAG_IS_UNIT_OF_TIME: ClassVar[str] = 'is_unit_of_time'
FLAG_IS_UNIT_OF_VOLUME: ClassVar[str] = 'is_unit_of_volume'
FLAG_NAME_PLURAL: ClassVar[str] = 'name_plural'
FLAG_NAME_SINGULAR: ClassVar[str] = 'name_singular'
FLAG_SYMBOL: ClassVar[str] = 'symbol'
FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX: ClassVar[str] = 'symbol_is_suffix_not_prefix'
# KEY_UNIT_MEASUREMENT: ClassVar[str] = 'unit_of_measurement'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = ATTR_ID_UNIT_MEASUREMENT
NAME_ATTR_OPTION_TEXT: ClassVar[str] = FLAG_NAME_SINGULAR
id_unit_measurement = db.Column(db.Integer, primary_key=True)
name_singular = db.Column(db.String(255))
name_plural = db.Column(db.String(256))
symbol = db.Column(db.String(50))
symbol_is_suffix_not_prefix = db.Column(db.Boolean)
is_base_unit = db.Column(db.Boolean)
is_unit_of_distance = db.Column(db.Boolean)
is_unit_of_mass = db.Column(db.Boolean)
is_unit_of_time = db.Column(db.Boolean)
is_unit_of_volume = db.Column(db.Boolean)
active = db.Column(db.Boolean)
def from_DB_unit_measurement(query_row):
_m = 'Unit_Measurement.from_DB_unit_measurement'
unit = Unit_Measurement()
unit.id_unit_measurement = query_row[0]
unit.name_singular = query_row[1]
unit.name_plural = query_row[2]
unit.symbol = query_row[3]
unit.symbol_is_suffix_not_prefix = av.input_bool(query_row[4], 'symbol_is_suffix_not_prefix', _m)
unit.is_base_unit = av.input_bool(query_row[5], 'is_base_unit', _m)
unit.is_unit_of_distance = av.input_bool(query_row[6], 'is_unit_of_distance', _m)
unit.is_unit_of_mass = av.input_bool(query_row[7], 'is_unit_of_mass', _m)
unit.is_unit_of_time = av.input_bool(query_row[8], 'is_unit_of_time', _m)
unit.is_unit_of_volume = av.input_bool(query_row[9], 'is_unit_of_volume', _m)
unit.active = av.input_bool(query_row[10], 'active', _m)
return unit
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_UNIT_MEASUREMENT: self.id_unit_measurement,
self.FLAG_NAME_SINGULAR: self.name_singular,
self.FLAG_NAME_PLURAL: self.name_plural,
self.FLAG_SYMBOL: self.symbol,
self.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX: self.symbol_is_suffix_not_prefix,
self.FLAG_IS_BASE_UNIT: self.is_base_unit,
self.FLAG_IS_UNIT_OF_DISTANCE: self.is_unit_of_distance,
self.FLAG_IS_UNIT_OF_MASS: self.is_unit_of_mass,
self.FLAG_IS_UNIT_OF_TIME: self.is_unit_of_time,
self.FLAG_IS_UNIT_OF_VOLUME: self.is_unit_of_volume,
self.FLAG_ACTIVE: self.active,
}
@classmethod
def from_json(cls, json):
print(f' Unit_Measurement.from_json: {json}')
unit = cls()
unit.id_unit_measurement = json[cls.ATTR_ID_UNIT_MEASUREMENT]
unit.name_singular = json[cls.FLAG_NAME_SINGULAR]
unit.name_plural = json[cls.FLAG_NAME_PLURAL]
unit.symbol = json[cls.FLAG_SYMBOL]
unit.symbol_is_suffix_not_prefix = json[cls.FLAG_SYMBOL_IS_SUFFIX_NOT_PREFIX]
unit.is_base_unit = json[cls.FLAG_IS_BASE_UNIT]
unit.is_unit_of_distance = json[cls.FLAG_IS_UNIT_OF_DISTANCE]
unit.is_unit_of_mass = json[cls.FLAG_IS_UNIT_OF_MASS]
unit.is_unit_of_time = json[cls.FLAG_IS_UNIT_OF_TIME]
unit.is_unit_of_volume = json[cls.FLAG_IS_UNIT_OF_VOLUME]
unit.active = json[cls.FLAG_ACTIVE]
return unit
def __repr__(self):
return f'''
id_unit_of_measurement: {self.id_unit_measurement},
name_singular: {self.name_singular},
name_plural: {self.name_plural},
symbol: {self.symbol},
symbol_is_suffix_not_prefix: {self.symbol_is_suffix_not_prefix},
is_base_unit: {self.is_base_unit},
is_unit_of_distance: {self.is_unit_of_distance},
is_unit_of_mass: {self.is_unit_of_mass},
is_unit_of_time: {self.is_unit_of_time},
is_unit_of_volume: {self.is_unit_of_volume},
active: {self.active}
'''
"""
@dataclass
class Filters_Unit_Measurement():
active_only: bool = False
def to_json(self):
return {
**self.get_shared_json_attributes(self),
'a_get_all_user': self.get_all_user,
'a_get_inactive_user': self.get_inactive_user,
'a_get_first_user_only': self.get_first_user_only,
'a_ids_user': self.ids_user,
'a_ids_user_auth0': self.ids_user_auth0,
}
@staticmethod
def from_form(form):
av.val_instance(form, 'form', 'User_Filters.from_form', Form_Filters_User)
get_inactive = av.input_bool(form.active.data, "active", "User_Filters.from_form")
id_user = form.id_user.data
return User_Filters(
get_all_user = (id_user is None),
get_inactive_user = get_inactive,
get_first_user_only = False,
ids_user = id_user,
ids_user_auth0 = '',
)
@staticmethod
def from_user(user):
av.val_instance(user, 'user', 'User_Filters.from_user', User)
return User_Filters(
get_all_user = (user.id_user is None and user.id_user_auth0 is None),
get_inactive_user = False,
get_first_user_only = False,
ids_user = user.id_user,
ids_user_auth0 = user.id_user_auth0,
)
@staticmethod
def get_default(datastore_store):
user = datastore_store.get_login_user()
return User_Filters(
get_all_user = False,
get_inactive_user = False,
get_first_user_only = False,
ids_user = user.id_user,
ids_user_auth0 = '',
)
"""

View File

@@ -8,6 +8,7 @@ Feature: User Business Object
"""
# internal
from business_objects.base import Base
import lib.argument_validation as av
from forms.forms import Form_Filters_User
from extensions import db
@@ -16,8 +17,10 @@ from dataclasses import dataclass
from typing import ClassVar
class User(db.Model):
class User(db.Model, Base):
KEY_USER: ClassVar[str] = 'authorisedUser' # 'user' already used
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_USER
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'email'
id_user = db.Column(db.Integer, primary_key=True)
id_user_auth0 = db.Column(db.String(255))
@@ -200,8 +203,9 @@ class User_Filters():
ids_access_level: str
ids_product: str
def to_json(self):
def to_json(self):
return {
**self.get_shared_json_attributes(self),
'a_ids_user': self.ids_user,
'a_get_inactive_users': self.get_inactive_users,
'a_ids_permission': self.ids_permission,

Binary file not shown.

View File

@@ -12,6 +12,7 @@ Initializes the Flask application, sets the configuration based on the environme
# IMPORTS
# internal
from datastores.datastore_base import DataStore_Base
from forms.forms import Form_Contact
from models.model_view_admin_home import Model_View_Admin_Home
from models.model_view_contact import Model_View_Contact
@@ -44,7 +45,10 @@ def home():
@routes_core.route(Model_View_Contact.HASH_PAGE_CONTACT, methods=['GET'])
def contact():
try:
user = DataStore_Base.get_user_session()
form = Form_Contact()
form.email.data = user.email
form.name.data = user.firstname + (' ' if user.firstname and user.surname else '') + user.surname
model = Model_View_Contact(form)
html_body = render_template('pages/core/_contact.html', model = model)
except Exception as e:

View File

@@ -64,7 +64,7 @@ def filter_category():
form_filters = Filters_Product_Category.from_json(data)
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'Form invalid.\n{form_filters.errors}'
})
# ToDo: manually validate category, product
@@ -76,7 +76,7 @@ def filter_category():
})
except Exception as e:
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})
@@ -87,7 +87,7 @@ def save_category():
form_filters = Filters_Product_Category.from_json(data[Model_View_Store_Product_Category.FLAG_FORM_FILTERS])
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
})
# filters_form = Filters_Product_Category.from_form(form_filters)
@@ -95,7 +95,7 @@ def save_category():
categories = data[Model_View_Store_Product_Category.FLAG_PRODUCT_CATEGORY]
if len(categories) == 0:
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'No categories.'
})
objsCategory = []
@@ -113,7 +113,7 @@ def save_category():
})
except Exception as e:
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -0,0 +1,159 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Product Permutation Routes
Description:
Initializes the Flask application, sets the configuration based on the environment, and defines two routes (/ and /about) that render templates with the specified titles.
"""
# internal
from business_objects.store.product import Filters_Product, Product_Permutation
from forms.store.product_permutation import Filters_Product_Permutation
from models.model_view_base import Model_View_Base
from models.model_view_store import Model_View_Store
from models.model_view_store_product_permutation import Model_View_Store_Product_Permutation
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app
from extensions import db, oauth
from urllib.parse import quote_plus, urlencode
from authlib.integrations.flask_client import OAuth
from authlib.integrations.base_client import OAuthError
from urllib.parse import quote, urlparse, parse_qs
routes_store_product_permutation = Blueprint('routes_store_product_permutation', __name__)
"""
@routes_store_product_permutation.route('/store/permutations', methods=['GET'])
def permutation():
filters = Filters_Product.get_default()
model = Model_View_Store_Product_Permutation(filters_product=filters)
return render_template('pages/store/_product_permutations.html', model = model)
@routes_store_product_permutation.route('/store/permutation_filter', methods=['POST'])
def permutation_filter():
data = Helper_App.get_request_data(request)
form_filters = None
try:
form_filters = get_Form_Filters_Permutation(data)
if not form_filters.validate_on_submit():
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Form invalid.\n{form_filters.errors}'})
# ToDo: manually validate category, product
filters_form = Filters_Product.from_form(form_filters)
model = Model_View_Store_Product_Permutation(filters_product=filters_form)
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_SUCCESS, 'Success': True, Model_View_Base.KEY_DATA: model.category_list.to_permutation_row_list()})
except Exception as e:
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'})
def get_Form_Filters_Permutation(data_request):
data_form = data_request[Model_View_Store_Product_Permutation.KEY_FORM]
form_filters = Filters_Product_Permutation(**data_form)
form_filters.is_out_of_stock.data = av.input_bool(data_form['is_out_of_stock'], 'is_out_of_stock', 'permutations_post')
return form_filters
@routes_store_product_permutation.route('/store/permutation_save', methods=['POST'])
def permutation_save():
data = Helper_App.get_request_data(request)
form_filters = None
try:
form_filters = get_Form_Filters_Permutation(data)
if not form_filters.validate_on_submit():
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'})
permutations = data[Model_View_Store_Product_Permutation.FLAG_PRODUCT_PERMUTATION]
if len(permutations) == 0:
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'No permutations.'})
objsPermutation = []
for permutation in permutations:
objsPermutation.append(Product_Permutation.from_json(permutation))
# ToDo: manually validate category, product
filters_form = Filters_Product.from_form(form_filters)
model_save = Model_View_Store_Product_Permutation(filters_product=filters_form)
model_save.save_permutations(data.comment, objsPermutation)
model_return = Model_View_Store_Product_Permutation(filters_product=filters_form)
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_SUCCESS, 'Success': True, Model_View_Base.KEY_DATA: model_return.category_list.to_permutation_row_list()})
except Exception as e:
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'})
"""
@routes_store_product_permutation.route(Model_View_Store_Product_Permutation.HASH_PAGE_STORE_PRODUCT_PERMUTATIONS, methods=['GET'])
def permutations():
print('permutations')
try:
form_filters = Filters_Product_Permutation.from_json(request.args)
except Exception as e:
print(f'Error: {e}')
form_filters = Filters_Product_Permutation()
print(f'form_filters={form_filters}')
model = Model_View_Store_Product_Permutation(form_filters)
return render_template('pages/store/_product_permutations.html', model = model)
@routes_store_product_permutation.route(Model_View_Store_Product_Permutation.HASH_GET_STORE_PRODUCT_PERMUTATION, methods=['POST'])
def filter_permutation():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Product_Permutation.from_json(data)
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_FAILURE,
Model_View_Store_Product_Permutation.FLAG_MESSAGE: f'Form invalid.\n{form_filters.errors}'
})
model = Model_View_Store_Product_Permutation(form_filters = form_filters)
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_SUCCESS,
Model_View_Store_Product_Permutation.KEY_DATA: model.category_list.to_json()
})
except Exception as e:
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_FAILURE,
Model_View_Store_Product_Permutation.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})
@routes_store_product_permutation.route(Model_View_Store_Product_Permutation.HASH_SAVE_STORE_PRODUCT_PERMUTATION, methods=['POST'])
def save_permutation():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Product_Permutation.from_json(data[Model_View_Store_Product_Permutation.FLAG_FORM_FILTERS])
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_FAILURE,
Model_View_Store_Product_Permutation.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
})
# filters_form = Filters_Product_Permutation.from_form(form_filters)
print(f'form_filters: {form_filters}')
permutations = data[Model_View_Store_Product_Permutation.FLAG_PRODUCT_PERMUTATION]
if len(permutations) == 0:
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_FAILURE,
Model_View_Store_Product_Permutation.FLAG_MESSAGE: f'No permutations.'
})
objsPermutation = []
for permutation in permutations:
objsPermutation.append(Product_Permutation.from_json(permutation))
# model_save = Model_View_Store_Product_Permutation() # filters_product=filters_form)
print(f'objsPermutation={objsPermutation}')
Model_View_Store_Product_Permutation.save_permutations(data.get('comment', 'No comment'), objsPermutation)
model_return = Model_View_Store_Product_Permutation(form_filters=form_filters)
print('nips')
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_SUCCESS,
Model_View_Store_Product_Permutation.KEY_DATA: model_return.category_list.to_json()
})
except Exception as e:
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_FAILURE,
Model_View_Store_Product_Permutation.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -13,7 +13,7 @@ Initializes the Flask application, sets the configuration based on the environme
# internal
from business_objects.store.product import Product, Filters_Product, Product_Permutation
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from forms.forms import Form_Supplier, Form_Filters_Permutation, Form_Filters_Stock_Item
from forms.forms import Form_Filters_Stock_Item
from models.model_view_base import Model_View_Base
from models.model_view_store import Model_View_Store
from models.model_view_store_supplier import Model_View_Store_Supplier

View File

@@ -14,7 +14,7 @@ Initializes the Flask application, sets the configuration based on the environme
# internal
from business_objects.store.product import Product, Filters_Product, Product_Permutation
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from forms.forms import Form_Supplier, Form_Filters_Permutation, Form_Filters_Stock_Item
from forms.forms import Form_Supplier, Form_Filters_Stock_Item
from models.model_view_base import Model_View_Base
from models.model_view_store import Model_View_Store
from models.model_view_store_supplier import Model_View_Store_Supplier

View File

@@ -13,7 +13,8 @@ Datastore for Store
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.access_level import Access_Level, Filters_Access_Level
from business_objects.store.access_level import Access_Level
"""
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
@@ -23,13 +24,17 @@ from business_objects.store.delivery_region import Delivery_Region
from business_objects.store.discount import Discount
from business_objects.store.order import Order
from business_objects.store.product import Product, Product_Permutation, Product_Price, Filters_Product # Permutation_Variation_Link
"""
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.unit_measurement import Unit_Measurement
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
# from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
from forms.access_level import Filters_Access_Level
from forms.unit_measurement import Filters_Unit_Measurement
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
@@ -105,8 +110,8 @@ class DataStore_Base(BaseModel):
while cursor.nextset():
print(f'new result set: {cursor.fetchall()}')
@classmethod
def get_regions_and_currencies(cls):
_m = 'DataStore_Base.get_regions_and_currencies'
def get_many_region_and_currency(cls):
_m = 'DataStore_Base.get_many_region_and_currency'
_m_db_currency = 'p_shop_get_many_currency'
_m_db_region = 'p_shop_get_many_region'
@@ -168,20 +173,45 @@ class DataStore_Base(BaseModel):
def upload_bulk(permanent_table_name, records, batch_size):
_m = 'DataStore_Base.upload_bulk'
print(f'{_m}\nstarting...')
print(f'permanent_table_name: {permanent_table_name}')
if db.session.dirty or db.session.new or db.session.deleted:
print("Session is not clean")
return
# Assuming `permanent_table_name` is a string representing the table name
table_object = db.metadata.tables.get(permanent_table_name)
if table_object is None:
print(f"Table {permanent_table_name} not found in metadata.")
return
else:
expected_columns = set(column.name for column in db.inspect(table_object).columns)
print(f'expected_columns: {expected_columns}')
try:
for i in range(0, len(records), batch_size):
batch = records[i:i+batch_size]
print(f'batch: {batch}')
db.session.bulk_save_objects(batch)
"""
data = [object.to_json() for object in batch]
print(f'batch: {batch}\ndata: {data}')
db.session.bulk_insert_mappings(permanent_table_name, data)
print(f'data: {data}')
for row in data:
row_keys = set(row.keys())
if row_keys != expected_columns:
print(f"Column mismatch in row: {row}")
print(f'missing columns: {expected_columns - row_keys}')
print(f'extra columns: {row_keys - expected_columns}')
# db.session.bulk_insert_mappings(permanent_table_name, data)
"""
db.session.commit()
except Exception as e:
print(f'{_m}\n{e}')
db.session.rollback()
raise e
@classmethod
def get_many_access_level(cls, filters):
def get_many_access_level(cls, filters=None):
_m = 'DataStore_Store_Base.get_many_access_level'
if filters is None:
filters = Filters_Access_Level()
av.val_instance(filters, 'filters', _m, Filters_Access_Level)
argument_dict = filters.to_json()
# user = cls.get_user_session()
@@ -213,4 +243,41 @@ class DataStore_Base(BaseModel):
DataStore_Base.db_cursor_clear(cursor)
cursor.close()
return access_levels, errors
return access_levels, errors
@classmethod
def get_many_unit_measurement(cls, filters=None):
_m = 'DataStore_Store_Base.get_many_unit_measurement'
if filters is None:
filters = Filters_Unit_Measurement()
av.val_instance(filters, 'filters', _m, Filters_Unit_Measurement)
argument_dict = filters.to_json()
# user = cls.get_user_session()
# argument_dict['a_id_user'] = 1 # 'auth0|6582b95c895d09a70ba10fef' # id_user
print(f'argument_dict: {argument_dict}')
print('executing p_shop_get_many_unit_measurement')
result = cls.db_procedure_execute('p_shop_get_many_unit_measurement', argument_dict)
cursor = result.cursor
print('data received')
# units of measurement
result_set_1 = cursor.fetchall()
print(f'raw units of measurement: {result_set_1}')
units = []
for row in result_set_1:
new_unit = Unit_Measurement.from_DB_unit_measurement(row)
units.append(new_unit)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
print(f'raw errors: {result_set_e}')
errors = []
if len(result_set_e) > 0:
errors = [SQL_Error.from_DB_record(row) for row in result_set_e] # (row[0], row[1])
for error in errors:
print(f"Error [{error.code}]: {error.msg}")
DataStore_Base.db_cursor_clear(cursor)
cursor.close()
return units, errors

View File

@@ -12,8 +12,6 @@ Datastore for Store
# internal
# from routes import bp_home
import lib.argument_validation as av
# from business_objects.store.access_level import Access_Level, Filters_Access_Level
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
@@ -26,11 +24,12 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation_Type, Product_Variation, Product_Variation_Filters, Product_Variation_Container
from datastores.datastore_base import DataStore_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
from helpers.helper_db_mysql import Helper_DB_MySQL
import lib.argument_validation as av
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
@@ -118,12 +117,8 @@ class DataStore_Store_Base(DataStore_Base):
# variations = [Product_Variation(**row) for row in result_set_4]
variations = []
for row in result_set_4:
new_variation = Product_Variation.from_DB_get_many_product_catalogue(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
new_variation = Product_Variation.from_DB_get_many_product_catalogue(row)
variations.append(new_variation)
# products[product_index[new_variation.id_product]].variations.append(new_variation)
# index_category = category_index[new_variation.id_category]
# index_product = categories[index_category].index_product_from_ids_product_permutation(new_variation.id_product, new_variation.id_permutation)
# categories[index_category].products[index_product].variations.append(new_variation)
category_list.add_product_variation(new_variation)
# print(f'variations: {variations}')
# print(f'products: {[p.id_product for p in products]}')
@@ -209,17 +204,13 @@ class DataStore_Store_Base(DataStore_Base):
ids_permutation.append(msg_error_availability[:index_comma])
return ids_permutation
@classmethod
def get_regions_and_currencies(cls):
_m = 'DataStore_Store_Base.get_regions_and_currencies'
def get_many_currency(cls):
_m = 'DataStore_Store_Base.get_many_currency'
_m_db_currency = 'p_shop_get_many_currency'
_m_db_region = 'p_shop_get_many_region'
argument_dict_list_currency = {
'a_get_inactive_currency': 0
}
argument_dict_list_region = {
'a_get_inactive_currency': 0
}
print(f'executing {_m_db_currency}')
result = cls.db_procedure_execute(_m_db_currency, argument_dict_list_currency)
@@ -235,6 +226,17 @@ class DataStore_Store_Base(DataStore_Base):
print(f'currencies: {currencies}')
DataStore_Store_Base.db_cursor_clear(cursor)
return currencies
@classmethod
def get_many_region(cls):
_m = 'DataStore_Store_Base.get_many_region'
_m_db_region = 'p_shop_get_many_region'
argument_dict_list_region = {
'a_get_inactive_currency': 0
}
print(f'executing {_m_db_region}')
result = cls.db_procedure_execute(_m_db_region, argument_dict_list_region)
cursor = result.cursor
@@ -250,7 +252,15 @@ class DataStore_Store_Base(DataStore_Base):
DataStore_Store_Base.db_cursor_clear(cursor)
cursor.close()
return regions
@classmethod
def get_many_region_and_currency(cls):
_m = 'DataStore_Store_Base.get_many_region_and_currency'
currencies = cls.get_many_currency()
regions = cls.get_many_region()
return regions, currencies
@classmethod
def get_many_product_variation(cls, variation_filters):
_m = 'DataStore_Store_Base.get_many_product_variation'
@@ -278,13 +288,30 @@ class DataStore_Store_Base(DataStore_Base):
result = cls.db_procedure_execute('p_shop_get_many_product_variation', argument_dict_list)
cursor = result.cursor
result_set = cursor.fetchall()
result_set_vt = cursor.fetchall()
# Product_Variation Types
# variation_container = Product_Variation_Container()
variation_types = []
variation_types_dict = {}
for row in result_set_vt:
new_variation_type = Product_Variation_Type.from_DB_get_many_product_variation(row)
# variation_container.add_product_variation_type(new_variation_type)
variation_types.append(new_variation_type)
variation_types_dict[new_variation_type.id_type] = new_variation_type
print(f'variation_types_dict: {variation_types_dict}')
# Product_Variations
variations = Product_Variation_List()
for row in result_set:
new_variation = Product_Variation.from_DB_variation(row)
variations.add_product_variation(new_variation)
cursor.nextset()
result_set_v = cursor.fetchall()
# variations = Product_Variation_Container()
variations = []
for row in result_set_v:
new_variation = Product_Variation.from_DB_get_many_product_variation(row)
new_variation.variation_type = variation_types_dict[new_variation.id_type]
# variation_container.add_product_variation(new_variation)
variations.append(new_variation)
errors = []
cursor.nextset()
@@ -299,5 +326,5 @@ class DataStore_Store_Base(DataStore_Base):
cursor.close()
return variations, errors
return variation_types, variations, errors

View File

@@ -25,7 +25,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!

View File

@@ -24,7 +24,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
# from datastores.datastore_base import Table_Shop_Product_Category, Table_Shop_Product_Category_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
@@ -83,7 +83,7 @@ class Row_Shop_Product_Temp(db.Model):
'id_category': self.id_category,
'name': self.name,
'id_access_level_required': self.id_access_level_required,
'active': self.active,
'active': av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
'display_order': self.display_order,
'guid': self.guid,
}

View File

@@ -13,7 +13,7 @@ Datastore for Store Product Categories
# internal
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category, Product_Category_Temp
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -24,7 +24,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
# from datastores.datastore_base import Table_Shop_Product_Category, Table_Shop_Product_Category_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
@@ -41,67 +41,6 @@ from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
# db = SQLAlchemy()
"""
class Table_Shop_Product_Category(db.Model):
__tablename__ = 'Shop_Product_Category'
id_category: int = db.Column(db.Integer, primary_key=True)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
created_on: datetime = db.Column(db.DateTime)
created_by: int = db.Column(db.Integer)
id_change_set: int = db.Column(db.Integer)
"""
class Row_Shop_Product_Category_Temp(db.Model):
__tablename__ = 'Shop_Product_Category_Temp'
__table_args__ = { 'extend_existing': True }
id_category: int = db.Column(db.Integer, primary_key=True)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
id_access_level_required: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.BINARY(36))
# created_on: datetime = db.Column(db.DateTime)
# created_by: int = db.Column(db.Integer)
@classmethod
def from_product_category(cls, product_category):
row = cls()
row.id_category = product_category.id_category[0] if isinstance(product_category.id_category, tuple) else product_category.id_category
row.code = product_category.code[0] if isinstance(product_category.code, tuple) else product_category.code
row.name = product_category.name[0] if isinstance(product_category.name, tuple) else product_category.name
row.description = product_category.description[0] if isinstance(product_category.description, tuple) else product_category.description
row.id_access_level_required = product_category.id_access_level_required[0] if isinstance(product_category.id_access_level_required, tuple) else product_category.id_access_level_required
row.active = product_category.active
row.display_order = product_category.display_order
"""
row.guid = product_category.guid
row.created_on = product_category.created_on
row.created_by = product_category.created_by
"""
return row
def to_json(self):
return {
'id_category': self.id_category,
'code': self.code,
'name': self.name,
'description': self.description,
'id_access_level_required': self.id_access_level_required,
'active': self.active,
'display_order': self.display_order,
'guid': self.guid,
}
"""
'created_on': self.created_on,
'created_by': self.created_by
"""
class DataStore_Store_Product_Category(DataStore_Store_Base):
def __init__(self):
@@ -120,8 +59,8 @@ class DataStore_Store_Product_Category(DataStore_Store_Base):
rows = []
id_category_new = 0
for category in categories:
# row = Row_Shop_Product_Category_Temp.from_product_category(category)
row = category.to_temporary_record()
row = Product_Category_Temp.from_product_category(category)
# row = category.to_temporary_record()
# id_tmp =
if row.id_category == '':
id_category_new -= 1
@@ -147,7 +86,7 @@ class DataStore_Store_Product_Category(DataStore_Store_Base):
cursor.close()
print('cursor closed')
"""
DataStore_Store_Base.upload_bulk(rows, Product_Category.__tablename__, 1000)
DataStore_Store_Base.upload_bulk(Product_Category_Temp.__tablename__, rows, 1000)
argument_dict_list = {
'a_id_user': user.id_user,

View File

@@ -12,21 +12,10 @@ Datastore for Store Product Permutations
# internal
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.delivery_region import Delivery_Region
from business_objects.store.discount import Discount
from business_objects.store.order import Order
from business_objects.store.product import Product, Product_Permutation, Product_Price, Filters_Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.store_base import Store_Base
from business_objects.store.product_permutation import Product_Permutation, Product_Permutation_Temp
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
@@ -43,35 +32,72 @@ from datetime import datetime
# db = SQLAlchemy()
class DataStore_Store_Product_Permutation(DataStore_Store_Base):
def __init__(self):
super().__init__()
def save_permutations(self, comment, permutations):
_m = 'DataStore_Store_Base.save_permutations'
@classmethod
def save_permutations(cls, comment, permutations):
_m = 'DataStore_Store_Product_Permutation.save_permutations'
av.val_str(comment, 'comment', _m)
av.val_list(permutations, 'list_permutations', _m, Product_Permutation, 1)
# av.val_list(permutations, 'list_permutations', _m, Product_Permutation, 1)
guid = Helper_DB_MySQL.create_guid()
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = self.get_user_session()
user = cls.get_user_session()
rows = []
for permutation in permutations:
setattr(permutation, 'guid', guid)
setattr(permutation, 'created_on', now)
setattr(permutation, 'created_by', user.id_user)
# row = permutation.to_temporary_record()
row = Product_Permutation_Temp.from_product_permutation(permutation)
row.guid = guid
rows.append(row)
cursor = self.db.cursor()
print(f'rows: {rows}')
"""
cursor = db.cursor()
print('cursor created')
cursor.executemany(
'INSERT INTO Shop_Product_Permutation_Temp (id_permutation, id_product, description, cost_local, id_currency_cost, profit_local_min, latency_manufacture, quantity_min, quantity_max, quantity_step, quantity_stock, is_subscription, id_recurrence_interval, count_recurrence_interval, id_stripe_product, active, display_order, guid) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
permutations
'''INSERT INTO Shop_Product_Permutation_Temp (
id_permutation,
id_product,
description,
cost_local,
id_currency_cost,
profit_local_min,
latency_manufacture_days,
id_unit_measurement_quantity,
count_unit_measurement_quantity,
quantity_min,
quantity_max,
quantity_stock,
is_subscription,
id_unit_measurement_interval_recurrence,
count_interval_recurrence,
id_stripe_product,
does_expire_faster_once_unsealed,
id_unit_measurement_interval_expiration_unsealed,
count_interval_expiration_unsealed,
active,
guid
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)''',
rows
)
self.db.commit()
print('cursor executed')
db.commit()
print('cursor committed')
cursor.close()
print('cursor closed')
"""
DataStore_Store_Base.upload_bulk(Product_Permutation_Temp.__tablename__, rows, 1000)
print('bulk uploaded')
argument_dict_list = {
'a_id_user': user.id_user,
'a_comment': comment,
'a_guid': guid
}
self.db_procedure_execute('p_shop_save_permutation', argument_dict_list)
cursor.close()
cls.db_procedure_execute('p_shop_save_product_permutation', argument_dict_list)
print('saved product permutations')

View File

@@ -25,7 +25,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
@@ -80,7 +80,7 @@ class DataStore_Store_Product_Variation(DataStore_Store_Base):
result_set = cursor.fetchall()
# Product_Variations
variations = Product_Variation_List()
variations = Product_Variation_Container()
for row in result_set:
new_variation = Product_Variation.from_DB_variation(row)
variations.add_product_variation(new_variation)

View File

@@ -25,7 +25,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!

View File

@@ -25,7 +25,7 @@ from business_objects.store.product import Product, Product_Permutation, Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_Container
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!

View File

@@ -13,19 +13,9 @@ Datastore for Users
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.delivery_region import Delivery_Region
from business_objects.store.discount import Discount
from business_objects.store.order import Order
from business_objects.store.product import Product, Product_Permutation, Product_Price, Filters_Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_base import DataStore_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!

Binary file not shown.

Binary file not shown.

37
forms/access_level.py Normal file
View File

@@ -0,0 +1,37 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Access Level Filters data input
Description:
Defines Flask-WTF forms for handling access level filter input.
"""
# internal
from business_objects.base import Base
from forms.base import Form_Base
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Access_Level(Form_Base):
active = BooleanField("Active only?")
def __repr__(self):
return f'{self.__class__.__name__}(active={self.active.data})'
@classmethod
def from_json(cls, json):
form = Filters_Access_Level()
form.active.data = av.input_bool(json[Base.FLAG_ACTIVE], Base.FLAG_ACTIVE, f'{cls.__name__}.from_json')
return form
def to_json(self):
return {
Base.FLAG_ACTIVE: 1 if av.input_bool(self.active.data, Base.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0,
}

View File

@@ -21,10 +21,12 @@ class Form_Base_Meta(type(FlaskForm), ABCMeta):
class Form_Base(FlaskForm, metaclass=Form_Base_Meta):
"""
@classmethod
@abstractmethod
def from_filters(cls, filters):
pass
"""
@abstractmethod
def __repr__(self):
pass
@@ -43,4 +45,20 @@ class Form_Base(FlaskForm, metaclass=Form_Base_Meta):
form_filters.is_not_empty.data = av.input_bool(data_form['is_not_empty'], 'is_not_empty', 'filter_category')
form_filters.active.data = av.input_bool(data_form['active'], 'active', 'filter_category')
return form_filters
"""
"""
'''
class Filters_Stored_Procedure_Base(Form_Base):
"""
@abstractmethod
def __repr__(self):
pass
@classmethod
@abstractmethod
def from_json(cls, json):
pass
"""
@abstractmethod
def to_json(self):
pass
'''

View File

@@ -126,13 +126,6 @@ class Form_Supplier(FlaskForm):
# class Form_Supplier_Purchase_Order(FlaskForm):
class Form_Filters_Permutation(FlaskForm):
id_category = SelectField('Category', validators=[Optional()], choices=[])
id_product = SelectField('Product', validators=[Optional()], choices=[])
is_out_of_stock = BooleanField('Out of stock only?')
quantity_min = FloatField('Min stock')
quantity_max = FloatField('Max stock')
# submit = SubmitField('Submit')
class Form_Filters_Stock_Item(FlaskForm):
id_category = SelectField('Category', validators=[Optional()], choices=[])

View File

@@ -28,12 +28,14 @@ from abc import ABCMeta, abstractmethod
class Filters_Product_Category(Form_Base):
is_not_empty = BooleanField('Not empty only?')
active = BooleanField("Active only?")
"""
@classmethod
def from_filters(cls, filters):
form = Filters_Product_Category()
form.is_not_empty.data = filters.is_not_empty
form.active.data = filters.active
return form
"""
def __repr__(self):
return f'Filters_Product_Category(is_not_empty={self.is_not_empty.data}, active={self.active.data})'
@classmethod

View File

@@ -0,0 +1,65 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Product Category Filters data input
Description:
Defines Flask-WTF forms for handling product category filter input.
"""
# internal
from business_objects.store.store_base import Store_Base
# from business_objects.store.product_category import Filters_Product_Category
# from models.model_view_store import Model_View_Store # circular
# from helpers.DEPRECATED.helper_abc import Interface_ABC
from forms.base import Form_Base
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Product_Permutation(Form_Base):
id_category = SelectField('Category', validators=[Optional()], choices=[])
id_product = SelectField('Product', validators=[Optional()], choices=[])
is_out_of_stock = BooleanField('Out of stock only?')
quantity_min = FloatField('Min stock')
quantity_max = FloatField('Max stock')
# submit = SubmitField('Submit')
"""
@classmethod
def from_filters(cls, filters):
form = Filters_Product_Permutation()
form.id_category.choices = Store_Base.convert_list_objects_to_list_options(filters.categories)
form.id_product.choices = Store_Base.convert_list_objects_to_list_options(filters.products)
form.is_out_of_stock.data = filters.is_out_of_stock
form.quantity_min.data = filters.quantity_min
form.quantity_max.data = filters.quantity_max
return form
"""
def __repr__(self):
return f'''
Filters_Product_Permutation(
id_category={self.id_category.data},
id_product={self.id_product.data},
is_out_of_stock={self.is_out_of_stock.data},
quantity_min={self.quantity_min.data},
quantity_max={self.quantity_max.data})
'''
@classmethod
def from_json(cls, json):
form = Filters_Product_Permutation()
form.id_category.choices = [(json[Store_Base.ATTR_ID_PRODUCT_CATEGORY], json[Store_Base.ATTR_ID_PRODUCT_CATEGORY])]
form.id_category.data = json[Store_Base.ATTR_ID_PRODUCT_CATEGORY]
form.id_product.choices = [(json[Store_Base.ATTR_ID_PRODUCT], json[Store_Base.ATTR_ID_PRODUCT])]
form.id_product.data = json[Store_Base.ATTR_ID_PRODUCT]
form.is_out_of_stock.data = av.input_bool(json[Store_Base.FLAG_IS_OUT_OF_STOCK], 'is_out_of_stock', 'Filters_Product_Permutation')
form.quantity_min.data = json[Store_Base.FLAG_QUANTITY_MIN]
form.quantity_max.data = json[Store_Base.FLAG_QUANTITY_MAX]
return form

42
forms/unit_measurement.py Normal file
View File

@@ -0,0 +1,42 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Unit of Measurement Filters data input
Description:
Defines Flask-WTF forms for handling unit of measurement filter input.
"""
# internal
from business_objects.store.store_base import Store_Base
from forms.base import Form_Base
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Unit_Measurement(Form_Base):
active = BooleanField("Active only?")
@classmethod
def from_filters(cls, filters):
form = Filters_Unit_Measurement()
form.active.data = filters.active
return form
def __repr__(self):
return f'Filters_Unit_Measurement(active={self.active.data})'
@classmethod
def from_json(cls, json):
form = Filters_Unit_Measurement()
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], 'active', 'Filters_Unit_Measurement')
return form
def to_json(self):
return {
Store_Base.FLAG_ACTIVE: av.input_bool(self.active.data, Store_Base.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
}

Some files were not shown because too many files have changed in this diff Show More