1. PostgreSQL copy of all MySQL created and tested.\n 2. Purchase Orders and Sales Orders and stock level management added to MySQL, PostgreSQL, and server and front end code.

This commit is contained in:
2024-07-05 20:34:11 +01:00
parent 3a2a164213
commit 6f4e329258
3049 changed files with 535753 additions and 3022 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -19,75 +19,97 @@ Base data model for views
# from routes import bp_home
import lib.argument_validation as av
from forms import Form_Is_Included_VAT, Form_Delivery_Region, Form_Currency
# external
from abc import ABC, abstractmethod, abstractproperty
from abc import ABC, abstractmethod
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from flask import Flask, session
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
# VARIABLE INSTANTIATION
# CLASSES
class Model_View_Base(ABC):
class Model_View_Base(BaseModel, ABC):
# Global constants
ATTR_TEXT_COLLAPSED: ClassVar[str] = 'textCollapsed'
ATTR_TEXT_EXPANDED: ClassVar[str] = 'textExpanded'
FLAG_BUTTON_MODAL_CLOSE: ClassVar[str] = 'btn-overlay-close'
FLAG_BUTTON_SUBMIT: ClassVar[str] = 'btn-submit'
FLAG_CARD: ClassVar[str] = 'card'
FLAG_COLLAPSED: ClassVar[str] = 'collapsed'
FLAG_COLLAPSIBLE: ClassVar[str] = 'collapsible'
FLAG_COLUMN: ClassVar[str] = 'column'
FLAG_CONTAINER: ClassVar[str] = 'container'
FLAG_CONTAINER_INPUT: ClassVar[str] = FLAG_CONTAINER + '-input'
FLAG_ERROR: ClassVar[str] = 'error'
FLAG_EXPANDED: ClassVar[str] = 'expanded'
FLAG_HAMBURGER: ClassVar[str] = 'hamburger'
FLAG_INITIALISED: ClassVar[str] = 'initialised'
FLAG_OVERLAY: ClassVar[str] = 'overlay'
FLAG_PAGE_BODY: ClassVar[str] = 'page-body'
FLAG_ROW: ClassVar[str] = 'row'
FLAG_ROW_NEW: ClassVar[str] = 'row-new'
FLAG_SCROLLABLE: ClassVar[str] = 'scrollable'
FLAG_SUBMITTED: ClassVar[str] = 'submitted'
# flagIsDatePicker: ClassVar[str] = 'is-date-picker'
HASH_PAGE_ACCESSIBILITY_STATEMENT: ClassVar[str] = '/accessibility-statement'
HASH_PAGE_CONTACT: ClassVar[str] = '/contact'
HASH_PAGE_ERROR_NO_PERMISSION: ClassVar[str] = '/error'
HASH_PAGE_HOME: ClassVar[str] = '/'
HASH_PAGE_LICENSE: ClassVar[str] = '/license'
HASH_PAGE_SERVICES: ClassVar[str] = '/services'
HASH_PAGE_STORE_HOME: ClassVar[str] = '/store'
HASH_PAGE_STORE_PERMUTATIONS: ClassVar[str] = '/store/permutations'
HASH_PAGE_STORE_PRODUCT: ClassVar[str] = '/store/product'
ID_BUTTON_HAMBURGER: ClassVar[str] = 'btnHamburger'
ID_FORM_CONTACT: ClassVar[str] = 'formContact'
ID_FORM_CURRENCY: ClassVar[str] = 'formCurrency'
ID_FORM_DELIVERY_REGION: ClassVar[str] = 'formDeliveryRegion'
ID_FORM_IS_INCLUDED_VAT: ClassVar[str] = 'formIsIncludedVAT'
ID_MODAL_SERVICES: ClassVar[str] = 'modalServices'
ID_MODAL_TECHNOLOGIES: ClassVar[str] = 'modalTechnologies'
ID_NAV_CONTACT: ClassVar[str] = 'navContact'
ID_NAV_HOME: ClassVar[str] = 'navHome'
ID_NAV_SERVICES: ClassVar[str] = 'navServices'
ID_NAV_STORE_HOME: ClassVar[str] = 'navStoreHome'
ID_NAV_STORE_PERMUTATIONS: ClassVar[str] = 'navStorePermutations'
ID_NAV_STORE_PRODUCT: ClassVar[str] = 'navStoreProduct'
ID_OVERLAY_HAMBURGER: ClassVar[str] = 'overlayHamburger'
ID_PAGE_BODY: ClassVar[str] = 'pageBody'
ID_TABLE_MAIN: ClassVar[str] = 'tableMain'
KEY_FORM: ClassVar[str] = 'form'
KEY_FORM_FILTERS: ClassVar[str] = KEY_FORM + 'Filters'
NAME_COMPANY: ClassVar[str] = 'Precision And Research Technology Systems Limited'
URL_HOST: ClassVar[str] = 'http://127.0.0.1:5000' # 'https://www.partsltd.co.uk'
URL_GITHUB: ClassVar[str] = 'https://github.com/Teddy-1024'
URL_LINKEDIN: ClassVar[str] = 'https://uk.linkedin.com/in/lordteddyms'
# Attributes
"""
is_user_logged_in: bool
id_user: str
form_is_included_VAT: Form_Is_Included_VAT
form_delivery_region: Form_Delivery_Region
form_currency: Form_Currency
# app: Flask
is_page_store: bool
# Global constants
ATTR_TEXT_COLLAPSED = 'textCollapsed'
ATTR_TEXT_EXPANDED = 'textExpanded'
FLAG_BUTTON_MODAL_CLOSE = 'btn-overlay-close'
FLAG_BUTTON_SUBMIT = 'btn-submit'
FLAG_CARD = 'card'
FLAG_COLLAPSED = 'collapsed'
FLAG_COLLAPSIBLE = 'collapsible'
FLAG_COLUMN = 'column'
FLAG_CONTAINER = 'container'
FLAG_CONTAINER_INPUT = FLAG_CONTAINER + '-input'
FLAG_EXPANDED = 'expanded'
FLAG_HAMBURGER = 'hamburger'
FLAG_INITIALISED = 'initialised'
FLAG_OVERLAY = 'overlay'
FLAG_PAGE_BODY = 'page-body'
FLAG_ROW = 'row'
FLAG_SCROLLABLE = 'scrollable'
FLAG_SUBMITTED = 'submitted'
# flagIsDatePicker = 'is-date-picker'
HASH_PAGE_ACCESSIBILITY_STATEMENT = '/accessibility-statement'
HASH_PAGE_CONTACT = '/contact'
HASH_PAGE_ERROR_NO_PERMISSION = '/error'
HASH_PAGE_HOME = '/'
HASH_PAGE_LICENSE = '/license'
HASH_PAGE_SERVICES = '/services'
HASH_PAGE_STORE_HOME = '/store'
HASH_PAGE_STORE_PRODUCT = '/store/product'
ID_BUTTON_HAMBURGER = 'btnHamburger'
ID_FORM_CONTACT = 'formContact'
ID_FORM_CURRENCY = 'formCurrency'
ID_FORM_DELIVERY_REGION = 'formDeliveryRegion'
ID_FORM_IS_INCLUDED_VAT = 'formIsIncludedVAT'
ID_MODAL_SERVICES = 'modalServices'
ID_MODAL_TECHNOLOGIES = 'modalTechnologies'
ID_NAV_CONTACT = 'navContact'
ID_NAV_HOME = 'navHome'
ID_NAV_SERVICES = 'navServices'
ID_NAV_STORE_HOME = 'navStoreHome'
ID_NAV_STORE_PRODUCT = 'navStoreProduct'
ID_OVERLAY_HAMBURGER = 'overlayHamburger'
ID_PAGE_BODY = 'pageBody'
NAME_COMPANY = 'Precision And Research Technology Systems Limited'
URL_HOST = 'https://www.partsltd.co.uk' # 'http://127.0.0.1:5000'
URL_GITHUB = 'https://github.com/Teddy-1024'
URL_LINKEDIN = 'https://uk.linkedin.com/in/lordteddyms'
"""
# """
app: Flask
db: SQLAlchemy
# """
session: None = None
is_page_store: bool = None
@abstractproperty
model_config = ConfigDict(arbitrary_types_allowed=True)
@property
@abstractmethod
def title(self):
pass
"""
def __new__(cls, db, info_user, app): # , *args, **kwargs
# Initialiser - validation
_m = 'Model_View_Base.__new__'
@@ -96,23 +118,31 @@ class Model_View_Base(ABC):
# return super().__new__(cls, *args, **kwargs)
av.val_instance(db, 'db', _m, SQLAlchemy, v_arg_type=v_arg_type)
return super(Model_View_Base, cls).__new__(cls)
def __init__(self, db, info_user, app):
"""
def __init__(self, app, db, **kwargs):
# Constructor
"""
_m = 'Model_View_Base.__init__'
v_arg_type = 'class attribute'
print(f'{_m}\nstarting')
av.val_instance(db, 'db', _m, SQLAlchemy, v_arg_type=v_arg_type)
"""
BaseModel.__init__(self, app=app, db=db, **kwargs)
"""
self.db = db
self.info_user = info_user
self.session = session
info_user = self.get_info_user()
print(f'info_user: {info_user}\ntype: {str(type(info_user))}')
self.is_user_logged_in = ('sub' in list(info_user.keys()) and not info_user['sub'] == '' and not str(type(info_user['sub'])) == "<class 'NoneType'?")
print(f'is_user_logged_in: {self.is_user_logged_in}')
self.id_user = info_user['sub'] if self.is_user_logged_in else ''
self.form_is_included_VAT = Form_Is_Included_VAT()
self.form_delivery_region = Form_Delivery_Region()
self.form_currency = Form_Currency()
self.app = app
"""
with app.app_context():
self.session = session
# self.form_is_included_VAT = Form_Is_Included_VAT()
# self.form_delivery_region = Form_Delivery_Region()
# self.form_currency = Form_Currency()
self.is_page_store = False
def output_bool(self, boolean):

