Initial commit

This commit is contained in:
2024-04-17 15:07:51 +01:00
commit f1b095ba83
280 changed files with 30850 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: Business Objects
Description:
Initialises business objects module.
"""

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

164
business_objects/basket.py Normal file
View File

@@ -0,0 +1,164 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Basket Business Object
Description:
Business object for basket
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
# from lib import data_types
from business_objects.product import Product, Product_Filters
from business_objects.discount import Discount
from business_objects.delivery_option import Delivery_Option
# from forms import Form_Product
# from models.model_view_store import Model_View_Store # circular
# from datastores.datastore_store import DataStore_Store # circular
# from forms import Form_Basket_Add, Form_Basket_Edit # possibly circular
# external
# from enum import Enum
from flask import jsonify
import locale
# VARIABLE INSTANTIATION
# CLASSES
class Basket_Item():
product: Product
quantity: int
delivery_option: Delivery_Option
discounts: list
# form: Form_Product
is_included_VAT: bool
"""
def __init__(self):
self.is_unavailable_in_currency_or_region = False
self.is_available = True
"""
def make_from_product_and_quantity_and_VAT_included(product, quantity, is_included_VAT):
# Initialiser - validation
_m = 'Basket_Item.make_from_product_and_quantity'
v_arg_type = 'class attribute'
av.val_instance(product, 'product', _m, Product, v_arg_type=v_arg_type)
av.full_val_float(quantity, 'quantity', _m, product.get_quantity_min(), v_arg_type=v_arg_type)
basket_item = Basket_Item()
basket_item.product = product
basket_item.quantity = quantity
basket_item.is_included_VAT = is_included_VAT
return basket_item
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Basket_Item.add_discount', Discount, v_arg_type='class attribute')
self.discounts.append(discount)
def set_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Basket_Item.set_delivery_option', Delivery_Option, v_arg_type='class attribute')
self.delivery_option = delivery_option
def update_quantity(self, quantity):
_m = 'Basket_Item.update_quantity'
v_arg_type = 'class attribute'
av.full_val_float(quantity, 'quantity', _m, self.product.get_quantity_min(), v_arg_type=v_arg_type)
self.quantity = quantity
def jsonify(self):
return jsonify(self)
def to_json(self):
permutation = self.product.get_permutation_selected()
return {
'product_id': self.product.id_product,
'permutation_id': permutation.id_permutation,
'price': permutation.output_price(self.is_included_VAT),
'quantity': self.quantity
}
def get_subtotal(self):
permutation = self.product.get_permutation_selected()
return round(self.product.get_price_local(self.is_included_VAT) * self.quantity, 2) if permutation.is_available else 0
def output_currency(self):
return self.product.output_currency()
def output_subtotal(self):
locale.setlocale(locale.LC_ALL, '')
subtotal = self.get_subtotal()
permutation = self.product.get_permutation_selected()
return 'Not available in this currency or region' if permutation.is_unavailable_in_currency_or_region else 'Not available' if not permutation.is_available else f'{self.product.output_currency()} {locale.format_string("%d", subtotal, grouping=True)}'
def __repr__(self):
return f'''
product: {self.product}
quantity: {self.quantity}
subtotal: {self.get_subtotal()}
'''
class Basket():
items: list
def __init__(self):
self.items = []
def add_item(self, item):
av.val_instance(item, 'item', 'Basket.add_item', Basket_Item)
self.items.append(item)
def to_csv(self):
ids_permutation = ''
quantities_permutation = ''
for b_i in range(len(self.items)):
basket_item = self.items[b_i]
product = basket_item.product
if b_i > 0:
ids_permutation += ','
quantities_permutation += ','
ids_permutation += str(product.get_id_permutation())
quantities_permutation += str(basket_item.quantity)
print(f'ids_permutation_basket = {ids_permutation}')
print(f'quantities_permutation_basket = {quantities_permutation}')
return ids_permutation, quantities_permutation
def to_json_list(self):
json_list = []
for item in self.items:
json_list.append(item.to_json())
return json_list
def to_json(self):
return {'items': self.to_json_list()}
def output_total(self):
sum = 0
for b_i in self.items:
sum += b_i.get_subtotal()
symbol = self.items[0].output_currency() if len(self.items) > 0 else ''
return f'{symbol} {locale.format_string("%d", sum, grouping=True)}'
def len(self):
return len(self.items)
"""
def get_key_product_index_from_ids_product_permutation(id_product, id_permutation):
return f'{id_product},{"" if id_permutation is None else id_permutation}'
"""
def __repr__(self):
repr = f'Basket:'
for basket_item in self.items:
print(f'{basket_item}')
repr = f'{repr}\n{basket_item}'
return repr
def get_ids_permutation_unavailable(self):
ids_permutation = []
for item in self.items:
permutation = item.product.get_permutation_selected()
if not permutation.is_available:
ids_permutation.append(permutation.id_permutation)
return ids_permutation

View File

@@ -0,0 +1,273 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Category Business Object
Description:
Business object for product
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
from business_objects.product import Product, Product_Permutation, Price
from business_objects.variation import Variation
from business_objects.image import Image
from business_objects.delivery_option import Delivery_Option
from business_objects.discount import Discount
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Enum_Product_Category(Enum):
ASSISTIVE_DEVICES = 0
HOME_DECOR = 1
MISCELLANEOUS = 99
def text(self):
return Enum_Product_Category.Enum_Product_Category_Text(self)
def Enum_Product_Category_Text(category):
av.val_instance(category, 'category', 'Product_Category_Enum_Text', Enum_Product_Category)
if category == Enum_Product_Category.ASSISTIVE_DEVICES:
return 'Assistive devices'
elif category == Enum_Product_Category.HOME_DECOR:
return 'Home decor'
else:
return 'Other'
def get_member_by_text(text):
av.val_str(text, 'text', 'Enum_Product_Category.get_member_by_text')
return data_types.get_enum_member_by_text(Enum_Product_Category, text.upper())
class Category(db.Model):
id_category = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
description = db.Column(db.String(4000))
display_order = db.Column(db.Integer)
"""
def __new__(cls, id, name, description, display_order):
_m = 'Category.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type=v_arg_type)
av.val_str(name, 'name', _m, max_len=256, v_arg_type=v_arg_type)
av.val_str(description, 'description', _m, max_len=4001, v_arg_type=v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type=v_arg_type)
return super(Category, cls).__new__(cls)
"""
def __init__(self): # , id, name, description, display_order):
"""
self.id_category = id
self.name = name
self.description = description
self.display_order = display_order
"""
self.products = []
self.product_index = {}
super().__init__()
def make_from_DB_product(query_row):
category = Category()
category.id_category = query_row[0]
category.name = query_row[1]
category.description = query_row[2]
category.display_order = query_row[3]
return category
"""
def key_product_index_from_ids_product_permutation(id_product, id_permutation):
return f'{id_product},{"" if id_permutation is None else id_permutation}'
def key_product_index_from_product(product):
av.val_instance(product, 'product', 'Category.key_product_index_from_product', Product)
return f'{product.id_product},{"" if product.id_permutation is None else product.id_permutation}'
"""
def get_index_product(self, product):
return self.get_index_product_from_id(product.id_product)
def get_index_product_from_id(self, id_product):
try:
return self.product_index[id_product]
except:
raise KeyError(f'product id: {id_product} not in product index keys: {self.product_index.keys()} with category id: {self.id_category}')
def get_index_product_from_id_permutation(self, id_permutation):
for product in self.products:
try:
index_permutation = product.get_index_permutation_from_id(id_permutation)
return self.get_index_product(product)
except:
pass
raise KeyError(f'permutation id: {id_permutation} not in category id: {self.id_category}')
def add_product(self, product):
_m = 'Category.add_product'
av.val_instance(product, 'product', _m, Product)
# self.product_index.append(len(self.products))
# self.product_index[Category.key_product_index_from_ids_product_permutation(product.id_product, product.id_permutation)] = len(self.products)
try:
self.get_index_product(product)
raise ValueError(f"{av.error_msg_str(product, 'product', _m, Product)}\nProduct already in category.")
except KeyError:
self.product_index[product.id_product] = len(self.products)
self.products.append(product)
def add_permutation(self, permutation):
_m = 'Category.add_permutation'
av.val_instance(permutation, 'permutation', _m, Product_Permutation)
# self.product_index.append(len(self.products))
# self.product_index[Category.key_product_index_from_ids_product_permutation(product.id_product, product.id_permutation)] = len(self.products)
index_product = self.get_index_product_from_id(permutation.id_product)
# index_product = self.product_index[permutation.id_product]
self.products[index_product].add_permutation(permutation)
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Category.add_variation', Variation)
index_product = self.get_index_product_from_id(variation.id_product)
self.products[index_product].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Category.add_price', Price)
index_product = self.get_index_product_from_id(price.id_product)
self.products[index_product].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Category.add_image', Image)
index_product = self.get_index_product_from_id(image.id_product)
self.products[index_product].add_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Category.add_delivery_option', Delivery_Option)
index_product = self.get_index_product_from_id(delivery_option.id_product)
self.products[index_product].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Category.add_discount', Discount)
index_product = self.get_index_product_from_id(discount.id_product)
self.products[index_product].add_discount(discount)
def get_all_variation_trees(self):
for product in self.products:
if product.has_variations:
print(f'product with id:{product.id_product} has variations')
product.get_variation_trees()
"""
def index_product_from_ids_product_permutation(self, id_product, id_permutation):
key = Category.key_product_index_from_ids_product_permutation(id_product, id_permutation)
print(f'product_index: {self.product_index}')
print(f'Key Error: {key}')
try:
return self.product_index[key]
except KeyError:
pass
"""
def __repr__(self):
return f'''
id: {self.id_category}
name: {self.name}
description: {self.description}
display_order: {self.display_order}
products: {self.products}
'''
def get_permutation_first(self):
if not (len(self.products) == 0):
print(f'getting first permutation from product')
return None if len(self.products) == 0 else self.products[0].get_permutation_selected()
class Product_Category_Filters():
category_ids: str # csv
product_ids: str # csv
def __new__(cls, product_ids, product_categories):
# Initialiser - validation
_m = 'Product_Filters.__new__'
v_arg_type = 'class attribute'
# av.val_list_instances(product_ids, 'product_ids', _m, str, v_arg_type=v_arg_type)
# av.val_list_instances(product_categories, 'product_categories', _m, Product_Category_Enum, v_arg_type=v_arg_type)
av.val_str(product_ids, 'product_ids', _m, v_arg_type=v_arg_type)
av.val_str(product_categories, 'product_categories', _m, v_arg_type=v_arg_type)
return super(Product_Category_Filters, cls).__new__(cls)
def __init__(self, product_ids, product_categories):
# Constructor
self.ids = product_ids
self.categories = product_categories
class Category_List():
categories: list
def __init__(self):
self.categories = []
def add_category(self, category):
av.val_instance(category, 'category', 'Category_List.add_category', Category)
self.categories.append(category)
def get_index_category_from_id(self, id_category):
for index_category in range(len(self.categories)):
category = self.categories[index_category]
if category.id_category == id_category:
return index_category
raise ValueError(f"{av.error_msg_str(id_category, 'id_category', 'Category_List.get_index_category_from_id', int)}\nID not in list")
def get_index_category_from_id_permutation(self, id_permutation):
for index_category in range(len(self.categories)):
category = self.categories[index_category]
try:
index_product = category.get_index_product_from_id_permutation(id_permutation)
return index_category
except:
pass
raise ValueError(f"{av.error_msg_str(id_permutation, 'id_permutation', 'Category_List.get_index_category_from_id_permutation', int)}. Permutation ID not in list")
def add_product(self, product):
av.val_instance(product, 'product', 'Category_List.add_product', Product)
index_category = self.get_index_category_from_id(product.id_category)
self.categories[index_category].add_product(product)
def add_permutation(self, permutation):
av.val_instance(permutation, 'permutation', 'Category_List.add_permutation', Product_Permutation)
index_category = self.get_index_category_from_id(permutation.id_category)
self.categories[index_category].add_permutation(permutation)
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Category.add_variation', Variation)
index_category = self.get_index_category_from_id(variation.id_category)
self.categories[index_category].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Category.add_price', Price)
index_category = self.get_index_category_from_id(price.id_category)
self.categories[index_category].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Category.add_image', Image)
index_category = self.get_index_category_from_id(image.id_category)
self.categories[index_category].add_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Category.add_delivery_option', Delivery_Option)
index_category = self.get_index_category_from_id(delivery_option.id_category)
self.categories[index_category].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Category.add_discount', Discount)
index_category = self.get_index_category_from_id(discount.id_category)
self.categories[index_category].add_discount(discount)
def get_all_variation_trees(self):
for category in self.categories:
category.get_all_variation_trees()
def __repr__(self):
return f'categories: {self.categories}'
def get_permutation_first(self):
print(f'getting first permutation from category list')
if not (len(self.categories) == 0):
print(f'getting first permutation from category')
return None if len(self.categories) == 0 else self.categories[0].get_permutation_first()
def get_count_categories(self):
return len(self.categories)

View File

@@ -0,0 +1,96 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Business Object
Description:
Business object for product
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
"""
class Currency_Enum(Enum):
GBP = 1
def text(self):
return Currency_Enum.Currency_Enum_Text(self)
def Currency_Enum_Text(currency):
av.val_instance(currency, 'currency', 'Currency_Enum_Text', Currency_Enum)
if currency == Currency_Enum.GBP:
return 'GBP'
else:
# return 'Unknown'
raise ValueError("Unknown Currency Enum.")
def get_member_by_text(text):
for member in Resolution_Level_Enum.__members__.values():
if member.name == text:
return member
raise ValueError("Unknown Currency Enum.")
# return Resolution_Level_Enum.HIGH
"""
class Currency(db.Model):
id_currency = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String)
name = db.Column(db.String)
symbol = db.Column(db.String)
factor_from_GBP = db.Column(db.Float)
display_order = db.Column(db.Integer)
def make_from_DB_currency(query_row):
# _m = 'Currency.make_from_DB_currency'
# v_arg_type = 'class attribute'
currency = Currency()
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]
return currency
"""
def make_from_DB_product(query_row):
_m = 'Currency.make_from_DB_product'
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]
return currency
"""
def __repr__(self):
return f'''
id: {self.id_currency}
name: {self.name}
code: {self.code}
symbol: {self.symbol}
factor from GBP: {self.factor_from_GBP}
display_order: {self.display_order}
'''

View File

@@ -0,0 +1,115 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Delivery Option Business Object
Description:
Business object for delivery option
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
"""
class Delivery_Option():
name: str
delay_min: int # days
delay_max: int
quantity_min: float
quantity_max: float
regions: list # [Enum_Delivery_Region]
cost: float
def __new__(cls, name, delay_min, delay_max, quantity_min, quantity_max, regions, cost):
_m = 'Delivery_Option.__new__'
v_arg_type = 'class attribute'
av.val_str(name, 'name', _m, v_arg_type = v_arg_type)
av.val_int(delay_min, 'delay_min', _m, 0, v_arg_type = v_arg_type)
av.val_int(delay_max, 'delay_max', _m, 0, v_arg_type = v_arg_type)
av.val_float(quantity_min, 'quantity_min', _m, 0, v_arg_type = v_arg_type)
av.val_float(quantity_max, 'quantity_max', _m, 0, v_arg_type = v_arg_type)
av.val_list_instances(regions, 'regions', _m, Enum_Delivery_Region, v_arg_type = v_arg_type)
av.val_float(cost, 'cost', _m, 0, v_arg_type = v_arg_type)
return super(Delivery_Option, cls).__new__(cls)
def __init__(self, name, delay_min, delay_max, quantity_min, quantity_max, regions, cost):
self.name = name
self.delay_min = delay_min
self.delay_max = delay_max
self.quantity_min = quantity_min
self.quantity_max = quantity_max
self.regions = regions
self.cost = cost
"""
class Delivery_Option(db.Model):
id_option = db.Column(db.Integer, primary_key=True)
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(100))
latency_min = db.Column(db.Integer)
latency_max = db.Column(db.Integer)
quantity_min = db.Column(db.Integer)
quantity_max = db.Column(db.Integer)
codes_region = db.Column(db.String(4000))
names_region = db.Column(db.String(4000))
price_GBP = db.Column(db.Float)
display_order = db.Column(db.Integer)
def __init__(self):
self.delivery_regions = []
def make_from_DB_product(query_row):
option = Delivery_Option()
option.id_option = query_row[0]
option.id_product = query_row[1]
option.id_permutation = query_row[2]
option.id_category = query_row[3]
option.code = query_row[4]
option.name = query_row[5]
option.latency_min = query_row[6]
option.latency_max = query_row[7]
option.quantity_min = query_row[8]
option.quantity_max = query_row[9]
option.codes_region = query_row[10]
option.names_region = query_row[11]
option.price_GBP = query_row[12]
option.display_order = query_row[13]
return option
def __repr__(self):
return f'''
id: {self.id_option}
id_product: {self.id_product}
id_category: {self.id_category}
name: {self.name}
code: {self.code}
latency_min: {self.latency_min}
latency_max: {self.latency_max}
quantity_min: {self.quantity_min}
quantity_max: {self.quantity_max}
codes_region: {self.codes_region}
names_region: {self.names_region}
price_GBP: {self.price_GBP}
display_order: {self.display_order}
'''

View File

@@ -0,0 +1,96 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Delivery Region Business Object
Description:
Business object for delivery region
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Enum_Delivery_Region(Enum):
UK = 0
class Delivery_Region(db.Model):
id_region = db.Column(db.Integer, primary_key=True)
"""
id_category = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_discount = db.Column(db.Integer)
"""
code = db.Column(db.String(50))
name = db.Column(db.String(200))
active = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
"""
def __new__(cls, id, id_category, id_product, id_discount, code, name, display_order):
_m = 'Delivery_Region.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type = v_arg_type)
av.val_int(id_category, 'id_category', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_product, 'id_product', _m, 0, v_arg_type = v_arg_type)
av.val_int(id_discount, 'id_discount', _m, v_arg_type = v_arg_type)
av.val_str(code, 'code', _m, max_len = 50, v_arg_type = v_arg_type)
av.val_str(name, 'name', _m, max_len = 100, v_arg_type = v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type = v_arg_type)
return super(Delivery_Region, cls).__new__(cls)
def __init__(self, id, id_category, id_product, id_discount, code, name, display_order):
self.id_region = id
self.id_category = id_category
self.id_product = id_product
self.id_discount = id_discount
self.name = name
self.code = code
self.display_order = display_order
"""
def make_from_DB_product(query_row):
region = Delivery_Region()
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 make_from_DB_region(query_row):
region = Delivery_Region()
region.id_region = query_row[0]
region.code = query_row[1]
region.name = query_row[2]
region.active = query_row[3]
region.display_order = query_row[4]
return region
def __repr__(self):
return f'''
id: {self.id_region}
name: {self.name}
code: {self.code}
active: {self.active}
display_order: {self.display_order}
'''

View File

@@ -0,0 +1,92 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Discount Business Object
Description:
Business object for discount
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Discount(db.Model):
id_discount = db.Column(db.Integer, primary_key=True)
id_category = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
code = db.Column(db.String(50))
name = db.Column(db.String(200))
multiplier = db.Column(db.Float)
subtractor = db.Column(db.Float)
apply_multiplier_first = db.Column(db.Boolean)
quantity_min = db.Column(db.Integer)
quantity_max = db.Column(db.Integer)
date_start = db.Column(db.DateTime)
date_end = db.Column(db.DateTime)
codes_region = db.Column(db.String(4000))
names_region = db.Column(db.String(4000))
codes_currency = db.Column(db.String(4000))
names_currency = db.Column(db.String(4000))
display_order = db.Column(db.Integer)
def __init__(self):
self.delivery_regions = []
def make_from_DB_product(query_row):
discount = Discount()
discount.id_discount = query_row[0]
discount.id_category = query_row[1]
discount.id_product = query_row[2]
discount.id_permutation = query_row[3]
discount.code = query_row[4]
discount.name = query_row[5]
discount.multiplier = query_row[6]
discount.subtractor = query_row[7]
discount.apply_multiplier_first = query_row[8]
discount.quantity_min = query_row[9]
discount.quantity_max = query_row[10]
discount.date_start = query_row[11]
discount.date_end = query_row[12]
discount.codes_region = query_row[13]
discount.names_region = query_row[14]
discount.codes_currency = query_row[15]
discount.names_currency = query_row[16]
discount.display_order = query_row[17]
return discount
def __repr__(self):
return f'''
id: {self.id_discount}
id_category: {self.id_category}
id_product: {self.id_product}
name: {self.name}
code: {self.code}
multiplier: {self.multiplier}
quantity_min: {self.quantity_min}
quantity_max: {self.quantity_max}
date_start: {self.date_start}
date_end: {self.date_end}
display_order: {self.display_order}
'''

132
business_objects/image.py Normal file
View File

@@ -0,0 +1,132 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Image Business Object
Description:
Business object for product image
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Resolution_Level_Enum(Enum):
THUMBNAIL = 0
LOW = 1
HIGH = 2
FULL = 3
def text(self):
return Resolution_Level_Enum.Resolution_Level_Enum_Text(self)
def Resolution_Level_Enum_Text(category):
av.val_instance(category, 'category', 'Resolution_Level_Enum_Text', Resolution_Level_Enum)
if category == Resolution_Level_Enum.THUMBNAIL:
return 'Thumbnail'
elif category == Resolution_Level_Enum.LOW:
return 'Low resolution'
elif category == Resolution_Level_Enum.HIGH:
return 'High resolution'
elif category == Resolution_Level_Enum.FULL:
return 'Full resolution'
else:
return 'Unknown'
def get_member_by_text(text):
for member in Resolution_Level_Enum.__members__.values():
if member.name == text:
return member
return Resolution_Level_Enum.HIGH
class Image(db.Model):
id_image = db.Column(db.Integer, primary_key=True)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
id_category = db.Column(db.Integer)
url = db.Column(db.String(255))
active = db.Column(db.Boolean)
display_order = db.Column(db.Integer)
"""
def __new__(cls, id, id_product, id_category, url, display_order):
_m = 'Image.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_product, 'id_product', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_category, 'id_category', _m, 0, v_arg_type=v_arg_type)
av.val_str(url, 'url', _m, max_len=254, v_arg_type=v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type=v_arg_type)
return super(Image, cls).__new__(cls)
def __init__(self, id, id_product, id_category, url, display_order):
self.id_image = id
self.id_product = id_product
self.id_category = id_category
self.url = url
self.display_order = display_order
super().__init__()
"""
def make_from_DB_product(query_row):
_m = 'Image.make_from_DB_product'
# print(f'image: {query_row}')
image = Image()
image.id_image = query_row[0]
image.id_product = query_row[1]
image.id_permutation = query_row[2]
image.id_category = query_row[3]
image.url = query_row[4]
image.active = query_row[5]
image.display_order = query_row[6]
return image
def __repr__(self):
return f'''
id: {self.id_image}
id_product: {self.id_product}
id_category: {self.id_category}
url: {self.url}
display_order: {self.display_order}
'''
class Product_Image_Filters():
product_id: int
get_thumbnail: bool
get_remaining_LQ: bool
def __new__(cls, product_id, get_thumbnail, get_remaining_LQ):
# Initialiser - validation
_m = 'Product_Filters.__new__'
v_arg_type = 'class attribute'
av.val_int(product_id, 'product_id', _m, v_arg_type=v_arg_type)
av.val_bool(get_thumbnail, 'get_thumbnail', _m, v_arg_type=v_arg_type)
av.val_bool(get_remaining_LQ, 'get_remaining_LQ', _m, v_arg_type=v_arg_type)
return super(Product, cls).__new__(cls)
def __init__(self, product_id, get_thumbnail, get_remaining_LQ):
# Constructor
self.product_id = product_id
self.get_thumbnail = get_thumbnail
self.get_remaining_LQ = get_remaining_LQ

93
business_objects/order.py Normal file
View File

@@ -0,0 +1,93 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Order Business Object
Description:
Business object for order
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
# from lib import data_types
from business_objects.product import Product
from business_objects.delivery_option import Delivery_Option
# from forms import Form_Product
# from models.model_view_store import Model_View_Store # circular
# external
# from enum import Enum
from flask import jsonify
import locale
# VARIABLE INSTANTIATION
# CLASSES
class Order():
category: str
product: Product
quantity: int
subtotal: float
delivery_option: Delivery_Option
# form: Form_Product
def __new__(cls, category, product, quantity):
# Initialiser - validation
_m = 'Product.__new__'
v_arg_type = 'class attribute'
av.val_str(category, 'category', _m, v_arg_type=v_arg_type)
av.val_instance(product, 'product', _m, Product, v_arg_type=v_arg_type)
av.full_val_float(quantity, 'quantity', _m, product.quantity_min, v_arg_type=v_arg_type)
return super(Basket_Item, cls).__new__(cls)
def __init__(self, category, product, quantity):
# Constructor
self.category = category
self.product = product
self.quantity = quantity
self.subtotal = round(self.product.price_GBP_full * self.quantity, 2)
"""
self.form = Form_Product()
if self.form.validate_on_submit():
# Handle form submission
pass
"""
def update_quantity(self, quantity):
_m = 'Basket_Item.update_quantity'
v_arg_type = 'class attribute'
av.full_val_float(quantity, 'quantity', _m, self.product.quantity_min, v_arg_type=v_arg_type)
self.quantity = quantity
self.subtotal = round(self.product.price_GBP_full * self.quantity, 2)
def jsonify(self):
return jsonify(self)
def to_json(self):
return {
'product_id': self.product.id_product,
'price': self.product.price_GBP_full,
'quantity': self.quantity
}
def output_subtotal(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.subtotal, grouping=True)
def __repr__(self):
return f'''
category: {self.category}
product: {self.product}
quantity: {self.quantity}
subtotal: {self.subtotal}
'''

704
business_objects/product.py Normal file
View File

@@ -0,0 +1,704 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Business Object
Description:
Business object for product
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
from business_objects.discount import Discount
from business_objects.variation import Variation
from business_objects.image import Image
from business_objects.delivery_option import Delivery_Option
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
from dataclasses import dataclass
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Enum_Status_Stock(Enum):
OUT = 0
LOW = 1
IN = 99
def text(self):
return Enum_Status_Stock.Enum_Status_Stock_Text(self)
def Enum_Status_Stock_Text(status):
av.val_instance(status, 'category', 'Enum_Status_Stock_Text', Enum_Status_Stock)
if status == Enum_Status_Stock.OUT:
return 'Out of stock'
elif status == Enum_Status_Stock.LOW:
return 'Low on stock'
else:
return 'Fully stocked'
def get_member_by_text(text):
return data_types.get_enum_member_by_text(Enum_Status_Stock, text.upper())
class Variation_Tree_Node():
variation: Variation
node_parent: None
nodes_child: list
def __init__(self):
self.nodes_child = []
def make_from_variation_and_node_parent(variation, node_parent):
node = Variation_Tree_Node()
node.variation = variation
node.node_parent = node_parent
return node
def make_from_node_parent(node_parent):
node = Variation_Tree_Node()
node.node_parent = node_parent
return node
def add_child(self, node_child):
self.nodes_child.append(node_child)
def is_leaf(self):
return (len(self.nodes_child) == 0)
class Variation_Tree:
node_root: Variation_Tree_Node
def make_from_node_root(node_root):
tree = Variation_Tree()
tree.node_root = node_root
return tree
def get_variation_type_list(self):
variation_types = []
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
variation_types.append(node.variation.name_variation_type)
at_leaf_node = node.is_leaf()
if not at_leaf_node:
node = node.nodes_child[0]
return variation_types
def is_equal(self, tree):
my_type_list = self.get_variation_type_list()
sz_me = len(my_type_list)
other_type_list = tree.get_variation_type_list()
sz_other = len(other_type_list)
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]:
is_equal = False
break
return is_equal
def make_from_product_permutation(product_permutation):
depth_max = len(product_permutation.variations)
node_root = Variation_Tree_Node.make_from_variation_and_node_parent(product_permutation.variations[0], None)
node = node_root
for depth in range(depth_max - 1):
node = Variation_Tree_Node.make_from_variation_and_node_parent(product_permutation.variations[depth + 1], node)
return Variation_Tree.make_from_node_root(node_root)
class Product(db.Model):
id_product = db.Column(db.Integer, primary_key=True)
id_category = db.Column(db.Integer)
name = db.Column(db.String(255))
display_order = db.Column(db.Integer)
can_view = db.Column(db.Boolean)
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
# form_basket_add: Form_Basket_Add
# form_basket_edit: Form_Basket_Edit
# has_variations: bool
# index_permutation_selected: int
def __init__(self):
self.permutations = []
self.permutation_index = {}
self.variation_trees = []
self.index_permutation_selected = None
self.has_variations = False
super().__init__()
self.form_basket_add = Form_Basket_Add()
self.form_basket_edit = Form_Basket_Edit()
def make_from_DB_product(query_row):
_m = 'Product.make_from_DB_product'
v_arg_type = 'class attribute'
product = Product()
product.id_product = query_row[0]
product.id_category = query_row[5]
product.name = query_row[2]
product.has_variations = av.input_bool(query_row[4], "has_variations", _m, v_arg_type=v_arg_type)
product.display_order = query_row[17]
product.can_view = av.input_bool(query_row[19], "can_view", _m, v_arg_type=v_arg_type)
product.can_edit = av.input_bool(query_row[20], "can_edit", _m, v_arg_type=v_arg_type)
product.can_admin = av.input_bool(query_row[21], "can_admin", _m, v_arg_type=v_arg_type)
return product
"""
def make_from_permutation(permutation, has_variations = False):
_m = 'Product.make_from_permutation'
v_arg_type = 'class attribute'
av.val_instance(permutation, 'permutation', _m, Product_Permutation, v_arg_type=v_arg_type)
product = Product()
product.has_variations = has_variations
product.index_permutation_selected = 0
product.id_product = permutation.id_product
product.id_category = permutation.id_category
product.display_order = permutation.display_order
product.can_view = permutation.can_view
product.can_edit = permutation.can_edit
product.can_admin = permutation.can_admin
product.permutations.append(permutation)
# product.get_variation_trees()
return product
"""
def add_permutation(self, permutation):
_m = 'Product.add_permutation'
av.val_instance(permutation, 'permutation', _m, Product_Permutation)
try:
self.permutation_index[permutation.id_permutation]
raise ValueError(f"{av.error_msg_str(permutation, 'permutation', _m, Product_Permutation)}\nPermutation ID already in product")
except KeyError:
self.permutation_index[permutation.id_permutation] = len(self.permutations)
self.permutations.append(permutation)
"""
if self.has_variations:
self.has_variations = False
"""
if self.index_permutation_selected is None:
self.index_permutation_selected = self.permutation_index[permutation.id_permutation]
print(f'setting selected permutation for product {self.id_product} to {self.index_permutation_selected}') # :\n{self.permutations[self.index_permutation_selected]}
"""
def make_from_permutations(permutations):
_m = 'Product.make_from_permutations'
v_arg_type = 'class attribute'
if len(permutations) == 0:
raise ValueError(av.error_msg_str(permutations, 'permutations', _m, list, v_arg_type=v_arg_type))
product = Product()
product.has_variations = True
product.index_permutation_selected = 0
product.id_product = permutations[0].id_product
product.id_category = permutations[0].id_category
product.display_order = permutations[0].display_order
product.can_view = True
product.can_edit = True
product.can_admin = True
for permutation in permutations:
product.can_view &= permutation.can_view
product.can_edit &= permutation.can_edit
product.can_admin &= permutation.can_admin
product.permutations.append(permutations)
product.get_variation_trees()
return product
"""
def get_variation_trees(self):
for index_permutation in range(len(self.permutations)):
variation_tree = Variation_Tree.make_from_product_permutation(self.permutations[index_permutation])
found_variation_tree_match = False
for index_tree in range(len(self.variation_trees)):
if self.variation_trees[index_tree].is_equal(variation_tree):
found_variation_tree_match = True
break
if not found_variation_tree_match:
self.variation_trees.append(variation_tree)
def make_from_DB_Stripe_product(query_row):
permutation = Product_Permutation.make_from_DB_Stripe_product(query_row)
product = Product.make_from_permutation(permutation)
return product
def make_from_DB_Stripe_price(query_row):
permutation = Product_Permutation.make_from_DB_Stripe_price(query_row)
product = Product.make_from_permutation(permutation)
return product
def make_from_json(json_basket_item, key_id_product, key_id_permutation):
permutation = Product_Permutation.make_from_json(json_basket_item, key_id_product, key_id_permutation)
product = Product.make_from_permutation(permutation)
return product
def get_permutation_selected(self):
try:
return self.permutations[self.index_permutation_selected]
except:
raise ValueError(f'list index {self.index_permutation_selected} out of range')
def output_lead_time(self):
return self.get_permutation_selected().output_lead_time()
def output_delivery_date(self):
return self.get_permutation_selected().output_delivery_date()
def output_price(self, is_included_VAT):
return self.get_permutation_selected().output_price(is_included_VAT)
def output_price_VAT_incl(self):
return self.get_permutation_selected().output_price(True)
def output_price_VAT_excl(self):
return self.get_permutation_selected().output_price(False)
def get_price_local(self, is_included_VAT):
if is_included_VAT:
return self.get_price_local_VAT_incl()
else:
return self.get_price_local_VAT_excl()
def get_price_local_VAT_incl(self):
return self.get_permutation_selected().get_price_local_VAT_incl()
def get_price_local_VAT_excl(self):
return self.get_permutation_selected().get_price_local_VAT_excl()
def get_quantity_min(self):
return self.get_permutation_selected().quantity_min
def get_id_permutation(self):
return self.get_permutation_selected().id_permutation
def get_image_from_index(self, index_image):
return self.get_permutation_selected().images[index_image]
def get_name(self):
return self.get_permutation_selected().name
def get_description(self):
return self.get_permutation_selected().description
def output_currency(self):
return self.get_permutation_selected().get_price().symbol_currency
"""
def add_form_basket_add(self):
self.form_basket_add = None
def add_form_basket_edit(self):
self.form_basket_edit = None
"""
def __repr__(self):
return f'''Product
id_product: {self.id_product}
id_category: {self.id_category}
name: {self.name}
display_order: {self.display_order}
can_view: {self.can_view}
can_edit: {self.can_edit}
can_admin: {self.can_admin}
has_variations: {self.has_variations}
permutations: {self.permutations}
variation trees: {self.variation_trees}
'''
"""
def get_index_permutation_from_id(self, id_permutation):
if id_permutation is None and not self.has_variations:
return 0
for index_permutation in range(len(self.permutations)):
permutation = self.permutations[index_permutation]
if permutation.id_permutation == id_permutation:
return index_permutation
raise ValueError(f"{av.error_msg_str(id_permutation, 'id_permutation', 'Product.get_index_permutation_from_id', int)}\nPermutation ID not found.")
"""
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Product.add_variation', Variation)
# print(f'variation: {variation}')
index_permutation = self.permutation_index[variation.id_permutation] # self.get_index_permutation_from_id(variation.id_permutation)
self.permutations[index_permutation].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Product.add_price', Price)
index_permutation = self.permutation_index[price.id_permutation] # self.get_index_permutation_from_id(price.id_permutation)
self.permutations[index_permutation].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Product.add_image', Image)
index_permutation = self.permutation_index[image.id_permutation] # self.get_index_permutation_from_id(image.id_permutation)
self.permutations[index_permutation].add_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Product.add_delivery_option', Delivery_Option)
index_permutation = self.permutation_index[delivery_option.id_permutation] # self.get_index_permutation_from_id(delivery_option.id_permutation)
self.permutations[index_permutation].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Product.add_discount', Discount)
index_permutation = self.permutation_index[discount.id_permutation] # self.get_index_permutation_from_id(discount.id_permutation)
self.permutations[index_permutation].add_discount(discount)
class Product_Permutation(db.Model):
id_product = db.Column(db.Integer, primary_key=True)
id_permutation = db.Column(db.Integer, primary_key=True)
# 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)
has_variations = db.Column(db.Boolean)
id_category = db.Column(db.Integer)
latency_manufacture = 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)
display_order = db.Column(db.Integer)
can_view = db.Column(db.Boolean)
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
# form_basket_add: Form_Basket_Add
# form_basket_edit: Form_Basket_Edit
# is_unavailable_in_currency_or_region: bool
# is_available: bool
def __init__(self):
self.variations = []
self.variation_index = {}
self.prices = []
self.price_index = {}
self.images = []
self.image_index = {}
self.delivery_options = []
self.delivery_option_index = {}
self.discounts = []
self.discount_index = {}
super().__init__()
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 = True
def make_from_DB_product(query_row):
_m = 'Product_Permutation.make_from_DB_product'
v_arg_type = 'class attribute'
print(f'query_row: {query_row}')
permutation = Product_Permutation()
permutation.id_product = query_row[0]
permutation.id_permutation = query_row[1]
# permutation.name = query_row[2]
permutation.description = query_row[3]
# permutation.price_GBP_full = query_row[4]
# permutation.price_GBP_min = query_row[5]
permutation.has_variations = query_row[4]
permutation.id_category = query_row[5]
permutation.latency_manufacture = query_row[6]
permutation.quantity_min = query_row[7]
permutation.quantity_max = query_row[8]
permutation.quantity_step = query_row[9]
permutation.quantity_stock = query_row[10]
permutation.id_stripe_product = query_row[11]
permutation.is_subscription = av.input_bool(query_row[12], "is_subscription", _m, v_arg_type=v_arg_type)
permutation.name_recurrence_interval = query_row[13]
permutation.name_plural_recurrence_interval = query_row[14]
permutation.count_recurrence_interval = query_row[15]
permutation.display_order = query_row[18]
permutation.can_view = av.input_bool(query_row[19], "can_view", _m, v_arg_type=v_arg_type)
permutation.can_edit = av.input_bool(query_row[20], "can_edit", _m, v_arg_type=v_arg_type)
permutation.can_admin = av.input_bool(query_row[21], "can_admin", _m, v_arg_type=v_arg_type)
return permutation
def make_from_DB_Stripe_product(query_row):
_m = 'Product_Permutation.make_from_DB_Stripe_product'
v_arg_type = 'class attribute'
permutation = Product_Permutation()
permutation.id_product = query_row[0]
# permutation.name = query_row[1]
permutation.description = query_row[2]
return permutation
def make_from_DB_Stripe_price(query_row):
_m = 'Product_Permutation.make_from_DB_Stripe_price'
v_arg_type = 'class attribute'
permutation = Product_Permutation()
permutation.id_product = query_row[0]
# 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]
return permutation
def make_from_json(json_basket_item, key_id_product, key_id_permutation):
_m = 'Product_Permutation.make_from_json'
v_arg_type = 'class attribute'
permutation = Product_Permutation()
permutation.id_product = json_basket_item[key_id_product]
permutation.id_permutation = json_basket_item[key_id_permutation]
return permutation
def get_price(self):
return self.prices[0]
def get_price_local_VAT_incl(self):
price = self.get_price()
return price.value_local_VAT_incl
def get_price_local_VAT_excl(self):
price = self.get_price()
return price.value_local_VAT_excl
def output_lead_time(self):
return '1 day' if self.latency_manufacture == 1 else f'{self.latency_manufacture} days'
def output_delivery_date(self):
return (datetime.now() + timedelta(days=self.latency_manufacture)).strftime('%A, %d %B %Y')
def output_price(self, is_included_VAT):
if self.is_unavailable_in_currency_or_region:
return 'Not available in currency and region'
if not self.is_available:
return 'Not available'
price = self.get_price()
locale.setlocale(locale.LC_ALL, '')
if is_included_VAT:
return f'{price.symbol_currency} {locale.format_string("%d", price.value_local_VAT_incl, grouping=True)}'
else:
return f'{price.symbol_currency} {locale.format_string("%d", price.value_local_VAT_excl, grouping=True)}'
def output_currency(self):
if not self.is_available:
return ''
price = self.get_price()
return price.code_currency
"""
def output_price_VAT_incl(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.price_GBP_VAT_incl, grouping=True)
def output_price_VAT_excl(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.price_GBP_VAT_excl, grouping=True)
def add_form_basket_add(self):
self.form_basket_add = None
def add_form_basket_edit(self):
self.form_basket_edit = None
"""
def __repr__(self):
return f'''Product_Permutation
id_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}
'''
"""
price_GBP_full: {self.price_GBP_full}
price_GBP_min: {self.price_GBP_min}
"""
def add_variation(self, variation):
_m = 'Product_Permutation.add_variation'
av.val_instance(variation, 'variation', _m, Variation)
try:
self.variation_index[variation.id_variation]
raise ValueError(f"{av.error_msg_str(variation, 'variation', _m, Variation)}\nVariation already in product.")
except KeyError:
self.variation_index[variation.id_variation] = len(self.variations)
self.variations.append(variation)
def add_price(self, price):
_m = 'Product_Permutation.add_price'
av.val_instance(price, 'price', _m, Price)
try:
self.price_index[price.display_order]
raise ValueError(f"{av.error_msg_str(price, 'price', _m, Price)}\nPrice already in product.")
except KeyError:
self.price_index[price.display_order] = len(self.prices)
self.prices.append(price)
def add_image(self, image):
_m = 'Product_Permutation.add_image'
av.val_instance(image, 'image', _m, Image)
try:
self.image_index[image.id_image]
raise ValueError(f"{av.error_msg_str(image, 'image', _m, Image)}\nImage already in product.")
except KeyError:
self.image_index[image.id_image] = len(self.images)
self.images.append(image)
def add_delivery_option(self, delivery_option):
_m = 'Product_Permutation.add_delivery_option'
av.val_instance(delivery_option, 'delivery_option', _m, Delivery_Option)
try:
self.delivery_option_index[delivery_option.id_option]
raise ValueError(f"{av.error_msg_str(delivery_option, 'delivery_option', _m, Delivery_Option)}\nDelivery_Option already in product.")
except KeyError:
self.delivery_option_index[delivery_option.id_option] = len(self.delivery_options)
self.delivery_options.append(delivery_option)
def add_discount(self, discount):
_m = 'Product_Permutation.add_discount'
av.val_instance(discount, 'discount', _m, Discount)
try:
self.discount_index[discount.display_order]
raise ValueError(f"{av.error_msg_str(discount, 'discount', _m, Discount)}\nDiscount already in product.")
except KeyError:
self.discount_index[discount.display_order] = len(self.discounts)
self.discounts.append(discount)
def get_image_from_index(self, index_image):
try:
return self.images[index_image]
except:
raise IndexError(f"Invalid image index: {index_image}")
def get_price_from_code_currency(self, code_currency):
for price in self.prices:
if price.code_currency == code_currency:
return price
"""
class Product_Filters():
ids: str # csv
categories: str # csv
def __new__(cls, product_ids, product_categories):
# Initialiser - validation
_m = 'Product_Filters.__new__'
v_arg_type = 'class attribute'
# av.val_list_instances(product_ids, 'product_ids', _m, str, v_arg_type=v_arg_type)
# av.val_list_instances(product_categories, 'product_categories', _m, Product_Category_Enum, v_arg_type=v_arg_type)
av.val_str(product_ids, 'product_ids', _m, v_arg_type=v_arg_type)
av.val_str(product_categories, 'product_categories', _m, v_arg_type=v_arg_type)
return super(Product, cls).__new__(cls)
def __init__(self, product_ids, product_categories):
# Constructor
self.ids = product_ids
self.categories = product_categories
class Price():
product: Product
currency: Currency_Enum
def make_from_product_currency(product, code_currency):
name_method = 'make_from_product_currency'
av.val_instance(product, 'product', name_method, Product)
price = Price()
price.product = product
price.currency = Currency_Enum.get_member_by_text(code_currency)
"""
class Price(db.Model):
id_price = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
id_category = db.Column(db.Integer)
id_currency = db.Column(db.Integer)
code_currency = db.Column(db.String)
name_currency = db.Column(db.String)
symbol_currency = db.Column(db.String)
id_region = db.Column(db.Integer)
value_local_VAT_incl = db.Column(db.Float)
value_local_VAT_excl = db.Column(db.Float)
display_order = db.Column(db.Float, primary_key=True)
def make_from_DB_product(query_row):
_m = 'Price.make_from_DB_product'
price = Price()
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.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]
price.display_order = query_row[11]
return price
def __repr__(self):
return f'''Price
id: {self.id_price}
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}
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}
'''
"""
class Permutation_Variation_Link(db.Model):
id_permutation = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
id_variation = db.Column(db.Integer)
def make_from_DB_product(query_row):
_m = 'Permutation_Variation_Link.make_from_DB_product'
v_arg_type = 'class attribute'
link = Permutation_Variation_Link()
link.id_permutation = query_row[0]
link.id_product = query_row[1]
link.id_category = query_row[2]
link.id_variation = query_row[3]
return link
"""
@dataclass
class Product_Filters():
id_user: str
get_all_category: bool
ids_category: str
get_inactive_category: bool
get_all_product: bool
ids_product: str
get_inactive_product: bool
get_first_product_only: bool
get_all_permutation: bool
ids_permutation: str
get_inactive_permutation: bool
get_all_image: bool
ids_image: str
get_inactive_image: bool
get_first_image_only: bool
get_all_region: bool
ids_region: str
get_inactive_region: bool
get_all_currency: bool
ids_currency: str
get_inactive_currency: bool
get_all_discount: bool
ids_discount: str
get_inactive_discount: bool
def to_json(self):
return {
'a_id_user': self.id_user,
'a_get_all_category': self.get_all_category,
'a_ids_category': self.ids_category,
'a_get_inactive_category': self.get_inactive_category,
'a_get_all_product': self.get_all_product,
'a_ids_product': self.ids_product,
'a_get_inactive_product': self.get_inactive_product,
'a_get_first_product_only': self.get_first_product_only,
'a_get_all_permutation': self.get_all_permutation,
'a_ids_permutation': self.ids_permutation,
'a_get_inactive_permutation': self.get_inactive_permutation,
'a_get_all_image': self.get_all_image,
'a_ids_image': self.ids_image,
'a_get_inactive_image': self.get_inactive_image,
'a_get_first_image_only': self.get_first_image_only,
'a_get_all_delivery_region': self.get_all_region,
'a_ids_delivery_region': self.ids_region,
'a_get_inactive_delivery_region': self.get_inactive_region,
'a_get_all_currency': self.get_all_currency,
'a_ids_currency': self.ids_currency,
'a_get_inactive_currency': self.get_inactive_currency,
'a_get_all_discount': self.get_all_discount,
'a_ids_discount': self.ids_discount,
'a_get_inactive_discount': self.get_inactive_discount
}

View File

@@ -0,0 +1,65 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: SQL Error Business Object
Description:
Business object for SQL errors
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class SQL_Error(db.Model):
display_order = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
msg = db.Column(db.String(4000))
name = db.Column(db.String(500))
description = db.Column(db.String(4000))
"""
def __new__(cls, display_order, code, msg):
_m = 'SQL_Error.__new__'
v_arg_type = 'class attribute'
av.val_int(display_order, 'display_order', _m)
av.val_str(code, 'code', _m, max_len=50, v_arg_type=v_arg_type)
av.val_str(msg, 'msg', _m, max_len=4000, v_arg_type=v_arg_type)
return super(SQL_Error, cls).__new__(cls)
def __init__(self, display_order, code, msg):
self.display_order = display_order
self.code = code
self.msg = msg
super().__init__()
"""
def make_from_DB_record(record):
error = SQL_Error()
error.display_order = record[0]
error.code = record[1]
error.msg = record[2]
error.name = record[3]
error.description = record[4]
return error

164
business_objects/stripe.py Normal file
View File

@@ -0,0 +1,164 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Stripe Business Object
Description:
Business objects for Stripe
"""
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Stripe_Product(db.Model):
id_product = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
description = db.Column(db.String(4000))
price_GBP_full = db.Column(db.Float)
id_category = db.Column(db.Integer)
lead_time_manuf = 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(255))
id_stripe_price = db.Column(db.String(255))
is_subscription = db.Column(db.Boolean)
name_recurring_interval = db.Column(db.String(255))
name_plural_recurring_interval = db.Column(db.String(256))
count_recurring_interval = 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)
# form_basket_add: Form_Basket_Add
# form_basket_edit: Form_Basket_Edit
def __new__(cls, id, name, description, price_GBP_full, id_category, lead_time_manuf, quantity_min, quantity_max, quantity_step, quantity_stock, id_stripe_product, id_stripe_price,
is_subscription, name_recurring_interval, name_plural_recurring_interval, count_recurring_interval, display_order, can_view, can_edit, can_admin):
_m = 'Product.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type=v_arg_type)
av.val_str(name, 'name', _m, max_len=256, v_arg_type=v_arg_type)
av.val_str(description, 'description', _m, max_len=4000, v_arg_type=v_arg_type)
av.full_val_float(price_GBP_full, 'price_GBP_full', _m, 0., v_arg_type=v_arg_type)
av.val_int(id_category, 'id_category', _m, 0, v_arg_type=v_arg_type)
av.val_int(lead_time_manuf, 'lead_time_manuf', _m, 0, v_arg_type=v_arg_type)
av.full_val_float(quantity_step, 'quantity_step', _m, 0., v_arg_type=v_arg_type)
av.full_val_float(quantity_min, 'quantity_min', _m, quantity_step, v_arg_type=v_arg_type)
av.full_val_float(quantity_max, 'quantity_max', _m, quantity_min, v_arg_type=v_arg_type)
av.full_val_float(quantity_stock, 'quantity_stock', _m, 0, v_arg_type=v_arg_type)
av.val_str(id_stripe_product, 'id_stripe_product', _m, max_len=100, v_arg_type=v_arg_type)
av.val_str(id_stripe_price, 'id_stripe_price', _m, max_len=100, v_arg_type=v_arg_type)
av.full_val_bool(is_subscription, 'is_subscription', _m, v_arg_type=v_arg_type)
print(f'is_subscription: {is_subscription}, {av.input_bool(is_subscription, "is_subscription", _m, v_arg_type=v_arg_type)}')
is_subscription = av.input_bool(is_subscription, "is_subscription", _m, v_arg_type=v_arg_type)
if is_subscription:
av.val_str(name_recurring_interval, 'name_recurring_interval', _m, max_len=255, v_arg_type=v_arg_type)
av.val_str(name_plural_recurring_interval, 'name_plural_recurring_interval', _m, max_len=256, v_arg_type=v_arg_type)
av.val_int(count_recurring_interval, 'count_recurring_interval', _m, 0, v_arg_type=v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type=v_arg_type)
av.full_val_bool(can_view, 'can_view', _m, v_arg_type=v_arg_type)
# can_view = av.input_bool(can_view, "can_view", _m, v_arg_type=v_arg_type)
av.full_val_bool(can_edit, 'can_edit', _m, v_arg_type=v_arg_type)
# can_edit = av.input_bool(can_edit, "can_edit", _m, v_arg_type=v_arg_type)
av.full_val_bool(can_admin, 'can_admin', _m, v_arg_type=v_arg_type)
# can_admin = av.input_bool(can_admin, "can_admin", _m, v_arg_type=v_arg_type)
return super(Product, cls).__new__(cls) # , id, name, description, price_GBP, id_category, lead_time_manuf, quantity_min, quantity_max, quantity_step, quantity_stock, id_stripe_product, id_stripe_price,
# is_subscription, name_recurring_interval, name_plural_recurring_interval, count_recurring_interval, can_view, can_edit, can_admin)
def __init__(self, id, name, description, price_GBP_full, id_category, lead_time_manuf, quantity_min, quantity_max, quantity_step, quantity_stock, id_stripe_product, id_stripe_price,
is_subscription, name_recurring_interval, name_plural_recurring_interval, count_recurring_interval, display_order, can_view, can_edit, can_admin):
_m = 'Product.__new__'
v_arg_type = 'class attribute'
self.id_product = id
self.name = name
self.description = description
self.price_GBP_full = price_GBP_full
self.id_category = id_category
self.lead_time_manuf = lead_time_manuf
self.quantity_min = quantity_min
self.quantity_max = quantity_max
self.quantity_step = quantity_step
self.quantity_stock = quantity_stock
self.id_stripe_product = id_stripe_product
self.id_stripe_price = id_stripe_price
self.is_subscription = av.input_bool(is_subscription, "is_subscription", _m, v_arg_type=v_arg_type)
self.name_recurring_interval = name_recurring_interval
self.name_plural_recurring_interval = name_plural_recurring_interval
self.count_recurring_interval = count_recurring_interval
self.display_order = display_order
self.can_view = av.input_bool(can_view, "can_view", _m, v_arg_type=v_arg_type)
self.can_edit = av.input_bool(can_edit, "can_edit", _m, v_arg_type=v_arg_type)
self.can_admin = av.input_bool(can_admin, "can_admin", _m, v_arg_type=v_arg_type)
self.variations = []
self.images = []
self.delivery_options = []
self.discounts = []
self.discount_index = {}
super().__init__()
self.form_basket_add = Form_Basket_Add()
self.form_basket_edit = Form_Basket_Edit()
def output_lead_time(self):
return '1 day' if self.lead_time_manuf == 1 else f'{self.lead_time_manuf} days'
def output_delivery_date(self):
return (datetime.now() + timedelta(days=self.lead_time_manuf)).strftime('%A, %d %B %Y')
def output_price(self):
locale.setlocale(locale.LC_ALL, '')
return locale.format_string("%d", self.price_GBP_full, grouping=True)
"""
def add_form_basket_add(self):
self.form_basket_add = None
def add_form_basket_edit(self):
self.form_basket_edit = None
"""
def __repr__(self):
return f'''Product
id: {self.id_product}
name: {self.name}
description: {self.description}
price_GBP_full: {self.price_GBP_full}
id_category: {self.id_category}
lead_time_manuf: {self.lead_time_manuf}
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}
id_stripe_price: {self.id_stripe_price}
is_subscription: {self.is_subscription}
name_recurring_interval: {self.name_recurring_interval}
name_plural_recurring_interval: {self.name_plural_recurring_interval}
count_recurring_interval: {self.count_recurring_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}
'''
def add_discount(self, discount):
_m = 'Category.add_product'
av.val_instance(discount, 'discount', _m, Discount)
# self.product_index.append(len(self.products))
self.discount_index[discount.id_discount] = len(self.discounts)
self.discounts.append(discount)

