20 Commits

Author SHA1 Message Date
Teddy-1024
7b9e900cfe Merge pull request #4 from Teddy-1024/root_server
Root server
2025-03-15 17:59:03 +00:00
bade1f11dd Feat: Update CAPTCHA service to ALTCHA self-hosted. 2025-03-15 17:47:36 +00:00
b843849af9 Feat: Replace Google ReCAPTCHA with ALTCHA using API - non-tracking, GDPR compliant without cookies or fingerprinting. 2025-03-13 15:36:41 +00:00
29205de12f Fix(UI): Home page: improved content for all sections, including new CTAs for buttons. 2025-01-25 18:10:17 +00:00
a105372638 Fix(UI): Home page: improved content for all sections, including new CTAs for buttons. 2025-01-25 18:07:27 +00:00
7e8266d735 Fix: Run NPM build. 2025-01-25 16:59:47 +00:00
cc6c3699f6 Fix(UI): Contact form reCAPTCHA style correction for extreme zooming. 2025-01-25 16:55:19 +00:00
d84cc29edb Fix: Re-add requirements.txt after accidental delete in merging. 2025-01-25 16:27:13 +00:00
Teddy-1024
2fcd7f4276 Merge pull request #3 from Teddy-1024/oracle_vps
Feat(Security):  a. Update CORS settings  b. Update session cookie settings  c. Remove comments that expose project architecture (and that Claude made most of it :P) d. Remove server console logging from root server setup.
2025-01-25 16:10:02 +00:00
56ed26b3f0 Merge conflicts. 2025-01-25 16:07:58 +00:00
18a9a65f70 Merge conflicts. 2025-01-25 16:06:55 +00:00
Teddy-1024
d07f409426 Merge conflic: Update app.py 2025-01-25 16:05:07 +00:00
Teddy-1024
2461aef77b Merge pull request #2 from Teddy-1024/root_server
Initial commit. Functional draft content - requires further update for automotive speciality.
2025-01-25 15:53:24 +00:00
baa158fcd0 Feat(Security):
a. Update CORS settings
 b. Update session cookie settings
 c. Remove comments that expose project architecture (and that Claude made most of it :P)
2025-01-25 14:31:03 +00:00
2f63d27c5b Initial commit. Functional draft content - requires further update for automotive speciality. 2025-01-17 05:54:06 +01:00
e7231cb67a Clean up of table structure for Contact Us form. 2025-01-15 23:55:14 +00:00
d1b90db6d7 New website focusing on ERP services. 2025-01-15 23:52:09 +00:00
120cccd0d5 Fix (MySQL): Update for MariaDB v10.3 to match ERPNext installation on Oracle VPS. 2024-12-23 23:47:09 +00:00
2364931054 Fix (MySQL): Update for MariaDB v10.3 to match ERPNext installation on Oracle VPS. 2024-12-23 23:10:16 +00:00
decab6a638 Fix (MySQL): Update for MySQL version 8.0.40-0ubuntu0.20.04.1 on Oracle VPS 2024-12-23 19:37:22 +00:00
466 changed files with 13415 additions and 28469 deletions

53
app.py
View File

@@ -19,17 +19,8 @@ Initializes the Flask application, sets the configuration based on the environme
from config import app_config, Config
from controllers.core import routes_core
from controllers.legal import routes_legal
from controllers.store.store import routes_store
from controllers.store.manufacturing_purchase_order import routes_store_manufacturing_purchase_order
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.product_variation import routes_store_product_variation
from controllers.store.stock_item import routes_store_stock_item
from controllers.store.supplier import routes_store_supplier
from controllers.store.supplier_purchase_order import routes_store_supplier_purchase_order
from controllers.user import routes_user
from extensions import db, csrf, cors, mail, oauth
from extensions import db, csrf, mail, oauth
from helpers.helper_app import Helper_App
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session
@@ -50,12 +41,20 @@ sys.path.insert(0, os.path.dirname(__file__)) # Todo: why?
app = Flask(__name__)
app.secret_key = os.getenv('KEY_SECRET_FLASK')
# AppConfig(app)
app.config.from_object(app_config) # for db init with required keys
app.app_config = app_config
# app.config["config"] = app_config()
print('sql vars')
print(app.config['DB_PASSWORD'])
print(app.config['DB_USER'])
print(app.config['SQLALCHEMY_DATABASE_URI'])
print(app.config['SECRET_KEY'])
print(os.getenv('KEY_SECRET_FLASK'))
# logging
handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=3)
handler.setLevel(logging.DEBUG)
@@ -82,12 +81,13 @@ def make_session_permanent():
session.permanent = True
csrf = CSRFProtect()
"""
cors = CORS()
db = SQLAlchemy()
mail = Mail()
oauth = OAuth()
"""
cors = CORS(app, resources={
r"/static/*": {
"origins": [app.config["URL_HOST"]],
"methods": ["GET"],
"max_age": 3600
}
})
csrf.init_app(app)
cors.init_app(app)
@@ -116,15 +116,6 @@ with app.app_context():
app.register_blueprint(routes_core)
app.register_blueprint(routes_legal)
app.register_blueprint(routes_store)
app.register_blueprint(routes_store_manufacturing_purchase_order)
app.register_blueprint(routes_store_product)
app.register_blueprint(routes_store_product_category)
app.register_blueprint(routes_store_product_permutation)
app.register_blueprint(routes_store_product_variation)
app.register_blueprint(routes_store_stock_item)
app.register_blueprint(routes_store_supplier)
app.register_blueprint(routes_store_supplier_purchase_order)
app.register_blueprint(routes_user)
@@ -132,4 +123,14 @@ app.register_blueprint(routes_user)
@app.template_filter('console_log')
def console_log(value):
Helper_App.console_log(value)
return value
return value
@app.after_request
def add_cache_headers(response):
if request.path.startswith('/static/'):
# Cache static assets
response.headers['Cache-Control'] = 'public, max-age=31536000'
else:
# No caching for dynamic content
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
return response

View File