View File

@@ -29,9 +29,9 @@ from abc import abstractproperty
# CLASSES
class Model_View_Contact(Model_View_Base):
# Attributes
ID_EMAIL = 'email'
ID_MESSAGE = 'msg'
ID_NAME = 'name'
ID_EMAIL: str = 'email'
ID_MESSAGE: str = 'msg'
ID_NAME: str = 'name'
@property
def title(self):

View File

@@ -31,11 +31,13 @@ class Model_View_Home(Model_View_Base):
def title(self):
return 'Home'
"""
def __new__(cls, db, info_user, app):
# Initialiser - validation
print(f'info_user: {info_user}')
return super(Model_View_Home, cls).__new__(cls, db, info_user, app)
def __init__(self, db, info_user, app):
"""
def __init__(self, app):
# Constructor
super().__init__(db, info_user, app)
super().__init__(app)

View File

@@ -29,67 +29,77 @@ from business_objects.category import Category
from flask import send_file, jsonify
from flask_sqlalchemy import SQLAlchemy
import locale
from typing import ClassVar
# VARIABLE INSTANTIATION
# CLASSES
class Model_View_Store(Model_View_Base):
# Global constants
ATTR_FORM_TYPE: ClassVar[str] = 'form-type'
ATTR_ID_PRODUCT_CATEGORY : ClassVar[str] = 'id-product-category'
ATTR_ID_PRODUCT : ClassVar[str] = 'id-product'
ATTR_ID_PERMUTATION : ClassVar[str] = 'id-permutation'
FLAG_BASKET_ITEM_DELETE : ClassVar[str] = 'basket-item-delete'
FLAG_BUTTON_BASKET_ADD : ClassVar[str] = Model_View_Base.FLAG_BUTTON_SUBMIT + '.btnAdd2Basket'
FLAG_BUTTON_BUY_NOW : ClassVar[str] = 'btnBuyNow'
FLAG_CATEGORY: ClassVar[str] = 'category'
FLAG_PRODUCT: ClassVar[str] = 'product'
FLAG_VARIATIONS: ClassVar[str] = 'variations'
FLAG_QUANTITY_STOCK: ClassVar[str] = 'quantity-stock'
FLAG_QUANTITY_MIN: ClassVar[str] = 'quantity-min'
FLAG_QUANTITY_MAX: ClassVar[str] = 'quantity-max'
FLAG_COST_LOCAL: ClassVar[str] = 'cost-local'
HASH_PAGE_STORE_BASKET : ClassVar[str] = '/store/basket'
HASH_STORE_BASKET_ADD : ClassVar[str] = '/store/basket_add'
HASH_STORE_BASKET_DELETE : ClassVar[str] = '/store/basket_delete'
HASH_STORE_BASKET_EDIT : ClassVar[str] = '/store/basket_edit'
HASH_STORE_BASKET_LOAD : ClassVar[str] = '/store/basket_load'
HASH_STORE_SET_CURRENCY : ClassVar[str] = '/store/set_currency'
HASH_STORE_SET_DELIVERY_REGION : ClassVar[str] = '/store/set_delivery_region'
HASH_STORE_SET_IS_INCLUDED_VAT : ClassVar[str] = '/store/set_is_included_VAT'
ID_BASKET : ClassVar[str] = 'basket'
ID_BASKET_CONTAINER : ClassVar[str] = 'basketContainer'
ID_BASKET_TOTAL : ClassVar[str] = 'basketTotal'
ID_BUTTON_CHECKOUT : ClassVar[str] = 'btnCheckout'
ID_BUTTON_BASKET_ADD : ClassVar[str] = 'btnBasketAdd'
ID_BUTTON_BUY_NOW : ClassVar[str] = 'btnBuyNow'
ID_CURRENCY : ClassVar[str] = Form_Currency.id_id_currency # 'id_currency'
ID_CURRENCY_DEFAULT : ClassVar[str] = 1
ID_LABEL_BASKET_EMPTY : ClassVar[str] = 'basketEmpty'
ID_REGION_DELIVERY : ClassVar[str] = Form_Delivery_Region.id_id_region_delivery # 'id_region_delivery'
ID_REGION_DELIVERY_DEFAULT : ClassVar[str] = 1
IS_INCLUDED_VAT_DEFAULT : ClassVar[str] = True
KEY_BASKET : ClassVar[str] = Basket.KEY_BASKET # 'basket'
# KEY_CODE_CURRENCY : ClassVar[str] = 'code_currency'
# KEY_FORM : ClassVar[str] = 'form'
KEY_ID_CURRENCY : ClassVar[str] = Basket.KEY_ID_CURRENCY # 'id_currency'
KEY_ID_PRODUCT : ClassVar[str] = 'product_id'
KEY_ID_PERMUTATION : ClassVar[str] = 'permutation_id'
KEY_ID_REGION_DELIVERY : ClassVar[str] = Basket.KEY_ID_REGION_DELIVERY # 'id_region_delivery'
KEY_IS_INCLUDED_VAT : ClassVar[str] = Basket.KEY_IS_INCLUDED_VAT # 'is_included_VAT'
KEY_ITEMS : ClassVar[str] = Basket.KEY_ITEMS # 'items'
KEY_PRICE : ClassVar[str] = 'price'
KEY_QUANTITY : ClassVar[str] = 'quantity'
KEY_VALUE_DEFAULT : ClassVar[str] = 'default'
TYPE_FORM_BASKET_ADD : ClassVar[str] = 'Form_Basket_Add'
TYPE_FORM_BASKET_EDIT : ClassVar[str] = 'Form_Basket_Edit'
# development variables
# valid_product_id_list = ['prod_PB0NUOSEs06ymG']
# Attributes
# id_user: str
db: SQLAlchemy
basket: Basket # list # dict
# db: SQLAlchemy
# basket: Basket # list # dict
# basket_total: float
"""
id_currency: bool
id_region_delivery: bool
is_included_VAT: bool
show_delivery_option: bool # for checkout page
# Global constants
ATTR_FORM_TYPE = 'form-type'
ATTR_ID_PRODUCT_CATEGORY = 'id-product-category'
ATTR_ID_PRODUCT = 'id-product'
ATTR_ID_PERMUTATION = 'id-permutation'
FLAG_BASKET_ITEM_DELETE = 'basket-item-delete'
FLAG_BUTTON_BASKET_ADD = Model_View_Base.FLAG_BUTTON_SUBMIT + '.btnAdd2Basket'
FLAG_BUTTON_BUY_NOW = 'btnBuyNow'
HASH_PAGE_STORE_BASKET = '/store/basket'
HASH_STORE_BASKET_ADD = '/store/basket_add'
HASH_STORE_BASKET_DELETE = '/store/basket_delete'
HASH_STORE_BASKET_EDIT = '/store/basket_edit'
HASH_STORE_BASKET_LOAD = '/store/basket_load'
HASH_STORE_SET_CURRENCY = '/store/set_currency'
HASH_STORE_SET_DELIVERY_REGION = '/store/set_delivery_region'
HASH_STORE_SET_IS_INCLUDED_VAT = '/store/set_is_included_VAT'
ID_BASKET = 'basket'
ID_BASKET_CONTAINER = 'basketContainer'
ID_BASKET_TOTAL = 'basketTotal'
ID_BUTTON_CHECKOUT = 'btnCheckout'
ID_BUTTON_BASKET_ADD = 'btnBasketAdd'
ID_BUTTON_BUY_NOW = 'btnBuyNow'
ID_CURRENCY = Form_Currency.id_id_currency # 'id_currency'
ID_CURRENCY_DEFAULT = 1
ID_LABEL_BASKET_EMPTY = 'basketEmpty'
ID_REGION_DELIVERY = Form_Delivery_Region.id_id_region_delivery # 'id_region_delivery'
ID_REGION_DELIVERY_DEFAULT = 1
IS_INCLUDED_VAT_DEFAULT = True
KEY_BASKET = Basket.KEY_BASKET # 'basket'
# KEY_CODE_CURRENCY = 'code_currency'
KEY_FORM = 'form'
KEY_ID_CURRENCY = Basket.KEY_ID_CURRENCY # 'id_currency'
KEY_ID_PRODUCT = 'product_id'
KEY_ID_PERMUTATION = 'permutation_id'
KEY_ID_REGION_DELIVERY = Basket.KEY_ID_REGION_DELIVERY # 'id_region_delivery'
KEY_IS_INCLUDED_VAT = Basket.KEY_IS_INCLUDED_VAT # 'is_included_VAT'
KEY_ITEMS = Basket.KEY_ITEMS # 'items'
KEY_PRICE = 'price'
KEY_QUANTITY = 'quantity'
KEY_VALUE_DEFAULT = 'default'
TYPE_FORM_BASKET_ADD = 'Form_Basket_Add'
TYPE_FORM_BASKET_EDIT = 'Form_Basket_Edit'
# development variables
# valid_product_id_list = ['prod_PB0NUOSEs06ymG']
"""
"""
def __new__(cls, db, info_user, app, id_currency, id_region_delivery, is_included_VAT): # , *args, **kwargs``
# Initialiser - validation
_m = 'Model_View_Store.__new__'
@@ -97,14 +107,17 @@ class Model_View_Store(Model_View_Base):
print(f'{_m}\nstarting')
# av.val_str(id_user, 'id_user', _m)
# return super().__new__(cls, *args, **kwargs)
# cls.FLAG_BUTTON_BASKET_ADD = cls.FLAG_BUTTON_SUBMIT + '.btnAdd2Basket'
return super().__new__(cls, db, info_user, app) # Model_View_Store, cls
def __init__(self, db, info_user, app, id_currency, id_region_delivery, is_included_VAT):
"""
def __init__(self, app, db, **kwargs): # , id_currency, id_region_delivery, is_included_VAT):
# Constructor
_m = 'Model_View_Store.__init__'
print(f'{_m}\nstarting')
super().__init__(db, info_user, app)
super().__init__(app=app, db=db, **kwargs)
self.is_page_store = True
"""
self.basket = Basket(id_currency, id_region_delivery, is_included_VAT)
# self.basket_total = 0
# self.db = db
@@ -122,6 +135,7 @@ class Model_View_Store(Model_View_Base):
self.form_delivery_region = Form_Delivery_Region(id_region_delivery=self.id_region_delivery)
self.form_delivery_region.id_region_delivery.choices = [(region.id_region, f'{region.code} - {region.name}') for region in regions]
self.form_delivery_region.id_region_delivery.data = str(self.id_region_delivery) if len(regions) > 0 else None
"""
def get_many_product_category(self, product_filters): # category_ids = '', product_ids = '', get_all_category = True, get_all_product = True, max_products_per_category = -1):
_m = 'Model_View_Store.get_many_product_category'