View File

@@ -0,0 +1,94 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Variation Business Object
Description:
Business object for product variation
"""
# IMPORTS
# VARIABLE INSTANTIATION
# CLASSES
# METHODS
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
# VARIABLE INSTANTIATION
db = SQLAlchemy()
# CLASSES
class Variation(db.Model):
id_variation = db.Column(db.Integer, primary_key=True)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
id_category = db.Column(db.Integer)
code_variation_type = db.Column(db.String(50))
name_variation_type = db.Column(db.String(255))
code_variation = db.Column(db.String(50))
name_variation = db.Column(db.String(255))
display_order = db.Column(db.Integer)
"""
def __new__(cls, id, id_product, id_category, name_type, code_type, name, code, display_order):
_m = 'Variation.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_product, 'id_product', _m, 0, v_arg_type=v_arg_type)
av.val_int(id_category, 'id_category', _m, 0, v_arg_type=v_arg_type)
av.val_str(code_type, 'code_type', _m, max_len=50, v_arg_type=v_arg_type)
av.val_str(name_type, 'name_type', _m, max_len=256, v_arg_type=v_arg_type)
av.val_str(code, 'code', _m, max_len=50, v_arg_type=v_arg_type)
av.val_str(name, 'name', _m, max_len=256, v_arg_type=v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type=v_arg_type)
return super(Variation, cls).__new__(cls)
def __init__(self, id, id_product, id_category, name_type, code_type, name, code, display_order):
self.id_variation = id
self.id_product = id_product
self.id_category = id_category
self.name_variation_type = name_type
self.code_variation_type = code_type
self.name_variation = name
self.code_variation = code
self.display_order = display_order
super().__init__()
"""
def make_from_DB_product(query_row):
variation = Variation()
variation.id_variation = query_row[0]
variation.id_product = query_row[1]
variation.id_permutation = query_row[2]
variation.id_category = query_row[3]
variation.code_variation_type = query_row[4]
variation.name_variation_type = query_row[5]
variation.code_variation = query_row[6]
variation.name_variation = query_row[7]
variation.display_order = query_row[8]
return variation
def __repr__(self):
return f'''
id: {self.id_variation}
id_product: {self.id_product}
id_permutation: {self.id_permutation}
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}
display_order: {self.display_order}
'''