@@ -12,7 +12,7 @@ Business object for product
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from business_objects.base import Base
from extensions import db
from helpers.helper_app import Helper_App
# external
@@ -20,9 +20,9 @@ from pydantic import BaseModel
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
class Access_Level(db.Model, Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_ACCESS_LEVEL
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME
__tablename__ = 'Shop_Access_Level_Temp'
id_access_level = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
@@ -35,7 +35,7 @@ class Access_Level(db.Model, Store_Base):
created_by = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
Base.__init__(self)
@classmethod
def from_DB_access_level(cls, query_row):
access_level = cls()
@@ -83,4 +83,4 @@ class Access_Level(db.Model, Store_Base):
access_level.description = json[cls.FLAG_DESCRIPTION],
access_level.display_order = json[cls.FLAG_DISPLAY_ORDER]
access_level.active = json[cls.FLAG_ACTIVE]
return access_level
return access_level

View File

@@ -1,184 +0,0 @@
"""
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.store.product import Product #, Parameters_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
# from forms import Form_Basket_Add, Form_Basket_Edit # possibly circular
from helpers.helper_app import Helper_App
# 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 from_product_and_quantity_and_VAT_included(product, quantity, is_included_VAT):
# Initialiser - validation
_m = 'Basket_Item.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_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Basket_Item.add_product_price_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(Store_Base):
KEY_BASKET: str = 'basket'
KEY_ID_CURRENCY: str = 'id_currency'
KEY_ID_REGION_DELIVERY: str = 'id_region_delivery'
KEY_IS_INCLUDED_VAT: str = 'is_included_VAT'
KEY_ITEMS: str = 'items'
items: list
is_included_VAT: bool
id_region_delivery: int
id_currency: int
def __init__(self, is_included_VAT, id_currency, id_region_delivery):
self.items = []
self.is_included_VAT = is_included_VAT
self.id_currency = id_currency
self.id_region_delivery = id_region_delivery
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)
Helper_App.console_log(f'ids_permutation_basket = {ids_permutation}')
Helper_App.console_log(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 {
**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,
Basket.KEY_ID_REGION_DELIVERY: self.id_region_delivery
}
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:
Helper_App.console_log(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

@@ -1,98 +0,0 @@
"""
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
"""
# internal
from extensions import db
# CLASSES
"""
class Delivery_Option():
name: str
delay_min: int # days
delay_max: int
quantity_min: float
quantity_max: float
regions: list # [Enum_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_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 from_DB_get_many_product_catalogue(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

@@ -1,75 +0,0 @@
"""
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
"""
# internal
from extensions import db
# 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 from_DB_get_many_product_catalogue(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}
'''

View File

@@ -1,131 +0,0 @@
"""
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
"""
# internal
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from enum import Enum
from typing import ClassVar
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, Store_Base):
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'url'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'id_image'
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 from_DB_get_many_product_catalogue(query_row):
_m = 'Image.from_DB_get_many_product_catalogue'
# Helper_App.console_log(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}
'''
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():
product_id: int
get_thumbnail: bool
get_remaining_LQ: bool
def __new__(cls, product_id, get_thumbnail, get_remaining_LQ):
# Initialiser - validation
_m = 'Parameters_Product.__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

View File

@@ -1,354 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Manufacturing_Purchase_Order Business Object
Description:
Business object for manufacturing_purchase_order
"""
# internal
import lib.argument_validation as av
from business_objects.currency import Currency
from business_objects.db_base import Get_Many_Parameters_Base
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from pydantic import BaseModel
from typing import ClassVar, Optional
from datetime import datetime
class Manufacturing_Purchase_Order(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_MANUFACTURING_PURCHASE_ORDER
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
# __tablename__ = 'Shop_Manufacturing_Purchase_Order_Temp'
id_order = db.Column(db.Integer, primary_key=True)
id_currency = db.Column(db.Integer)
cost_total_local_VAT_excl = db.Column(db.Float)
cost_total_local_VAT_incl = db.Column(db.Float)
price_total_local_VAT_excl = db.Column(db.Float)
price_total_local_VAT_incl = db.Column(db.Float)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
name = db.Column(db.String(255))
# items: list = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.items = []
self.currency = None
@classmethod
def from_DB_manufacturing_purchase_order(cls, query_row):
manufacturing_purchase_order = cls()
manufacturing_purchase_order.id_order = query_row[0]
manufacturing_purchase_order.id_currency = query_row[1]
manufacturing_purchase_order.currency = Currency.from_DB_manufacturing_purchase_order(query_row)
manufacturing_purchase_order.cost_total_local_VAT_excl = query_row[4]
manufacturing_purchase_order.cost_total_local_VAT_incl = query_row[5]
manufacturing_purchase_order.price_total_local_VAT_excl = query_row[6]
manufacturing_purchase_order.price_total_local_VAT_incl = query_row[7]
manufacturing_purchase_order.active = av.input_bool(query_row[8], 'active', f'{cls.__name__}.from_DB_manufacturing_purchase_order')
manufacturing_purchase_order.created_on = query_row[9]
manufacturing_purchase_order.name = query_row[10]
return manufacturing_purchase_order
def __repr__(self):
return f'''
{self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER}: {self.id_order},
{self.ATTR_ID_CURRENCY}: {self.id_currency},
{self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL}: {self.cost_total_local_VAT_excl},
{self.FLAG_COST_TOTAL_LOCAL_VAT_INCL}: {self.cost_total_local_VAT_incl},
{self.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL}: {self.price_total_local_VAT_excl},
{self.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL}: {self.price_total_local_VAT_incl},
{self.FLAG_ACTIVE}: {self.active},
{self.FLAG_CREATED_ON}: {self.created_on},
{self.FLAG_NAME}: {self.name}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER: self.id_order,
self.ATTR_ID_CURRENCY: self.id_currency,
self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL: self.cost_total_local_VAT_excl,
self.FLAG_COST_TOTAL_LOCAL_VAT_INCL: self.cost_total_local_VAT_incl,
self.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL: self.price_total_local_VAT_excl,
self.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL: self.price_total_local_VAT_incl,
self.FLAG_ORDER_ITEMS: [item.to_json() for item in self.items],
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
self.FLAG_CREATED_ON: self.created_on,
self.FLAG_NAME: self.name,
}
def to_json_option(self):
return {
'value': self.id_order,
'text': self.name,
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
manufacturing_purchase_order = cls()
manufacturing_purchase_order.id_order = json[cls.ATTR_ID_MANUFACTURING_PURCHASE_ORDER]
manufacturing_purchase_order.id_currency = json[cls.ATTR_ID_CURRENCY]
manufacturing_purchase_order.cost_total_local_VAT_excl = json.get(cls.FLAG_COST_TOTAL_LOCAL_VAT_EXCL, None)
manufacturing_purchase_order.cost_total_local_VAT_incl = json.get(cls.FLAG_COST_TOTAL_LOCAL_VAT_INCL, None)
manufacturing_purchase_order.price_total_local_VAT_excl = json.get(cls.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL, None)
manufacturing_purchase_order.price_total_local_VAT_incl = json.get(cls.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL, None)
manufacturing_purchase_order.items = [Manufacturing_Purchase_Order_Product_Link.from_json(item) for item in json[cls.FLAG_ORDER_ITEMS]]
manufacturing_purchase_order.active = json[cls.FLAG_ACTIVE]
manufacturing_purchase_order.created_on = json.get(cls.FLAG_CREATED_ON, None)
manufacturing_purchase_order.name = json.get(cls.FLAG_NAME, None)
return manufacturing_purchase_order
def get_items_preview_str(self):
preview = ''
if self.items is not None:
for item in self.items:
if preview != '':
preview += '\n'
preview += f'{item.name_permutation}{f" -(x{item.quantity_used})" if item.quantity_used > 0 else ""}{f" +(x{item.quantity_produced})" if item.quantity_produced > 0 else ""}'
return preview
class Manufacturing_Purchase_Order_Product_Link(db.Model, Store_Base):
FLAG_QUANTITY_PRODUCED: ClassVar[str] = 'quantity_produced'
FLAG_QUANTITY_USED: ClassVar[str] = 'quantity_used'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_MANUFACTURING_PURCHASE_ORDER_PRODUCT_LINK
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
# __tablename__ = 'Shop_Manufacturing_Purchase_Order_Temp'
id_link = db.Column(db.Integer, primary_key=True)
id_order = db.Column(db.Integer)
id_category = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
id_unit_quantity = db.Column(db.Integer)
name_permutation = db.Column(db.String(255))
csv_id_pairs_variation = db.Column(db.String)
quantity_used = db.Column(db.Float)
quantity_produced = db.Column(db.Float)
id_unit_latency_manufacture = db.Column(db.Integer)
latency_manufacture = db.Column(db.Integer)
display_order = db.Column(db.Integer)
cost_unit_local_VAT_excl = db.Column(db.Float)
cost_unit_local_VAT_incl = db.Column(db.Float)
price_unit_local_VAT_excl = db.Column(db.Float)
price_unit_local_VAT_incl = db.Column(db.Float)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
@classmethod
def from_DB_manufacturing_purchase_order(cls, query_row):
link = cls()
link.id_link = query_row[0]
link.id_order = query_row[1]
link.id_category = query_row[2]
link.id_product = query_row[3]
link.id_permutation = query_row[4]
link.name_permutation = query_row[5]
link.csv_id_pairs_variation = query_row[6]
link.id_unit_quantity = query_row[7]
link.quantity_used = query_row[8]
link.quantity_produced = query_row[9]
link.id_unit_latency_manufacture = query_row[10]
link.latency_manufacture = query_row[11]
link.display_order = query_row[12]
link.cost_unit_local_VAT_excl = query_row[13]
link.cost_unit_local_VAT_incl = query_row[14]
link.price_unit_local_VAT_excl = query_row[15]
link.price_unit_local_VAT_incl = query_row[16]
link.active = query_row[17]
return link
def __repr__(self):
return f'''
{self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER_PRODUCT_LINK}: {self.id_link},
{self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER}: {self.id_order},
{self.ATTR_ID_PRODUCT_CATEGORY}: {self.id_category},
{self.ATTR_ID_PRODUCT}: {self.id_product},
{self.ATTR_ID_PRODUCT_PERMUTATION}: {self.id_permutation},
{self.FLAG_NAME}: {self.name_permutation},
{self.FLAG_PRODUCT_VARIATIONS}: {self.csv_id_pairs_variation},
{self.ATTR_ID_UNIT_MEASUREMENT_QUANTITY}: {self.id_unit_quantity},
{self.FLAG_QUANTITY_USED}: {self.quantity_used},
{self.FLAG_QUANTITY_PRODUCED}: {self.quantity_produced},
{self.ATTR_ID_UNIT_MEASUREMENT_LATENCY_MANUFACTURE}: {self.id_unit_latency_manufacture},
{self.FLAG_LATENCY_MANUFACTURE}: {self.latency_manufacture},
{self.FLAG_DISPLAY_ORDER}: {self.display_order},
{self.FLAG_COST_UNIT_LOCAL_VAT_EXCL}: {self.cost_unit_local_VAT_excl},
{self.FLAG_COST_UNIT_LOCAL_VAT_INCL}: {self.cost_unit_local_VAT_incl},
{self.FLAG_PRICE_UNIT_LOCAL_VAT_EXCL}: {self.price_unit_local_VAT_excl},
{self.FLAG_PRICE_UNIT_LOCAL_VAT_INCL}: {self.price_unit_local_VAT_incl},
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER_PRODUCT_LINK: self.id_link,
self.ATTR_ID_MANUFACTURING_PURCHASE_ORDER: self.id_order,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_PERMUTATION: self.id_permutation,
self.FLAG_NAME: self.name_permutation,
self.FLAG_PRODUCT_VARIATIONS: self.csv_id_pairs_variation,
self.ATTR_ID_UNIT_MEASUREMENT_QUANTITY: self.id_unit_quantity,
self.FLAG_QUANTITY_USED: self.quantity_used,
self.FLAG_QUANTITY_PRODUCED: self.quantity_produced,
self.ATTR_ID_UNIT_MEASUREMENT_LATENCY_MANUFACTURE: self.id_unit_latency_manufacture,
self.FLAG_LATENCY_MANUFACTURE: self.latency_manufacture,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_COST_UNIT_LOCAL_VAT_EXCL: self.cost_unit_local_VAT_excl,
self.FLAG_COST_UNIT_LOCAL_VAT_INCL: self.cost_unit_local_VAT_incl,
self.FLAG_PRICE_UNIT_LOCAL_VAT_EXCL: self.price_unit_local_VAT_excl,
self.FLAG_PRICE_UNIT_LOCAL_VAT_INCL: self.price_unit_local_VAT_incl,
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
}
def to_json_option(self):
return {
'value': self.id_order,
'text': self.name_permutation,
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
link = cls()
link.id_link = json[cls.ATTR_ID_MANUFACTURING_PURCHASE_ORDER_PRODUCT_LINK]
link.id_order = json[cls.ATTR_ID_MANUFACTURING_PURCHASE_ORDER]
link.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
link.id_product = json[cls.ATTR_ID_PRODUCT]
link.id_permutation = json.get(cls.ATTR_ID_PRODUCT_PERMUTATION, None)
link.name_permutation = json.get(cls.FLAG_NAME, None)
link.csv_id_pairs_variation = json[cls.FLAG_PRODUCT_VARIATIONS]
link.id_unit_quantity = json[cls.ATTR_ID_UNIT_MEASUREMENT_QUANTITY]
link.quantity_used = json[cls.FLAG_QUANTITY_USED]
link.quantity_produced = json[cls.FLAG_QUANTITY_PRODUCED]
link.id_unit_latency_manufacture = json[cls.ATTR_ID_UNIT_MEASUREMENT_LATENCY_MANUFACTURE]
link.latency_manufacture = json[cls.FLAG_LATENCY_MANUFACTURE]
link.display_order = json[cls.FLAG_DISPLAY_ORDER]
link.cost_unit_local_VAT_excl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_EXCL, None)
link.cost_unit_local_VAT_incl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_INCL, None)
link.active = json[cls.FLAG_ACTIVE]
return link
class Parameters_Manufacturing_Purchase_Order(Get_Many_Parameters_Base):
a_get_all_order: bool
a_get_inactive_order: bool
a_ids_order: str
a_ids_permutation: str
a_date_from: Optional[datetime]
a_date_to: Optional[datetime]
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_default(cls):
return cls(
a_get_all_order = True,
a_get_inactive_order = False,
a_ids_order = '',
a_ids_permutation = '',
a_date_from = None,
a_date_to = None
)
@classmethod
def from_filters_manufacturing_purchase_order(cls, form):
parameters = cls.get_default()
parameters.a_get_inactive_order = form.active.data
parameters.a_date_from = form.date_from.data
parameters.a_date_to = form.date_to.data
return parameters
class Manufacturing_Purchase_Order_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Manufacturing_Purchase_Order_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_order: int = db.Column(db.Integer)
id_currency: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_manufacturing_purchase_order(cls, manufacturing_purchase_order):
row = cls()
row.id_order = manufacturing_purchase_order.id_order
row.id_currency = manufacturing_purchase_order.id_currency
row.active = 1 if manufacturing_purchase_order.active else 0
return row
def __repr__(self):
return f'''
id_order: {self.id_order}
id_currency: {self.id_currency}
active: {self.active}
guid: {self.guid}
'''
class Manufacturing_Purchase_Order_Product_Link_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Manufacturing_Purchase_Order_Product_Link_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_link: int = db.Column(db.Integer)
id_order: int = db.Column(db.Integer)
# id_category: int = db.Column(db.Integer)
id_product: int = db.Column(db.Integer)
id_permutation: int = db.Column(db.Integer)
csv_list_variations: str = db.Column(db.String)
id_unit_quantity: int = db.Column(db.Integer)
quantity_used: float = db.Column(db.Float)
quantity_produced: float = db.Column(db.Float)
id_unit_latency_manufacture = db.Column(db.Integer)
latency_manufacture: int = db.Column(db.Integer)
display_order: int = db.Column(db.Integer)
"""
cost_unit_local_VAT_excl: float = db.Column(db.Float)
cost_unit_local_VAT_incl: float = db.Column(db.Float)
price_unit_local_VAT_excl: float = db.Column(db.Float)
price_unit_local_VAT_incl: float = db.Column(db.Float)
"""
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_manufacturing_purchase_order_product_link(cls, manufacturing_purchase_order_product_link):
row = cls()
row.id_link = manufacturing_purchase_order_product_link.id_link
row.id_order = manufacturing_purchase_order_product_link.id_order
# row.id_category = manufacturing_purchase_order_product_link.id_category
row.id_product = manufacturing_purchase_order_product_link.id_product
row.id_permutation = manufacturing_purchase_order_product_link.id_permutation
row.csv_list_variations = manufacturing_purchase_order_product_link.csv_id_pairs_variation
row.id_unit_quantity = manufacturing_purchase_order_product_link.id_unit_quantity
row.quantity_used = manufacturing_purchase_order_product_link.quantity_used
row.quantity_produced = manufacturing_purchase_order_product_link.quantity_produced
row.id_unit_latency_manufacture = manufacturing_purchase_order_product_link.id_unit_latency_manufacture
row.latency_manufacture = manufacturing_purchase_order_product_link.latency_manufacture
row.display_order = manufacturing_purchase_order_product_link.display_order
"""
row.cost_unit_local_VAT_excl = manufacturing_purchase_order_product_link.cost_unit_local_VAT_excl
row.cost_unit_local_VAT_incl = manufacturing_purchase_order_product_link.cost_unit_local_VAT_incl
row.price_unit_local_VAT_excl = manufacturing_purchase_order_product_link.price_unit_local_VAT_excl
row.price_unit_local_VAT_incl = manufacturing_purchase_order_product_link.price_unit_local_VAT_incl
"""
row.active = 1 if manufacturing_purchase_order_product_link.active else 0
return row
def __repr__(self):
return f'''
id_link: {self.id_link}
id_order: {self.id_order}
id_product: {self.id_product}
id_permutation: {self.id_permutation}
csv_list_variations: {self.csv_list_variations}
id_unit_quantity: {self.id_unit_quantity}
quantity_used: {self.quantity_used}
quantity_produced: {self.quantity_produced}
{self.ATTR_ID_UNIT_MEASUREMENT_LATENCY_MANUFACTURE}: {self.id_unit_latency_manufacture}
latency_manufacture: {self.latency_manufacture}
display_order: {self.display_order}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -1,89 +0,0 @@
"""
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
"""
# internal
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
# from enum import Enum
from flask import jsonify
import locale
# VARIABLE INSTANTIATION
# CLASSES
class Order(Store_Base):
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 {
**self.get_shared_json_attributes(self),
'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}
'''

View File

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

View File

@@ -1,917 +0,0 @@
"""
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
"""
# internal
import lib.argument_validation as av
from lib import data_types
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
from business_objects.store.image import Image
from business_objects.store.product_permutation import Product_Permutation
from business_objects.store.product_price import Product_Price
from business_objects.store.store_base import Store_Base
from business_objects.store.stock_item import Stock_Item
from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_tree import Product_Variation_Tree
from extensions import db
from forms.base import Form_Base
from forms.store.product import Filters_Product
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar, List
"""
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 Product(SQLAlchemy_ABC, Store_Base):
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_PRODUCT_VARIATION_TREES: ClassVar[str] = 'variation-trees'
id_product = db.Column(db.Integer, primary_key=True)
id_category = db.Column(db.Integer)
name = db.Column(db.String(255))
id_access_level_required = db.Column(db.Integer)
active = db.Column(db.Boolean)
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
"""
permutations: list = None
permutation_index: dict = None
variation_trees: list = None
"""
def __init__(self):
self.permutations = []
self.permutation_index = {}
self.variation_trees = []
self.index_permutation_selected = None
self.has_variations = False
super().__init__()
Store_Base.__init__(self)
self.form_basket_add = Form_Basket_Add()
self.form_basket_edit = Form_Basket_Edit()
self.name_access_level_required = None
def from_DB_get_many_product_catalogue(query_row):
_m = 'Product.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
product = Product()
product.id_product = query_row[0]
product.id_category = query_row[1]
product.name = query_row[2]
product.has_variations = av.input_bool(query_row[3], "has_variations", _m, v_arg_type=v_arg_type)
product.id_access_level_required = query_row[4]
product.name_access_level_required = query_row[5]
product.active = av.input_bool(query_row[6], "active", _m, v_arg_type=v_arg_type)
product.display_order = query_row[7]
product.can_view = av.input_bool(query_row[8], "can_view", _m, v_arg_type=v_arg_type)
product.can_edit = av.input_bool(query_row[9], "can_edit", _m, v_arg_type=v_arg_type)
product.can_admin = av.input_bool(query_row[10], "can_admin", _m, v_arg_type=v_arg_type)
return product
"""
def from_permutation(permutation, has_variations = False):
_m = 'Product.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_product_permutation(self, permutation):
_m = 'Product.add_product_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]
Helper_App.console_log(f'setting selected permutation for product {self.id_product} to {self.index_permutation_selected}') # :\n{self.permutations[self.index_permutation_selected]}
"""
def from_permutations(permutations):
_m = 'Product.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):
self.variation_trees = []
for index_permutation in range(len(self.permutations)):
permutation = self.permutations[index_permutation]
variation_tree = permutation.variation_tree # Product_Variation_Tree.from_product_permutation(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 and variation_tree is not None:
self.variation_trees.append(variation_tree)
def from_DB_Stripe_product(query_row):
permutation = Product_Permutation.from_DB_Stripe_product(query_row)
product = Product.from_permutation(permutation)
return product
def from_DB_Stripe_price(query_row):
permutation = Product_Permutation.from_DB_Stripe_price(query_row)
product = Product.from_permutation(permutation)
return product
"""
def from_json(json_basket_item, key_id_product, key_id_permutation):
permutation = Product_Permutation.from_json(json_basket_item, key_id_product, key_id_permutation)
product = Product.from_permutation(permutation)
return product
"""
@classmethod
def from_json(cls, json):
product = cls()
product.id_product = json[cls.ATTR_ID_PRODUCT]
product.display_order = json[cls.FLAG_DISPLAY_ORDER]
product.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
product.name = json[cls.FLAG_NAME]
product.has_variations = av.input_bool(json.get(cls.FLAG_HAS_VARIATIONS, None), cls.FLAG_HAS_VARIATIONS, f'{cls.__name__}.from_json')
product.id_access_level_required = json.get(cls.ATTR_ID_ACCESS_LEVEL, None)
product.active = 1 if av.input_bool(json[cls.FLAG_ACTIVE], cls.FLAG_ACTIVE, f'{cls.__name__}.from_json') else 0
product.can_view = json.get(cls.FLAG_CAN_VIEW, None)
product.can_edit = json.get(cls.FLAG_CAN_EDIT, None)
product.can_admin = json.get(cls.FLAG_CAN_ADMIN, None)
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}
active: {self.active}
'''
"""
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_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Product.add_product_variation', Product_Variation)
# Helper_App.console_log(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_product_variation(variation)
"""
def add_product_variation_type(self, variation_type):
variation = variation_type.variations[0]
index_permutation = self.permutation_index[variation.id_permutation]
self.permutations[index_permutation].add_product_variation_type(variation_type)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Product.add_product_price', Product_Price)
index_permutation = self.permutation_index[price.id_permutation] # self.get_index_permutation_from_id(price.id_permutation)
self.permutations[index_permutation].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Product.add_product_image', Image)
index_permutation = self.permutation_index[image.id_permutation] # self.get_index_permutation_from_id(image.id_permutation)
self.permutations[index_permutation].add_product_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_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Product.add_product_price_discount', Discount)
index_permutation = self.permutation_index[discount.id_permutation] # self.get_index_permutation_from_id(discount.id_permutation)
self.permutations[index_permutation].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Product.add_stock_item', Stock_Item)
index_permutation = self.permutation_index[stock_item.id_permutation]
self.permutations[index_permutation].add_stock_item(stock_item)
def has_permutations(self):
return len(self.permutations) > 0
def is_available(self):
if len(self.permutations) == 0:
return False
for permutation in self.permutations:
if permutation.is_available():
return True
return False
def to_permutation_row_list(self):
list_rows = []
for permutation in self.permutations:
list_rows.append(permutation.to_row_permutation())
return list_rows
"""
@classmethod
def from_json(cls, json):
product = cls()
product.id_product = json[cls.ATTR_ID_PRODUCT]
product.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
product.name = json[cls.FLAG_NAME]
product.display_order = json[cls.FLAG_DISPLAY_ORDER]
product.can_view = json[cls.FLAG_CAN_VIEW]
product.can_edit = json[cls.FLAG_CAN_EDIT]
product.can_admin = json[cls.FLAG_CAN_ADMIN]
product.has_variations = json[cls.FLAG_HAS_VARIATIONS]
product.index_permutation_selected = json[cls.FLAG_INDEX_PERMUTATION_SELECTED]
product.permutations = []
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_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,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_CAN_VIEW: self.can_view,
self.FLAG_CAN_EDIT: self.can_edit,
self.FLAG_CAN_ADMIN: self.can_admin,
self.FLAG_HAS_VARIATIONS: self.has_variations,
self.FLAG_INDEX_PERMUTATION_SELECTED: self.index_permutation_selected,
self.ATTR_ID_PRODUCT_PERMUTATION: [permutation.to_json() for permutation in self.permutations],
self.FLAG_PRODUCT_VARIATION_TREES: [tree.to_json() for tree in self.variation_trees]
}
def to_json_option(self):
return {
'value': self.id_product,
'text': self.name
}
def get_variation_types_unique(self):
list_types = []
for tree in self.variation_trees:
for type in tree.get_types_unique():
if type not in list_types:
list_types.append(type)
return list_types
"""
def get_json_str_types_variation_trees(self):
json_str = ''
for tree in self.variation_trees:
if json_str != '':
json_str += '\n'
json_str += tree.get_json_str_types()
return json_str
def get_text_input_variation_types(self):
text_input = ''
for tree in self.variation_trees:
if text_input != '':
text_input += '\n'
text_input += tree.get_text_input_types()
return text_input
"""
def get_csv_ids_permutation(self):
csv = ''
for permutation in self.permutations:
if csv != '':
csv += ','
csv += str(permutation.id_permutation)
return csv
class Parameters_Product(Get_Many_Parameters_Base):
# id_user: str
get_all_product_category: bool
get_inactive_product_category: bool
# get_first_product_category_only: bool
ids_product_category: str
get_all_product: bool
get_inactive_product: bool
# get_first_product_only: bool
ids_product: str
get_all_permutation: bool
get_inactive_permutation: bool
# get_first_permutation_only: bool
ids_permutation: str
get_all_image: bool
get_inactive_image: bool
# get_first_image_only: bool
ids_image: str
"""
get_all_region: bool
get_inactive_region: bool
get_first_region_only: bool
ids_region: str
get_all_currency: bool
get_inactive_currency: bool
get_first_currency_only: bool
ids_currency: str
get_all_discount: bool
get_inactive_discount: bool
ids_discount: str
"""
get_products_quantity_stock_below_min: bool
def to_json(self):
return {
# 'a_id_user': None,
'a_get_all_product_category': self.get_all_product_category,
'a_get_inactive_product_category': self.get_inactive_product_category,
# 'a_get_first_product_category_only': self.get_first_product_category_only,
'a_ids_product_category': self.ids_product_category,
'a_get_all_product': self.get_all_product,
'a_get_inactive_product': self.get_inactive_product,
# 'a_get_first_product_only': self.get_first_product_only,
'a_ids_product': self.ids_product,
'a_get_all_permutation': self.get_all_permutation,
'a_get_inactive_permutation': self.get_inactive_permutation,
# 'a_get_first_permutation_only': self.get_first_permutation_only,
'a_ids_permutation': self.ids_permutation,
'a_get_all_image': self.get_all_image,
'a_get_inactive_image': self.get_inactive_image,
# 'a_get_first_image_only': self.get_first_image_only,
'a_ids_image': self.ids_image,
# 'a_get_all_delivery_region': self.get_all_region,
# 'a_get_inactive_delivery_region': self.get_inactive_region,
# 'a_get_first_delivery_region_only': self.get_first_region_only,
# 'a_ids_delivery_region': self.ids_region,
# 'a_get_all_currency': self.get_all_currency,
# 'a_get_inactive_currency': self.get_inactive_currency,
# 'a_get_first_currency_only': self.get_first_currency_only,
# 'a_ids_currency': self.ids_currency,
# 'a_get_all_discount': self.get_all_discount,
# 'a_get_inactive_discount': self.get_inactive_discount,
# 'a_ids_discount': self.ids_discount,
'a_get_products_quantity_stock_below_min': self.get_products_quantity_stock_below_min
}
@staticmethod
def from_form_filters_product(form):
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Parameters_Product.from_form', Filters_Product)
has_filter_category = not (form.id_category.data == '0' or form.id_category.data == '' or form.id_category.data is None)
is_not_empty = av.input_bool(form.is_not_empty.data, "is_not_empty", "Parameters_Product.from_form_filters_product")
active = av.input_bool(form.active.data, "active", "Parameters_Product.from_form_filters_product")
return Parameters_Product(
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 if form.id_category.data is not None else '',
get_all_product = True,
get_inactive_product = not active,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = not active,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = False
)
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
# av.val_instance(form, 'form', 'Parameters_Product.from_form', Filters_Product_Permutation)
has_category_filter = not (form.id_category.data == '0' or form.id_category.data == '' or form.id_category.data is None)
has_product_filter = not (form.id_product.data == '0' or form.id_product.data == '' or form.id_product.data is None)
get_permutations_stock_below_min = av.input_bool(form.is_out_of_stock.data, "is_out_of_stock", "Parameters_Product.from_form_filters_product_permutation")
get_inactive = not av.input_bool(form.active.data, "active", "Parameters_Product.from_form_filters_product_permutation")
Helper_App.console_log(f'form question: {type(form.is_out_of_stock)}\nbool interpretted: {get_permutations_stock_below_min}\nform question: {type(form.active)}\nget_inactive:{get_inactive}\ntype form: {type(form)}')
return Parameters_Product(
get_all_product_category = not has_category_filter,
get_inactive_product_category = get_inactive,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data if form.id_category.data is not None else '',
get_all_product = not has_product_filter,
get_inactive_product = get_inactive,
# get_first_product_only = False,
ids_product = form.id_product.data if form.id_product.data is not None else '',
get_all_permutation = not get_permutations_stock_below_min,
get_inactive_permutation = get_inactive,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = get_permutations_stock_below_min
)
@staticmethod
def get_default():
return Parameters_Product(
get_all_product_category = True,
get_inactive_product_category = False,
# get_first_product_category_only = False,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = True,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = True,
# et_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = True,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = True,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = True
)
@classmethod
def from_json(cls, json):
return cls(
get_all_product_category = json.get('a_get_all_product_category', False),
get_inactive_product_category = json.get('a_get_inactive_product_category', False),
# get_first_product_category_only = json.get('a_get_first_product_category_only', False),
ids_product_category = json.get('a_ids_product_category', ''),
get_all_product = json.get('a_get_all_product', False),
get_inactive_product = json.get('a_get_inactive_product', False),
# get_first_product_only = json.get('a_get_first_product_only', False),
ids_product = json.get('a_ids_product', ''),
get_all_permutation = json.get('a_get_all_permutation', False),
get_inactive_permutation = json.get('a_get_inactive_permutation', False),
# get_first_permutation_only = json.get('a_get_first_permutation_only', False),
ids_permutation = json.get('a_ids_permutation', ''),
get_all_image = json.get('a_get_all_image', False),
get_inactive_image = json.get('a_get_inactive_image', False),
# get_first_image_only = json.get('a_get_first_image_only', False),
ids_image = json.get('a_ids_image', ''),
# get_all_region = json.get('a_get_all_region', False),
# get_inactive_region = json.get('a_get_inactive_region', False),
# get_first_region_only = json.get('a_get_first_region_only', False),
# ids_region = json.get('a_ids_region', ''),
# get_all_currency = json.get('a_get_all_currency', False),
# get_inactive_currency = json.get('a_get_inactive_currency', False),
# get_first_currency_only = json.get('a_get_first_currency_only', False),
# ids_currency = json.get('a_ids_currency', ''),
# get_all_discount = json.get('a_get_all_discount', False),
# get_inactive_discount = json.get('a_get_inactive_discount', False),
# ids_discount = json.get('a_ids_discount', ''),
get_products_quantity_stock_below_min = json.get('a_get_products_quantity_stock_below_min', False)
)
@classmethod
def from_filters_product_category(cls, filters_category):
return cls(
get_all_product_category = True,
get_inactive_product_category = not filters_category.active.data,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
ids_image = '',
get_products_quantity_stock_below_min = False
)
@classmethod
def from_filters_stock_item(cls, filters_stock_item):
return cls.from_form_filters_product_permutation(filters_stock_item)
"""
class Parameters_Product(Get_Many_Parameters_Base):
# id_user: str
get_all_product_category: bool
get_inactive_product_category: bool
# get_first_product_category_only: bool
ids_product_category: str
get_all_product: bool
get_inactive_product: bool
# get_first_product_only: bool
ids_product: str
get_all_permutation: bool
get_inactive_permutation: bool
# get_first_permutation_only: bool
ids_permutation: str
get_all_image: bool
get_inactive_image: bool
# get_first_image_only: bool
ids_image: str
""
get_all_region: bool
get_inactive_region: bool
get_first_region_only: bool
ids_region: str
get_all_currency: bool
get_inactive_currency: bool
get_first_currency_only: bool
ids_currency: str
get_all_discount: bool
get_inactive_discount: bool
ids_discount: str
""
get_products_quantity_stock_below_min: bool
def __init__(self, a_id_user, **kwargs):
super().__init__(a_id_user, **kwargs)
def to_json(self):
return {
'a_id_user': self.a_id_user,
'a_get_all_product_category': self.get_all_product_category,
'a_get_inactive_product_category': self.get_inactive_product_category,
# 'a_get_first_product_category_only': self.get_first_product_category_only,
'a_ids_product_category': self.ids_product_category,
'a_get_all_product': self.get_all_product,
'a_get_inactive_product': self.get_inactive_product,
# 'a_get_first_product_only': self.get_first_product_only,
'a_ids_product': self.ids_product,
'a_get_all_permutation': self.get_all_permutation,
'a_get_inactive_permutation': self.get_inactive_permutation,
# 'a_get_first_permutation_only': self.get_first_permutation_only,
'a_ids_permutation': self.ids_permutation,
'a_get_all_image': self.get_all_image,
'a_get_inactive_image': self.get_inactive_image,
# 'a_get_first_image_only': self.get_first_image_only,
'a_ids_image': self.ids_image,
# 'a_get_all_delivery_region': self.get_all_region,
# 'a_get_inactive_delivery_region': self.get_inactive_region,
# 'a_get_first_delivery_region_only': self.get_first_region_only,
# 'a_ids_delivery_region': self.ids_region,
# 'a_get_all_currency': self.get_all_currency,
# 'a_get_inactive_currency': self.get_inactive_currency,
# 'a_get_first_currency_only': self.get_first_currency_only,
# 'a_ids_currency': self.ids_currency,
# 'a_get_all_discount': self.get_all_discount,
# 'a_get_inactive_discount': self.get_inactive_discount,
# 'a_ids_discount': self.ids_discount,
'a_get_products_quantity_stock_below_min': self.get_products_quantity_stock_below_min
}
@staticmethod
def from_form_filters_product(form, id_user):
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Parameters_Product.from_form', Filters_Product)
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", "Parameters_Product.from_form_filters_product")
active = av.input_bool(form.active.data, "active", "Parameters_Product.from_form_filters_product")
return Parameters_Product(
a_id_user = id_user,
get_all_product_category = not has_filter_category,
get_inactive_product_category = not active,
# get_first_product_category_only = False,
ids_product_category = str(form.id_category.data),
get_all_product = True,
get_inactive_product = not active,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = not active,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = False
)
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Filters_Product_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
# av.val_instance(form, 'form', 'Parameters_Product.from_form', Form_Base)
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", "Parameters_Product.from_form")
Helper_App.console_log(f'form question: {type(form.is_out_of_stock)}\nbool interpretted: {get_permutations_stock_below_min}\type form: {type(form)}')
return Parameters_Product(
get_all_product_category = not has_category_filter,
get_inactive_product_category = False,
# get_first_product_category_only = False,
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 = 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,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = get_permutations_stock_below_min
)
@classmethod
def get_default(cls, id_user):
return cls(
a_id_user = id_user,
get_all_product_category = True,
get_inactive_product_category = False,
# get_first_product_category_only = False,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = True,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = True,
# et_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = True,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = True,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = True
)
@classmethod
def from_json(cls, json):
return cls(
get_all_product_category = json.get('a_get_all_product_category', False),
get_inactive_product_category = json.get('a_get_inactive_product_category', False),
# get_first_product_category_only = json.get('a_get_first_product_category_only', False),
ids_product_category = json.get('a_ids_product_category', ''),
get_all_product = json.get('a_get_all_product', False),
get_inactive_product = json.get('a_get_inactive_product', False),
# get_first_product_only = json.get('a_get_first_product_only', False),
ids_product = json.get('a_ids_product', ''),
get_all_permutation = json.get('a_get_all_permutation', False),
get_inactive_permutation = json.get('a_get_inactive_permutation', False),
# get_first_permutation_only = json.get('a_get_first_permutation_only', False),
ids_permutation = json.get('a_ids_permutation', ''),
get_all_image = json.get('a_get_all_image', False),
get_inactive_image = json.get('a_get_inactive_image', False),
# get_first_image_only = json.get('a_get_first_image_only', False),
ids_image = json.get('a_ids_image', ''),
# get_all_region = json.get('a_get_all_region', False),
# get_inactive_region = json.get('a_get_inactive_region', False),
# get_first_region_only = json.get('a_get_first_region_only', False),
# ids_region = json.get('a_ids_region', ''),
# get_all_currency = json.get('a_get_all_currency', False),
# get_inactive_currency = json.get('a_get_inactive_currency', False),
# get_first_currency_only = json.get('a_get_first_currency_only', False),
# ids_currency = json.get('a_ids_currency', ''),
# get_all_discount = json.get('a_get_all_discount', False),
# get_inactive_discount = json.get('a_get_inactive_discount', False),
# ids_discount = json.get('a_ids_discount', ''),
get_products_quantity_stock_below_min = json.get('a_get_products_quantity_stock_below_min', False)
)
@classmethod
def from_filters_product_category(cls, filters_category):
return cls(
get_all_product_category = True,
get_inactive_product_category = filters_category.active.data,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
ids_image = '',
get_products_quantity_stock_below_min = False
)
@classmethod
def from_filters_stock_item(cls, filters_stock_item):
return cls.from_form_filters_product_permutation(filters_stock_item)
"""
class Product_Temp(db.Model, Store_Base):
__tablename__ = 'Shop_Product_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_product: int = db.Column(db.Integer)
id_category: int = db.Column(db.Integer)
name: str = db.Column(db.String(255))
has_variations: bool = db.Column(db.Boolean)
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)
def __init__(self):
self.id_temp = None
@classmethod
def from_product(cls, product):
row = cls()
row.id_product = product.id_product[0] if isinstance(product.id_product, tuple) else product.id_product
row.id_category = product.id_category[0] if isinstance(product.id_category, tuple) else product.id_category
row.name = product.name[0] if isinstance(product.name, tuple) else product.name
row.has_variations = product.has_variations
row.id_access_level_required = product.id_access_level_required[0] if isinstance(product.id_access_level_required, tuple) else product.id_access_level_required
row.active = product.active
row.display_order = product.display_order
"""
row.guid = product.guid
row.created_on = product.created_on
row.created_by = product.created_by
"""
return row
def to_json(self):
return {
'id_product': self.id_product,
'id_category': self.id_category,
'name': self.name,
'has_variations': av.input_bool(self.has_variations, self.FLAG_HAS_VARIATIONS, f'{self.__class__.__name__}.to_json'),
'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
"""
def __repr__(self):
return f'''Product_Temp
id_product: {self.id_product}
id_category: {self.id_category}
name: {self.name}
has_variations: {self.has_variations}
id_access_level_required: {self.id_access_level_required}
active: {self.active}
display_order: {self.display_order}
guid: {self.guid}
'''

View File

@@ -1,529 +0,0 @@
"""
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 category
"""
# internal
import lib.argument_validation as av
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from business_objects.store.product import Product, Product_Permutation, Product_Price
# from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_type import Product_Variation_Type
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.discount import Discount
from business_objects.store.stock_item import Stock_Item
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from pydantic import BaseModel
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'
id_category = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
description = db.Column(db.String(4000))
id_access_level_required = db.Column(db.Integer)
name_access_level_required = db.Column(db.String(255))
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
can_view = db.Column(db.Boolean)
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
"""
products: list = None # []
product_index: dict = None # {}
"""
def __init__(self):
self.products = []
self.product_index = {}
super().__init__()
Store_Base.__init__(self)
self.name_access_level_required = None
@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]
category.description = query_row[3]
category.id_access_level_required = query_row[4]
category.name_access_level_required = query_row[5]
category.display_order = query_row[6]
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):
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)
Helper_App.console_log(f'category: {self}')
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_product_permutation(self, permutation):
_m = 'Category.add_product_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_product_permutation(permutation)
"""
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Category.add_product_variation', Product_Variation)
index_product = self.get_index_product_from_id(variation.id_product)
self.products[index_product].add_product_variation(variation)
"""
def add_product_variation_type(self, variation_type):
av.val_instance(variation_type, 'variation_type', 'Category.add_product_variation_type', Product_Variation_Type)
variation = variation_type.variations[0]
index_product = self.get_index_product_from_id(variation.id_product)
self.products[index_product].add_product_variation_type(variation_type)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Category.add_product_price', Product_Price)
index_product = self.get_index_product_from_id(price.id_product)
self.products[index_product].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Category.add_product_image', Image)
index_product = self.get_index_product_from_id(image.id_product)
self.products[index_product].add_product_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_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Category.add_product_price_discount', Discount)
index_product = self.get_index_product_from_id(discount.id_product)
self.products[index_product].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Category.add_stock_item', Stock_Item)
index_product = self.get_index_product_from_id(stock_item.id_product)
self.products[index_product].add_stock_item(stock_item)
def get_all_product_variation_trees(self):
for product in self.products:
if product.has_variations:
Helper_App.console_log(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)
Helper_App.console_log(f'product_index: {self.product_index}')
Helper_App.console_log(f'Key Error: {key}')
try:
return self.product_index[key]
except KeyError:
pass
"""
def __repr__(self):
return f'''
id: {self.id_category[0] if isinstance(self.id_category, tuple) else self.id_category}
code: {self.code[0] if isinstance(self.code, tuple) else self.code}
name: {self.name[0] if isinstance(self.name, tuple) else self.name}
description: {self.description[0] if isinstance(self.description, tuple) else self.description}
access_level: {self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required}
display_order: {self.display_order}
active: {self.active}
products: {self.products}
'''
"""
def get_permutation_first(self):
if not (len(self.products) == 0):
Helper_App.console_log(f'getting first permutation from product')
return None if len(self.products) == 0 else self.products[0].get_permutation_selected()
"""
def is_available(self):
if len(self.products) == 0:
return False
for product in self.products:
if product.is_available():
return True
return False
def to_permutation_row_list(self):
list_rows = []
for product in self.products:
list_rows += product.to_permutation_row_list()
return list_rows
def to_product_option_list(self):
list_products = []
for product in self.products:
list_products.append({'value': product.id_product, 'text': product.name})
return list_products
def to_json(self):
return {
**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,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level_required[0] if isinstance(self.id_access_level_required, tuple) else self.id_access_level_required,
self.FLAG_ACCESS_LEVEL_REQUIRED: self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required,
self.FLAG_DISPLAY_ORDER: self.display_order,
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
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(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.display_order = json[cls.FLAG_DISPLAY_ORDER]
category.active = 1 if av.input_bool(json[cls.FLAG_ACTIVE], cls.FLAG_ACTIVE, f'{cls.__name__}.from_json') else 0
category.can_view = json.get(cls.FLAG_CAN_VIEW, False)
category.can_edit = json.get(cls.FLAG_CAN_EDIT, False)
category.can_admin = json.get(cls.FLAG_CAN_ADMIN, False)
return category
"""
def to_json_str(self):
return {
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,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level_required[0] if isinstance(self.id_access_level_required, tuple) else self.id_access_level_required,
self.FLAG_ACCESS_LEVEL_REQUIRED: self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.output_bool(self.active),
self.FLAG_CAN_VIEW: self.output_bool(self.can_view),
self.FLAG_CAN_EDIT: self.output_bool(self.can_edit),
self.FLAG_CAN_ADMIN: self.output_bool(self.can_admin)
}
"""
@staticmethod
def output_bool(value):
return av.input_bool(value, 'Product_Category bool attribute', 'Product_Category.output_bool')
def to_json_option(self):
return {
'value': self.id_category,
'text': self.name
}
def get_csv_ids_permutation(self):
list_ids = []
for product in self.products:
list_ids += product.get_csv_ids_permutation()
return list_ids
"""
class Filters_Product_Category(BaseModel, Store_Base):
ids_product_category: str
ids_product: str
""
def __new__(cls, product_ids, product_categories):
_m = 'Parameters_Product.__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(Parameters_Product_Category, cls).__new__(cls)
""
def __init__(self, ids_product, ids_product_category):
super().__init__(ids_product=ids_product, ids_product_category=ids_product_category)
""
# Constructor
self.ids = product_ids
self.categories = product_categories
""
@classmethod
def get_default(cls):
return Filters_Product_Category(
ids_product_category = '',
ids_product = ''
)
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
}
@classmethod
def from_json(cls, json):
filters = cls()
filters.ids_product_category = json['a_ids_product_category'],
filters.ids_product = json['a_ids_product']
class Filters_Product_Category(Get_Many_Parameters_Base):
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
is_not_empty: bool
active: bool
def __init__(self, is_not_empty, active):
super().__init__(is_not_empty=is_not_empty, active=active)
@classmethod
def get_default(cls):
return cls(
is_not_empty = False,
active = True
)
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.FLAG_IS_NOT_EMPTY: self.is_not_empty,
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json')
}
@classmethod
def from_json(cls, json):
return cls(
is_not_empty = json['is_not_empty'],
active = json['active']
)
@classmethod
def from_form(cls, form):
return cls(
is_not_empty = av.input_bool(form.is_not_empty.data, 'is_not_empty', 'Filters_Product_Category.from_form'),
active = av.input_bool(form.active.data, 'active', 'Filters_Product_Category.from_form')
)
"""
class Product_Category_Container(Store_Base):
NAME_ATTR_OPTION_TEXT: ClassVar[str] = ''
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.FLAG_ROWS
categories: list
def __init__(self):
self.categories = []
def add_product_category(self, category):
av.val_instance(category, 'category', 'Container_Product_Categories.add_product_category', Product_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', 'Container_Product_Categories.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', 'Container_Product_Categories.get_index_category_from_id_permutation', int)}. Permutation ID not in list")
def add_product(self, product):
av.val_instance(product, 'product', 'Container_Product_Categories.add_product', Product)
index_category = self.get_index_category_from_id(product.id_category)
self.categories[index_category].add_product(product)
def add_product_permutation(self, permutation):
av.val_instance(permutation, 'permutation', 'Container_Product_Categories.add_product_permutation', Product_Permutation)
index_category = self.get_index_category_from_id(permutation.id_category)
self.categories[index_category].add_product_permutation(permutation)
"""
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Container_Product_Categories.add_product_variation', Product_Variation)
index_category = self.get_index_category_from_id(variation.id_category)
self.categories[index_category].add_product_variation(variation)
"""
def add_product_variation_type(self, variation_type):
av.val_instance(variation_type, 'variation_type', 'Container_Product_Categories.add_product_variation_type', Product_Variation_Type)
variation = variation_type.variations[0]
index_category = self.get_index_category_from_id(variation.id_category)
self.categories[index_category].add_product_variation_type(variation_type)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Container_Product_Categories.add_product_price', Product_Price)
index_category = self.get_index_category_from_id(price.id_category)
self.categories[index_category].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Container_Product_Categories.add_product_image', Image)
index_category = self.get_index_category_from_id(image.id_category)
self.categories[index_category].add_product_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Container_Product_Categories.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_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Container_Product_Categories.add_product_price_discount', Discount)
index_category = self.get_index_category_from_id(discount.id_category)
self.categories[index_category].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Container_Product_Categories.add_stock_item', Stock_Item)
index_category = self.get_index_category_from_id(stock_item.id_category)
self.categories[index_category].add_stock_item(stock_item)
def get_all_product_variation_trees(self):
for category in self.categories:
category.get_all_product_variation_trees()
def __repr__(self):
return f'categories: {self.categories}'
"""
def get_permutation_first(self):
Helper_App.console_log(f'getting first permutation from category list')
if not (len(self.categories) == 0):
Helper_App.console_log(f'getting first permutation from category')
return None if len(self.categories) == 0 else self.categories[0].get_permutation_first()
"""
def get_category_count(self):
return len(self.categories)
def to_permutation_row_list(self):
list_rows = []
for category in self.categories:
list_rows += category.to_permutation_row_list()
return list_rows
def to_category_option_list(self):
list_categories = []
for category in self.categories:
list_categories.append({'value': category.id_category, 'text': category.name})
return list_categories
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:
dict_lists_products[category.id_category] = category.to_product_option_list()
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]
}
"""
def to_json_str(self):
return {
f'{self.FLAG_ROWS}': [category.to_json_str() for category in self.categories]
}
"""
@classmethod
def from_json(cls, json):
return None
def to_json_option(self):
return None
def to_temporary_record(self):
excluded_attributes = {
column.name: getattr(self, column.name)
for column in self.__table__.columns
if column.name not in ['created_on', 'created_by']
}
return self.to_object_with_missing_attributes(excluded_attributes)
def get_csv_ids_permutation(self):
list_ids = []
for category in self.categories:
list_ids += category.get_csv_ids_permutation()
return ','.join(list_ids)
"""
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, Store_Base):
__tablename__ = 'Shop_Product_Category_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_category: int = db.Column(db.Integer)
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.String(36))
# created_on: datetime = db.Column(db.DateTime)
# created_by: int = db.Column(db.Integer)
def __init__(self):
super().__init__()
self.id_temp = None
@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
"""
def __repr__(self):
return str(self.__dict__)

View File

@@ -1,617 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Permutation Business Object
Description:
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
from business_objects.currency import Currency
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.discount import Discount
from business_objects.store.image import Image
from business_objects.store.product_price import Product_Price
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
from helpers.helper_app import Helper_App
# external
from datetime import datetime, timedelta
import locale
from dataclasses import dataclass
from typing import ClassVar
class Product_Permutation(db.Model, Store_Base):
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_PROFIT_LOCAL_MIN = 'profit_local_min'
FLAG_HAS_VARIATIONS = 'has_variations'
FLAG_LATENCY_MANUFACTURE = 'latency_manufacture'
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_permutation = db.Column(db.Integer, primary_key=True)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
csv_id_pairs_variation = db.Column(db.String(4000))
# 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_VAT_excl = db.Column(db.Float)
cost_local_VAT_incl = db.Column(db.Float)
profit_local_min = db.Column(db.Float)
has_variations = db.Column(db.Boolean)
latency_manufacture = 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_stock = db.Column(db.Float)
is_subscription = db.Column(db.Boolean)
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)
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
# variation_tree
def __init__(self):
self.prices = []
self.price_index = {}
self.images = []
self.image_index = {}
self.delivery_options = []
self.delivery_option_index = {}
self.discounts = []
self.discount_index = {}
self.stock_items = []
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
# self.variations = []
@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'
Helper_App.console_log(f'query_row: {query_row}')
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_VAT_excl = query_row[4]
permutation.cost_local_VAT_incl = query_row[5]
permutation.currency_cost = Currency.from_DB_get_many_product_catalogue_product_permutation(query_row)
permutation.profit_local_min = query_row[9]
permutation.latency_manufacture = query_row[10]
permutation.id_unit_measurement_quantity = query_row[11]
permutation.symbol_unit_measurement_quantity = query_row[12]
permutation.symbol_is_suffix_not_prefix_unit_measurement_quantity = av.input_bool(query_row[13], 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[14]
permutation.name_plural_unit_measurement_quantity = query_row[15]
permutation.count_unit_measurement_per_quantity_step = query_row[16]
permutation.quantity_min = query_row[17]
permutation.quantity_max = query_row[18]
permutation.quantity_stock = query_row[19]
permutation.is_subscription = av.input_bool(query_row[20], "is_subscription", _m, v_arg_type=v_arg_type)
permutation.id_unit_measurement_interval_recurrence = query_row[21]
permutation.symbol_unit_measurement_interval_recurrence = query_row[22]
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_recurrence = av.input_bool(query_row[23], 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[24]
permutation.name_plural_unit_measurement_interval_recurrence = query_row[25]
permutation.count_interval_recurrence = query_row[26]
permutation.id_stripe_product = query_row[27]
permutation.does_expire_faster_once_unsealed = av.input_bool(query_row[28], "does_expire_faster_once_unsealed", _m, v_arg_type=v_arg_type)
permutation.id_unit_measurement_interval_expiration_unsealed = query_row[29]
permutation.symbol_unit_measurement_interval_expiration_unsealed = query_row[30]
permutation.symbol_is_suffix_not_prefix_unit_measurement_interval_expiration_unsealed = av.input_bool(query_row[31], 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[32]
permutation.name_plural_unit_measurement_interval_expiration_unsealed = query_row[33]
permutation.count_interval_expiration_unsealed = query_row[34]
permutation.has_variations = av.input_bool(query_row[35], cls.FLAG_HAS_VARIATIONS, _m, v_arg_type=v_arg_type)
permutation.active = av.input_bool(query_row[36], cls.FLAG_ACTIVE, _m, v_arg_type=v_arg_type)
# permutation.display_order = query_row[27]
permutation.can_view = av.input_bool(query_row[37], "can_view", _m, v_arg_type=v_arg_type)
permutation.can_edit = av.input_bool(query_row[38], "can_edit", _m, v_arg_type=v_arg_type)
permutation.can_admin = av.input_bool(query_row[39], "can_admin", _m, v_arg_type=v_arg_type)
return permutation
def from_DB_Stripe_product(query_row):
_m = 'Product_Permutation.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 from_DB_Stripe_price(query_row):
_m = 'Product_Permutation.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_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):
_m = 'Product_Permutation.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
"""
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
permutation = cls()
permutation.id_permutation = json[cls.ATTR_ID_PRODUCT_PERMUTATION]
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_VAT_excl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_EXCL, None)
permutation.cost_local_VAT_incl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_INCL, None)
permutation.currency_cost = Currency.from_json(json, '_cost')
permutation.profit_local_min = json[cls.FLAG_PROFIT_LOCAL_MIN]
permutation.latency_manufacture = json[cls.FLAG_LATENCY_MANUFACTURE]
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.csv_id_pairs_variation = json[cls.FLAG_PRODUCT_VARIATIONS]
permutation.variation_tree = Product_Variation_Tree.from_json_str(permutation.csv_id_pairs_variation)
"""
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 {
**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_UNIT_LOCAL_VAT_EXCL: self.cost_local_VAT_excl,
self.FLAG_COST_UNIT_LOCAL_VAT_INCL: self.cost_local_VAT_incl,
self.FLAG_CURRENCY_COST: self.currency_cost.to_json(),
self.FLAG_PROFIT_LOCAL_MIN: self.profit_local_min,
self.FLAG_LATENCY_MANUFACTURE: self.latency_manufacture,
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_VARIATIONS: [] if self.variation_tree is None else [variation_type.to_json() for variation_type in self.variation_tree.get_product_variation_types()],
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):
return len(self.prices) > 0
def get_price(self):
return self.prices[0]
def output_delivery_date(self):
return (datetime.now() + timedelta(days=self.latency_manufacture)).strftime('%A, %d %B %Y')
"""
def output_lead_time(self):
return '1 day' if self.latency_manufacture == 1 else f'{self.latency_manufacture} days'
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_variations(self):
if not self.has_variations: return ''
return '\n'.join([f'{variation.name_variation_type}: {variation.name_variation}' for variation in self.variations])
def output_variations_jsonify(self):
if not self.has_variations: return ''
return ','.join([f'{variation.id_type}: {variation.id_variation}' for variation in self.variations])
"""
def __repr__(self):
return f'''Product_Permutation
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
description: {self.description}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
currency_cost: {self.currency_cost}
latency_manufacture: {self.latency_manufacture}
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_stock: {self.quantity_stock}
is_subscription: {self.is_subscription}
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}
csv_id_pairs_variation: {self.csv_id_pairs_variation}
can_view: {self.can_view}
can_edit: {self.can_edit}
can_admin: {self.can_admin}
variation tree: {self.variation_tree}
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_product_variation(self, variation):
_m = 'Product_Permutation.add_product_variation'
""
av.val_instance(variation, 'variation', _m, Product_Variation)
try:
self.variation_index[variation.id_variation]
raise ValueError(f"{av.error_msg_str(variation, 'variation', _m, Product_Variation)}\nProduct_Variation already in product.")
except KeyError:
self.variation_index[variation.id_variation] = len(self.variations)
self.variations.append(variation)
""
if self.variation_tree is None:
self.variation_tree = Product_Variation_Tree.from_product_variation(variation)
else:
self.variation_tree.add_product_variation(variation)
"""
def add_product_variation_type(self, variation_type):
_m = 'Product_Permutation.add_product_variation_type'
if self.variation_tree is None:
self.variation_tree = Product_Variation_Tree.from_product_variation_type(variation_type)
else:
self.variation_tree.add_product_variation_type(variation_type)
def add_product_price(self, price):
_m = 'Product_Permutation.add_product_price'
av.val_instance(price, 'price', _m, Product_Price)
try:
self.price_index[price.display_order]
raise ValueError(f"{av.error_msg_str(price, 'price', _m, Product_Price)}\nPrice already in product.")
except KeyError:
self.price_index[price.display_order] = len(self.prices)
self.prices.append(price)
def add_product_image(self, image):
_m = 'Product_Permutation.add_product_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_product_price_discount(self, discount):
_m = 'Product_Permutation.add_product_price_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 add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Product_Permutation.add_stock_item', Stock_Item)
self.stock_items.append(stock_item)
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
def to_row_permutation(self):
a = {
Product_Permutation.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
Product_Permutation.ATTR_ID_PRODUCT: self.id_product,
Product_Permutation.ATTR_ID_PRODUCT_VARIATION: self.output_variations(),
Product_Permutation.FLAG_QUANTITY_STOCK: self.quantity_stock,
Product_Permutation.FLAG_QUANTITY_MIN: self.quantity_min,
Product_Permutation.FLAG_QUANTITY_MAX: self.quantity_max,
Product_Permutation.FLAG_COST_UNIT_LOCAL_VAT_EXCL: f"<strong>{self.symbol_currency_cost}</strong>{self.cost_local_VAT_excl}"
}
Helper_App.console_log('permutation row: ', a)
return a
"""
class Permutation_Product_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 from_DB_get_many_product_catalogue(query_row):
_m = 'Permutation_Product_Variation_Link.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
link = Permutation_Product_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
"""
class Product_Permutation_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Product_Permutation_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_permutation: int = db.Column(db.Integer)
id_product: int = db.Column(db.Integer)
csv_id_pairs_variation: str = db.Column(db.String(4000))
description: str = db.Column(db.String(4000))
cost_local_VAT_excl: float = db.Column(db.Float)
cost_local_VAT_incl: float = db.Column(db.Float)
id_currency_cost: int = db.Column(db.Integer)
profit_local_min: float = db.Column(db.Float)
latency_manufacture: 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))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_product_permutation(cls, product_permutation):
Helper_App.console_log(f'Product_Permutation_Temp.from_product_permutation: {product_permutation}\ntype(cost local): {str(type(product_permutation.cost_local_VAT_excl))}')
row = cls()
row.id_permutation = product_permutation.id_permutation
row.id_product = product_permutation.id_product
row.csv_id_pairs_variation = product_permutation.csv_id_pairs_variation
row.description = product_permutation.description
row.cost_local_VAT_excl = product_permutation.cost_local_VAT_excl if product_permutation.cost_local_VAT_excl != 'None' else None
row.cost_local_VAT_incl = product_permutation.cost_local_VAT_incl if product_permutation.cost_local_VAT_incl != 'None' else None
row.id_currency_cost = product_permutation.currency_cost.id_currency
row.profit_local_min = product_permutation.profit_local_min if product_permutation.profit_local_min != 'None' else None
row.latency_manufacture = product_permutation.latency_manufacture
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}
csv_id_pairs_variation: {self.csv_id_pairs_variation}
description: {self.description}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
id_currency_cost: {self.id_currency_cost}
profit_local_min: {self.profit_local_min}
latency_manufacture: {self.latency_manufacture}
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: int(self.latency_manufacture),
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

@@ -1,106 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Price Business Object
Description:
Business object for product price
"""
# internal
from business_objects.currency import Currency
from business_objects.region import Region
from business_objects.store.store_base import Store_Base
from extensions import db
# external
from dataclasses import dataclass
from typing import ClassVar
class Product_Price(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_PRODUCT_PRICE
NAME_ATTR_OPTION_TEXT = Store_Base.FLAG_TEXT
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)
display_order = db.Column(db.Float)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
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 = 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]
price.display_order = query_row[11]
return price
def __repr__(self):
return f'''Product_Price
id: {self.id_price}
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
currency: {self.currency}
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.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(),
# Region.ATTR_ID_REGION_DELIVERY: self.id_region,
self.FLAG_VALUE_LOCAL_VAT_INCL: self.value_local_VAT_incl,
self.FLAG_VALUE_LOCAL_VAT_EXCL: self.value_local_VAT_excl,
self.FLAG_DISPLAY_ORDER: self.display_order
}
@classmethod
def from_json(cls, json):
price = cls()
price.id_price = json[cls.ATTR_ID_PRODUCT_PRICE]
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.currency = Currency.from_json(json)
# price.id_region = json[Region.ATTR_ID_REGION_DELIVERY]
price.value_local_VAT_incl = json[cls.FLAG_VALUE_LOCAL_VAT_INCL]
price.value_local_VAT_excl = json[cls.FLAG_VALUE_LOCAL_VAT_EXCL]
price.display_order = json[cls.FLAG_DISPLAY_ORDER]
return price

View File

@@ -1,217 +0,0 @@
"""
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.db_base import Get_Many_Parameters_Base
from business_objects.store.store_base import Store_Base
# from business_objects.store.product_variation_type import Product_Variation_Type
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar
from pydantic import BaseModel
from itertools import filterfalse
from operator import attrgetter
class Product_Variation(db.Model, Store_Base):
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)
id_type = 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__()
self.variation_type = None
@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
@classmethod
def from_DB_get_many_product_variation(cls, query_row):
variation = cls()
variation.id_variation = query_row[0]
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_PRODUCT_VARIATION]
variation.id_type = json[cls.ATTR_ID_PRODUCT_VARIATION_TYPE]
variation.code = json[cls.FLAG_CODE]
variation.name = json[cls.FLAG_NAME]
variation.display_order = json[cls.FLAG_DISPLAY_ORDER]
variation.active = 1 if av.input_bool(json[cls.FLAG_ACTIVE], cls.FLAG_ACTIVE, f'{cls.__name__}.from_json') else 0
variation.id_permutation = json.get(cls.ATTR_ID_PRODUCT_PERMUTATION, None)
variation.id_product = json.get(cls.ATTR_ID_PRODUCT, None)
variation.id_category = json.get(cls.ATTR_ID_PRODUCT_CATEGORY, None)
return variation
def __repr__(self):
return f'''
{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}
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,
}
def to_json_option(self):
return {
'value': self.id_variation,
'text': self.name
}
class Parameters_Product_Variation(Get_Many_Parameters_Base):
a_get_all_variation_type: bool
a_get_inactive_variation_type: bool
a_ids_variation_type: str
a_get_all_variation: bool
a_get_inactive_variation: bool
a_ids_variation: str
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_default(cls):
return cls(
a_get_all_variation_type = True,
a_get_inactive_variation_type = False,
a_ids_variation_type = '',
a_get_all_variation = True,
a_get_inactive_variation = False,
a_ids_variation = ''
)
@classmethod
def from_filters_product_variation(cls, form):
parameters = cls.get_default()
get_inactive = not form.active.data
parameters.a_get_inactive_variation_type = get_inactive
parameters.a_get_inactive_variation = get_inactive
return parameters
"""
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())
Helper_App.console_log(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
"""
class Product_Variation_Temp(db.Model, Store_Base):
__tablename__ = 'Shop_Variation_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_variation: int = db.Column(db.Integer) # , primary_key=True)
id_type: int = db.Column(db.Integer, nullable=False)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.String(36))
def __repr__(self):
attrs = '\n'.join(f'{k}={v!r}' for k, v in self.__dict__.items())
return f'<{self.__class__.__name__}(\n{attrs}\n)>'
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_product_variation(cls, product_variation):
row = cls()
row.id_variation = product_variation.id_variation
row.id_type = product_variation.id_type
row.code = product_variation.code
row.name = product_variation.name
row.active = 1 if av.input_bool(product_variation.active, cls.FLAG_ACTIVE, f'{cls.__name__}.to_json') else 0
row.display_order = product_variation.display_order
return row
def to_json(self):
return {
'id_variation': self.id_variation,
'id_type': self.id_type,
'code': self.code,
'name': self.name,
'active': self.active,
'display_order': self.display_order,
'guid': self.guid,
}