View File

@@ -0,0 +1,61 @@
"""
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 models.model_view_store import Model_View_Store
from datastores.datastore_store import DataStore_Store
from business_objects.category import Category_List
from forms import Form_Filters_Permutations
# from routes import bp_home
from business_objects.product import Product, Product_Filters, Product_Permutation
import lib.argument_validation as av
# external
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Store_Permutations(Model_View_Store):
ID_FILTER_CATEGORY: ClassVar[str] = 'id_category'
ID_FILTER_PRODUCT: ClassVar[str] = 'id_product'
ID_FILTER_IS_OUT_OF_STOCK: ClassVar[str] = 'is_out_of_stock'
ID_FILTER_QUANTITY_MIN: ClassVar[str] = 'quantity_min'
ID_FILTER_QUANTITY_MAX: ClassVar[str] = 'quantity_max'
category_list: Category_List = None # (str)
filters_product: Product_Filters
form_filters: Form_Filters_Permutations = None
permutation_blank: Product_Permutation = None
@property
def title(self):
return 'Store Permutations'
def __init__(self, app, db, filters_product, **kwargs):
_m = 'Model_View_Store_Permutations.__init__'
print(f'{_m}\nstarting...')
super().__init__(app=app, db=db, filters_product=filters_product, **kwargs)
# BaseModel.__init__(self, app=app, filters_product=filters_product, **kwargs)
self.form_filters = Form_Filters_Permutations()
self.category_list, errors = DataStore_Store(self.app, self.db).get_many_product_category(filters_product)
"""Product_Filters(
self.info_user['sub'],
True, False, False, '',
True, False, False, '',
False, False, False, '',
True, False, False, '',
False, False, False, '',
False, False, False, '',
True, False, '',
False
))
"""
self.permutation_blank = Product_Permutation()

View File

@@ -35,7 +35,7 @@ class Model_View_Store_Product(Model_View_Store):
# Attributes
@property
def title(self):
return 'Store Home'
return 'Store Product'
def __new__(cls, db, id_user, app, id_permutation, id_currency, id_region_delivery, is_included_VAT): # *args, **kwargs
# Initialiser - validation

View File

@@ -0,0 +1,43 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Supplier View Model
Description:
Data model for supplier view page
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
from lib import argument_validation as av
from forms import Form_Supplier
# external
from flask_wtf import FlaskForm
from abc import abstractproperty
from pydantic import BaseModel
class Model_View_Supplier(Model_View_Base):
# Attributes
form: Form_Supplier
@property
def title(self):
return 'Supplier'
"""
def __new__(cls, db, info_user, app, form):
# Initialiser - validation
_m = 'Model_View_Supplier.__new__'
av.val_instance(form, 'form', _m, FlaskForm)
return super(Model_View_Supplier, cls).__new__(cls, db, info_user, app)
"""
def __init__(self, db, info_user, app, form):
# Constructor
super().__init__(db, info_user, app)
BaseModel.__init__(self, form=form)
# self.form = form