View File

@@ -1,161 +0,0 @@
"""
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
"""
# internal
from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_type import Product_Variation_Type
from extensions import db
from helpers.helper_app import Helper_App
# external
class Product_Variation_Tree_Node():
variation_type: Product_Variation_Type
node_parent: None
nodes_child: list
def __init__(self):
self.nodes_child = []
def from_variation_type_and_node_parent(variation_type, node_parent):
node = Product_Variation_Tree_Node()
node.variation_type = variation_type
node.node_parent = node_parent
return node
def from_node_parent(node_parent):
node = Product_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 Product_Variation_Tree():
node_root: Product_Variation_Tree_Node
@classmethod
def from_node_root(cls, node_root):
tree = cls()
tree.node_root = node_root
return tree
@classmethod
def from_variation_type_root(cls, variation_type_root):
node_root = Product_Variation_Tree_Node.from_variation_type_and_node_parent(variation_type_root, None)
return cls.from_node_root(node_root)
def is_equal(self, tree):
my_type_list = self.get_product_variation_types()
sz_me = len(my_type_list)
other_type_list = tree.get_product_variation_types()
sz_other = len(other_type_list)
is_equal = (sz_me == sz_other)
if is_equal:
for index_type in range(sz_me):
my_variation_type = my_type_list[index_type]
other_variation_type = other_type_list[index_type]
if my_variation_type.id_type != other_variation_type.id_type:
is_equal = False
break
my_variation = my_variation_type.variations[0]
other_variation = other_variation_type.variations[0]
if my_variation.id_variation != other_variation.id_variation:
is_equal = False
break
return is_equal
@classmethod
def from_product_permutation(cls, product_permutation):
depth_max = len(product_permutation.variation_types)
node_root = Product_Variation_Tree_Node.from_variation_type_and_node_parent(product_permutation.variation_types[0], None)
node = node_root
for depth in range(depth_max - 1):
node = Product_Variation_Tree_Node.from_variation_type_and_node_parent(product_permutation.variation_types[depth + 1], node)
return cls.from_node_root(node_root)
@classmethod
def from_product_variation_type(cls, product_variation_type):
node_root = Product_Variation_Tree_Node.from_variation_type_and_node_parent(product_variation_type, None)
return cls.from_node_root(node_root)
@classmethod
def from_product_variation_types(cls, product_variation_types):
node_root = Product_Variation_Tree_Node.from_variation_type_and_node_parent(product_variation_types[0], None)
tree = cls.from_node_root(node_root)
if len(product_variation_types) > 1:
for variation_type in product_variation_types[1:]:
tree.add_product_variation_type(variation_type)
return tree
@classmethod
def from_json_str(cls, json_str):
variation_types = []
if json_str is None or json_str == '': return None
for json_variation_type in json_str.split(','):
parts = json_variation_type.split(':')
if len(parts) != 2: continue
variation_type = Product_Variation_Type()
variation_type.id_type = parts[0]
variation = Product_Variation()
variation_type.id_variation = parts[1]
variation_type.variations = [variation]
variation_types.append(variation_type)
return cls.from_product_variation_types(variation_types)
def get_node_leaf(self):
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
return node
def add_product_variation_type(self, variation_type):
node_leaf = self.get_node_leaf()
node_new = Product_Variation_Tree_Node.from_variation_type_and_node_parent(variation_type, node_leaf)
node_leaf.add_child(node_new)
def get_product_variation_types(self):
types = []
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
types.append(node.variation_type)
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
types.append(node.variation_type)
return types
"""
def get_product_variations(self):
variations = []
node = self.node_root
at_leaf_node = node.is_leaf()
variations.append(node.variation)
while not at_leaf_node:
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
variations.append(node.variation)
return variations
"""
def to_preview_str(self):
Helper_App.console_log(f'Product_Variation_Tree.to_preview_str')
variation_types = self.get_product_variation_types()
Helper_App.console_log(f'variation_types: {variation_types}')
preview_str = ''
for variation_type in variation_types:
is_first = (preview_str == '')
preview_str += f'{variation_type.name_singular}: {variation_type.variations[0].name}'
if is_first:
preview_str += '\n'
Helper_App.console_log(f'preview_str: {preview_str}')
return preview_str
def to_json(self):
variation_types = self.get_product_variation_types()
json_variation_types = []
for variation_type in variation_types:
json_variation_types.append(variation_type.to_json())
return json_variation_types
def to_variation_id_pairs_str(self):
variation_types = self.get_product_variation_types()
pairs_str = ''
for variation_type in variation_types:
pairs_str += f'{variation_type.id_type}:{variation_type.variations[0].id_variation},'
return pairs_str

View File

@@ -1,169 +0,0 @@
"""
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.product_variation import Product_Variation
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
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__()
self.variations = []
@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')
variation_type.variations = [Product_Variation.from_DB_get_many_product_catalogue(query_row)]
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.get(cls.FLAG_NAME_SINGULAR, json.get(cls.FLAG_NAME, ''))
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]
variations = json.get(cls.FLAG_PRODUCT_VARIATIONS, [])
if variations is not None and len(variations) > 0:
variation_type.variations = [Product_Variation.from_json(variation) for variation in variations]
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: self.name_singular,
self.FLAG_NAME_PLURAL: self.name_plural,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active,
self.FLAG_PRODUCT_VARIATIONS: [variation.to_json() for variation in self.variations]
}
def to_json_option(self):
return {
'value': self.id_type,
'text': self.name_singular
}
"""
def get_preview_variations(self):
preview = ''
if len(self.variations) > 0:
# preview = '\n'.join([variation.name for variation in self.variations])
preview = '<p>' + '</p><p>'.join([variation.name for variation in self.variations]) + '</p>'
return preview
def get_str_list_ids_variation(self):
if self.variations is None or len(self.variations) == 0:
return ''
return ','.join([str(variation.id_variation) for variation in self.variations])
"""
class Product_Variation_Type_Temp(db.Model, Store_Base):
__tablename__ = 'Shop_Variation_Type_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_type: int = db.Column(db.Integer)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
name_plural: str = db.Column(db.String(256))
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_product_variation_type(cls, product_variation_type):
row = cls()
row.id_type = product_variation_type.id_type
row.code = product_variation_type.code
row.name = product_variation_type.name_singular
row.name_plural = product_variation_type.name_plural
row.active = 1 if av.input_bool(product_variation_type.active, cls.FLAG_ACTIVE, f'{cls.__name__}.from_product_variation_type') else 0
row.display_order = product_variation_type.display_order
return row
def to_json(self):
return {
'id_type': self.id_type,
'code': self.code,
'name': self.name,
'name_plural': self.name_plural,
'active': self.active,
'display_order': self.display_order,
'guid': self.guid,
}
def __repr__(self):
return f'''
{self.__class__.__name__}
id_temp: {self.id_temp}
id_type: {self.id_type}
code: {self.code}
name: {self.name}
name_plural: {self.name_plural}
active: {self.active}
display_order: {self.display_order}
guid: {self.guid}
'''

View File

@@ -1,303 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Stock Item Business Object
"""
# IMPORTS
# internal
import lib.argument_validation as av
from lib import data_types
from forms.store.stock_item import Filters_Stock_Item
from business_objects.db_base import Get_Many_Parameters_Base
from business_objects.currency import Currency
# from business_objects.discount import Discount
from business_objects.store.product_variation_tree import Product_Variation_Tree
from business_objects.store.storage_location import Storage_Location
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar, Optional
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'
FLAG_DATE_RECEIVED: ClassVar[str] = 'date_received'
FLAG_DATE_UNSEALED: ClassVar[str] = 'date_unsealed'
FLAG_IS_CONSUMED: ClassVar[str] = 'is_consumed'
FLAG_IS_SEALED: ClassVar[str] = 'is_sealed'
id_stock = db.Column(db.Integer, primary_key=True)
id_permutation = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_category = db.Column(db.Integer)
id_location_storage = db.Column(db.Integer)
id_plant = db.Column(db.Integer)
id_region = db.Column(db.Integer)
id_currency_cost = db.Column(db.Integer)
cost_local_VAT_excl = db.Column(db.Float)
cost_local_VAT_incl = db.Column(db.Float)
date_purchased = db.Column(db.DateTime)
date_received = db.Column(db.DateTime)
is_sealed = db.Column(db.Boolean)
date_unsealed = db.Column(db.DateTime)
date_expiration = db.Column(db.DateTime)
is_consumed = db.Column(db.Boolean)
date_consumed = db.Column(db.DateTime)
active = db.Column(db.Boolean)
"""
can_view = db.Column(db.Boolean)
can_edit = db.Column(db.Boolean)
can_admin = db.Column(db.Boolean)
"""
# variation_tree: Product_Variation_Tree = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.currency_cost = None
self.storage_location = None
self.variation_tree = None
self.has_variations = False
def from_DB_stock_item(query_row):
_m = 'Product.from_DB_stock_item'
v_arg_type = 'class attribute'
stock_item = Stock_Item()
stock_item.id_stock = query_row[0]
stock_item.id_permutation = query_row[1]
stock_item.id_product = query_row[2]
stock_item.id_category = query_row[3]
stock_item.id_location_storage = query_row[4]
stock_item.storage_location = Storage_Location.from_DB_stock_item(query_row)
stock_item.id_currency_cost = query_row[12]
stock_item.currency_cost = Currency.from_DB_stock_item(query_row)
stock_item.cost_local_VAT_excl = query_row[15]
stock_item.cost_local_VAT_incl = query_row[16]
stock_item.date_purchased = query_row[17]
stock_item.date_received = query_row[18]
stock_item.is_sealed = av.input_bool(query_row[19], "is_sealed", _m, v_arg_type=v_arg_type)
stock_item.date_unsealed = query_row[20]
stock_item.date_expiration = query_row[21]
stock_item.is_consumed = av.input_bool(query_row[22], "is_consumed", _m, v_arg_type=v_arg_type)
stock_item.date_consumed = query_row[23]
stock_item.active = av.input_bool(query_row[24], "active", _m, v_arg_type=v_arg_type)
"""
stock_item.can_view = av.input_bool(query_row[24], "can_view", _m, v_arg_type=v_arg_type)
stock_item.can_edit = av.input_bool(query_row[25], "can_edit", _m, v_arg_type=v_arg_type)
stock_item.can_admin = av.input_bool(query_row[26], "can_admin", _m, v_arg_type=v_arg_type)
"""
return stock_item
@classmethod
def from_json(cls, json):
stock_item = cls()
stock_item.id_stock = json[cls.ATTR_ID_STOCK_ITEM]
stock_item.id_permutation = json.get(cls.ATTR_ID_PRODUCT_PERMUTATION, 0)
stock_item.id_product = json[cls.ATTR_ID_PRODUCT]
stock_item.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY]
Helper_App.console_log(f'json: {json}\nhalf stock item: {stock_item}')
stock_item.variation_tree = Product_Variation_Tree.from_json_str(json[cls.FLAG_PRODUCT_VARIATIONS])
stock_item.date_purchased = json[cls.FLAG_DATE_PURCHASED]
stock_item.date_received = json[cls.FLAG_DATE_RECEIVED]
stock_item.id_location_storage = json[cls.ATTR_ID_STORAGE_LOCATION]
stock_item.id_currency_cost = json[cls.ATTR_ID_CURRENCY_COST]
stock_item.cost_local_VAT_excl = json[cls.FLAG_COST_UNIT_LOCAL_VAT_EXCL]
stock_item.cost_local_VAT_incl = json[cls.FLAG_COST_UNIT_LOCAL_VAT_INCL]
stock_item.is_sealed = json[cls.FLAG_IS_SEALED]
stock_item.date_unsealed = json[cls.FLAG_DATE_UNSEALED]
stock_item.date_expiration = json[cls.FLAG_DATE_EXPIRATION]
stock_item.is_consumed = json[cls.FLAG_IS_CONSUMED]
stock_item.date_consumed = json[cls.FLAG_DATE_CONSUMED]
stock_item.active = json[cls.FLAG_ACTIVE]
return stock_item
def __repr__(self):
return f'''Stock Item
id_stock: {self.id_stock}
id_permutation: {self.id_permutation}
id_product: {self.id_product}
id_category: {self.id_category}
variations: {self.variation_tree.to_preview_str() if self.variation_tree is not None else 'None'}
storage_location: {self.storage_location}
currency: {self.currency_cost}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
date_purchased: {self.date_purchased}
date_received: {self.date_received}
is_sealed: {self.is_sealed}
date_unsealed: {self.date_unsealed}
date_expiration: {self.date_expiration}
is_consumed: {self.is_consumed}
date_consumed: {self.date_consumed}
active: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_STOCK_ITEM: self.id_stock,
self.ATTR_ID_PRODUCT_PERMUTATION: self.id_permutation,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.FLAG_STORAGE_LOCATION: self.storage_location.to_json(),
self.FLAG_CURRENCY_COST: self.currency_cost.to_json(),
self.FLAG_COST_UNIT_LOCAL_VAT_EXCL: self.cost_local_VAT_excl,
self.FLAG_COST_UNIT_LOCAL_VAT_INCL: self.cost_local_VAT_incl,
}
def has_permutations(self):
return len(self.permutations) > 0
def is_available(self):
if len(self.permutations) == 0:
return False
for permutation in self.permutations:
if permutation.is_available():
return True
return False
"""
def to_permutation_row_list(self):
list_rows = []
for permutation in self.permutations:
list_rows.append(permutation.to_row_permutation())
return list_rows
def to_json_option(self):
return {
'value': self.id_stock_item,
'text': self.id_stock_item
}
"""
class Parameters_Stock_Item(Get_Many_Parameters_Base):
a_get_all_product_permutation: bool
a_get_inactive_product_permutation: bool
a_ids_product_permutation: str
a_get_all_stock_item: bool
a_get_inactive_stock_item: bool
a_ids_stock_item: str
a_get_all_region_storage: bool
a_get_inactive_region_storage: bool
a_ids_region_storage: str
a_get_all_plant_storage: bool
a_get_inactive_plant_storage: bool
a_ids_plant_storage: str
a_get_all_location_storage: bool
a_get_inactive_location_storage: bool
a_ids_location_storage: str
a_date_received_to: Optional[datetime] = None
a_get_sealed_stock_item_only: bool
a_get_unsealed_stock_item_only: bool
a_get_expired_stock_item_only: bool
a_get_nonexpired_stock_item_only: bool
a_get_consumed_stock_item_only: bool
a_get_nonconsumed_stock_item_only: bool
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_default(cls):
return cls(
# a_id_user = id_user,
a_get_all_product_permutation = False,
a_get_inactive_product_permutation = False,
a_ids_product_permutation = '',
a_get_all_stock_item = True,
a_get_inactive_stock_item = False,
a_ids_stock_item = '',
a_get_all_region_storage = True,
a_get_inactive_region_storage = False,
a_ids_region_storage = '',
a_get_all_plant_storage = True,
a_get_inactive_plant_storage = False,
a_ids_plant_storage = '',
a_get_all_location_storage = True,
a_get_inactive_location_storage = False,
a_ids_location_storage = '',
a_date_received_to = None,
a_get_sealed_stock_item_only = False,
a_get_unsealed_stock_item_only = False,
a_get_expired_stock_item_only = False,
a_get_nonexpired_stock_item_only = False,
a_get_consumed_stock_item_only = False,
a_get_nonconsumed_stock_item_only = False
)
@classmethod
def from_form_stock_item(cls, form):
return cls.get_default()
class Stock_Item_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Stock_Item_Temp'
__table_args__ = { 'extend_existing': True }
id_stock: int = db.Column(db.Integer, primary_key=True)
# id_category: int = db.Column(db.Integer)
id_product: int = db.Column(db.Integer)
# has_variations: bool = db.Column(db.Boolean)
id_permutation: int = db.Column(db.Integer)
id_pairs_variations: str = db.Column(db.String(4000))
date_purchased: datetime = db.Column(db.DateTime)
date_received: datetime = db.Column(db.DateTime, nullable=True)
id_location_storage: int = db.Column(db.Integer)
id_currency_cost: int = db.Column(db.Integer)
cost_local_VAT_excl: float = db.Column(db.Float)
cost_local_VAT_incl: float = db.Column(db.Float)
is_sealed: bool = db.Column(db.Boolean)
date_unsealed: datetime = db.Column(db.DateTime, nullable=True)
date_expiration: datetime = db.Column(db.DateTime, nullable=True)
is_consumed: bool = db.Column(db.Boolean)
date_consumed: datetime = db.Column(db.DateTime, nullable=True)
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
@classmethod
def from_stock_item(cls, stock_item):
row = cls()
row.id_stock = stock_item.id_stock
# row.id_category = stock_item.id_category
row.id_product = stock_item.id_product
row.id_permutation = stock_item.id_permutation
# row.has_variations = stock_item.has_variations
row.id_pairs_variations = stock_item.variation_tree.to_json_str() if stock_item.variation_tree is not None else ''
row.date_purchased = stock_item.date_purchased
row.date_received = stock_item.date_received if stock_item.date_received else None
row.id_location_storage = stock_item.id_location_storage
row.id_currency_cost = stock_item.id_currency_cost
row.cost_local_VAT_excl = stock_item.cost_local_VAT_excl
row.cost_local_VAT_incl = stock_item.cost_local_VAT_incl
row.is_sealed = 1 if stock_item.is_sealed else 0
row.date_unsealed = stock_item.date_unsealed if stock_item.date_unsealed else None
row.date_expiration = stock_item.date_expiration if stock_item.date_expiration else None
row.is_consumed = 1 if stock_item.is_consumed else 0
row.date_consumed = stock_item.date_consumed if stock_item.date_consumed else None
row.active = 1 if stock_item.active else 0
return row
def __repr__(self):
return f'''
id_stock: {self.id_stock}
id_product: {self.id_product}
id_permutation: {self.id_permutation}
id_pairs_variations: {self.id_pairs_variations}
date_purchased: {self.date_purchased}
date_received: {self.date_received}
id_location_storage: {self.id_location_storage}
id_currency_cost: {self.id_currency_cost}
cost_local_VAT_excl: {self.cost_local_VAT_excl}
cost_local_VAT_incl: {self.cost_local_VAT_incl}
is_sealed: {self.is_sealed}
date_unsealed: {self.date_unsealed}
date_expiration: {self.date_expiration}
is_consumed: {self.is_consumed}
date_consumed: {self.date_consumed}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -1,88 +0,0 @@
"""
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
"""
# internal
import lib.argument_validation as av
from business_objects.store.plant import Plant
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from typing import ClassVar
class Storage_Location(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_STORAGE_LOCATION
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Storage_Location_Temp'
id_location = db.Column(db.Integer, primary_key=True)
id_plant = db.Column(db.Integer)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
active = db.Column(db.Boolean)
# plant = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.plant = None
@classmethod
def from_DB_storage_location(cls, query_row):
location = cls()
location.id_location = query_row[0]
location.id_plant = query_row[1]
location.plant = Plant.from_DB_storage_location(query_row)
location.code = query_row[4]
location.name = query_row[5]
location.active = query_row[6]
return location
@classmethod
def from_DB_stock_item(cls, query_row):
location = cls()
location.id_location = query_row[4]
location.id_plant = query_row[5]
location.code = query_row[8]
location.name = query_row[9]
location.plant = Plant.from_DB_stock_item(query_row)
return location
def __repr__(self):
return f'''
{self.ATTR_ID_STORAGE_LOCATION}: {self.id_location}
{self.ATTR_ID_PLANT}: {self.id_plant}
{self.FLAG_CODE}: {self.code}
{self.FLAG_NAME}: {self.name}
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_STORAGE_LOCATION: self.id_location,
self.ATTR_ID_PLANT: self.id_plant,
self.FLAG_CODE: self.code,
self.FLAG_NAME: self.name,
self.FLAG_ACTIVE: 1 if av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
location = cls()
location.id_location = json[cls.ATTR_ID_STORAGE_LOCATION],
location.id_plant = json[cls.ATTR_ID_PLANT],
location.code = json[cls.FLAG_CODE],
location.name = json[cls.FLAG_NAME],
location.active = json[cls.FLAG_ACTIVE]
return location
def get_full_name(self):
return f'{self.plant.name} - {self.name}'

View File

@@ -1,129 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Base Store Business Object
Description:
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
from typing import ClassVar
"""
class I_Store_Base():
@abstractmethod
def __repr__(self):
pass
@classmethod
@abstractmethod
def from_json(cls, json):
pass
@abstractmethod
def to_json(self):
pass
@abstractmethod
def to_json_option(self):
pass
@abstractmethod
def test_69 (self):
pass
""
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
for name, value in vars(Store_Base).items():
if getattr(value, "__isabstractmethod__", False):
if name not in cls.__dict__:
raise TypeError(f"Can't instantiate class {cls.__name__} "
f"without implementation of abstract method {name}")
subclass_value = cls.__dict__[name]
if (isinstance(value, (staticmethod, classmethod)) and
not isinstance(subclass_value, type(value))):
raise TypeError(f"Abstract {type(value).__name__} {name} in {cls.__name__} "
f"must be implemented as a {type(value).__name__}")
def __new__(cls, *args, **kwargs):
if cls is Store_Base:
raise TypeError("Can't instantiate abstract class Store_Base directly")
return super().__new__(cls)
""
"""
class Store_Base(Base):
# ATTR_ID_CURRENCY_COST: ClassVar[str] = 'id_currency_cost'
ATTR_ID_CUSTOMER: ClassVar[str] = 'id_customer'
ATTR_ID_CUSTOMER_ADDRESS: ClassVar[str] = 'id_address'
ATTR_ID_CUSTOMER_SALES_ORDER: ClassVar[str] = 'id_customer_sales_order'
ATTR_ID_DELIVERY_OPTION: ClassVar[str] = 'id_delivery_option'
ATTR_ID_DISCOUNT: ClassVar[str] = 'id_discount'
ATTR_ID_IMAGE: ClassVar[str] = 'id_image'
ATTR_ID_MANUFACTURING_PURCHASE_ORDER: ClassVar[str] = 'id_order'
ATTR_ID_MANUFACTURING_PURCHASE_ORDER_PRODUCT_LINK: ClassVar[str] = 'id_link'
ATTR_ID_PLANT: ClassVar[str] = 'id_plant'
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_type'
ATTR_ID_STOCK_ITEM: ClassVar[str] = 'id_stock_item'
ATTR_ID_STORAGE_LOCATION: ClassVar[str] = 'id_location'
ATTR_ID_SUPPLIER: ClassVar[str] = 'id_supplier'
ATTR_ID_SUPPLIER_ADDRESS: ClassVar[str] = 'id_address'
ATTR_ID_SUPPLIER_PURCHASE_ORDER: ClassVar[str] = 'id_order'
ATTR_ID_SUPPLIER_PURCHASE_ORDER_PRODUCT_LINK: ClassVar[str] = 'id_link'
ATTR_ID_UNIT_MEASUREMENT_LATENCY_MANUFACTURE: ClassVar[str] = 'id_unit_latency_manufacture'
ATTR_ID_UNIT_MEASUREMENT_QUANTITY: ClassVar[str] = 'id_unit_quantity'
# FLAG_COST_LOCAL: ClassVar[str] = 'cost_local'
FLAG_COST_TOTAL_LOCAL_VAT_EXCL: ClassVar[str] = 'cost_total_local_vat_excl'
FLAG_COST_TOTAL_LOCAL_VAT_INCL: ClassVar[str] = 'cost_total_local_vat_incl'
FLAG_COST_UNIT_LOCAL_VAT_EXCL: ClassVar[str] = 'cost_unit_local_vat_excl'
FLAG_COST_UNIT_LOCAL_VAT_INCL: ClassVar[str] = 'cost_unit_local_vat_incl'
FLAG_CUSTOMER: ClassVar[str] = 'customer'
FLAG_CUSTOMER_ADDRESS: ClassVar[str] = 'customer_address'
FLAG_CUSTOMER_SALES_ORDER: ClassVar[str] = 'customer_sales_order'
FLAG_DELIVERY_OPTION: ClassVar[str] = 'delivery_option'
FLAG_DISCOUNT: ClassVar[str] = 'discount'
FLAG_HAS_VARIATIONS: ClassVar[str] = 'has_variations'
FLAG_IS_OUT_OF_STOCK: ClassVar[str] = 'is_out_of_stock'
FLAG_LATENCY_DELIVERY_DAYS: ClassVar[str] = 'latency_delivery_days'
FLAG_LATENCY_MANUFACTURE: ClassVar[str] = 'latency_manufacture'
FLAG_MANUFACTURING_PURCHASE_ORDER: ClassVar[str] = 'manufacturing_purchase_order'
FLAG_ORDER_ITEMS: ClassVar[str] = 'order_items'
FLAG_PLANT: ClassVar[str] = 'plant'
FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL: ClassVar[str] = 'price_total_local_vat_excl'
FLAG_PRICE_TOTAL_LOCAL_VAT_INCL: ClassVar[str] = 'price_total_local_vat_incl'
FLAG_PRICE_UNIT_LOCAL_VAT_EXCL: ClassVar[str] = 'price_unit_local_vat_excl'
FLAG_PRICE_UNIT_LOCAL_VAT_INCL: ClassVar[str] = 'price_unit_local_vat_incl'
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_QUANTITY_ORDERED: ClassVar[str] = 'quantity_ordered'
FLAG_QUANTITY_RECEIVED: ClassVar[str] = 'quantity_received'
FLAG_STOCK_ITEM: ClassVar[str] = 'stock_item'
FLAG_STORAGE_LOCATION: ClassVar[str] = 'storage_location'
FLAG_SUPPLIER: ClassVar[str] = 'supplier'
FLAG_SUPPLIER_ADDRESS: ClassVar[str] = 'supplier_address'
FLAG_SUPPLIER_PURCHASE_ORDER: ClassVar[str] = 'supplier_purchase_order'
FLAG_TEXT: ClassVar[str] = 'text'
FLAG_UNIT_MEASUREMENT_LATENCY_MANUFACTURE: ClassVar[str] = 'unit_measurement_latency_manufacture'
FLAG_UNIT_MEASUREMENT_QUANTITY: ClassVar[str] = 'unit_measurement_quantity'
FLAG_VALUE_TEXT: ClassVar[str] = 'value_text'
def __repr__(self):
return str(self.__dict__)

View File

@@ -1,161 +0,0 @@
"""
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.forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
from extensions import db
from helpers.helper_app import Helper_App
# external
from datetime import datetime, timedelta
import locale
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)
Helper_App.console_log(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_product_price_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

@@ -1,200 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Supplier Business Object
Description:
Business object for supplier
"""
# internal
import lib.argument_validation as av
from business_objects.store.supplier_address import Supplier_Address
from business_objects.currency import Currency
from business_objects.db_base import Get_Many_Parameters_Base
from business_objects.store.store_base import Store_Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from pydantic import BaseModel
from typing import ClassVar
class Supplier(db.Model, Store_Base):
FLAG_DEPARTMENT_CONTACT: ClassVar[str] = 'department_contact'
FLAG_NAME_COMPANY: ClassVar[str] = 'name_company'
FLAG_NAME_CONTACT: ClassVar[str] = 'name_contact'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_SUPPLIER
NAME_ATTR_OPTION_TEXT: ClassVar[str] = FLAG_NAME_COMPANY
__tablename__ = 'Shop_Supplier'
id_supplier = db.Column(db.Integer, primary_key=True)
# id_address = db.Column(db.Integer)
id_currency = db.Column(db.Integer)
name_company = db.Column(db.String(255))
name_contact = db.Column(db.String(255))
department_contact = db.Column(db.String(255))
phone_number = db.Column(db.String(50))
fax = db.Column(db.String(50))
email = db.Column(db.String(255))
website = db.Column(db.String(255))
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.addresses = []
self.currency = None
@classmethod
def from_DB_supplier(cls, query_row):
supplier = cls()
supplier.id_supplier = query_row[0]
# supplier.id_address = query_row[1]
# supplier.address = Supplier_Address.from_DB_supplier(query_row)
supplier.id_currency = query_row[1]
supplier.currency = Currency.from_DB_supplier(query_row)
supplier.name_company = query_row[4]
supplier.name_contact = query_row[5]
supplier.department_contact = query_row[6]
supplier.phone_number = query_row[7]
supplier.fax = query_row[8]
supplier.email = query_row[9]
supplier.website = query_row[10]
supplier.active = av.input_bool(query_row[11], 'active', f'{cls.__name__}.from_DB_supplier')
return supplier
@classmethod
def from_DB_supplier_purchase_order(cls, query_row):
supplier = cls()
supplier.id_supplier = query_row[1]
supplier.name_company = query_row[2]
return supplier
def __repr__(self):
return f'''
id: {self.id_supplier},
id_currency: {self.id_currency},
name_company: {self.name_company},
name_contact: {self.name_contact},
department_contact: {self.department_contact},
phone_number: {self.phone_number},
fax: {self.fax},
email: {self.email},
website: {self.website},
active: {self.active},
addresses: {self.addresses}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_SUPPLIER: self.id_supplier,
# self.ATTR_ID_ADDRESS: self.id_address,
self.ATTR_ID_CURRENCY: self.id_currency,
self.FLAG_NAME_COMPANY: self.name_company,
self.FLAG_NAME_CONTACT: self.name_contact,
self.FLAG_DEPARTMENT_CONTACT: self.department_contact,
self.FLAG_PHONE_NUMBER: self.phone_number,
self.FLAG_FAX: self.fax,
self.FLAG_EMAIL: self.email,
self.FLAG_WEBSITE: self.website,
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json')
}
def to_json_option(self):
return {
'value': self.id_supplier,
'text': self.name_company
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
supplier = cls()
supplier.id_supplier = json[cls.ATTR_ID_SUPPLIER]
supplier.id_currency = json[cls.ATTR_ID_CURRENCY]
supplier.name_company = json[cls.FLAG_NAME_COMPANY]
supplier.name_contact = json[cls.FLAG_NAME_CONTACT]
supplier.department_contact = json[cls.FLAG_DEPARTMENT_CONTACT]
supplier.phone_number = json[cls.FLAG_PHONE_NUMBER]
supplier.fax = json[cls.FLAG_FAX]
supplier.email = json[cls.FLAG_EMAIL]
supplier.website = json[cls.FLAG_WEBSITE]
supplier.active = json[cls.FLAG_ACTIVE]
addresses = json.get(cls.FLAG_SUPPLIER_ADDRESS, [])
supplier.addresses = [Supplier_Address.from_json(address) for address in addresses]
return supplier
def get_address_active(self):
for address in self.addresses:
if address.active:
return address
return Supplier_Address()
address = Supplier_Address()
address.postcode = ''
return address
class Parameters_Supplier(Get_Many_Parameters_Base):
a_get_all_supplier: bool
a_get_inactive_supplier: bool
a_ids_supplier: str
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_default(cls):
return cls(
a_get_all_supplier = True,
a_get_inactive_supplier = False,
a_ids_supplier = '',
)
@classmethod
def from_filters_supplier(cls, form):
parameters = cls.get_default()
parameters.a_get_inactive_supplier = form.active.data
return parameters
class Supplier_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Supplier_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_supplier: int = db.Column(db.Integer)
id_currency: int = db.Column(db.Integer)
# id_address: int = db.Column(db.Integer)
name_company: str = db.Column(db.String(255))
name_contact: str = db.Column(db.String(255))
department_contact: str = db.Column(db.String(255))
phone_number: str = db.Column(db.String(50))
fax: str = db.Column(db.String(50))
email: str = db.Column(db.String(255))
website: str = db.Column(db.String(255))
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_supplier(cls, supplier):
row = cls()
row.id_supplier = supplier.id_supplier
row.id_currency = supplier.id_currency
# row.id_address = supplier.id_address
row.name_company = supplier.name_company
row.name_contact = supplier.name_contact
row.department_contact = supplier.department_contact
row.phone_number = supplier.phone_number
row.fax = supplier.fax
row.email = supplier.email
row.website = supplier.website
row.active = 1 if supplier.active else 0
return row
def __repr__(self):
return f'''
id_supplier: {self.id_supplier}
id_currency: {self.id_currency}
name_company: {self.name_company}
name_contact: {self.name_contact}
department_contact: {self.department_contact}
phone_number: {self.phone_number}
fax: {self.fax}
email: {self.email}
website: {self.website}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -1,146 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Supplier Address Business Object
Description:
Business object for supplier address
"""
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from business_objects.region import Region
from extensions import db
from helpers.helper_app import Helper_App
# external
from typing import ClassVar
from flask import jsonify
class Supplier_Address(db.Model, Store_Base):
FLAG_ADDRESS_LINE_1: ClassVar[str] = 'address_line_1'
FLAG_ADDRESS_LINE_2: ClassVar[str] = 'address_line_2'
FLAG_CITY: ClassVar[str] = 'city'
FLAG_COUNTY: ClassVar[str] = 'county'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_ADDRESS
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_POSTCODE
__tablename__ = 'Shop_Supplier_Address'
id_address = db.Column(db.Integer, primary_key=True)
id_supplier = db.Column(db.Integer)
id_region = db.Column(db.Integer)
postcode = db.Column(db.String(20))
address_line_1 = db.Column(db.String(256))
address_line_2 = db.Column(db.String(256))
city = db.Column(db.String(256))
county = db.Column(db.String(256))
active = db.Column(db.Boolean)
# region = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.region = Region()
@classmethod
def from_DB_supplier(cls, query_row):
address = cls()
address.id_supplier = query_row[0]
address.id_address = query_row[1]
address.id_region = query_row[2]
address.region = Region.from_DB_supplier(query_row)
address.postcode = query_row[4]
address.address_line_1 = query_row[5]
address.address_line_2 = query_row[6]
address.city = query_row[7]
address.county = query_row[8]
address.active = av.input_bool(query_row[9], 'active', f'{cls.__name__}.from_DB_supplier')
return address
def __repr__(self):
return f'''
{self.ATTR_ID_ADDRESS}: {self.id_address}
{self.ATTR_ID_SUPPLIER}: {self.id_supplier}
{self.FLAG_REGION}: {self.region}
{self.FLAG_POSTCODE}: {self.postcode}
{self.FLAG_ADDRESS_LINE_1}: {self.address_line_1}
{self.FLAG_ADDRESS_LINE_2}: {self.address_line_2}
{self.FLAG_CITY}: {self.city}
{self.FLAG_COUNTY}: {self.county}
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
Helper_App.console_log(f'{self.__class__.__name__}.to_json\n{self.__dict__}\n{self}')
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_ADDRESS: self.id_address,
self.ATTR_ID_SUPPLIER: self.id_supplier,
self.FLAG_REGION: self.region.to_json(),
self.FLAG_POSTCODE: self.postcode,
self.FLAG_ADDRESS_LINE_1: self.address_line_1,
self.FLAG_ADDRESS_LINE_2: self.address_line_2,
self.FLAG_CITY: self.city,
self.FLAG_COUNTY: self.county,
self.FLAG_ACTIVE: 1 if av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0
}
def to_json_str(self):
return jsonify(self.to_json())
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
address = cls()
address.id_address = json[cls.ATTR_ID_ADDRESS]
address.id_supplier = json[cls.ATTR_ID_SUPPLIER]
address.id_region = json[cls.ATTR_ID_REGION]
address.region = Region()
address.region.id_region = json[cls.ATTR_ID_REGION]
address.postcode = json[cls.FLAG_POSTCODE]
address.address_line_1 = json[cls.FLAG_ADDRESS_LINE_1]
address.address_line_2 = json.get(cls.FLAG_ADDRESS_LINE_2, '')
address.city = json[cls.FLAG_CITY]
address.county = json[cls.FLAG_COUNTY]
address.active = json[cls.FLAG_ACTIVE]
return address
class Supplier_Address_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Supplier_Address_Temp'
__table_args__ = { 'extend_existing': True }
id_address: int = db.Column(db.Integer, primary_key=True)
id_supplier: int = db.Column(db.Integer)
id_region: int = db.Column(db.Integer)
postcode: str = db.Column(db.String(20))
address_line_1: str = db.Column(db.String(256))
address_line_2: str = db.Column(db.String(256))
city: str = db.Column(db.String(256))
county: str = db.Column(db.String(256))
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
@classmethod
def from_supplier_address(cls, address):
row = cls()
row.id_address = address.id_address
row.id_supplier = address.id_supplier
row.id_region = address.id_region
row.postcode = address.postcode
row.address_line_1 = address.address_line_1
row.address_line_2 = address.address_line_2
row.city = address.city
row.county = address.county
row.active = 1 if address.active else 0
return row
def __repr__(self):
return f'''
id_address: {self.id_address}
id_supplier: {self.id_supplier}
id_region: {self.id_region}
postcode: {self.postcode}
address_line_1: {self.address_line_1}
address_line_2: {self.address_line_2}
city: {self.city}
county: {self.county}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -1,343 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Supplier_Purchase_Order Business Object
Description:
Business object for supplier_purchase_order
"""
# internal
import lib.argument_validation as av
from business_objects.currency import Currency
from business_objects.db_base import Get_Many_Parameters_Base
from business_objects.store.store_base import Store_Base
from business_objects.store.supplier import Supplier
from extensions import db
from helpers.helper_app import Helper_App
# external
from pydantic import BaseModel
from typing import ClassVar, Optional
from datetime import datetime
class Supplier_Purchase_Order(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_SUPPLIER_PURCHASE_ORDER
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Supplier_Purchase_Order'
id_order = db.Column(db.Integer, primary_key=True)
id_supplier = db.Column(db.Integer)
id_currency = db.Column(db.Integer)
cost_total_local_VAT_excl = db.Column(db.Float)
cost_total_local_VAT_incl = db.Column(db.Float)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
name = db.Column(db.String(255))
# items: list = None
def __init__(self):
super().__init__()
Store_Base.__init__(self)
self.currency = None
self.items = []
self.supplier = None
@classmethod
def from_DB_supplier_purchase_order(cls, query_row):
supplier_purchase_order = cls()
supplier_purchase_order.id_order = query_row[0]
supplier_purchase_order.id_supplier = query_row[1]
supplier_purchase_order.supplier = Supplier.from_DB_supplier_purchase_order(query_row)
supplier_purchase_order.id_currency = query_row[3]
supplier_purchase_order.currency = Currency.from_DB_supplier_purchase_order(query_row)
supplier_purchase_order.cost_total_local_VAT_excl = query_row[6]
supplier_purchase_order.cost_total_local_VAT_incl = query_row[7]
supplier_purchase_order.active = av.input_bool(query_row[8], 'active', f'{cls.__name__}.from_DB_supplier_purchase_order')
supplier_purchase_order.created_on = query_row[9]
supplier_purchase_order.name = query_row[10]
return supplier_purchase_order
def __repr__(self):
return f'''
{self.ATTR_ID_SUPPLIER_PURCHASE_ORDER}: {self.id_order},
{self.ATTR_ID_SUPPLIER}: {self.id_supplier},
{self.FLAG_SUPPLIER}: {self.supplier},
{self.ATTR_ID_CURRENCY}: {self.id_currency},
{self.FLAG_CURRENCY}: {self.currency},
{self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL}: {self.cost_total_local_VAT_excl},
{self.FLAG_COST_TOTAL_LOCAL_VAT_INCL}: {self.cost_total_local_VAT_incl},
{self.FLAG_ACTIVE}: {self.active},
{self.FLAG_CREATED_ON}: {self.created_on},
{self.FLAG_NAME}: {self.name}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_SUPPLIER_PURCHASE_ORDER: self.id_order,
self.ATTR_ID_SUPPLIER: self.id_supplier,
self.FLAG_SUPPLIER: self.supplier.to_json(),
self.ATTR_ID_CURRENCY: self.id_currency,
self.FLAG_CURRENCY: self.currency.to_json(),
self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL: self.cost_total_local_VAT_excl,
self.FLAG_COST_TOTAL_LOCAL_VAT_INCL: self.cost_total_local_VAT_incl,
self.FLAG_ORDER_ITEMS: [item.to_json() for item in self.items],
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
self.FLAG_CREATED_ON: self.created_on,
self.FLAG_NAME: self.name,
}
def to_json_option(self):
return {
'value': self.id_order,
'text': self.name,
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
supplier_purchase_order = cls()
supplier_purchase_order.id_order = json[cls.ATTR_ID_SUPPLIER_PURCHASE_ORDER]
supplier_purchase_order.id_supplier = json[cls.ATTR_ID_SUPPLIER]
supplier_purchase_order.id_currency = json[cls.ATTR_ID_CURRENCY]
supplier_purchase_order.cost_total_local_VAT_excl = json[cls.FLAG_COST_TOTAL_LOCAL_VAT_EXCL]
supplier_purchase_order.cost_total_local_VAT_incl = json[cls.FLAG_COST_TOTAL_LOCAL_VAT_INCL]
supplier_purchase_order.items = [Supplier_Purchase_Order_Product_Link.from_json(item) for item in json[cls.FLAG_ORDER_ITEMS]]
supplier_purchase_order.active = json[cls.FLAG_ACTIVE]
supplier_purchase_order.created_on = json.get(cls.FLAG_CREATED_ON, None)
supplier_purchase_order.created_by = json.get(cls.FLAG_CREATED_BY, None)
supplier_purchase_order.name = json.get(cls.FLAG_NAME, None)
return supplier_purchase_order
class Supplier_Purchase_Order_Product_Link(db.Model, Store_Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Store_Base.ATTR_ID_SUPPLIER_PURCHASE_ORDER_PRODUCT_LINK
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Store_Base.FLAG_NAME
__tablename__ = 'Shop_Supplier_Purchase_Order_Product_Link'
id_link = db.Column(db.Integer, primary_key=True)
id_order = db.Column(db.Integer)
id_category = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
csv_id_pairs_variation = db.Column(db.String)
id_unit_quantity = db.Column(db.Integer)
name_permutation = db.Column(db.String(255))
quantity_ordered = db.Column(db.Float)
quantity_received = db.Column(db.Float)
latency_delivery_days = db.Column(db.Integer)
display_order = db.Column(db.Integer)
cost_total_local_VAT_excl = db.Column(db.Float)
cost_total_local_VAT_incl = db.Column(db.Float)
cost_unit_local_VAT_excl = db.Column(db.Float)
cost_unit_local_VAT_incl = db.Column(db.Float)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
# self.unit_quantity = None
@classmethod
def from_DB_supplier_purchase_order(cls, query_row):
link = cls()
link.id_link = query_row[0]
link.id_order = query_row[1]
link.id_category = query_row[2]
link.id_product = query_row[3]
link.id_permutation = query_row[4]
link.name_permutation = query_row[5]
link.csv_id_pairs_variation = query_row[6]
link.id_unit_quantity = query_row[7]
link.quantity_ordered = query_row[8]
link.quantity_received = query_row[9]
link.latency_delivery_days = query_row[10]
link.display_order = query_row[11]
link.cost_total_local_VAT_excl = query_row[12]
link.cost_total_local_VAT_incl = query_row[13]
link.cost_unit_local_VAT_excl = query_row[14]
link.cost_unit_local_VAT_incl = query_row[15]
link.active = query_row[16]
return link
def __repr__(self):
return f'''
{self.ATTR_ID_SUPPLIER_PURCHASE_ORDER_PRODUCT_LINK}: {self.id_link},
{self.ATTR_ID_SUPPLIER_PURCHASE_ORDER}: {self.id_order},
{self.ATTR_ID_PRODUCT_CATEGORY}: {self.id_category},
{self.ATTR_ID_PRODUCT}: {self.id_product},
{self.ATTR_ID_PRODUCT_PERMUTATION}: {self.id_permutation},
{self.FLAG_NAME}: {self.name_permutation},
{self.FLAG_PRODUCT_VARIATIONS}: {self.csv_id_pairs_variation},
{self.ATTR_ID_UNIT_MEASUREMENT_QUANTITY}: {self.id_unit_quantity},
{self.FLAG_QUANTITY_ORDERED}: {self.quantity_ordered},
{self.FLAG_QUANTITY_RECEIVED}: {self.quantity_received},
{self.FLAG_LATENCY_DELIVERY_DAYS}: {self.latency_delivery_days},
{self.FLAG_DISPLAY_ORDER}: {self.display_order},
{self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL}: {self.cost_total_local_VAT_excl},
{self.FLAG_COST_TOTAL_LOCAL_VAT_INCL}: {self.cost_total_local_VAT_incl},
{self.FLAG_COST_UNIT_LOCAL_VAT_EXCL}: {self.cost_unit_local_VAT_excl},
{self.FLAG_COST_UNIT_LOCAL_VAT_INCL}: {self.cost_unit_local_VAT_incl},
{self.FLAG_ACTIVE}: {self.active}
'''
def to_json(self):
return {
**self.get_shared_json_attributes(self),
self.ATTR_ID_SUPPLIER_PURCHASE_ORDER_PRODUCT_LINK: self.id_link,
self.ATTR_ID_SUPPLIER_PURCHASE_ORDER: self.id_order,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category,
self.ATTR_ID_PRODUCT: self.id_product,
self.ATTR_ID_PRODUCT_PERMUTATION: self.id_permutation,
self.FLAG_NAME: self.name_permutation,
self.FLAG_PRODUCT_VARIATIONS: self.csv_id_pairs_variation,
self.ATTR_ID_UNIT_MEASUREMENT_QUANTITY: self.id_unit_quantity,
self.FLAG_QUANTITY_ORDERED: self.quantity_ordered,
self.FLAG_QUANTITY_RECEIVED: self.quantity_received,
self.FLAG_LATENCY_DELIVERY_DAYS: self.latency_delivery_days,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_COST_TOTAL_LOCAL_VAT_EXCL: self.cost_total_local_VAT_excl,
self.FLAG_COST_TOTAL_LOCAL_VAT_INCL: self.cost_total_local_VAT_incl,
self.FLAG_COST_UNIT_LOCAL_VAT_EXCL: self.cost_unit_local_VAT_excl,
self.FLAG_COST_UNIT_LOCAL_VAT_INCL: self.cost_unit_local_VAT_incl,
self.FLAG_ACTIVE: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
}
def to_json_option(self):
return {
'value': self.id_order,
'text': self.name_permutation,
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
link = cls()
link.id_link = json[cls.ATTR_ID_SUPPLIER_PURCHASE_ORDER_PRODUCT_LINK]
link.id_order = json[cls.ATTR_ID_SUPPLIER_PURCHASE_ORDER]
link.id_category = json.get(cls.ATTR_ID_PRODUCT_CATEGORY, None)
link.id_product = json.get(cls.ATTR_ID_PRODUCT, None)
link.id_permutation = json.get(cls.ATTR_ID_PRODUCT_PERMUTATION, None)
link.name_permutation = json.get(cls.FLAG_NAME, None)
link.csv_id_pairs_variation = json.get(cls.FLAG_PRODUCT_VARIATIONS, '')
link.id_unit_quantity = json[cls.ATTR_ID_UNIT_MEASUREMENT_QUANTITY]
link.quantity_ordered = json[cls.FLAG_QUANTITY_ORDERED]
link.quantity_received = json[cls.FLAG_QUANTITY_RECEIVED]
link.latency_delivery_days = json[cls.FLAG_LATENCY_DELIVERY_DAYS]
link.display_order = json[cls.FLAG_DISPLAY_ORDER]
link.cost_total_local_VAT_excl = json[cls.FLAG_COST_TOTAL_LOCAL_VAT_EXCL]
link.cost_total_local_VAT_incl = json[cls.FLAG_COST_TOTAL_LOCAL_VAT_INCL]
link.cost_unit_local_VAT_excl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_EXCL, None)
link.cost_unit_local_VAT_incl = json.get(cls.FLAG_COST_UNIT_LOCAL_VAT_INCL, None)
link.active = json[cls.FLAG_ACTIVE]
return link
class Parameters_Supplier_Purchase_Order(Get_Many_Parameters_Base):
a_get_all_supplier: bool
a_get_inactive_supplier: bool
a_ids_supplier: str
a_get_all_order: bool
a_get_inactive_order: bool
a_ids_order: str
a_ids_permutation: str
a_date_from: Optional[datetime]
a_date_to: Optional[datetime]
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_default(cls):
return cls(
a_get_all_supplier = True,
a_get_inactive_supplier = False,
a_ids_supplier = '',
a_get_all_order = True,
a_get_inactive_order = False,
a_ids_order = '',
a_ids_permutation = '',
a_date_from = None,
a_date_to = None
)
@classmethod
def from_filters_supplier_purchase_order(cls, form):
parameters = cls.get_default()
parameters.a_get_inactive_order = form.active.data
parameters.a_date_from = None if form.date_from.data == '' else form.date_from.data
parameters.a_date_to = None if form.date_to.data == '' else form.date_to.data
return parameters
class Supplier_Purchase_Order_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Supplier_Purchase_Order_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_order: int = db.Column(db.Integer)
id_supplier_ordered: int = db.Column(db.Integer)
id_currency_cost: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_supplier_purchase_order(cls, supplier_purchase_order):
row = cls()
row.id_order = supplier_purchase_order.id_order
row.id_supplier_ordered = supplier_purchase_order.id_supplier
row.id_currency_cost = supplier_purchase_order.id_currency
row.active = 1 if supplier_purchase_order.active else 0
return row
def __repr__(self):
return f'''
id_order: {self.id_order}
id_supplier_ordered: {self.id_supplier_ordered}
id_currency_cost: {self.id_currency_cost}
active: {self.active}
guid: {self.guid}
'''
class Supplier_Purchase_Order_Product_Link_Temp(db.Model, Store_Base):
__tablename__: ClassVar[str] = 'Shop_Supplier_Purchase_Order_Product_Link_Temp'
__table_args__ = { 'extend_existing': True }
id_temp: int = db.Column(db.Integer, primary_key=True, autoincrement=True)
id_link = db.Column(db.Integer)
id_order = db.Column(db.Integer)
id_product = db.Column(db.Integer)
id_permutation = db.Column(db.Integer)
csv_list_variations = db.Column(db.String)
id_unit_quantity = db.Column(db.Integer)
quantity_ordered = db.Column(db.Float)
quantity_received = db.Column(db.Float)
latency_delivery_days = db.Column(db.Integer)
display_order = db.Column(db.Integer)
cost_total_local_VAT_excl = db.Column(db.Float)
cost_total_local_VAT_incl = db.Column(db.Float)
active = db.Column(db.Boolean)
guid = db.Column(db.String(36))
def __init__(self):
super().__init__()
self.id_temp = None
@classmethod
def from_supplier_purchase_order_product_link(cls, supplier_purchase_order_product_link):
row = cls()
row.id_link = supplier_purchase_order_product_link.id_link
row.id_order = supplier_purchase_order_product_link.id_order
row.id_product = supplier_purchase_order_product_link.id_product
row.id_permutation = supplier_purchase_order_product_link.id_permutation
row.csv_list_variations = supplier_purchase_order_product_link.csv_id_pairs_variation
row.id_unit_quantity = supplier_purchase_order_product_link.id_unit_quantity
row.quantity_ordered = supplier_purchase_order_product_link.quantity_ordered
row.quantity_received = supplier_purchase_order_product_link.quantity_received
row.latency_delivery_days = supplier_purchase_order_product_link.latency_delivery_days
row.display_order = supplier_purchase_order_product_link.display_order
row.cost_total_local_VAT_excl = supplier_purchase_order_product_link.cost_total_local_VAT_excl
row.cost_total_local_VAT_incl = supplier_purchase_order_product_link.cost_total_local_VAT_incl
row.active = 1 if supplier_purchase_order_product_link.active else 0
return row
def __repr__(self):
return f'''
id_link: {self.id_link}
id_order: {self.id_order}
id_product: {self.id_product}
id_permutation: {self.id_permutation}
csv_list_variations: {self.csv_list_variations}
id_unit_quantity: {self.id_unit_quantity}
quantity_ordered: {self.quantity_ordered}
quantity_received: {self.quantity_received}
latency_delivery_days: {self.latency_delivery_days}
display_order: {self.display_order}
cost_total_local_VAT_excl: {self.cost_total_local_VAT_excl}
cost_total_local_VAT_incl: {self.cost_total_local_VAT_incl}
active: {self.active}
guid: {self.guid}
'''

View File

@@ -40,8 +40,8 @@ class Config:
# Auth0
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
# SESSION_COOKIE_SAMESITE = 'Lax'
# PERMANENT_SESSION_LIFETIME = 3600
SESSION_COOKIE_SAMESITE = 'Strict'
REMEMBER_COOKIE_SECURE = True
WTF_CSRF_ENABLED = True
# WTF_CSRF_CHECK_DEFAULT = False # We'll check it manually for API routes
# WTF_CSRF_HEADERS = ['X-CSRFToken'] # Accept CSRF token from this header
@@ -52,7 +52,7 @@ class Config:
DOMAIN_AUTH0 = os.getenv('DOMAIN_AUTH0')
ID_TOKEN_USER = 'user'
# PostgreSQL
DB_NAME = os.getenv('partsltd')
DB_NAME = os.getenv('partsltd_prod')
DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_HOST = os.getenv('DB_HOST')
@@ -80,9 +80,15 @@ class Config:
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.getenv('MAIL_DEFAULT_SENDER')
MAIL_CONTACT_PUBLIC = os.getenv('MAIL_CONTACT_PUBLIC')
"""
# Recaptcha
RECAPTCHA_PUBLIC_KEY = os.getenv('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = os.getenv('RECAPTCHA_PRIVATE_KEY')
"""
# ALTCHA
ALTCHA_API_KEY = os.getenv('ALTCHA_API_KEY')
ALTCHA_SECRET_KEY = os.getenv('ALTCHA_SECRET_KEY')
ALTCHA_REGION = 'eu'
class DevelopmentConfig(Config):
is_development = True

View File

@@ -13,21 +13,25 @@ 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 forms.contact import Form_Contact
from helpers.helper_app import Helper_App
from models.model_view_contact import Model_View_Contact
from models.model_view_home import Model_View_Home
from models.model_view_services import Model_View_Services
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 flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app, flash
from flask_mail import Mail, Message
from extensions import db, oauth, mail
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
import json
import base64
import hmac
import hashlib
import datetime
from altcha import ChallengeOptions, create_challenge, verify_solution
routes_core = Blueprint('routes_core', __name__)
@@ -44,10 +48,7 @@ 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 else '') + (' ' if user.firstname and user.surname else '') + (user.surname if user.surname else '')
model = Model_View_Contact(form)
html_body = render_template('pages/core/_contact.html', model = model)
except Exception as e:
@@ -58,41 +59,100 @@ def contact():
def contact_post():
try:
form = Form_Contact()
Helper_App.console_log(f"Form submitted: {request.form}")
Helper_App.console_log(f"ALTCHA data in request: {request.form.get('altcha')}")
if form.validate_on_submit():
# Handle form submission
email = form.email.data
CC = form.CC.data # not in use
name = form.name.data
message = form.message.data
# send email
mailItem = Message("PARTS Website Contact Us Message", recipients=[current_app.config['MAIL_CONTACT_PUBLIC']])
mailItem.body = f"Dear Lord Edward Middleton-Smith,\n\n{message}\n\nKind regards,\n{name}\n{email}"
mail.send(mailItem)
return "Submitted."
try:
email = form.email.data
# CC = form.CC.data # not in use
contact_name = form.contact_name.data
company_name = form.company_name.data
message = form.message.data
receive_marketing = form.receive_marketing.data
receive_marketing_text = "I would like to receive marketing emails." if receive_marketing else ""
# send email
mailItem = Message("PARTS Website Contact Us Message", recipients=[current_app.config['MAIL_CONTACT_PUBLIC']])
mailItem.body = f"Dear Lord Edward Middleton-Smith,\n\n{message}\n{receive_marketing_text}\nKind regards,\n{contact_name}\n{company_name}\n{email}"
mail.send(mailItem)
flash('Thank you for your message. We will get back to you soon!', 'success')
return "Submitted."
except Exception as e:
return f"Error: {e}"
print(f"Form validation errors: {form.errors}")
return "Invalid. Failed to submit."
# html_body = render_template('pages/core/_contact.html', model = model)
except Exception as e:
return jsonify(error=str(e)), 403
@routes_core.route(Model_View_Services.HASH_PAGE_SERVICES, methods=['GET', 'POST'])
def services():
try:
model = Model_View_Services()
html_body = render_template('pages/core/_services.html', model = model)
except Exception as e:
return jsonify(error=str(e)), 403
return html_body
@routes_core.route(Model_View_Contact.HASH_ALTCHA_CREATE_CHALLENGE, methods=['GET'])
def create_altcha_challenge():
options = ChallengeOptions(
expires = datetime.datetime.now() + datetime.timedelta(hours=1),
max_number = 100000, # The maximum random number
hmac_key = current_app.config["ALTCHA_SECRET_KEY"],
)
challenge = create_challenge(options)
print("Challenge created:", challenge)
# return jsonify({"challenge": challenge})
return jsonify({
"algorithm": challenge.algorithm,
"challenge": challenge.challenge,
"salt": challenge.salt,
"signature": challenge.signature,
})
@routes_core.route(Model_View_Admin_Home.HASH_PAGE_ADMIN_HOME, methods=['GET', 'POST'])
def admin_home():
try:
model = Model_View_Admin_Home()
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', callback = Model_View_Admin_Home.HASH_PAGE_ADMIN_HOME))
return redirect(url_for('routes_core.home'))
if not (model.user.can_admin_store or model.user.can_admin_user):
return redirect(url_for('routes_core.home'))
html_body = render_template('pages/core/_admin_home.html', model = model)
except Exception as e:
return jsonify(error=str(e)), 403
return html_body
"""
def verify_altcha_signature(payload):
"" "Verify the ALTCHA signature"" "
if 'algorithm' not in payload or 'signature' not in payload or 'verificationData' not in payload:
return False
algorithm = payload['algorithm']
signature = payload['signature']
verification_data = payload['verificationData']
# Calculate SHA hash of the verification data
if algorithm == 'SHA-256':
hash_func = hashlib.sha256
else:
# Fallback to SHA-256 if algorithm not specified
hash_func = hashlib.sha256
# Calculate the hash of verification_data
data_hash = hash_func(verification_data.encode('utf-8')).digest()
# Calculate the HMAC signature
calculated_signature = hmac.new(
current_app.config["ALTCHA_SECRET_KEY"].encode('utf-8'),
data_hash,
hash_func
).hexdigest()
# Compare the calculated signature with the provided signature
return hmac.compare_digest(calculated_signature, signature)
def create_altcha_dummy_signature(challenge):
# Example payload to verify
payload = {
"algorithm": challenge.algorithm,
"challenge": challenge.challenge,
"number": 12345, # Example number
"salt": challenge.salt,
"signature": challenge.signature,
}
return payload
@routes_core.route(Model_View_Contact.HASH_ALTCHA_VERIFY_SOLUTION, methods=['POST'])
def verify_altcha_challenge():
payload = request.json
ok, err = verify_solution(payload, current_app.config["ALTCHA_SECRET_KEY"], check_expires=True)
if err:
return jsonify({"error": err}), 400
elif ok:
return jsonify({"verified": True})
else:
return jsonify({"verified": False}), 403
"""

View File

@@ -1,82 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Manufacturing_Purchase_Order 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.manufacturing_purchase_order import Manufacturing_Purchase_Order
from forms.store.manufacturing_purchase_order import Filters_Manufacturing_Purchase_Order
from models.model_view_store_manufacturing_purchase_order import Model_View_Store_Manufacturing_Purchase_Order
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from datetime import datetime
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
import requests
routes_store_manufacturing_purchase_order = Blueprint('routes_store_manufacturing_purchase_order', __name__)
@routes_store_manufacturing_purchase_order.route(Model_View_Store_Manufacturing_Purchase_Order.HASH_PAGE_STORE_MANUFACTURING_PURCHASE_ORDERS, methods=['GET'])
def manufacturing_purchase_orders():
Helper_App.console_log('manufacturing_purchase_orders')
try:
form_filters = Filters_Manufacturing_Purchase_Order.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Manufacturing_Purchase_Order()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Manufacturing_Purchase_Order(form_filters)
if not model.is_user_logged_in:
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_manufacturing_purchase_orders.html', model = model, datetime = datetime)
@routes_store_manufacturing_purchase_order.route(Model_View_Store_Manufacturing_Purchase_Order.HASH_SAVE_STORE_MANUFACTURING_PURCHASE_ORDER, methods=['POST'])
def save_manufacturing_purchase_order():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Manufacturing_Purchase_Order.from_json(data[Model_View_Store_Manufacturing_Purchase_Order.FLAG_FORM_FILTERS])
Helper_App.console_log(f'form_filters: {form_filters}')
manufacturing_purchase_orders = data[Model_View_Store_Manufacturing_Purchase_Order.FLAG_MANUFACTURING_PURCHASE_ORDER]
if len(manufacturing_purchase_orders) == 0:
return jsonify({
Model_View_Store_Manufacturing_Purchase_Order.FLAG_STATUS: Model_View_Store_Manufacturing_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Manufacturing_Purchase_Order.FLAG_MESSAGE: f'No stock items.'
})
Helper_App.console_log(f'manufacturing_purchase_orders={manufacturing_purchase_orders}')
objs_manufacturing_purchase_order = []
for manufacturing_purchase_order in manufacturing_purchase_orders:
objs_manufacturing_purchase_order.append(Manufacturing_Purchase_Order.from_json(manufacturing_purchase_order))
Helper_App.console_log(f'objs_manufacturing_purchase_order={objs_manufacturing_purchase_order}')
save_errors = Model_View_Store_Manufacturing_Purchase_Order.save_manufacturing_purchase_orders(data.get('comment', 'No comment'), objs_manufacturing_purchase_order)
if len(save_errors) > 0:
return jsonify({
Model_View_Store_Manufacturing_Purchase_Order.FLAG_STATUS: Model_View_Store_Manufacturing_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Manufacturing_Purchase_Order.FLAG_MESSAGE: f'Save errors: {save_errors}'
})
model_return = Model_View_Store_Manufacturing_Purchase_Order(form_filters_old = form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in.')
return jsonify({
Model_View_Store_Manufacturing_Purchase_Order.FLAG_STATUS: Model_View_Store_Manufacturing_Purchase_Order.FLAG_SUCCESS,
Model_View_Store_Manufacturing_Purchase_Order.FLAG_DATA: model_return.convert_list_objects_to_json(model_return.manufacturing_purchase_orders)
})
except Exception as e:
return jsonify({
Model_View_Store_Manufacturing_Purchase_Order.FLAG_STATUS: Model_View_Store_Manufacturing_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Manufacturing_Purchase_Order.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -1,167 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App General
Feature: App
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.
"""
# IMPORTS
from helpers.helper_app import Helper_App
import os
import stripe
import json
from flask import Flask, render_template, render_template_string, jsonify, request, send_from_directory, redirect
from dotenv import load_dotenv, find_dotenv
# VARIABLE INSTANTIATION
key_secret = os.environ.get("KEY_SECRET_STRIPE")
key_public = os.environ.get("KEY_PUBLIC_STRIPE") # 'pk_test_51OGrxlL7BuLKjoMpfpfw7bSmZZK1MhqMoQ5VhW2jUj7YtoEejO4vqnxKPiqTHHuh9U4qqkywbPCSI9TpFKtr4SYH007KHMWs68'
# METHODS
def create_product_price():
Helper_App.console_log(f'stripe.api_key = {stripe.api_key}')
starter_subscription = stripe.Product.create(
name="Starter Subscription",
description="$12/Month subscription",
)
starter_subscription_price = stripe.Price.create(
unit_amount=1200,
currency="usd",
recurring={"interval": "month"},
product=starter_subscription['id'],
)
# Save these identifiers
Helper_App.console_log(f"Success! Here is your starter subscription product id: {starter_subscription.id}")
Helper_App.console_log(f"Success! Here is your starter subscription price id: {starter_subscription_price.id}")
return starter_subscription_price.id
def get_file_str(f_address):
f = open(f_address)
return f.read()
# Ensure environment variables are set.
price = os.getenv('PRICE')
if price is None or price == 'price_12345' or price == '':
Helper_App.console_log('You must set a Price ID in .env. Please see the README.')
exit(0)
# For sample support and debugging, not required for production:
stripe.set_app_info(
'stripe-samples/checkout-one-time-payments',
version='0.0.1',
url='https://github.com/stripe-samples/checkout-one-time-payments')
# stripe.api_version = '2020-08-27'
stripe.api_key = key_secret # os.getenv('KEY_SECRET_STRIPE')
app_dir = str(os.path.abspath(os.path.join(
__file__, "..", "..")))
static_dir = str(os.path.abspath(os.path.join(
app_dir, os.getenv("STATIC_DIR"))))
app = Flask(__name__, static_folder=static_dir,
static_url_path="", template_folder=static_dir)
@app.route('/', methods=['GET'])
def get_example():
# return render_template(f'{app_dir}\\templates\\_home.html') # f'{app_dir}\\templates\\layout.html')
# return render_template_string(get_file_str(f'{app_dir}\\templates\\_home.html')) # f'{app_dir}\\templates\\layout.html')
return render_template('../templates/_home.html')
@app.route('/config', methods=['GET'])
def get_publishable_key():
price = stripe.Price.retrieve(os.getenv('PRICE'))
return jsonify({
'publicKey': key_public, # os.getenv('KEY_PUBLIC_STRIPE'),
'unitAmount': price['unit_amount'],
'currency': price['currency']
})
# Fetch the Checkout Session to display the JSON result on the success page
@app.route('/checkout-session', methods=['GET'])
def get_checkout_session():
id = request.args.get('sessionId')
checkout_session = stripe.checkout.Session.retrieve(id)
return jsonify(checkout_session)
@app.route('/create-checkout-session', methods=['POST'])
def create_checkout_session():
quantity = request.form.get('quantity', 1)
domain_url = os.getenv('DOMAIN')
try:
# Create new Checkout Session for the order
# Other optional params include:
# [billing_address_collection] - to display billing address details on the page
# [customer] - if you have an existing Stripe Customer ID
# [payment_intent_data] - lets capture the payment later
# [customer_email] - lets you prefill the email input in the form
# [automatic_tax] - to automatically calculate sales tax, VAT and GST in the checkout page
# For full details see https://stripe.com/docs/api/checkout/sessions/create
# ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
checkout_session = stripe.checkout.Session.create(
success_url=domain_url + '/success.html?session_id={CHECKOUT_SESSION_ID}',
cancel_url=domain_url + '/canceled.html',
mode='payment',
# automatic_tax={'enabled': True},
line_items=[{
'price': os.getenv('PRICE'),
'quantity': quantity,
}]
)
return redirect(checkout_session.url, code=303)
except Exception as e:
return jsonify(error=str(e)), 403
@app.route('/webhook', methods=['POST'])
def webhook_received():
# You can use webhooks to receive information about asynchronous payment events.
# For more about our webhook events check out https://stripe.com/docs/webhooks.
webhook_secret = os.getenv('STRIPE_WEBHOOK_SECRET')
request_data = json.loads(request.data)
if webhook_secret:
# Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured.
signature = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
payload=request.data, sig_header=signature, secret=webhook_secret)
data = event['data']
except Exception as e:
return e
# Get the type of webhook event sent - used to check the status of PaymentIntents.
event_type = event['type']
else:
data = request_data['data']
event_type = request_data['type']
data_object = data['object']
Helper_App.console_log('event ' + event_type)
if event_type == 'checkout.session.completed':
Helper_App.console_log('🔔 Payment succeeded!')
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_SUCCESS})
if __name__ == '__main__':
# stripe.api_key = key_secret
# create_product_price()
# Setup Stripe python client library.
load_dotenv(find_dotenv())
app.run(port=4242, debug=True)

View File

@@ -1,86 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Product 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 Product, Parameters_Product
from datastores.datastore_store_product import DataStore_Store_Product
from forms.store.product import Filters_Product
from models.model_view_store_product import Model_View_Store_Product
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 = Blueprint('routes_store_product', __name__)
@routes_store_product.route(Model_View_Store_Product.HASH_PAGE_STORE_PRODUCTS, methods=['GET'])
def products():
Helper_App.console_log('products')
try:
form_filters = Filters_Product.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Product()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Product(form_filters)
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', data = jsonify({ Model_View_Store_Product.FLAG_CALLBACK: Model_View_Store_Product.HASH_PAGE_STORE_PRODUCTS })))
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_products.html', model = model)
@routes_store_product.route(Model_View_Store_Product.HASH_SAVE_STORE_PRODUCT, methods=['POST'])
def save_product():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Product.from_json(data[Model_View_Store_Product.FLAG_FORM_FILTERS])
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product.FLAG_STATUS: Model_View_Store_Product.FLAG_FAILURE,
Model_View_Store_Product.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
})
# filters_form = Filters_Product.from_form(form_filters)
Helper_App.console_log(f'form_filters: {form_filters}')
products = data[Model_View_Store_Product.FLAG_PRODUCT]
if len(products) == 0:
return jsonify({
Model_View_Store_Product.FLAG_STATUS: Model_View_Store_Product.FLAG_FAILURE,
Model_View_Store_Product.FLAG_MESSAGE: f'No products.'
})
objsProduct = []
for product in products:
objsProduct.append(Product.from_json(product))
# model_save = Model_View_Store_Product() # filters_product=filters_form)
Helper_App.console_log(f'objsProduct={objsProduct}')
save_errors = Model_View_Store_Product.save_products(data.get('comment', 'No comment'), objsProduct)
model_return = Model_View_Store_Product(form_filters_old=form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in')
Helper_App.console_log('nips')
return jsonify({
Model_View_Store_Product.FLAG_STATUS: Model_View_Store_Product.FLAG_SUCCESS,
Model_View_Store_Product.FLAG_DATA: model_return.category_list.to_json()
})
except Exception as e:
return jsonify({
Model_View_Store_Product.FLAG_STATUS: Model_View_Store_Product.FLAG_FAILURE,
Model_View_Store_Product.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -1,104 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Product Category 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_category import Product_Category #, Filters_Product_Category
from forms.store.product_category import Filters_Product_Category
from models.model_view_store_product_category import Model_View_Store_Product_Category
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_category = Blueprint('routes_store_product_category', __name__)
@routes_store_product_category.route(Model_View_Store_Product_Category.HASH_PAGE_STORE_PRODUCT_CATEGORIES, methods=['GET'])
def categories():
Helper_App.console_log('categories')
# data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Product_Category.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Product_Category()
Helper_App.console_log(f'form_filters={form_filters}')
"""
filters = Filters_Product_Category.get_default()
have_changed_filters = False
arg_filter_is_not_empty = request.args.get(Model_View_Store_Product_Category.FLAG_IS_NOT_EMPTY, None)
have_changed_filters = have_changed_filters or arg_filter_is_not_empty is None
Helper_App.console_log(f'arg_filter_is_not_empty={arg_filter_is_not_empty}')
filters.is_not_empty = filters.is_not_empty if arg_filter_is_not_empty is None else av.input_bool(arg_filter_is_not_empty, 'is_not_empty', 'filter_category')
arg_filter_active = request.args.get(Model_View_Store_Product_Category.FLAG_ACTIVE, None)
have_changed_filters = have_changed_filters or arg_filter_active is None
Helper_App.console_log(f'arg_filter_active={arg_filter_active}')
filters.active = filters.active if arg_filter_active is None else av.input_bool(arg_filter_active, 'active', 'filter_category')
if have_changed_filters:
Helper_App.console_log('redirecting')
return redirect(url_for('routes_store_product_category.categories', **filters.to_json()))
"""
model = Model_View_Store_Product_Category(form_filters)
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', data = jsonify({ Model_View_Store_Product_Category.FLAG_CALLBACK: Model_View_Store_Product_Category.HASH_PAGE_STORE_PRODUCT_CATEGORIES })))
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_product_categories.html', model = model)
@routes_store_product_category.route(Model_View_Store_Product_Category.HASH_SAVE_STORE_PRODUCT_CATEGORY, methods=['POST'])
def save_category():
data = Helper_App.get_request_data(request)
try:
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_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)
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_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'No categories.'
})
objsCategory = []
for category in categories:
objsCategory.append(Product_Category.from_json(category))
# model_save = Model_View_Store_Product_Category() # filters_product=filters_form)
Helper_App.console_log(f'objsCategory={objsCategory}')
errors = Model_View_Store_Product_Category.save_categories(data.get('comment', 'No comment'), objsCategory)
model_return = Model_View_Store_Product_Category(form_filters_old=form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in')
if (len(errors) > 0):
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_FAILURE,
Model_View_Store_Product_Category.FLAG_MESSAGE: f'Error saving categories.\n{model_return.convert_list_objects_to_json(errors)}'
})
return jsonify({
Model_View_Store_Product_Category.FLAG_STATUS: Model_View_Store_Product_Category.FLAG_SUCCESS,
Model_View_Store_Product_Category.FLAG_DATA: model_return.category_list.to_json()
})
except Exception as e:
return jsonify({
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

@@ -1,90 +0,0 @@
"""
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 Parameters_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(Model_View_Store_Product_Permutation.HASH_PAGE_STORE_PRODUCT_PERMUTATIONS, methods=['GET'])
def permutations():
Helper_App.console_log('permutations')
data = request.args
# Helper_App.console_log(f'data={data}\nrequest.args={request.args}\nrequest.form={request.form}\nrequest.data={request.data}\nrequest.values={request.values}\nrequest.headers={request.headers}')
try:
form_filters = Filters_Product_Permutation.from_json(data)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Product_Permutation()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Product_Permutation(form_filters)
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', data = jsonify({ Model_View_Store_Product_Permutation.FLAG_CALLBACK: Model_View_Store_Product_Permutation.HASH_PAGE_STORE_PRODUCT_PERMUTATIONS })))
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_product_permutations.html', model = model)
@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)
Helper_App.console_log(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)
Helper_App.console_log(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_old=form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in')
Helper_App.console_log('nips')
return jsonify({
Model_View_Store_Product_Permutation.FLAG_STATUS: Model_View_Store_Product_Permutation.FLAG_SUCCESS,
Model_View_Store_Product_Permutation.FLAG_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

@@ -1,87 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Product Variation 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_variation import Product_Variation, Parameters_Product_Variation
from business_objects.store.product_variation_type import Product_Variation_Type
from datastores.datastore_store_product_variation import DataStore_Store_Product_Variation
from forms.store.product_variation import Filters_Product_Variation
from models.model_view_store_product_variation import Model_View_Store_Product_Variation
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_variation = Blueprint('routes_store_product_variation', __name__)
@routes_store_product_variation.route(Model_View_Store_Product_Variation.HASH_PAGE_STORE_PRODUCT_VARIATIONS, methods=['GET'])
def product_variations():
Helper_App.console_log('product_variations')
try:
form_filters = Filters_Product_Variation.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Product_Variation()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Product_Variation(form_filters)
if not model.is_user_logged_in:
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_product_variations.html', model = model)
@routes_store_product_variation.route(Model_View_Store_Product_Variation.HASH_SAVE_STORE_PRODUCT_VARIATION, methods=['POST'])
def save_product_variation():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Product_Variation.from_json(data[Model_View_Store_Product_Variation.FLAG_FORM_FILTERS])
if not form_filters.validate_on_submit():
return jsonify({
Model_View_Store_Product_Variation.FLAG_STATUS: Model_View_Store_Product_Variation.FLAG_FAILURE,
Model_View_Store_Product_Variation.FLAG_MESSAGE: f'Filters form invalid.\n{form_filters.errors}'
})
# filters_form = Filters_Product_Variation.from_form(form_filters)
Helper_App.console_log(f'form_filters: {form_filters}')
product_variation_types = data[Model_View_Store_Product_Variation.FLAG_PRODUCT_VARIATION_TYPE]
if len(product_variation_types) == 0:
return jsonify({
Model_View_Store_Product_Variation.FLAG_STATUS: Model_View_Store_Product_Variation.FLAG_FAILURE,
Model_View_Store_Product_Variation.FLAG_MESSAGE: f'No Product Variations.'
})
objs_product_variation_type = []
for product_variation_type in product_variation_types:
objs_product_variation_type.append(Product_Variation_Type.from_json(product_variation_type))
# model_save = Model_View_Store_Product_Variation() # filters_product_variation=filters_form)
Helper_App.console_log(f'objs_product_variation_type={objs_product_variation_type}')
save_errors = Model_View_Store_Product_Variation.save_product_variations(data.get('comment', 'No comment'), objs_product_variation_type)
model_return = Model_View_Store_Product_Variation(form_filters_old = form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in')
Helper_App.console_log('nips')
return jsonify({
Model_View_Store_Product_Variation.FLAG_STATUS: Model_View_Store_Product_Variation.FLAG_SUCCESS,
Model_View_Store_Product_Variation.FLAG_DATA: model_return.convert_list_objects_to_json(model_return.variation_types)
})
except Exception as e:
return jsonify({
Model_View_Store_Product_Variation.FLAG_STATUS: Model_View_Store_Product_Variation.FLAG_FAILURE,
Model_View_Store_Product_Variation.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -1,107 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Stock Item 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.stock_item import Stock_Item
from forms.store.stock_item import Filters_Stock_Item
from models.model_view_store_stock_item import Model_View_Store_Stock_Item
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from datetime import datetime
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
import requests
routes_store_stock_item = Blueprint('routes_store_stock_item', __name__)
@routes_store_stock_item.route(Model_View_Store_Stock_Item.HASH_PAGE_STORE_STOCK_ITEMS, methods=['GET'])
def stock_items():
Helper_App.console_log('stock_items')
try:
form_filters = Filters_Stock_Item.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Stock_Item()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Stock_Item(form_filters)
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', data = jsonify({ Model_View_Store_Stock_Item.FLAG_CALLBACK: Model_View_Store_Stock_Item.HASH_PAGE_STORE_STOCK_ITEMS })))
# return requests.post(f"{current_app.config['URL_HOST']}{url_for('routes_user.login')}", json={ Model_View_Store_Stock_Item.FLAG_CALLBACK: Model_View_Store_Stock_Item.HASH_PAGE_STORE_STOCK_ITEMS })
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_stock_items.html', model = model, datetime = datetime)
@routes_store_stock_item.route(Model_View_Store_Stock_Item.HASH_SAVE_STORE_STOCK_ITEM, methods=['POST'])
def save_stock_item():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Stock_Item.from_json(data[Model_View_Store_Stock_Item.FLAG_FORM_FILTERS])
"""
if not form_filters.validate_on_submit():
error_keys = list(form_filters.errors.keys())
try:
error_keys.remove(Stock_Item.ATTR_ID_PRODUCT_CATEGORY)
""
if not av.val_int(form_filters.id_product_category.data):
form_filters.errors[Stock_Item.ATTR_ID_PRODUCT_CATEGORY] = ['Invalid category.']
""
except:
pass
try:
error_keys.remove(Stock_Item.ATTR_ID_PRODUCT)
except:
pass
if error_keys:
return jsonify({
Model_View_Store_Stock_Item.FLAG_STATUS: Model_View_Store_Stock_Item.FLAG_FAILURE,
Model_View_Store_Stock_Item.FLAG_MESSAGE: f'Form invalid.\n{form_filters.errors}'
})
"""
# filters_form = Filters_Stock_Item.from_form(form_filters)
Helper_App.console_log(f'form_filters: {form_filters}')
stock_items = data[Model_View_Store_Stock_Item.FLAG_STOCK_ITEM]
if len(stock_items) == 0:
return jsonify({
Model_View_Store_Stock_Item.FLAG_STATUS: Model_View_Store_Stock_Item.FLAG_FAILURE,
Model_View_Store_Stock_Item.FLAG_MESSAGE: f'No stock items.'
})
Helper_App.console_log(f'stock_items={stock_items}')
objs_stock_item = []
for stock_item in stock_items:
objs_stock_item.append(Stock_Item.from_json(stock_item))
# model_save = Model_View_Store_Stock_Item() # filters_product=filters_form)
Helper_App.console_log(f'objs_stock_item={objs_stock_item}')
save_errors = Model_View_Store_Stock_Item.save_stock_items(data.get('comment', 'No comment'), objs_stock_item)
if len(save_errors) > 0:
return jsonify({
Model_View_Store_Stock_Item.FLAG_STATUS: Model_View_Store_Stock_Item.FLAG_FAILURE,
Model_View_Store_Stock_Item.FLAG_MESSAGE: f'Save errors: {save_errors}'
})
model_return = Model_View_Store_Stock_Item(filters_stock_item=form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in.')
return jsonify({
Model_View_Store_Stock_Item.FLAG_STATUS: Model_View_Store_Stock_Item.FLAG_SUCCESS,
Model_View_Store_Stock_Item.FLAG_DATA: model_return.category_list.to_json()
})
except Exception as e:
return jsonify({
Model_View_Store_Stock_Item.FLAG_STATUS: Model_View_Store_Stock_Item.FLAG_FAILURE,
Model_View_Store_Stock_Item.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -1,83 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Supplier 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.supplier import Supplier
from forms.store.supplier import Filters_Supplier
from models.model_view_store_supplier import Model_View_Store_Supplier
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from datetime import datetime
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
import requests
routes_store_supplier = Blueprint('routes_store_supplier', __name__)
@routes_store_supplier.route(Model_View_Store_Supplier.HASH_PAGE_STORE_SUPPLIERS, methods=['GET'])
def suppliers():
Helper_App.console_log('suppliers')
try:
form_filters = Filters_Supplier.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Supplier()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Supplier(form_filters)
if not model.is_user_logged_in:
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_suppliers.html', model = model, datetime = datetime)
@routes_store_supplier.route(Model_View_Store_Supplier.HASH_SAVE_STORE_SUPPLIER, methods=['POST'])
def save_supplier():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Supplier.from_json(data[Model_View_Store_Supplier.FLAG_FORM_FILTERS])
Helper_App.console_log(f'form_filters: {form_filters}')
suppliers = data[Model_View_Store_Supplier.FLAG_SUPPLIER]
if len(suppliers) == 0:
return jsonify({
Model_View_Store_Supplier.FLAG_STATUS: Model_View_Store_Supplier.FLAG_FAILURE,
Model_View_Store_Supplier.FLAG_MESSAGE: f'No stock items.'
})
Helper_App.console_log(f'suppliers={suppliers}')
objs_supplier = []
for supplier in suppliers:
objs_supplier.append(Supplier.from_json(supplier))
Helper_App.console_log(f'objs_supplier={objs_supplier}')
save_errors = Model_View_Store_Supplier.save_suppliers(data.get('comment', 'No comment'), objs_supplier)
if len(save_errors) > 0:
return jsonify({
Model_View_Store_Supplier.FLAG_STATUS: Model_View_Store_Supplier.FLAG_FAILURE,
Model_View_Store_Supplier.FLAG_MESSAGE: f'Save errors: {save_errors}'
})
model_return = Model_View_Store_Supplier(form_filters_old = form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in.')
return jsonify({
Model_View_Store_Supplier.FLAG_STATUS: Model_View_Store_Supplier.FLAG_SUCCESS,
Model_View_Store_Supplier.FLAG_DATA: {supplier.id_supplier: supplier.to_json() for supplier in model_return.suppliers}
})
except Exception as e:
return jsonify({
Model_View_Store_Supplier.FLAG_STATUS: Model_View_Store_Supplier.FLAG_FAILURE,
Model_View_Store_Supplier.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -1,82 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store Supplier_Purchase_Order 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.supplier_purchase_order import Supplier_Purchase_Order
from forms.store.supplier_purchase_order import Filters_Supplier_Purchase_Order
from models.model_view_store_supplier_purchase_order import Model_View_Store_Supplier_Purchase_Order
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from datetime import datetime
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
import requests
routes_store_supplier_purchase_order = Blueprint('routes_store_supplier_purchase_order', __name__)
@routes_store_supplier_purchase_order.route(Model_View_Store_Supplier_Purchase_Order.HASH_PAGE_STORE_SUPPLIER_PURCHASE_ORDERS, methods=['GET'])
def supplier_purchase_orders():
Helper_App.console_log('supplier_purchase_orders')
try:
form_filters = Filters_Supplier_Purchase_Order.from_json(request.args)
except Exception as e:
Helper_App.console_log(f'Error: {e}')
form_filters = Filters_Supplier_Purchase_Order()
Helper_App.console_log(f'form_filters={form_filters}')
model = Model_View_Store_Supplier_Purchase_Order(form_filters)
if not model.is_user_logged_in:
return redirect(url_for('routes_core.home'))
return render_template('pages/store/_supplier_purchase_orders.html', model = model, datetime = datetime)
@routes_store_supplier_purchase_order.route(Model_View_Store_Supplier_Purchase_Order.HASH_SAVE_STORE_SUPPLIER_PURCHASE_ORDER, methods=['POST'])
def save_supplier_purchase_order():
data = Helper_App.get_request_data(request)
try:
form_filters = Filters_Supplier_Purchase_Order.from_json(data[Model_View_Store_Supplier_Purchase_Order.FLAG_FORM_FILTERS])
Helper_App.console_log(f'form_filters: {form_filters}')
supplier_purchase_orders = data[Model_View_Store_Supplier_Purchase_Order.FLAG_SUPPLIER_PURCHASE_ORDER]
if len(supplier_purchase_orders) == 0:
return jsonify({
Model_View_Store_Supplier_Purchase_Order.FLAG_STATUS: Model_View_Store_Supplier_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Supplier_Purchase_Order.FLAG_MESSAGE: f'No stock items.'
})
Helper_App.console_log(f'supplier_purchase_orders={supplier_purchase_orders}')
objs_supplier_purchase_order = []
for supplier_purchase_order in supplier_purchase_orders:
objs_supplier_purchase_order.append(Supplier_Purchase_Order.from_json(supplier_purchase_order))
Helper_App.console_log(f'objs_supplier_purchase_order={objs_supplier_purchase_order}')
save_errors, save_warnings = Model_View_Store_Supplier_Purchase_Order.save_supplier_purchase_orders(data.get('comment', 'No comment'), objs_supplier_purchase_order)
if len(save_errors) > 0:
return jsonify({
Model_View_Store_Supplier_Purchase_Order.FLAG_STATUS: Model_View_Store_Supplier_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Supplier_Purchase_Order.FLAG_MESSAGE: f'Save errors: {save_errors}'
})
model_return = Model_View_Store_Supplier_Purchase_Order(form_filters_old = form_filters)
if not model_return.is_user_logged_in:
raise Exception('User not logged in.')
return jsonify({
Model_View_Store_Supplier_Purchase_Order.FLAG_STATUS: Model_View_Store_Supplier_Purchase_Order.FLAG_SUCCESS,
Model_View_Store_Supplier_Purchase_Order.FLAG_DATA: model_return.convert_list_objects_to_json(model_return.supplier_purchase_orders)
})
except Exception as e:
return jsonify({
Model_View_Store_Supplier_Purchase_Order.FLAG_STATUS: Model_View_Store_Supplier_Purchase_Order.FLAG_FAILURE,
Model_View_Store_Supplier_Purchase_Order.FLAG_MESSAGE: f'Bad data received by controller.\n{e}'
})

View File

@@ -13,20 +13,9 @@ Datastore for Store
# internal
# from routes import bp_home
import lib.argument_validation as av
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
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
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, Parameters_Product # Permutation_Variation_Link
"""
from business_objects.access_level import Access_Level
from business_objects.region import Region
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item
from business_objects.unit_measurement import Unit_Measurement
from business_objects.user import User, Parameters_User, User_Permission_Evaluation
# from helpers.helper_db_mysql import Helper_DB_MySQL

View File

@@ -1,143 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Basket DataStore
Description:
Datastore for Store Baskets
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.sql_error import SQL_Error
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
# 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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
# db = SQLAlchemy()
class DataStore_Store_Basket(DataStore_Store_Base):
# Global constants
KEY_BASKET: ClassVar[str] = Basket.KEY_BASKET
# Attributes
def __init__(self, **kwargs):
super().__init__(**kwargs)
def get_metadata_basket(json_request):
try:
basket = json_request[DataStore_Store_Basket.KEY_BASKET]
except KeyError:
basket = {DataStore_Store_Basket.KEY_IS_INCLUDED_VAT: True, DataStore_Store_Basket.KEY_ID_CURRENCY: 1, DataStore_Store_Basket.KEY_ID_REGION_DELIVERY: 1}
is_included_VAT = basket[DataStore_Store_Basket.KEY_IS_INCLUDED_VAT]
id_currency = basket[DataStore_Store_Basket.KEY_ID_CURRENCY]
id_region_delivery = basket[DataStore_Store_Basket.KEY_ID_REGION_DELIVERY]
return id_currency, id_region_delivery, is_included_VAT
def edit_basket(self, ids_permutation_basket, quantities_permutation_basket, id_permutation_edit, quantity_permutation_edit, sum_not_edit, id_currency, id_region_delivery, is_included_VAT):
# redundant argument validation?
_m = 'DataStore_Store_Base.edit_basket'
Helper_App.console_log(f'{_m}\nstarting...')
# av.val_instance(filters, 'filters', _m, Parameters_Product_Category)
# av.val_str(ids_product_basket, 'ids_product_basket', _m)
av.val_str(ids_permutation_basket, 'ids_permutation_basket', _m)
# av.val_str(quantities_product_basket, 'quantities_product_basket', _m)
av.val_str(quantities_permutation_basket, 'quantities_permutation_basket', _m)
"""
if id_product_edit == 'None':
id_product_edit = None
else:
Helper_App.console_log(f'id_product_edit: {id_product_edit}')
av.val_int(id_product_edit, 'id_product_edit', _m)
"""
if id_permutation_edit == 'None' or str(type(id_permutation_edit)) =="<class 'NoneType'>":
id_permutation_edit = None
else:
Helper_App.console_log(f'id_permutation_edit: {id_permutation_edit}')
Helper_App.console_log(str(type(id_permutation_edit)))
av.val_int(id_permutation_edit, 'id_permutation_edit', _m)
if quantity_permutation_edit == 'None' or str(type(quantity_permutation_edit)) =="<class 'NoneType'>":
quantity_permutation_edit = None
else:
Helper_App.console_log(f'quantity_permutation_edit: {quantity_permutation_edit}')
av.val_int(quantity_permutation_edit, 'quantity_permutation_edit', _m)
if sum_not_edit == 'None':
sum_not_edit = None
else:
Helper_App.console_log(f'sum_not_edit: {sum_not_edit}')
av.val_bool(sum_not_edit, 'sum_not_edit', _m)
argument_dict_list = {
'a_id_user': self.info_user.get('sub'),
# 'a_ids_product_basket': ids_product_basket,
'a_ids_permutation_basket': ids_permutation_basket,
# 'a_quantities_product_basket': quantities_product_basket,
'a_quantities_permutation_basket': quantities_permutation_basket,
# 'a_id_product_edit': id_product_edit if id_permutation_edit is None else None,
'a_id_permutation_edit': id_permutation_edit,
'a_quantity_permutation_edit': quantity_permutation_edit,
'a_sum_not_edit': 1 if sum_not_edit else 0,
'a_id_currency': id_currency,
'a_id_region_purchase': id_region_delivery
}
result = self.db_procedure_execute('p_shop_edit_user_basket', argument_dict_list)
Helper_App.console_log('data received')
cursor = result.cursor
# categories, category_index = DataStore_Store_Base.input_many_product(cursor)
category_list, errors = DataStore_Store_Base.input_many_product(cursor)
Helper_App.console_log(f'cursor: {str(cursor)}')
# Basket
if not cursor.nextset():
raise Exception("No more query results! Cannot open basket contents")
result_set = cursor.fetchall()
Helper_App.console_log(f'raw basket: {result_set}')
# Helper_App.console_log(f'variations: {result_set_3}')
# variations = [Product_Variation(**row) for row in result_set_3]
basket = Basket(is_included_VAT, id_currency, id_region_delivery)
for row in result_set:
index_category = category_list.get_index_category_from_id(row[0])
category = category_list.categories[index_category]
index_product = category.get_index_product_from_id(row[1])
product = category.products[index_product]
basket_item = Basket_Item.from_product_and_quantity_and_VAT_included(product, row[7], self.app.is_included_VAT)
Helper_App.console_log(f'adding basket item: {row}')
Helper_App.console_log(f'basket item: {basket_item}')
basket.add_item(basket_item) # basket.append(basket_item) # Basket_Item(category.name, product, row[4]))
Helper_App.console_log(f'basket: {basket}')
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(f'raw errors: {result_set_e}')
if len(result_set_e) > 0:
errors = [SQL_Error.from_DB_record(row) for row in result_set_e] # [SQL_Error(row[0], row[1]) for row in result_set_2]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Base.db_cursor_clear(cursor)
return basket

View File

@@ -1,137 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Manufacturing Purchase Order Purchase Order DataStore
Description:
Datastore for Store Manufacturing Purchase Order Purchase Orders
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.manufacturing_purchase_order import Manufacturing_Purchase_Order, Manufacturing_Purchase_Order_Product_Link, Parameters_Manufacturing_Purchase_Order, Manufacturing_Purchase_Order_Temp, Manufacturing_Purchase_Order_Product_Link_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
from helpers.helper_db_mysql import Helper_DB_MySQL
from extensions import db
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
class DataStore_Store_Manufacturing_Purchase_Order(DataStore_Store_Base):
def __init__(self):
super().__init__()
def get_many_manufacturing_purchase_order(self, parameters_manufacturing_purchase_order):
_m = 'DataStore_Store_Manufacturing_Purchase_Order.get_many_manufacturing_purchase_order'
av.val_instance(parameters_manufacturing_purchase_order, 'parameters_manufacturing_purchase_order', _m, Parameters_Manufacturing_Purchase_Order)
argument_dict = parameters_manufacturing_purchase_order.to_json()
user = self.get_user_session()
argument_dict = {
'a_id_user': user.id_user
, **argument_dict
, 'a_debug': 0
}
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_manufacturing_purchase_order')
result = self.db_procedure_execute('p_shop_get_many_manufacturing_purchase_order', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
# Manufacturing_Purchase_Orders
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw manufacturing_purchase_orders: {result_set_1}')
manufacturing_purchase_orders = []
indices_manufacturing_purchase_order = {}
for row in result_set_1:
new_manufacturing_purchase_order = Manufacturing_Purchase_Order.from_DB_manufacturing_purchase_order(row)
indices_manufacturing_purchase_order[new_manufacturing_purchase_order.id_order] = len(manufacturing_purchase_orders)
manufacturing_purchase_orders.append(new_manufacturing_purchase_order)
# Manufacturing_Purchase_Orders Items
cursor.nextset()
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw manufacturing_purchase_order_product_links: {result_set_1}')
order_product_links = []
for row in result_set_1:
new_link = Manufacturing_Purchase_Order_Product_Link.from_DB_manufacturing_purchase_order(row)
order_product_links.append(new_link)
manufacturing_purchase_orders[indices_manufacturing_purchase_order[new_link.id_order]].items.append(new_link)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Manufacturing_Purchase_Order.db_cursor_clear(cursor)
return manufacturing_purchase_orders, errors
@classmethod
def save_manufacturing_purchase_orders(cls, comment, manufacturing_purchase_orders):
_m = 'DataStore_Store_Manufacturing_Purchase_Order.save_manufacturing_purchase_orders'
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
rows_order = []
for manufacturing_purchase_order in manufacturing_purchase_orders:
row = Manufacturing_Purchase_Order_Temp.from_manufacturing_purchase_order(manufacturing_purchase_order)
row.guid = guid
rows_order.append(row)
Helper_App.console_log(f'order rows: {rows_order}')
DataStore_Store_Base.upload_bulk(Manufacturing_Purchase_Order_Temp.__tablename__, rows_order, 1000)
Helper_App.console_log('bulk uploaded orders')
rows_link = []
for manufacturing_purchase_order in manufacturing_purchase_orders:
for link in manufacturing_purchase_order.items:
row = Manufacturing_Purchase_Order_Product_Link_Temp.from_manufacturing_purchase_order_product_link(link)
row.guid = guid
rows_link.append(row)
Helper_App.console_log(f'link rows: {rows_link}')
DataStore_Store_Base.upload_bulk(Manufacturing_Purchase_Order_Product_Link_Temp.__tablename__, rows_link, 1000)
Helper_App.console_log('bulk uploaded links')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0
}
result = cls.db_procedure_execute('p_shop_save_manufacturing_purchase_order', argument_dict_list)
Helper_App.console_log('saved manufacturing purchase orders')
# Errors
cursor = result.cursor
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Manufacturing_Purchase_Order.db_cursor_clear(cursor)
return errors

View File

@@ -1,139 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Product DataStore
Description:
Datastore for Store Products
"""
# internal
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.product import Product, Product_Permutation, Product_Price, Parameters_Product, Product_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
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_Temp(db.Model):
__tablename__ = 'Shop_Product_Temp'
__table_args__ = { 'extend_existing': True }
id_product: int = db.Column(db.Integer, primary_key=True)
id_category: int = db.Column(db.Integer)
name: str = db.Column(db.String(50))
has_variations: str = db.Column(db.String(255))
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))
@classmethod
def from_product(cls, product):
row = cls()
row.id_product = product.id_product[0] if isinstance(product.id_product, tuple) else product.id_product
row.id_category = product.id_category[0] if isinstance(product.id_category, tuple) else product.id_category
row.name = product.name[0] if isinstance(product.name, tuple) else product.name
row.id_access_level_required = product.id_access_level_required[0] if isinstance(product.id_access_level_required, tuple) else product.id_access_level_required
row.active = product.active
row.display_order = product.display_order
return row
def to_json(self):
return {
'id_product': self.id_product,
'id_category': self.id_category,
'name': self.name,
'has_variations': self.has_variations,
'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,
}
"""
class DataStore_Store_Product(DataStore_Store_Base):
def __init__(self):
super().__init__()
@classmethod
def save_products(cls, comment, products):
_m = 'DataStore_Store_Product.save_products'
Helper_App.console_log(f'{_m}\nstarting...')
Helper_App.console_log(f'comment: {comment}\nproducts: {products}')
guid = Helper_DB_MySQL.create_guid()
user = cls.get_user_session()
rows = []
id_product_new = 0
for product in products:
row = Product_Temp.from_product(product)
if row.id_product == '':
id_product_new -= 1
row.id_product = id_product_new
else:
Helper_App.console_log(f'row.id_product: {row.id_product}')
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Product_Temp.__tablename__, rows, 1000)
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0,
}
save_result = cls.db_procedure_execute('p_shop_save_product', argument_dict_list)
cursor = save_result # .cursor
Helper_App.console_log('data received')
# Errors
# cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
try:
DataStore_Store_Base.db_cursor_clear(cursor)
except Exception as e:
Helper_App.console_log(f'Error clearing cursor: {e}')
cursor.close()
save_result.close()
Helper_App.console_log('save procedure executed')
return errors

View File

@@ -1,103 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Product Category DataStore
Description:
Datastore for Store Product Categories
"""
# internal
import lib.argument_validation as av
from business_objects.store.product_category import Product_Category_Container, Product_Category, Product_Category_Temp
from business_objects.sql_error import SQL_Error
# 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_app import Helper_App
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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
class DataStore_Store_Product_Category(DataStore_Store_Base):
def __init__(self):
super().__init__()
@classmethod
def save_categories(cls, comment, categories):
_m = 'DataStore_Store_Product_Category.save_categories'
Helper_App.console_log(f'{_m}\nstarting...')
Helper_App.console_log(f'comment: {comment}\ncategories: {categories}')
# av.val_str(comment, 'comment', _m)
# av.val_list_instances(categories, 'categories', _m, Product_Category, 1)
guid = Helper_DB_MySQL.create_guid()
now = datetime.now()
user = cls.get_user_session()
rows = []
id_category_new = 0
for category in categories:
row = Product_Category_Temp.from_product_category(category)
# row = category.to_temporary_record()
# id_tmp =
if row.id_category == '':
id_category_new -= 1
row.id_category = id_category_new
else:
Helper_App.console_log(f'row.id_category: {row.id_category}')
row.guid = guid
# row.created_on = now
# row.created_by = user.id_user
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
"""
cursor = db.cursor()
Helper_App.console_log('cursor created')
cursor.executemany(
'INSERT INTO Shop_Product_Category_Temp (id_category, code, name, description, active, display_order, guid, created_on, created_by) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
categories
)
Helper_App.console_log('bulk upload executed')
db.commit()
Helper_App.console_log('bulk upload committed')
cursor.close()
Helper_App.console_log('cursor closed')
"""
DataStore_Store_Base.upload_bulk(Product_Category_Temp.__tablename__, rows, 1000)
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0,
}
save_result = cls.db_procedure_execute('p_shop_save_product_category', argument_dict_list)
# Errors
cursor = save_result.cursor
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Product_Category.db_cursor_clear(cursor)
save_result.close()
Helper_App.console_log('save procedure executed')
return errors

View File

@@ -1,106 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Product Permutation DataStore
Description:
Datastore for Store Product Permutations
"""
# internal
import lib.argument_validation as av
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_app import Helper_App
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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
# db = SQLAlchemy()
class DataStore_Store_Product_Permutation(DataStore_Store_Base):
def __init__(self):
super().__init__()
@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)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
rows = []
for permutation in permutations:
# row = permutation.to_temporary_record()
row = Product_Permutation_Temp.from_product_permutation(permutation)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
"""
cursor = db.cursor()
Helper_App.console_log('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,
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
)
Helper_App.console_log('cursor executed')
db.commit()
Helper_App.console_log('cursor committed')
cursor.close()
Helper_App.console_log('cursor closed')
"""
DataStore_Store_Base.upload_bulk(Product_Permutation_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0,
}
results = cls.db_procedure_execute('p_shop_save_product_permutation', argument_dict_list)
DataStore_Store_Base.db_cursor_clear(results.cursor)
Helper_App.console_log('saved product permutations')

View File

@@ -1,77 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Product Variation DataStore
Description:
Datastore for Store Product Variations
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.product_variation import Product_Variation, Parameters_Product_Variation, Product_Variation_Temp
from business_objects.store.product_variation_type import Product_Variation_Type, Product_Variation_Type_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
class DataStore_Store_Product_Variation(DataStore_Store_Base):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def save_product_variations(cls, comment, variation_types):
_m = f'{cls.__class__}.save_product_variations'
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
user = cls.get_user_session()
rows = []
for variation_type in variation_types:
row = Product_Variation_Type_Temp.from_product_variation_type(variation_type)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Product_Variation_Type_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded product variation types')
rows = []
for variation_type in variation_types:
if variation_type.variations is not None:
for variation in variation_type.variations:
row = Product_Variation_Temp.from_product_variation(variation)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Product_Variation_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded product variations')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0,
}
cls.db_procedure_execute('p_shop_save_product_variation', argument_dict_list)
Helper_App.console_log('saved product variations')

View File

@@ -1,146 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Stock Item DataStore
Description:
Datastore for Store Stock Items
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Parameters_Stock_Item, Stock_Item_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
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
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
# db = SQLAlchemy()
class DataStore_Store_Stock_Item(DataStore_Store_Base):
# Global constants
# Attributes
def __init__(self):
super().__init__()
# Stock Items
def get_many_stock_item(self, parameters_stock_item, category_list):
# redundant argument validation?
_m = 'DataStore_Store_Stock_Item.get_many_stock_item'
av.val_instance(parameters_stock_item, 'parameters_stock_item', _m, Parameters_Stock_Item)
argument_dict = parameters_stock_item.to_json()
user = self.get_user_session()
argument_dict = {
'a_id_user': user.id_user
, **argument_dict
, 'a_debug': 0
}
ids_permutation = category_list.get_csv_ids_permutation()
Helper_App.console_log(f'ids_permutation: {ids_permutation}')
argument_dict['a_ids_product_permutation'] = ids_permutation
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_stock_item')
result = self.db_procedure_execute('p_shop_get_many_stock_item', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
category_list, errors = DataStore_Store_Stock_Item.input_many_stock_item(cursor, category_list)
DataStore_Store_Stock_Item.db_cursor_clear(cursor)
return category_list, errors
def input_many_stock_item(cursor, category_list):
_m = 'DataStore_Store_Stock_Item.input_many_stock_item'
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw categories: {result_set_1}')
for row in result_set_1:
new_stock_item = Stock_Item.from_DB_stock_item(row)
category_list.add_stock_item(new_stock_item) # , row)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
"""
if len(errors) > 0:
for error in errors:
if error.code == 'PRODUCT_AVAILABILITY':
ids_permutation_unavailable = DataStore_Store_Stock_Item.get_ids_permutation_from_error_availability(error.msg)
for id_permutation in ids_permutation_unavailable:
index_category = category_list.get_index_category_from_id_permutation(id_permutation)
category = category_list.categories[index_category]
index_product = category.get_index_product_from_id_permutation(id_permutation)
product = category.products[index_product]
index_permutation = product.get_index_permutation_from_id(id_permutation)
permutation = product.permutations[index_permutation]
permutation.is_available = False
if 'region' in error.msg or 'currency' in error.msg:
permutation.is_unavailable_in_currency_or_region = True
"""
DataStore_Store_Stock_Item.db_cursor_clear(cursor)
return category_list, errors # categories, category_index
@classmethod
def save_stock_items(cls, comment, stock_items):
_m = 'DataStore_Store_Stock_Item.save_stock_items'
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
rows = []
for stock_item in stock_items:
# row = permutation.to_temporary_record()
row = Stock_Item_Temp.from_stock_item(stock_item)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Stock_Item_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0
}
result = cls.db_procedure_execute('p_shop_save_stock_item', argument_dict_list)
Helper_App.console_log('saved product permutations')
# Errors
cursor = result.cursor
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Stock_Item.db_cursor_clear(cursor)
return errors

View File

@@ -1,143 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Supplier DataStore
Description:
Datastore for Store Suppliers
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.supplier_address import Supplier_Address, Supplier_Address_Temp
from business_objects.store.supplier import Supplier, Parameters_Supplier, Supplier_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
from helpers.helper_db_mysql import Helper_DB_MySQL
from extensions import db
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
class DataStore_Store_Supplier(DataStore_Store_Base):
def __init__(self):
super().__init__()
@classmethod
def get_many_supplier(cls, parameters_supplier):
_m = 'DataStore_Store_Supplier.get_many_supplier'
av.val_instance(parameters_supplier, 'parameters_supplier', _m, Parameters_Supplier)
argument_dict = parameters_supplier.to_json()
user = cls.get_user_session()
argument_dict = {
'a_id_user': user.id_user
, **argument_dict
, 'a_debug': 0
}
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_supplier')
result = cls.db_procedure_execute('p_shop_get_many_supplier', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
# Suppliers
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw suppliers: {result_set_1}')
suppliers = []
supplier_indexes = {}
for row in result_set_1:
new_supplier = Supplier.from_DB_supplier(row)
supplier_indexes[new_supplier.id_supplier] = len(suppliers)
suppliers.append(new_supplier)
# Supplier Addresses
cursor.nextset()
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw supplier addresses: {result_set_1}')
for row in result_set_1:
new_address = Supplier_Address.from_DB_supplier(row)
index_supplier = supplier_indexes[new_address.id_supplier]
suppliers[index_supplier].addresses.append(new_address)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Supplier.db_cursor_clear(cursor)
return suppliers, errors
@classmethod
def save_suppliers(cls, comment, suppliers):
_m = 'DataStore_Store_Supplier.save_suppliers'
Helper_App.console_log(f'{_m}\n{suppliers}')
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
rows = []
for supplier in suppliers:
row = Supplier_Temp.from_supplier(supplier)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Supplier_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded suppliers')
rows = []
for supplier in suppliers:
Helper_App.console_log(f'supplier: {supplier}')
for supplier_address in supplier.addresses:
row = Supplier_Address_Temp.from_supplier_address(supplier_address)
row.guid = guid
rows.append(row)
Helper_App.console_log(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(Supplier_Address_Temp.__tablename__, rows, 1000)
Helper_App.console_log('bulk uploaded supplier addresses')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0
}
result = cls.db_procedure_execute('p_shop_save_supplier', argument_dict_list)
Helper_App.console_log('saved suppliers')
# Errors
cursor = result.cursor
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Supplier.db_cursor_clear(cursor)
return errors

View File

@@ -1,143 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Supplier Purchase Order Purchase Order DataStore
Description:
Datastore for Store Supplier Purchase Order Purchase Orders
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.supplier_purchase_order import Supplier_Purchase_Order, Supplier_Purchase_Order_Product_Link, Parameters_Supplier_Purchase_Order, Supplier_Purchase_Order_Temp, Supplier_Purchase_Order_Product_Link_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_app import Helper_App
from helpers.helper_db_mysql import Helper_DB_MySQL
from extensions import db
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
class DataStore_Store_Supplier_Purchase_Order(DataStore_Store_Base):
def __init__(self):
super().__init__()
def get_many_supplier_purchase_order(self, parameters_supplier_purchase_order):
_m = 'DataStore_Store_Supplier_Purchase_Order.get_many_supplier_purchase_order'
av.val_instance(parameters_supplier_purchase_order, 'parameters_supplier_purchase_order', _m, Parameters_Supplier_Purchase_Order)
argument_dict = parameters_supplier_purchase_order.to_json()
user = self.get_user_session()
argument_dict = {
'a_id_user': user.id_user
, **argument_dict
, 'a_debug': 0
}
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_supplier_purchase_order')
result = self.db_procedure_execute('p_shop_get_many_supplier_purchase_order', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
# Supplier_Purchase_Orders
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw supplier_purchase_orders: {result_set_1}')
supplier_purchase_orders = []
indices_supplier_purchase_order = {}
for row in result_set_1:
new_supplier_purchase_order = Supplier_Purchase_Order.from_DB_supplier_purchase_order(row)
indices_supplier_purchase_order[new_supplier_purchase_order.id_order] = len(supplier_purchase_orders)
supplier_purchase_orders.append(new_supplier_purchase_order)
# Supplier_Purchase_Orders Items
cursor.nextset()
result_set_2 = cursor.fetchall()
Helper_App.console_log(f'raw supplier_purchase_order_product_links: {result_set_2}')
order_product_links = []
for row in result_set_2:
new_link = Supplier_Purchase_Order_Product_Link.from_DB_supplier_purchase_order(row)
order_product_links.append(new_link)
supplier_purchase_orders[indices_supplier_purchase_order[new_link.id_order]].items.append(new_link)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Supplier_Purchase_Order.db_cursor_clear(cursor)
return supplier_purchase_orders, errors
@classmethod
def save_supplier_purchase_orders(cls, comment, supplier_purchase_orders):
_m = 'DataStore_Store_Supplier_Purchase_Order.save_supplier_purchase_orders'
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
rows_order = []
for supplier_purchase_order in supplier_purchase_orders:
row = Supplier_Purchase_Order_Temp.from_supplier_purchase_order(supplier_purchase_order)
row.guid = guid
rows_order.append(row)
Helper_App.console_log(f'order rows: {rows_order}')
DataStore_Store_Base.upload_bulk(Supplier_Purchase_Order_Temp.__tablename__, rows_order, 1000)
Helper_App.console_log('bulk uploaded orders')
rows_link = []
for supplier_purchase_order in supplier_purchase_orders:
for link in supplier_purchase_order.items:
row = Supplier_Purchase_Order_Product_Link_Temp.from_supplier_purchase_order_product_link(link)
row.guid = guid
rows_link.append(row)
Helper_App.console_log(f'link rows: {rows_link}')
DataStore_Store_Base.upload_bulk(Supplier_Purchase_Order_Product_Link_Temp.__tablename__, rows_link, 1000)
Helper_App.console_log('bulk uploaded links')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0
}
result = cls.db_procedure_execute('p_shop_save_supplier_purchase_order', argument_dict_list)
Helper_App.console_log('saved supplier purchase orders')
# Errors
cursor = result.cursor
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(f'raw errors: {result_set_e}')
errors = []
warnings = []
if len(result_set_e) > 0:
for row in result_set_e:
new_error = SQL_Error.from_DB_record(row)
if new_error.code == 'WARNING':
warnings.append(new_error)
else:
errors.append(new_error)
Helper_App.console_log(f"Error [{new_error.code}]: {new_error.msg}")
cls.db_cursor_clear(cursor)
return errors, warnings

View File

@@ -14,10 +14,8 @@ Datastore for Users
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item
from business_objects.user import User, Parameters_User, User_Permission_Evaluation
# from datastores.datastore_base import DataStore_Base
from datastores.datastore_store_base import DataStore_Store_Base
from datastores.datastore_base import DataStore_Base
from helpers.helper_app import Helper_App
from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
@@ -36,7 +34,7 @@ from datetime import datetime
db = SQLAlchemy()
class DataStore_User(DataStore_Store_Base):
class DataStore_User(DataStore_Base):
# Global constants
# Attributes

View File

@@ -8,7 +8,7 @@ from authlib.integrations.flask_client import OAuth
csrf = CSRFProtect()
cors = CORS()
# cors = CORS()
db = SQLAlchemy()
mail = Mail()
oauth = OAuth()

87
forms/contact.py Normal file
View File

@@ -0,0 +1,87 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Contact Us Form
Description:
Defines Flask-WTF form for handling user input on Contact Us page.
"""
# IMPORTS
# internal
# from business_objects.store.product_category import Filters_Product_Category # circular
# from models.model_view_store import Model_View_Store # circular
from models.model_view_base import Model_View_Base
from forms.base import Form_Base
# external
from flask import Flask, render_template, request, flash, redirect, url_for, current_app
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, HiddenField, BooleanField, Field
from wtforms.validators import DataRequired, Email, ValidationError
import markupsafe
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
import json
from altcha import verify_solution
import base64
class ALTCHAValidator:
def __init__(self, message=None):
self.message = message or 'ALTCHA verification failed'
def __call__(self, form, field):
altcha_data = field.data
if not altcha_data:
raise ValidationError(self.message)
try:
# The data is base64 encoded JSON
try:
# First try to decode it as JSON directly (if it's not base64 encoded)
altcha_payload = json.loads(altcha_data)
except json.JSONDecodeError:
# If direct JSON decoding fails, try base64 decoding first
decoded_data = base64.b64decode(altcha_data).decode('utf-8')
altcha_payload = json.loads(decoded_data)
ok, err = verify_solution(altcha_payload, current_app.config["ALTCHA_SECRET_KEY"], check_expires=True)
if err or not ok:
raise ValidationError(self.message + ': ' + (err or 'Invalid solution'))
except Exception as e:
raise ValidationError(f'Invalid ALTCHA data: {str(e)}')
class ALTCHAField(Field):
def __init__(self, label='', validators=None, **kwargs):
validators = validators or []
validators.append(ALTCHAValidator())
super(ALTCHAField, self).__init__(label, validators, **kwargs)
def __call__(self, **kwargs):
html = f"""
<altcha-widget
challengeurl="/get-challenge"
auto="onload"
id="{self.id}"
name="{self.name}">
</altcha-widget>
"""
return markupsafe.Markup(html)
class Form_Contact(FlaskForm):
email = StringField('Email')
contact_name = StringField('Name')
company_name = StringField('Company')
message = TextAreaField('Message')
receive_marketing = BooleanField('I would like to receive marketing emails.')
# recaptcha = RecaptchaField()
# altcha = HiddenField('ALTCHA') # , validators=[validate_altcha]
altcha = ALTCHAField('Verification')
submit = SubmitField('Send Message')

View File

@@ -1,40 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Manufacturing Purchase Order Filters data input
Description:
Defines Flask-WTF forms for handling manufacturing purchase order 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, DateField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Manufacturing_Purchase_Order(Form_Base):
active = BooleanField("Active only?", default = True)
date_from = DateField('Date from')
date_to = DateField('Date to')
def __repr__(self):
return f'Filters_Manufacturing_Purchase_Order(active={self.active.data}, date_from={self.date_from.data}, date_to={self.date_to.data})'
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
form = cls()
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], 'active', _m)
if json[Store_Base.FLAG_DATE_FROM] != '':
form.date_from.data = json[Store_Base.FLAG_DATE_FROM]
if json[Store_Base.FLAG_DATE_TO] != '':
form.date_to.data = json[Store_Base.FLAG_DATE_TO]
return form

View File

@@ -1,43 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Product Filter data input
Description:
Defines Flask-WTF forms for handling user input.
"""
# IMPORTS
# internal
# from business_objects.store.product_category import Filters_Product_Category
# from models.model_view_store import Model_View_Store # circular
# 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
class Filters_Product(FlaskForm):
id_category = SelectField('Category', validators=[Optional()], choices=[('', 'All')], default='')
is_not_empty = BooleanField('Not empty only?')
active = BooleanField("Active only?", default = True)
@classmethod
def from_filters_product(cls, filters_product):
form = Filters_Product()
form.id_category = filters_product.id_category
form.is_not_empty.data = filters_product.is_not_empty
form.active.data = filters_product.active
return form
def __repr__(self):
return f'Filters_Product(id_category={self.id_category}, is_not_empty={self.is_not_empty.data}, active={self.active.data})'
@classmethod
def from_json(cls, json):
filters = cls()
filters.id_category.data = json['id_category']
filters.is_not_empty.data = json['is_not_empty']
filters.active.data = json['active']
return filters

View File

@@ -1,47 +0,0 @@
"""
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_Category(Form_Base):
is_not_empty = BooleanField('Not empty only?')
active = BooleanField("Active only?", default = True)
"""
@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
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
form = Filters_Product_Category() # is_not_empty=json['is_not_empty'], active=json['active'])
form.is_not_empty.data = av.input_bool(json[Store_Base.FLAG_IS_NOT_EMPTY], 'is_not_empty', _m)
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], 'active', _m)
return form

View File

@@ -1,68 +0,0 @@
"""
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=[('', 'All')], default='')
id_product = SelectField('Product', validators=[Optional()], choices=[('', 'All')], default='')
is_out_of_stock = BooleanField('Out of stock only?')
active = BooleanField('Active only?', default=True)
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},
active={self.active.data},
quantity_min={self.quantity_min.data},
quantity_max={self.quantity_max.data})
'''
@classmethod
def from_json(cls, json):
form = cls()
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], Store_Base.FLAG_IS_OUT_OF_STOCK, f'{cls.__name__}.from_json')
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], Store_Base.FLAG_ACTIVE, f'{cls.__name__}.from_json')
form.quantity_min.data = json[Store_Base.FLAG_QUANTITY_MIN]
form.quantity_max.data = json[Store_Base.FLAG_QUANTITY_MAX]
return form

View File

@@ -1,38 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Product Variation Filters data input
Description:
Defines Flask-WTF forms for handling product variation filter input.
"""
# internal
from business_objects.store.store_base import Store_Base
from forms.base import Form_Base
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField, DateField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Product_Variation(Form_Base):
is_not_empty = BooleanField('Not empty only?')
active = BooleanField("Active only?", default = True)
def __repr__(self):
return f'Filters_Product_Variation(is_not_empty={self.is_not_empty.data}, active={self.active.data})'
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
Helper_App.console_log(f'{_m}\njson={json}')
form = cls()
form.is_not_empty.data = av.input_bool(json[Store_Base.FLAG_IS_NOT_EMPTY], Store_Base.FLAG_IS_NOT_EMPTY, _m)
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], Store_Base.FLAG_ACTIVE, _m)
return form

View File

@@ -1,76 +0,0 @@
"""
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_Stock_Item(Form_Base):
id_category = SelectField('Category', choices=[Form_Base.get_choice_all()], default='')
id_product = SelectField('Product', choices=[Form_Base.get_choice_all()], default='')
is_out_of_stock = BooleanField('Out of stock only?')
quantity_min = FloatField('Min stock')
quantity_max = FloatField('Max stock')
# submit = SubmitField('Submit')
"""
def __repr__(self):
return f'''
{self.__class__.__name__}(
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 = cls()
# 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], Store_Base.FLAG_IS_OUT_OF_STOCK, f'{cls.__name__}.from_json')
form.quantity_min.data = json[Store_Base.FLAG_QUANTITY_MIN]
form.quantity_max.data = json[Store_Base.FLAG_QUANTITY_MAX]
return form
def to_json(self):
return {
Store_Base.ATTR_ID_PRODUCT_CATEGORY: self.id_category.data,
Store_Base.ATTR_ID_PRODUCT: self.id_product.data,
Store_Base.FLAG_IS_OUT_OF_STOCK: self.is_out_of_stock.data,
Store_Base.FLAG_QUANTITY_MIN: self.quantity_min.data,
Store_Base.FLAG_QUANTITY_MAX: self.quantity_max.data
}
@classmethod
def get_default(cls):
filters = cls()
filters.id_category.choices = cls.get_choices_blank()
filters.id_product.choices = cls.get_choices_blank()
"""
def import_values(self, form_filters):
self.id_category.data = form_filters.id_category.data
self.id_product.data = form_filters.id_product.data
self.is_out_of_stock.data = form_filters.is_out_of_stock.data
self.quantity_min.data = form_filters.quantity_min.data
self.quantity_max.data = form_filters.quantity_max.data
"""

View File

@@ -1,34 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Supplier Filters data input
Description:
Defines Flask-WTF forms for handling supplier 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_Supplier(Form_Base):
active = BooleanField("Active only?", default = True)
def __repr__(self):
return f'Filters_Supplier(active={self.active.data})'
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
form = cls()
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], 'active', _m)
return form

View File

@@ -1,40 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Supplier Purchase Order Filters data input
Description:
Defines Flask-WTF forms for handling supplier purchase order 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, DateField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Supplier_Purchase_Order(Form_Base):
active = BooleanField("Active only?", default = True)
date_from = DateField('Date from')
date_to = DateField('Date to')
def __repr__(self):
return f'Filters_Supplier_Purchase_Order(active={self.active.data}, date_from={self.date_from.data}, date_to={self.date_to.data})'
@classmethod
def from_json(cls, json):
_m = f'{cls.__name__}.from_json'
form = cls()
form.active.data = av.input_bool(json[Store_Base.FLAG_ACTIVE], 'active', _m)
if json[Store_Base.FLAG_DATE_FROM] != '':
form.date_from.data = json[Store_Base.FLAG_DATE_FROM]
if json[Store_Base.FLAG_DATE_TO] != '':
form.date_to.data = json[Store_Base.FLAG_DATE_TO]
return form

View File

@@ -11,7 +11,7 @@ Defines Flask-WTF forms for handling unit of measurement filter input.
"""
# internal
from business_objects.store.store_base import Store_Base
from business_objects.base import Base
from forms.base import Form_Base
import lib.argument_validation as av
# external
@@ -34,9 +34,9 @@ class Filters_Unit_Measurement(Form_Base):
@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')
form.active.data = av.input_bool(json[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'),
Base.FLAG_ACTIVE: av.input_bool(self.active.data, Base.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
}

View File

@@ -1,26 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Store Permutations View Model
Description:
Data model for store permutations view
"""
# internal
from helpers.helper_app import Helper_App
from models.model_view_base import Model_View_Base
# external
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Admin(Model_View_Base):
def __init__(self, hash_page_current, **kwargs):
_m = 'Model_View_Admin.__init__'
Helper_App.console_log(f'{_m}\nstarting')
super().__init__(hash_page_current=hash_page_current, **kwargs)

View File

@@ -1,27 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Admin Home View Model
Description:
Data model for admin home view
"""
# internal
from models.model_view_admin import Model_View_Admin
# external
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Admin_Home(Model_View_Admin):
@property
def title(self):
return 'Admin Home'
def __init__(self, hash_page_current=Model_View_Admin.HASH_PAGE_ADMIN_HOME):
super().__init__(hash_page_current=hash_page_current)

View File

@@ -18,7 +18,6 @@ Base data model for views
# internal
# from routes import bp_home
from business_objects.base import Base
from business_objects.store.product_category import Product_Category
from business_objects.user import User, Parameters_User
from datastores.datastore_base import DataStore_Base
from datastores.datastore_user import DataStore_User
@@ -46,8 +45,10 @@ class Model_View_Base(BaseModel, ABC):
ATTR_TEXT_EXPANDED: ClassVar[str] = 'textExpanded'
ATTR_VALUE_CURRENT: ClassVar[str] = 'current-value'
ATTR_VALUE_PREVIOUS: ClassVar[str] = 'previous-value'
COMPANY_ADDRESS_SHORT: ClassVar[str] = '53 Alfred Green Close, Rugby, United Kingdom, CV22 6DN'
COMPANY_NUMBER: ClassVar[str] = '13587499'
FLAG_ACCESS_LEVEL: ClassVar[str] = 'access_level'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = Product_Category.FLAG_ACCESS_LEVEL_REQUIRED
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = Base.FLAG_ACCESS_LEVEL_REQUIRED
FLAG_ACTIVE: ClassVar[str] = Base.FLAG_ACTIVE
FLAG_ADD: ClassVar[str] = 'add'
# FLAG_ADD_DELETE: ClassVar[str] = 'add-delete'
@@ -56,6 +57,9 @@ class Model_View_Base(BaseModel, ABC):
FLAG_ADDRESS_LINE_2: ClassVar[str] = Base.FLAG_ADDRESS_LINE_2
FLAG_BOOL_FALSE: ClassVar[str] = 'false'
FLAG_BOOL_TRUE: ClassVar[str] = 'true'
FLAG_BUTTON: ClassVar[str] = 'button'
FLAG_BUTTON_LIGHT: ClassVar[str] = 'button-light'
FLAG_BUTTON_PRIMARY: ClassVar[str] = 'button-primary'
FLAG_CANCEL: ClassVar[str] = 'button-cancel'
FLAG_CALLBACK: ClassVar[str] = 'callback'
FLAG_CARD: ClassVar[str] = 'card'
@@ -68,8 +72,9 @@ class Model_View_Base(BaseModel, ABC):
FLAG_COMMENT: ClassVar[str] = 'comment'
# FLAG_CONTACT_US: ClassVar[str] = 'button-contact'
FLAG_CONTAINER: ClassVar[str] = 'container'
FLAG_CONTAINER_CHECKBOX: ClassVar[str] = 'container-checkbox'
FLAG_CONTAINER_ICON_AND_LABEL: ClassVar[str] = 'container-icon-label'
FLAG_CONTAINER_INPUT: ClassVar[str] = FLAG_CONTAINER + '-input'
FLAG_CONTAINER_INPUT: ClassVar[str] = 'container-input'
FLAG_COUNTY: ClassVar[str] = Base.FLAG_COUNTY
FLAG_CSRF_TOKEN: ClassVar[str] = 'X-CSRFToken'
FLAG_CURRENCY: ClassVar[str] = 'currency'
@@ -95,8 +100,9 @@ class Model_View_Base(BaseModel, ABC):
FLAG_IMAGE_LOGO: ClassVar[str] = 'image-logo'
FLAG_INITIALISED: ClassVar[str] = 'initialised'
FLAG_IS_INCLUDED_VAT: ClassVar[str] = 'is_included_VAT'
# FLAG_KEY_PRIMARY: ClassVar[str] = Store_Base.FLAG_KEY_PRIMARY
FLAG_MESSAGE: ClassVar[str] = 'Message'
FLAG_LEFT_HAND_STUB: ClassVar[str] = 'lhs'
FLAG_LOGO: ClassVar[str] = 'logo'
FLAG_MESSAGE: ClassVar[str] = 'message'
FLAG_MODAL: ClassVar[str] = 'modal'
FLAG_NAME: ClassVar[str] = Base.FLAG_NAME
FLAG_NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME_ATTR_OPTION_TEXT
@@ -127,6 +133,8 @@ class Model_View_Base(BaseModel, ABC):
FLAG_PAGE_BODY: ClassVar[str] = 'page-body'
FLAG_PHONE_NUMBER: ClassVar[str] = Base.FLAG_PHONE_NUMBER
FLAG_POSTCODE: ClassVar[str] = Base.FLAG_POSTCODE
FLAG_CAPTCHA: ClassVar[str] = 'recaptcha'
FLAG_RIGHT_HAND_SIDE: ClassVar[str] = 'rhs'
FLAG_ROW: ClassVar[str] = 'row'
FLAG_ROW_NEW: ClassVar[str] = 'row-new'
FLAG_ROWS: ClassVar[str] = Base.FLAG_ROWS
@@ -141,6 +149,8 @@ class Model_View_Base(BaseModel, ABC):
FLAG_USER: ClassVar[str] = User.FLAG_USER
FLAG_WEBSITE: ClassVar[str] = Base.FLAG_WEBSITE
# flagIsDatePicker: ClassVar[str] = 'is-date-picker'
HASH_ALTCHA_CREATE_CHALLENGE: ClassVar[str] = '/altcha/create-challenge'
# HASH_ALTCHA_VERIFY_SOLUTION: ClassVar[str] = '/altcha/verify-solution'
HASH_APPLY_FILTERS_STORE_PRODUCT_PERMUTATION: ClassVar[str] = '/store/permutation_filter'
HASH_CALLBACK_LOGIN: ClassVar[str] = '/callback-login'
HASH_PAGE_ACCESSIBILITY_REPORT: ClassVar[str] = '/accessibility-report'
@@ -153,24 +163,10 @@ class Model_View_Base(BaseModel, ABC):
HASH_PAGE_LICENSE: ClassVar[str] = '/license'
HASH_PAGE_PRIVACY_POLICY: ClassVar[str] = '/privacy-policy'
HASH_PAGE_SERVICES: ClassVar[str] = '/services'
# HASH_PAGE_STORE_ADMIN: ClassVar[str] = '/store/admin'
HASH_PAGE_STORE_BASKET: ClassVar[str] = '/store/basket'
HASH_PAGE_STORE_CUSTOMER_SALES_ORDERS: ClassVar[str] = '/store/customer_sales_orders'
HASH_PAGE_STORE_HOME: ClassVar[str] = '/store'
HASH_PAGE_STORE_MANUFACTURING_PURCHASE_ORDERS: ClassVar[str] = '/store/manufacturing_purchase_orders'
HASH_PAGE_STORE_PRODUCT_CATEGORIES: ClassVar[str] = '/store/categories'
HASH_PAGE_STORE_PRODUCTS: ClassVar[str] = '/store/products'
HASH_PAGE_STORE_PRODUCT_PERMUTATIONS: ClassVar[str] = '/store/permutations'
HASH_PAGE_STORE_PRODUCT_PRICES: ClassVar[str] = '/store/prices'
HASH_PAGE_STORE_PRODUCT_VARIATIONS: ClassVar[str] = '/store/variations'
HASH_PAGE_STORE_STOCK_ITEMS: ClassVar[str] = '/store/stock_items'
HASH_PAGE_STORE_SUPPLIERS: ClassVar[str] = '/store/suppliers'
HASH_PAGE_STORE_SUPPLIER_PURCHASE_ORDERS: ClassVar[str] = '/store/supplier_purchase_orders'
HASH_PAGE_USER_ACCOUNT: ClassVar[str] = '/user'
HASH_PAGE_USER_ADMIN: ClassVar[str] = '/user/admin'
HASH_PAGE_USER_LOGIN: ClassVar[str] = '/login'
HASH_PAGE_USER_LOGOUT: ClassVar[str] = '/logout'
HASH_SCRIPTS_SECTION_STORE: ClassVar[str] = '/scripts_store'
ID_BUTTON_ADD: ClassVar[str] = 'buttonAdd'
ID_BUTTON_APPLY_FILTERS: ClassVar[str] = 'buttonApplyFilters'
ID_BUTTON_CANCEL: ClassVar[str] = 'buttonCancel'
@@ -192,14 +188,6 @@ class Model_View_Base(BaseModel, ABC):
# ID_BUTTON_NAV_CONTACT: ClassVar[str] = 'navContact'
ID_BUTTON_NAV_HOME: ClassVar[str] = 'navHome'
ID_BUTTON_NAV_SERVICES: ClassVar[str] = 'navServices'
ID_BUTTON_NAV_STORE_HOME: ClassVar[str] = 'navStoreHome'
ID_BUTTON_NAV_STORE_PRODUCT: ClassVar[str] = 'navStoreProduct'
ID_BUTTON_NAV_STORE_PRODUCT_CATEGORIES: ClassVar[str] = 'navStoreProductCategories'
ID_BUTTON_NAV_STORE_PRODUCT_PERMUTATIONS: ClassVar[str] = 'navStoreProductPermutations'
ID_BUTTON_NAV_STORE_PRODUCT_PRICES: ClassVar[str] = 'navStoreProductPrices'
ID_BUTTON_NAV_STORE_PRODUCT_VARIATIONS: ClassVar[str] = 'navStoreProductVariations'
ID_BUTTON_NAV_STORE_STOCK_ITEMS: ClassVar[str] = 'navStoreStockItems'
ID_BUTTON_NAV_USER_ACCOUNT: ClassVar[str] = 'navUserAccount'
ID_BUTTON_NAV_USER_ADMIN: ClassVar[str] = 'navUserAdmin'
ID_BUTTON_NAV_USER_LOGIN: ClassVar[str] = 'navUserLogin'
ID_BUTTON_NAV_USER_LOGOUT: ClassVar[str] = 'navUserLogout'
@@ -211,6 +199,7 @@ class Model_View_Base(BaseModel, ABC):
ID_TABLE_MAIN: ClassVar[str] = 'tableMain'
ID_TEXTAREA_CONFIRM: ClassVar[str] = 'textareaConfirm'
NAME_COMPANY: ClassVar[str] = 'Precision And Research Technology Systems Limited'
NAME_COMPANY_SHORT: ClassVar[str] = 'PARTS Ltd'
NAME_CSRF_TOKEN: ClassVar[str] = 'csrf-token'
# URL_HOST: ClassVar[str] = os.env() 'http://127.0.0.1:5000' # 'https://www.partsltd.co.uk'
URL_GITHUB: ClassVar[str] = 'https://github.com/Teddy-1024'
@@ -392,4 +381,6 @@ class Model_View_Base(BaseModel, ABC):
return date_time.strftime('%Y-%m-%dT%H:%M')
@staticmethod
def jsonify(data):
return jsonify(data)
return jsonify(data)
def get_mail_contact_public(self):
return self.app.config['MAIL_CONTACT_PUBLIC']

View File

@@ -14,18 +14,20 @@ Data model for contact view
from models.model_view_base import Model_View_Base
# from routes import bp_home
from lib import argument_validation as av
from forms.forms import Form_Contact
from forms.contact import Form_Contact
# external
from flask_wtf import FlaskForm
from abc import abstractproperty
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Contact(Model_View_Base):
# Attributes
ID_EMAIL: str = 'email'
ID_MESSAGE: str = 'msg'
ID_NAME: str = 'name'
FLAG_ALTCHA_WIDGET: ClassVar[str] = 'altcha-widget'
FLAG_COMPANY_NAME: ClassVar[str] = 'company_name'
FLAG_CONTACT_NAME: ClassVar[str] = 'contact_name'
FLAG_RECEIVE_MARKETING: ClassVar[str] = 'receive_marketing'
ID_CONTACT_FORM: ClassVar[str] = 'contact-form'
form_contact: Form_Contact
@property

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