Feat: \n 1. Contact Us page form submission success page created. \n 2. Contact Us page styling and CAPTCHA text content. \n 3. Removal of ERP, Google CAPTCHA, and ALTCHA API code and left over comments in JavaScript, Python.

This commit is contained in:
2025-03-16 16:56:15 +00:00
parent 4add7e626e
commit dd608022cd
68 changed files with 597 additions and 3921 deletions

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@ __pycache__/
# Ignore logs and databases
*.log
*.log.*
# Ignore logs and databases
# *.sql

2
app.py
View File

@@ -19,7 +19,6 @@ 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.user import routes_user
from extensions import db, csrf, mail, oauth
from helpers.helper_app import Helper_App
# external
@@ -109,7 +108,6 @@ with app.app_context():
app.register_blueprint(routes_core)
app.register_blueprint(routes_legal)
app.register_blueprint(routes_user)

View File

@@ -74,7 +74,6 @@ class Access_Level(db.Model, Base):
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'Access Level.from_json: {json}')
access_level = cls()
access_level.id_access_level = json[cls.ATTR_ID_ACCESS_LEVEL],
access_level.code = json[cls.FLAG_CODE],

View File

@@ -94,7 +94,6 @@ class Address(db.Model, Base):
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.region = Region.from_json(json[cls.FLAG_REGION]),

32
business_objects/api.py Normal file
View File

@@ -0,0 +1,32 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Base Business Object
Description:
Abstract business object
"""
# internal
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
from flask import jsonify
class API():
@staticmethod
def get_standard_response(success: bool, status_code: int, message: str, data: any, errors: list, meta: dict):
return jsonify({
"success": success,
"status_code": status_code,
"message": message,
"data": data,
"errors": errors,
"meta": meta
})

View File

@@ -62,7 +62,6 @@ class Region(db.Model, Base):
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f'{cls.__name__}.from_json: {json}')
plant = cls()
plant.id_region = json[cls.ATTR_ID_REGION]
plant.code = json[cls.FLAG_CODE]

View File

@@ -79,7 +79,6 @@ class Unit_Measurement(SQLAlchemy_ABC, Base):
}
@classmethod
def from_json(cls, json):
Helper_App.console_log(f' Unit_Measurement.from_json: {json}')
unit = cls()
unit.id_unit_measurement = json[cls.ATTR_ID_UNIT_MEASUREMENT]
unit.name_singular = json[cls.FLAG_NAME_SINGULAR]

View File

@@ -11,7 +11,6 @@ Configuration variables
"""
# IMPORTS
from lib import argument_validation as av
import os
from dotenv import load_dotenv, find_dotenv
from flask import current_app

View File

@@ -12,10 +12,12 @@ Initializes the Flask application, sets the configuration based on the environme
# IMPORTS
# internal
from business_objects.api import API
from datastores.datastore_base import DataStore_Base
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_contact_success import Model_View_Contact_Success
from models.model_view_home import Model_View_Home
import lib.argument_validation as av
# external
@@ -51,49 +53,26 @@ def contact():
form = Form_Contact()
model = Model_View_Contact(form)
html_body = render_template('pages/core/_contact.html', model = model)
return html_body
except Exception as e:
return jsonify(error=str(e)), 403
return html_body
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
@routes_core.route(Model_View_Contact.HASH_PAGE_CONTACT, methods=['POST'])
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():
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_Contact.HASH_ALTCHA_CREATE_CHALLENGE, methods=['GET'])
@routes_core.route(Model_View_Contact.HASH_GET_ALTCHA_CHALLENGE, methods=['GET'])
def create_altcha_challenge():
Helper_App.console_log(f'secret key: {current_app.app_config.ALTCHA_SECRET_KEY}')
options = ChallengeOptions(
expires = datetime.datetime.now() + datetime.timedelta(hours=1),
max_number = 100000, # The maximum random number
hmac_key = current_app.app_config.ALTCHA_SECRET_KEY,
)
challenge = create_challenge(options)
print("Challenge created:", challenge)
Helper_App.console_log(f"Challenge created: {challenge}")
# return jsonify({"challenge": challenge})
return jsonify({
"algorithm": challenge.algorithm,
@@ -102,58 +81,64 @@ def create_altcha_challenge():
"signature": challenge.signature,
})
"""
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)
@routes_core.route(Model_View_Contact.HASH_PAGE_CONTACT, methods=['POST'])
def contact_post():
try:
form = Form_Contact()
if form.validate_on_submit():
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.\n" 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)
return redirect(url_for(Model_View_Contact.ENDPOINT_PAGE_CONTACT_SUCCESS))
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {form.errors}",
data = None,
errors = [str(form.errors)],
meta = None
)
# html_body = render_template('pages/core/_contact.html', model = model)
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
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
"""
@routes_core.route(Model_View_Contact.HASH_PAGE_CONTACT_SUCCESS, methods=['GET'])
def contact_success():
try:
model = Model_View_Contact_Success()
html_body = render_template('pages/core/_contact_success.html', model = model)
return html_body
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)

View File

@@ -1,31 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Store 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.
"""
# IMPORTS
# internal
from models.model_view_store import Model_View_Store
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app, Response
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 = Blueprint('routes_store', __name__)
@routes_store.route(Model_View_Store.HASH_SCRIPTS_SECTION_STORE, methods=['GET'])
def scripts_section_store():
hash_page_current = request.args.get('hash_page_current', default = Model_View_Store.HASH_SCRIPTS_SECTION_STORE, type = str)
model = Model_View_Store(hash_page_current=hash_page_current)
template = render_template('js/sections/store.js', model = model)
return Response(template, mimetype='application/javascript')

View File

@@ -1,243 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: User 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.
"""
# IMPORTS
# internal
from models.model_view_base import Model_View_Base
from models.model_view_user import Model_View_User
from business_objects.user import User, Parameters_User
from datastores.datastore_user import DataStore_User
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 flask_sqlalchemy import SQLAlchemy
from sqlalchemy import exc
from flask_wtf.csrf import generate_csrf
from werkzeug.exceptions import BadRequest
from extensions import oauth # db,
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
from functools import wraps
db = SQLAlchemy()
routes_user = Blueprint('routes_user', __name__)
def handle_db_disconnect(f):
@wraps(f)
def decorated_function(*args, **kwargs):
try:
return f(*args, **kwargs)
except exc.OperationalError as e:
if "MySQL server has gone away" in str(e):
# Close the session and create a new connection
db.session.remove()
db.session.rollback()
# Retry the operation
return f(*args, **kwargs)
raise
return decorated_function
# User authentication
@routes_user.route("/login", methods=['POST', 'OPTIONS'])
def login():
try:
Helper_App.console_log('login')
Helper_App.console_log(f'method={request.method}')
try:
data = request.json
try:
data = request.get_json()
except:
data = {}
except:
data = {}
Helper_App.console_log(f'data={data}')
hash_callback = data.get(Model_View_Base.FLAG_CALLBACK, Model_View_Base.HASH_PAGE_HOME)
Helper_App.console_log(f'hash_callback: {hash_callback}')
"""
# Verify CSRF token manually
Helper_App.console_log(f'request headers={request.headers}')
token = request.headers.get(Model_View_Base.FLAG_CSRF_TOKEN)
Helper_App.console_log(f'token={token}')
Helper_App.console_log(f'session={session}')
Helper_App.console_log(f'session token={session.get('csrf_token')}')
if not token or token != session.get('csrf_token'):
token = data.get(Model_View_Base.FLAG_CSRF_TOKEN, None)
Helper_App.console_log(f'token={token}')
if not token or token != session.get('csrf_token'):
raise BadRequest('Invalid or missing CSRF token')
"""
# OAuth login
# callback_login = F'{Model_View_Base.HASH_CALLBACK_LOGIN}{data.get(Model_View_Base.FLAG_CALLBACK, Model_View_Base.HASH_PAGE_HOME)}'
# encoded_path = quote(data.get(Model_View_Base.FLAG_CALLBACK, Model_View_Base.HASH_PAGE_HOME))
uri_redirect = url_for('routes_user.login_callback', _external=True) # , subpath=encoded_path
# uri_redirect = f'{current_app.URL_HOST}/login_callback?subpath={data.get(Model_View_Base.FLAG_CALLBACK, Model_View_Base.HASH_PAGE_HOME)}'
Helper_App.console_log(f'redirect uri: {uri_redirect}')
Helper_App.console_log(f'Before red')
red = oauth.auth0.authorize_redirect(
redirect_uri = uri_redirect,
state = quote(hash_callback)
)
Helper_App.console_log(f'redirect: {red}')
headers = red.headers['Location']
Helper_App.console_log(f'headers: {headers}')
parsed_url = urlparse(headers)
query_params = parse_qs(parsed_url.query)
Helper_App.console_log(f"""
OAuth Authorize Redirect URL:
Base URL: {parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}
{parsed_url}
Query Parameters: {query_params}
""")
return jsonify({'Success': True, Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_SUCCESS, f'{Model_View_Base.FLAG_CALLBACK}': headers})
return jsonify({'status': 'success', 'redirect': callback})
except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@routes_user.route("/login_callback") # <path:subpath>/<code>
@handle_db_disconnect
def login_callback():
Helper_App.console_log('login_callback')
try:
error_state = request.args.get(Model_View_User.FLAG_ERROR_OAUTH)
has_error = error_state is not None
if has_error:
error_description = request.args.get(Model_View_User.FLAG_ERROR_DESCRIPTION_OAUTH)
error_text = f'Error: {error_state}: {error_description}'
Helper_App.console_log(error_text)
return login()
# Helper_App.console_log(f'code: {code}')
token = None
try:
token = oauth.auth0.authorize_access_token()
except Exception as e:
# Log the error for debugging
Helper_App.console_log(f"Error: {str(e)}")
session[current_app.config['ID_TOKEN_USER']] = token
# import user id
"""
Helper_App.console_log(f'str(type(token)) = {str(type(token))}')
Helper_App.console_log(f'token = {token}')
userinfo = token.get('userinfo')
Helper_App.console_log(f'user info: {userinfo}')
# id_user = token.get('sub')
id_user = userinfo.get('sub')
Helper_App.console_log(f'user ID: {id_user}')
"""
user = User.from_json_auth0(token) # datastore_user.get_user_auth0()
Helper_App.console_log(f'user: {user}')
filters = Parameters_User.from_user(user)
datastore_user = DataStore_User()
users, errors = datastore_user.get_many_user(filters, user)
try:
user = users[0]
Helper_App.console_log('User logged in')
Helper_App.console_log(f'user ({str(type(user))}): {user}')
Helper_App.console_log(f'user key: {Model_View_Base.FLAG_USER}')
user_json = user.to_json()
session[Model_View_Base.FLAG_USER] = user_json
Helper_App.console_log(f'user stored on session')
except:
Helper_App.console_log(f'User not found: {Parameters_User}\nDatabase query error: {errors}')
try:
hash_callback = token.get('hash_callback')
if hash_callback is None:
Helper_App.console_log('hash is none')
state = request.args.get('state')
Helper_App.console_log(f'state: {state}')
hash_callback = state # .get('hash_callback')
Helper_App.console_log(f'hash_callback: {hash_callback}')
except:
Helper_App.console_log("get hash callback failed")
# id_user = get_id_user()
# add user to database
# DataStore_Store().add_new_user(id_user) # this is part of get basket - should occur on page load
Helper_App.console_log(f'user session: {session[Model_View_Base.FLAG_USER]}')
return redirect(f"{current_app.config['URL_HOST']}{hash_callback}")
except Exception as e:
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Controller error.\n{e}'})
@routes_user.route("/logout")
def logout():
session.clear()
url_logout = f"https://{current_app.config['DOMAIN_AUTH0']}/v2/logout?" + urlencode(
{
"returnTo": url_for("routes_user.logout_callback", _external=True),
"client_id": current_app.config['ID_AUTH0_CLIENT'],
}# ,
# quote_via=quote_plus,
)
Helper_App.console_log(f"Redirecting to {url_logout}")
return redirect(url_logout)
@routes_user.route("/logout_callback") # <path:subpath>/<code>
@handle_db_disconnect
def logout_callback():
return redirect(url_for('routes_core.home'))
try:
session[current_app.ID_TOKEN_USER] = None
user = User()
try:
hash_callback = token.get('hash_callback')
if hash_callback is None:
Helper_App.console_log('hash is none')
state = request.args.get('state')
Helper_App.console_log(f'state: {state}')
hash_callback = state # .get('hash_callback')
Helper_App.console_log(f'hash_callback: {hash_callback}')
except:
Helper_App.console_log("get hash callback failed")
# id_user = get_id_user()
# add user to database
# DataStore_Store().add_new_user(id_user) # this is part of get basket - should occur on page load
Helper_App.console_log(f'user session: {session[Model_View_Base.FLAG_USER]}')
return redirect(f'{current_app.URL_HOST}{hash_callback}')
except Exception as e:
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.FLAG_FAILURE, Model_View_Base.FLAG_MESSAGE: f'Controller error.\n{e}'})
@routes_user.route("/user")
def user():
try:
model = Model_View_User()
for currency in model.currencies:
if currency.id_currency == model.user.id_currency_default:
model.user.currency_default = currency
break
for region in model.regions:
if region.id_region == model.user.id_region_default:
model.user.region_default = region
break
model.users = [model.user]
if not model.is_user_logged_in:
# return redirect(url_for('routes_user.login', data = jsonify({ Model_View_User.FLAG_CALLBACK: Model_View_User.HASH_PAGE_USER_ACCOUNT })))
return redirect(url_for('routes_core.home'))
html_body = render_template('pages/user/_user.html', model = model)
except Exception as e:
return str(e)
return html_body

View File

@@ -89,60 +89,11 @@ class DataStore_Base(BaseModel):
Helper_App.console_log(f'result: {result}')
# conn.session.remove()
return result
cursor = result.cursor
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'categories: {result_set_1}')
cursor.nextset()
result_set_2 = cursor.fetchall()
Helper_App.console_log(f'products: {result_set_2}')
@staticmethod
def db_cursor_clear(cursor):
while cursor.nextset():
Helper_App.console_log(f'new result set: {cursor.fetchall()}')
@classmethod
def get_many_region_and_currency(cls):
_m = 'DataStore_Base.get_many_region_and_currency'
_m_db_currency = 'p_shop_get_many_currency'
_m_db_region = 'p_shop_get_many_region'
argument_dict_list_currency = {
'a_get_inactive_currency': 0
}
argument_dict_list_region = {
'a_get_inactive_currency': 0
}
Helper_App.console_log(f'executing {_m_db_currency}')
result = cls.db_procedure_execute(_m_db_currency, argument_dict_list_currency)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
currencies = []
for row in result_set_1:
currency = Currency.make_from_DB_currency(row)
currencies.append(currency)
Helper_App.console_log(f'currencies: {currencies}')
DataStore_Base.db_cursor_clear(cursor)
Helper_App.console_log(f'executing {_m_db_region}')
result = cls.db_procedure_execute(_m_db_region, argument_dict_list_region)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
regions = []
for row in result_set_1:
region = Region.make_from_DB_region(row)
regions.append(region)
Helper_App.console_log(f'regions: {regions}')
DataStore_Base.db_cursor_clear(cursor)
cursor.close()
return regions, currencies
@staticmethod
def get_user_session():
Helper_App.console_log('DataStore_Base.get_user_session')
@@ -214,30 +165,6 @@ class DataStore_Base(BaseModel):
else:
expected_columns = set(column.name for column in db.inspect(table_object).columns)
Helper_App.console_log(f'expected_columns: {expected_columns}')
""" v1, v2
try:
for i in range(0, len(records), batch_size):
"" v1
batch = records[i:i+batch_size]
Helper_App.console_log(f'batch: {batch}')
db.session.bulk_save_objects(batch)
""
""
data = [object.to_json() for object in batch]
Helper_App.console_log(f'data: {data}')
for row in data:
row_keys = set(row.keys())
if row_keys != expected_columns:
Helper_App.console_log(f"Column mismatch in row: {row}")
Helper_App.console_log(f'missing columns: {expected_columns - row_keys}')
Helper_App.console_log(f'extra columns: {row_keys - expected_columns}')
# db.session.bulk_insert_mappings(permanent_table_name, data)
""
except Exception as e:
Helper_App.console_log(f'{_m}\n{e}')
db.session.rollback()
raise e
"""
max_retries = 3
initial_backoff = 1
for i in range(0, len(records), batch_size):
@@ -271,17 +198,9 @@ class DataStore_Base(BaseModel):
filters = Filters_Access_Level()
av.val_instance(filters, 'filters', _m, Filters_Access_Level)
argument_dict = filters.to_json()
# user = cls.get_user_session()
# argument_dict['a_id_user'] = 1 # 'auth0|6582b95c895d09a70ba10fef' # id_user
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_access_level')
result = cls.db_procedure_execute('p_shop_get_many_access_level', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
# access_levels
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw access levels: {result_set_1}')
access_levels = []
for row in result_set_1:
new_access_level = Access_Level.from_DB_access_level(row)
@@ -290,7 +209,6 @@ class DataStore_Base(BaseModel):
# 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])
@@ -308,17 +226,9 @@ class DataStore_Base(BaseModel):
filters = Filters_Unit_Measurement()
av.val_instance(filters, 'filters', _m, Filters_Unit_Measurement)
argument_dict = filters.to_json()
# user = cls.get_user_session()
# argument_dict['a_id_user'] = 1 # 'auth0|6582b95c895d09a70ba10fef' # id_user
Helper_App.console_log(f'argument_dict: {argument_dict}')
Helper_App.console_log('executing p_shop_get_many_unit_measurement')
result = cls.db_procedure_execute('p_shop_get_many_unit_measurement', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
# units of measurement
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw units of measurement: {result_set_1}')
units = []
for row in result_set_1:
new_unit = Unit_Measurement.from_DB_unit_measurement(row)
@@ -327,7 +237,6 @@ class DataStore_Base(BaseModel):
# 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])
@@ -348,18 +257,13 @@ class DataStore_Base(BaseModel):
'a_get_inactive_region': 1 if get_inactive else 0
}
Helper_App.console_log(f'executing {_m_db_region}')
result = cls.db_procedure_execute(_m_db_region, argument_dict_list_region)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
regions = []
for row in result_set_1:
region = Region.from_DB_region(row)
regions.append(region)
Helper_App.console_log(f'regions: {regions}')
DataStore_Base.db_cursor_clear(cursor)
cursor.close()

View File

@@ -1,351 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Base Store DataStore
Description:
Datastore for Store
"""
# internal
# from routes import bp_home
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
from business_objects.region import Region
from business_objects.store.discount import Discount
from business_objects.store.order import Order
from business_objects.store.plant import Plant
from business_objects.store.product import Product, Product_Permutation, Parameters_Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item
from business_objects.store.storage_location import Storage_Location
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_base import DataStore_Base
from extensions import db
from helpers.helper_app import Helper_App
from helpers.helper_db_mysql import Helper_DB_MySQL
import lib.argument_validation as av
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
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_Base(DataStore_Base):
# Global constants
KEY_BASKET: ClassVar[str] = Basket.KEY_BASKET
KEY_IS_INCLUDED_VAT: ClassVar[str] = Basket.KEY_IS_INCLUDED_VAT # 'is_included_VAT'
KEY_ID_CURRENCY: ClassVar[str] = Basket.KEY_ID_CURRENCY # 'id_currency'
KEY_ID_REGION_DELIVERY: ClassVar[str] = Basket.KEY_ID_REGION_DELIVERY # 'id_region_delivery'
# Attributes
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
def get_many_product(cls, product_filters):
# redundant argument validation?
_m = 'DataStore_Store_Base.get_many_product'
av.val_instance(product_filters, 'product_filters', _m, Parameters_Product)
argument_dict = product_filters.to_json()
user = cls.get_user_session()
"""
argument_dict['a_id_user'] = user.id_user # 'auth0|6582b95c895d09a70ba10fef' # id_user
argument_dict['a_debug'] = 0
"""
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_product')
result = cls.db_procedure_execute('p_shop_get_many_product', argument_dict)
cursor = result.cursor
Helper_App.console_log('data received')
category_list = Product_Category_Container()
Helper_App.console_log(f'initial category_list: {category_list}')
# Categories
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw categories: {result_set_1}')
for row in result_set_1:
new_category = Product_Category.from_DB_get_many_product_catalogue(row)
Helper_App.console_log(f'new_category: {new_category}')
category_list.add_product_category(new_category)
Helper_App.console_log(f'category-loaded category_list: {category_list}')
# Products
cursor.nextset()
result_set_2 = cursor.fetchall()
Helper_App.console_log(f'raw products: {result_set_2}')
for row in result_set_2:
Helper_App.console_log(f'row: {row}')
new_product = Product.from_DB_get_many_product_catalogue(row)
Helper_App.console_log(f'new_product: {new_product}')
try:
category_list.add_product(new_product)
except Exception as e:
Helper_App.console_log(f'Error adding product: {e}')
# Permutations
cursor.nextset()
result_set_3 = cursor.fetchall()
for row in result_set_3:
new_permutation = Product_Permutation.from_DB_get_many_product_catalogue(row)
try:
category_list.add_product_permutation(new_permutation)
except Exception as e:
Helper_App.console_log(f'Error adding permutation: {e}')
# Product_Variations
cursor.nextset()
result_set_4 = cursor.fetchall()
for row in result_set_4:
new_variation_type = Product_Variation_Type.from_DB_get_many_product_catalogue(row)
try:
category_list.add_product_variation_type(new_variation_type)
except Exception as e:
Helper_App.console_log(f'Error adding variation: {e}')
# Images
cursor.nextset()
result_set_5 = cursor.fetchall()
for row in result_set_5:
new_image = Image.from_DB_get_many_product_catalogue(row)
category_list.add_product_image(new_image)
# 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}")
category_list.get_all_product_variation_trees()
"""
for category in category_list.categories:
Helper_App.console_log(f'category: {category.name}')
for product in category.products:
permutation = product.get_permutation_selected()
Helper_App.console_log(f'product: {product.name}\nselected permutation: {permutation}')
"""
if len(errors) > 0:
for error in errors:
if error.code == 'PRODUCT_AVAILABILITY':
ids_permutation_unavailable = DataStore_Store_Base.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_Base.db_cursor_clear(cursor)
cursor.close()
Helper_App.console_log(f'get many category_list: {category_list}')
return category_list, errors # categories, category_index
"""
def get_many_id_price(self, product_ids):
_m = 'DataStore_Store_Base.get_many_id_price'
av.val_str(product_ids, 'product_ids', _m)
price_ids = []
for product_id in product_ids.split(','):
if product_id == 'prod_PB0NUOSEs06ymG':
price_ids.append() # get price id
return price_ids
"""
@staticmethod
def get_ids_permutation_from_error_availability(msg_error_availability):
ids_permutation = []
index_colon = msg_error_availability.find(':', msg_error_availability.find(':'))
msg_error_availability = msg_error_availability[index_colon + 1:]
index_comma = 0
while index_comma > -1:
msg_error_availability = msg_error_availability[index_comma:]
index_comma = msg_error_availability.find(',')
ids_permutation.append(msg_error_availability[:index_comma])
return ids_permutation
@classmethod
def get_many_plant(cls, get_inactive = False):
_m = 'DataStore_Store_Base.get_many_plant'
_m_db_plant = 'p_shop_get_many_plant'
argument_dict_list_plant = {
'a_get_inactive_plant': 1 if get_inactive else 0
}
Helper_App.console_log(f'executing {_m_db_plant}')
result = cls.db_procedure_execute(_m_db_plant, argument_dict_list_plant)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
plants = []
for row in result_set_1:
plant = Plant.from_DB_plant(row)
plants.append(plant)
Helper_App.console_log(f'plants: {plants}')
DataStore_Store_Base.db_cursor_clear(cursor)
cursor.close()
return plants
@classmethod
def get_many_storage_location(self, get_inactive = False):
_m = 'DataStore_Store_Base.get_many_storage_location'
_m_db_storage_location = 'p_shop_get_many_storage_location'
argument_dict_list_storage_location = {
'a_get_inactive_storage_location': 1 if get_inactive else 0
}
Helper_App.console_log(f'executing {_m_db_storage_location}')
result = self.db_procedure_execute(_m_db_storage_location, argument_dict_list_storage_location)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
storage_locations = []
for row in result_set_1:
storage_location = Storage_Location.from_DB_storage_location(row)
storage_locations.append(storage_location)
Helper_App.console_log(f'storage_locations: {storage_locations}')
DataStore_Store_Base.db_cursor_clear(cursor)
cursor.close()
return storage_locations
@classmethod
def get_many_currency(cls, get_inactive = False):
_m = 'DataStore_Store_Base.get_many_currency'
_m_db_currency = 'p_shop_get_many_currency'
argument_dict_list_currency = {
'a_get_inactive_currency': 1 if get_inactive else 0
}
Helper_App.console_log(f'executing {_m_db_currency}')
result = cls.db_procedure_execute(_m_db_currency, argument_dict_list_currency)
cursor = result.cursor
Helper_App.console_log('data received')
# cursor.nextset()
result_set_1 = cursor.fetchall()
currencies = []
for row in result_set_1:
currency = Currency.from_DB_currency(row)
currencies.append(currency)
Helper_App.console_log(f'currencies: {currencies}')
DataStore_Store_Base.db_cursor_clear(cursor)
return currencies
@classmethod
def get_many_region_and_currency(cls, get_inactive_currency = False, get_inactive_region = False):
_m = 'DataStore_Store_Base.get_many_region_and_currency'
currencies = cls.get_many_currency(get_inactive_currency)
regions = cls.get_many_region(get_inactive_region)
return regions, currencies
@classmethod
def get_many_product_variation(cls, variation_filters):
_m = 'DataStore_Store_Base.get_many_product_variation'
Helper_App.console_log(_m)
av.val_instance(variation_filters, 'variation_filters', _m, Parameters_Product_Variation)
guid = Helper_DB_MySQL.create_guid()
# now = datetime.now()
# user = self.get_user_session()
"""
argument_dict_list = {
'a_id_user': id_user,
'a_comment': comment,
'a_guid': guid
}
"""
user = cls.get_user_session()
argument_dict_list = {
# 'a_guid': guid
'a_id_user': user.id_user
, **variation_filters.to_json()
, 'a_debug': 0
}
# argument_dict_list['a_guid'] = guid
result = cls.db_procedure_execute('p_shop_get_many_product_variation', argument_dict_list)
cursor = result.cursor
result_set_vt = cursor.fetchall()
# Product_Variation Types
# variation_container = Product_Variation_Container()
variation_types = []
index_variation_type = {}
for row in result_set_vt:
new_variation_type = Product_Variation_Type.from_DB_get_many_product_variation(row)
# variation_container.add_product_variation_type(new_variation_type)
index_variation_type[new_variation_type.id_type] = len(variation_types)
variation_types.append(new_variation_type)
Helper_App.console_log(f'index_variation_type: {index_variation_type}')
# Product_Variations
cursor.nextset()
result_set_v = cursor.fetchall()
# variations = Product_Variation_Container()
variations = []
for row in result_set_v:
new_variation = Product_Variation.from_DB_get_many_product_variation(row)
# new_variation.variation_type = variation_types_dict[new_variation.id_type]
# variation_container.add_product_variation(new_variation)
variation_types[index_variation_type[new_variation.id_type]].variations.append(new_variation)
variations.append(new_variation)
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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Base.db_cursor_clear(cursor)
cursor.close()
return variation_types, variations, errors

View File

@@ -1,195 +0,0 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Stripe DataStore
Description:
Datastore for Store Stripe service
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product import Product, Product_Permutation, Product_Price, Parameters_Product
from business_objects.sql_error import SQL_Error
from datastores.datastore_store_base import DataStore_Store_Base
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
from helpers.helper_app import Helper_App
# 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_Stripe(DataStore_Store_Base):
# Global constants
# Attributes
key_public_stripe: str = None
key_secret_stripe: str = None
def __init__(self):
super().__init__()
self.key_secret_stripe = os.environ.get("KEY_SECRET_STRIPE")
self.key_public_stripe = os.environ.get("KEY_PUBLIC_STRIPE")
# 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_key = self.key_secret_stripe
def get_many_stripe_product_new(self):
_m = 'DataStore_Store_Stripe.get_many_stripe_product_new'
_m_db = 'p_shop_get_many_stripe_product_new'
# av.val_str(id_user)
# validation conducted by server
argument_dict_list = {
'a_id_user': self.info_user
}
Helper_App.console_log(f'executing {_m_db}')
result = self.db_procedure_execute(_m_db, argument_dict_list)
cursor = result.cursor
Helper_App.console_log('data received')
# Products
cursor.nextset()
result_set_1 = cursor.fetchall()
products = []
for row in result_set_1:
new_product = Product.from_DB_Stripe_product(row) # Product(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
products.append(new_product)
Helper_App.console_log(f'products: {products}')
# 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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Stripe.db_cursor_clear(cursor)
return products
def get_many_stripe_price_new(self):
_m = 'DataStore_Store_Stripe.get_many_stripe_price_new'
_m_db = 'p_shop_get_many_stripe_price_new'
# av.val_str(id_user)
# validation conducted by server
argument_dict_list = {
'a_id_user': self.info_user
}
Helper_App.console_log(f'executing {_m_db}')
result = self.db_procedure_execute(_m_db, argument_dict_list)
cursor = result.cursor
Helper_App.console_log('data received')
# Products
cursor.nextset()
result_set_1 = cursor.fetchall()
products = []
for row in result_set_1:
new_product = Product.from_DB_Stripe_price(row) # Product(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
products.append(new_product)
Helper_App.console_log(f'products: {products}')
# 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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_Store_Stripe.db_cursor_clear(cursor)
return products
def get_many_product_new(self):
_m = 'DataStore_Store_Stripe.get_many_product_new'
# Stripe
new_products = self.get_many_stripe_product_new()
for product in new_products:
product.id_stripe_product = self.create_stripe_product(product)
return new_products
def get_many_price_new(self):
_m = 'DataStore_Store_Stripe.get_many_product_new'
# Stripe
new_products = self.get_many_stripe_price_new()
for product in new_products:
product.id_stripe_price = self.create_stripe_price(product)
return new_products
# Stripe
def create_stripe_product(self, product): # _name, product_description):
_m = 'DataStore_Store_Stripe_Checkout.create_stripe_product'
# av.val_str(product_name, 'product_name', _m)
# av.val_str(product_description, 'product_description', _m)
av.val_instance(product, 'product', _m, Product)
Helper_App.console_log(f'stripe.api_key = {stripe.api_key}')
new_product = stripe.Product.create(
name = product.name,
description = product.description,
)
# Save these identifiers
Helper_App.console_log(f"Success! Here is your new Stripe product id: {new_product.id}")
return new_product.id
def create_stripe_price(self, product, currency): # product_id, product_price, product_currency, product_is_subscription, product_recurring_interval = '', product_interval_count = 0):
_m = 'DataStore_Store_Stripe_Checkout.create_stripe_price'
"""
av.val_str(p_id, 'p_id', _m)
av.full_val_float(p_price, 'p_price', _m, 0.01)
p_price = round(p_price, 2)
av.val_str(p_currency, 'p_currency', _m)
av.full_val_bool(p_is_subscription, 'p_is_subscription', _m)
p_is_subscription = bool(p_is_subscription)
av.val_str(p_recurring_interval, 'p_recurring_interval', _m)
av.full_val_int(p_interval_count, 'p_interval_count', _m, 1 if p_is_subscription else 0)
p_interval_count = int(p_interval_count)
"""
av.val_instance(product, 'product', _m, Product)
av.val_str(currency, 'currency', _m)
Helper_App.console_log(f'stripe.api_key = {stripe.api_key}')
new_product_price = stripe.Price.create(
unit_amount = product.unit_price,
currency = currency,
recurring = { "interval": product.name_recurring_interval, "interval_count": product.count_recurring_interval } if product.is_subscription else None,
product = product.id_stripe_product
)
# Save these identifiers
Helper_App.console_log(f"Success! Here is your Stripe product price id: {new_product_price.id} for {product.name}")
return new_product_price.id

View File

@@ -42,9 +42,7 @@ class DataStore_User(DataStore_Base):
super().__init__()
def edit_user(self):
# redundant argument validation?
_m = 'DataStore_User.edit_user'
# av.val_instance(filters, 'filters', _m, Filters_Product_Category)
argument_dict_list = {
'a_id_user': self.info_user.get('sub'),
@@ -57,12 +55,10 @@ class DataStore_User(DataStore_Base):
cursor = result.cursor
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw user data: {result_set_1}')
# 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:
@@ -71,67 +67,12 @@ class DataStore_User(DataStore_Base):
DataStore_User.db_cursor_clear(cursor)
return (result_set_1[0][1] == b'\x01')
"""
def get_many_user_order(self, id_user, ids_order, n_order_max, id_checkout_session):
_m = 'DataStore_User.get_many_user_order'
# av.val_str(id_user)
# validation conducted by server
argument_dict_list = {
'a_id_user': id_user,
'a_ids_order': ids_order,
'a_n_order_max': n_order_max,
'a_id_checkout_session': id_checkout_session
}
Helper_App.console_log('executing p_shop_get_many_user_order')
result = self.db_procedure_execute('p_shop_get_many_user_order', argument_dict_list)
cursor = result.cursor
Helper_App.console_log('data received')
# Discount Delivery Regions
cursor.nextset()
result_set_1 = cursor.fetchall()
orders = []
for row in result_set_1:
new_order = Order(row[0], row[1], row[2], row[3], row[4], row[5], row[6])
orders.append(new_order)
Helper_App.console_log(f'orders: {orders}')
# 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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_User.db_cursor_clear(cursor)
return orders
"""
def get_many_user(self, user_filters, user=None):
_m = 'DataStore_User.get_many_user'
Helper_App.console_log(_m)
# av.val_str(user_filters, 'user_filters', _m)
# av.val_list(permutations, 'list_permutations', _m, Product_Permutation, 1)
av.val_instance(user_filters, 'user_filters', _m, Parameters_User)
guid = Helper_DB_MySQL.create_guid()
# now = datetime.now()
# user = self.get_user_session()
"""
argument_dict_list = {
'a_id_user': id_user,
'a_comment': comment,
'a_guid': guid
}
"""
if user is None:
user = self.get_user_session()
argument_dict_list = {
@@ -142,92 +83,47 @@ class DataStore_User(DataStore_Base):
, 'a_debug': 0
}
# argument_dict_list['a_guid'] = guid
result = self.db_procedure_execute('p_get_many_user', argument_dict_list)
"""
query = text(f"SELECT * FROM Shop_Calc_User_Temp UE_T WHERE UE_T.guid = '{guid}'")
result = self.db.session.execute(query)
"""
cursor = result.cursor
result_set = cursor.fetchall()
Helper_App.console_log(f'raw users: {result_set}')
Helper_App.console_log(f'type result set: {str(type(result_set))}')
Helper_App.console_log(f'len result set: {len(result_set)}')
"""
user_permission_evals = []
for row in result_set:
user_permission_eval = User_Permission_Evaluation.from_DB_user_eval(row)
user_permission_evals.append(user_permission_eval)
Helper_App.console_log(f'user_permission_evals: {user_permission_evals}')
"""
users = []
if len(result_set) > 0:
for row in result_set:
Helper_App.console_log(f'row: {row}')
user = User.from_DB_user(row)
users.append(user)
Helper_App.console_log(f'user {str(type(user))}: {user}')
Helper_App.console_log(f'type users: {str(type(users))}\n type user 0: {str(type(None if len(users) == 0 else users[0]))}')
# error_list, cursor = self.get_error_list_from_cursor(cursor)
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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_User.db_cursor_clear(cursor)
return users, errors
def get_many_user(self, user_filters, user=None):
_m = 'DataStore_User.get_many_user'
Helper_App.console_log(_m)
# av.val_str(user_filters, 'user_filters', _m)
# av.val_list(permutations, 'list_permutations', _m, Product_Permutation, 1)
av.val_instance(user_filters, 'user_filters', _m, Parameters_User)
guid = Helper_DB_MySQL.create_guid()
# now = datetime.now()
# user = self.get_user_session()
"""
argument_dict_list = {
'a_id_user': id_user,
'a_comment': comment,
'a_guid': guid
}
"""
if user is None:
user = self.get_user_session()
argument_dict_list = {
# 'a_guid': guid
'a_id_user': user.id_user
, 'a_id_user_auth0': user.id_user_auth0
, **user_filters.to_json()
, 'a_debug': 0
}
# argument_dict_list['a_guid'] = guid
result = self.db_procedure_execute('p_get_many_user', argument_dict_list)
"""
query = text(f"SELECT * FROM Shop_Calc_User_Temp UE_T WHERE UE_T.guid = '{guid}'")
result = self.db.session.execute(query)
"""
cursor = result.cursor
result_set = cursor.fetchall()
Helper_App.console_log(f'raw users: {result_set}')
Helper_App.console_log(f'type result set: {str(type(result_set))}')
Helper_App.console_log(f'len result set: {len(result_set)}')
"""
user_permission_evals = []
for row in result_set:
user_permission_eval = User_Permission_Evaluation.from_DB_user_eval(row)
user_permission_evals.append(user_permission_eval)
Helper_App.console_log(f'user_permission_evals: {user_permission_evals}')
"""
users = []
if len(result_set) > 0:
for row in result_set:
Helper_App.console_log(f'row: {row}')
user = User.from_DB_user(row)
users.append(user)
Helper_App.console_log(f'user {str(type(user))}: {user}')
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_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
DataStore_User.db_cursor_clear(cursor)
return users, errors
def get_many_user(self, user_filters, user=None):
_m = 'DataStore_User.get_many_user'
av.val_instance(user_filters, 'user_filters', _m, Parameters_User)
guid = Helper_DB_MySQL.create_guid()
if user is None:
user = self.get_user_session()
argument_dict_list = {
# 'a_guid': guid
'a_id_user': user.id_user
, 'a_id_user_auth0': user.id_user_auth0
, **user_filters.to_json()
, 'a_debug': 0
}
result = self.db_procedure_execute('p_get_many_user', argument_dict_list)
cursor = result.cursor
result_set = cursor.fetchall()
users = []
if len(result_set) > 0:
for row in result_set:

View File

@@ -19,7 +19,7 @@ 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 import StringField, TextAreaField, SubmitField, HiddenField, BooleanField, Field, EmailField
from wtforms.validators import DataRequired, Email, ValidationError
import markupsafe
from flask_wtf.recaptcha import RecaptchaField
@@ -76,12 +76,12 @@ class ALTCHAField(Field):
class Form_Contact(FlaskForm):
email = StringField('Email')
email = EmailField('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')
altcha = ALTCHAField('Verify you are human')
submit = SubmitField('Send Message')

View File

@@ -31,7 +31,7 @@ def error_msg_str(v, v_name, method, v_type, suppress_errors = False, suppress_c
if not val_bool(suppress_console_outputs, 'suppress_console_outputs', my_f, suppress_errors):
error_msg = error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors)
if suppress_errors:
print(error_msg)
Helper_App.console_log(error_msg)
return error_msg
else:
raise ValueError(error_msg)
@@ -40,7 +40,7 @@ def error_msg_str(v, v_name, method, v_type, suppress_errors = False, suppress_c
error_msg = error_msg_str(method, 'method', my_f, "<class 'str'>", suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return error_msg
else:
raise ValueError(error_msg)
@@ -50,7 +50,7 @@ def error_msg_str(v, v_name, method, v_type, suppress_errors = False, suppress_c
error_msg = error_msg_str(v_name, 'v_name', my_f, "<class 'str'>", suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return error_msg
else:
raise ValueError(error_msg)
@@ -61,7 +61,7 @@ def error_msg_str(v, v_name, method, v_type, suppress_errors = False, suppress_c
error_msg = error_msg_str(v_type, 'v_type', my_f, "<class 'str'>", suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return error_msg
else:
raise ValueError(error_msg)
@@ -70,7 +70,7 @@ def error_msg_str(v, v_name, method, v_type, suppress_errors = False, suppress_c
error_msg = error_msg_str(v_arg_type, 'v_arg_type', my_f, "<class 'str'>", suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return error_msg
else:
raise ValueError(error_msg)
@@ -96,7 +96,7 @@ def val_bool(v_input, v_name, method, suppress_errors = False, suppress_console_
if str(type(suppress_console_outputs)) != v_type:
error_msg = error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, v_type, suppress_errors)
if suppress_errors:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
v_type = "<class 'str'>"
@@ -111,7 +111,7 @@ def val_bool(v_input, v_name, method, suppress_errors = False, suppress_console_
error_msg = error_msg_str(method, 'method', my_f, v_type, suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
my_f = method + '.' + my_f
@@ -126,7 +126,7 @@ def val_bool(v_input, v_name, method, suppress_errors = False, suppress_console_
error_msg = error_msg_str(v_name, 'v_name', my_f, v_type, suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# v_arg_type
@@ -140,7 +140,7 @@ def val_bool(v_input, v_name, method, suppress_errors = False, suppress_console_
error_msg = error_msg_str(v_arg_type, 'v_arg_type', my_f, v_type, suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
else:
raise ValueError(error_msg)
@@ -150,7 +150,7 @@ def val_bool(v_input, v_name, method, suppress_errors = False, suppress_console_
error_msg = error_msg_str(v_input, v_name, method, v_type, suppress_errors, suppress_console_outputs)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# RETURNS
@@ -176,7 +176,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
val_bool(suppress_errors, 'suppress_errors', my_f)
# suppress_console_outputs
if not val_bool(suppress_console_outputs, 'suppress_console_outputs', my_f, suppress_errors):
print(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
Helper_App.console_log(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
return False
# method
valid = True
@@ -189,7 +189,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
error_msg = error_msg_str(method, 'method', my_f, v_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
my_f = method + '.' + my_f
@@ -204,7 +204,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
error_msg = error_msg_str(v_name, 'v_name', my_f, v_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# v_arg_type
@@ -218,7 +218,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
error_msg = error_msg_str(v_arg_type, 'v_arg_type', my_f, v_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# min_len
@@ -227,7 +227,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
error_msg = error_msg_str(min_len, 'min_len', my_f, v_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# max_len
@@ -242,7 +242,7 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
error_msg = error_msg_str(max_len, 'max_len', my_f, v_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# v_input
@@ -256,15 +256,15 @@ def val_str(v_input, v_name, method, min_len = -1, max_len = -1, suppress_errors
L = len(v_input)
if min_len != -1 and L < min_len:
valid = False
print(f"Minimum str length {min_len} not met.")
Helper_App.console_log(f"Minimum str length {min_len} not met.")
if max_len != -1 and L > max_len:
print(f"Maximum str length {max_len} not met.")
Helper_App.console_log(f"Maximum str length {max_len} not met.")
valid = False
if not valid:
error_msg = error_msg_str(v_input, v_name, method, v_type, suppress_errors, suppress_console_outputs, v_arg_type)
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
# RETURNS
@@ -297,7 +297,7 @@ def val_int(v_input, v_name, method, v_min: Optional[int] = None, v_max: Optiona
val_bool(suppress_errors, 'suppress_errors', my_f)
# suppress_console_outputs
if not val_bool(suppress_console_outputs, 'suppress_console_outputs', my_f, suppress_errors):
print(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
Helper_App.console_log(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
return False
# method
if not val_str(method, 'method', my_f, 1, -1, suppress_errors, suppress_console_outputs): return False
@@ -322,28 +322,28 @@ def val_int(v_input, v_name, method, v_min: Optional[int] = None, v_max: Optiona
if not mytype == str(type(v_input)):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
if (v_min != None and v_max != None):
if (v_min > v_max):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
Helper_App.console_log(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
return False
raise ValueError(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
if (v_min != None):
if (v_input < v_min):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nValue less than minimum {v_min}.")
Helper_App.console_log(error_msg + f"\nValue less than minimum {v_min}.")
return False
raise ValueError(error_msg + f"\nValue less than minimum {v_min}.")
if (v_max != None):
if (v_input > v_max):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nValue greater than maximum {v_max}.")
Helper_App.console_log(error_msg + f"\nValue greater than maximum {v_max}.")
return False
raise ValueError(error_msg + f"\nValue greater than maximum {v_max}.")
# RETURNS
@@ -368,7 +368,7 @@ def val_float(v_input, v_name, method, v_min = None, v_max = None, suppress_erro
val_bool(suppress_errors, 'suppress_errors', my_f)
# suppress_console_outputs
if not val_bool(suppress_console_outputs, 'suppress_console_outputs', my_f, suppress_errors):
print(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
Helper_App.console_log(error_msg_str(suppress_console_outputs, 'suppress_console_outputs', my_f, "<class 'bool'>", suppress_errors))
return False
# v_name
if not val_str(v_name, 'v_name', my_f, 1, -1, suppress_errors, suppress_console_outputs): return False
@@ -393,28 +393,28 @@ def val_float(v_input, v_name, method, v_min = None, v_max = None, suppress_erro
if not mytype == str(type(v_input)):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return False
raise ValueError(error_msg)
if (v_min != None and v_max != None):
if (v_min > v_max):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
Helper_App.console_log(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
return False
raise ValueError(error_msg + f"\nInverted minimum and maximum values {v_min} and {v_max}.")
if (v_min != None):
if (v_input < v_min):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nValue less than minimum {v_min}.")
Helper_App.console_log(error_msg + f"\nValue less than minimum {v_min}.")
return False
raise ValueError(error_msg + f"\nValue less than minimum {v_min}.")
if (v_max != None):
if (v_input > v_max):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg + f"\nValue greater than maximum {v_max}.")
Helper_App.console_log(error_msg + f"\nValue greater than maximum {v_max}.")
return False
raise ValueError(error_msg + f"\nValue greater than maximum {v_max}.")
# RETURNS
@@ -456,7 +456,7 @@ def input_bool(v_input, v_name, method, suppress_errors = False, suppress_consol
if not val_str(v_input, v_name, my_f, suppress_errors=True, suppress_console_outputs=True):
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return None
raise ValueError(error_msg)
else:
@@ -471,7 +471,7 @@ def input_bool(v_input, v_name, method, suppress_errors = False, suppress_consol
return False
if suppress_errors:
if not suppress_console_outputs:
print(error_msg)
Helper_App.console_log(error_msg)
return None
raise ValueError(error_msg)
else:

View File

@@ -47,6 +47,17 @@ class Model_View_Base(BaseModel, ABC):
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'
ENDPOINT_GET_ALTCHA_CHALLENGE: ClassVar[str] = 'routes_core.create_altcha_challenge'
ENDPOINT_PAGE_ACCESSIBILITY_REPORT: ClassVar[str] = 'routes_legal.accessibility_report'
ENDPOINT_PAGE_ACCESSIBILITY_STATEMENT: ClassVar[str] = 'routes_legal.accessibility_statement'
ENDPOINT_PAGE_CONTACT: ClassVar[str] = 'routes_core.contact'
ENDPOINT_PAGE_CONTACT_SUCCESS: ClassVar[str] = 'routes_core.contact_success'
ENDPOINT_PAGE_DATA_RETENTION_SCHEDULE: ClassVar[str] = 'routes_legal.retention_schedule'
ENDPOINT_PAGE_ERROR_NO_PERMISSION: ClassVar[str] = 'routes_core.error_no_permission'
ENDPOINT_PAGE_HOME: ClassVar[str] = 'routes_core.home'
ENDPOINT_PAGE_LICENSE: ClassVar[str] = 'routes_legal.license'
ENDPOINT_PAGE_PRIVACY_POLICY: ClassVar[str] = 'routes_legal.privacy_policy'
ENDPOINT_POST_CONTACT_FORM: ClassVar[str] = 'routes_core.contact_post'
FLAG_ACCESS_LEVEL: ClassVar[str] = 'access_level'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = Base.FLAG_ACCESS_LEVEL_REQUIRED
FLAG_ACTIVE: ClassVar[str] = Base.FLAG_ACTIVE
@@ -62,6 +73,7 @@ class Model_View_Base(BaseModel, ABC):
FLAG_BUTTON_PRIMARY: ClassVar[str] = 'button-primary'
FLAG_CANCEL: ClassVar[str] = 'button-cancel'
FLAG_CALLBACK: ClassVar[str] = 'callback'
FLAG_CAPTCHA: ClassVar[str] = 'captcha'
FLAG_CARD: ClassVar[str] = 'card'
FLAG_CITY: ClassVar[str] = Base.FLAG_CITY
FLAG_CLOSE_TEMPORARY_ELEMENT: ClassVar[str] = 'button-temporary-element-close'
@@ -109,31 +121,12 @@ class Model_View_Base(BaseModel, ABC):
FLAG_NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.FLAG_NAME_ATTR_OPTION_VALUE
FLAG_NAME_PLURAL: ClassVar[str] = Base.FLAG_NAME_PLURAL
# FLAG_NAME_SINGULAR: ClassVar[str] = Base.FLAG_NAME_SINGULAR
FLAG_NAV_ADMIN_HOME: ClassVar[str] = 'navAdminHome'
FLAG_NAV_ADMIN_STORE_STRIPE_PRICES: ClassVar[str] = 'navAdminStoreStripePrices'
FLAG_NAV_ADMIN_STORE_STRIPE_PRODUCTS: ClassVar[str] = 'navAdminStoreStripeProducts'
FLAG_NAV_CONTACT: ClassVar[str] = 'navContact'
FLAG_NAV_HOME: ClassVar[str] = 'navHome'
FLAG_NAV_SERVICES: ClassVar[str] = 'navServices'
FLAG_NAV_STORE_HOME: ClassVar[str] = 'navStoreHome'
FLAG_NAV_STORE_MANUFACTURING_PURCHASE_ORDERS: ClassVar[str] = 'navStoreManufacturingPurchaseOrders'
FLAG_NAV_STORE_PRODUCTS: ClassVar[str] = 'navStoreProducts'
FLAG_NAV_STORE_PRODUCT_CATEGORIES: ClassVar[str] = 'navStoreProductCategories'
FLAG_NAV_STORE_PRODUCT_PERMUTATIONS: ClassVar[str] = 'navStoreProductPermutations'
FLAG_NAV_STORE_PRODUCT_PRICES: ClassVar[str] = 'navStoreProductPrices'
FLAG_NAV_STORE_PRODUCT_VARIATIONS: ClassVar[str] = 'navStoreProductVariations'
FLAG_NAV_STORE_STOCK_ITEMS: ClassVar[str] = 'navStoreStockItems'
FLAG_NAV_STORE_SUPPLIERS: ClassVar[str] = 'navStoreSuppliers'
FLAG_NAV_STORE_SUPPLIER_PURCHASE_ORDERS: ClassVar[str] = 'navStoreSupplierPurchaseOrders'
FLAG_NAV_USER_ACCOUNT: ClassVar[str] = 'navUserAccount'
FLAG_NAV_USER_ADMIN: ClassVar[str] = 'navUserAdmin'
FLAG_NAV_USER_LOGIN: ClassVar[str] = 'navUserLogin'
FLAG_NAV_USER_LOGOUT: ClassVar[str] = 'navUserLogout'
FLAG_OVERLAY: ClassVar[str] = 'overlay'
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'
@@ -148,25 +141,16 @@ class Model_View_Base(BaseModel, ABC):
FLAG_TEMPORARY_ELEMENT: ClassVar[str] = 'temporary-element'
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_GET_ALTCHA_CHALLENGE: ClassVar[str] = '/altcha/create-challenge'
HASH_PAGE_ACCESSIBILITY_REPORT: ClassVar[str] = '/accessibility-report'
HASH_PAGE_ACCESSIBILITY_STATEMENT: ClassVar[str] = '/accessibility-statement'
HASH_PAGE_ADMIN_HOME: ClassVar[str] = '/admin'
HASH_PAGE_CONTACT: ClassVar[str] = '/contact'
HASH_PAGE_CONTACT_SUCCESS: ClassVar[str] = '/contact-success'
HASH_PAGE_DATA_RETENTION_SCHEDULE: ClassVar[str] = '/retention-schedule'
HASH_PAGE_ERROR_NO_PERMISSION: ClassVar[str] = '/error'
HASH_PAGE_HOME: ClassVar[str] = '/'
HASH_PAGE_LICENSE: ClassVar[str] = '/license'
HASH_PAGE_PRIVACY_POLICY: ClassVar[str] = '/privacy-policy'
HASH_PAGE_SERVICES: ClassVar[str] = '/services'
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'
ID_BUTTON_ADD: ClassVar[str] = 'buttonAdd'
ID_BUTTON_APPLY_FILTERS: ClassVar[str] = 'buttonApplyFilters'
ID_BUTTON_CANCEL: ClassVar[str] = 'buttonCancel'
@@ -174,24 +158,8 @@ class Model_View_Base(BaseModel, ABC):
ID_BUTTON_SAVE: ClassVar[str] = 'buttonSave'
ID_CSRF_TOKEN: ClassVar[str] = 'X-CSRFToken'
ID_FORM_CONTACT: ClassVar[str] = 'formContact'
ID_FORM_CURRENCY: ClassVar[str] = 'formCurrency'
ID_FORM_DELIVERY_REGION: ClassVar[str] = 'formDeliveryRegion'
ID_FORM_FILTERS: ClassVar[str] = 'formFilters'
ID_FORM_IS_INCLUDED_VAT: ClassVar[str] = 'formIsIncludedVAT'
ID_LABEL_ERROR: ClassVar[str] = 'labelError'
"""
ID_MODAL_SERVICES: ClassVar[str] = 'modalServices'
ID_MODAL_TECHNOLOGIES: ClassVar[str] = 'modalTechnologies'
ID_BUTTON_NAV_ADMIN_HOME: ClassVar[str] = 'navAdminHome'
ID_BUTTON_NAV_ADMIN_STORE_STRIPE_PRICE: ClassVar[str] = 'navAdminStoreStripePrice'
ID_BUTTON_NAV_ADMIN_STORE_STRIPE_PRODUCT: ClassVar[str] = 'navAdminStoreStripeProduct'
# ID_BUTTON_NAV_CONTACT: ClassVar[str] = 'navContact'
ID_BUTTON_NAV_HOME: ClassVar[str] = 'navHome'
ID_BUTTON_NAV_SERVICES: ClassVar[str] = 'navServices'
ID_BUTTON_NAV_USER_ADMIN: ClassVar[str] = 'navUserAdmin'
ID_BUTTON_NAV_USER_LOGIN: ClassVar[str] = 'navUserLogin'
ID_BUTTON_NAV_USER_LOGOUT: ClassVar[str] = 'navUserLogout'
"""
ID_OVERLAY_CONFIRM: ClassVar[str] = 'overlayConfirm'
ID_OVERLAY_ERROR: ClassVar[str] = 'overlayError'
ID_OVERLAY_HAMBURGER: ClassVar[str] = 'overlayHamburger'
@@ -201,23 +169,10 @@ class Model_View_Base(BaseModel, ABC):
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'
URL_LINKEDIN: ClassVar[str] = 'https://uk.linkedin.com/in/lordteddyms'
URL_LINKEDIN: ClassVar[str] = 'https://uk.linkedin.com/in/teddyms'
# 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
db: SQLAlchemy
"""
# """
hash_page_current: str
# """
app: Flask = None
session: None = None
is_page_store: bool = None
@@ -232,48 +187,18 @@ class Model_View_Base(BaseModel, ABC):
def title(self):
pass
"""
def __new__(cls, db, info_user, app): # , *args, **kwargs
# Initialiser - validation
_m = 'Model_View_Base.__new__'
v_arg_type = 'class attribute'
Helper_App.console_log(f'{_m}\nstarting')
# 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, hash_page_current, **kwargs):
# Constructor
"""
_m = 'Model_View_Base.__init__'
v_arg_type = 'class attribute'
Helper_App.console_log(f'{_m}\nstarting')
av.val_instance(db, 'db', _m, SQLAlchemy, v_arg_type=v_arg_type)
"""
BaseModel.__init__(self, hash_page_current=hash_page_current, **kwargs)
"""
self.db = db
self.session = session
info_user = self.get_info_user()
Helper_App.console_log(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'?")
Helper_App.console_log(f'is_user_logged_in: {self.is_user_logged_in}')
self.id_user = info_user['sub'] if self.is_user_logged_in else ''
self.app = app
"""
self.app = current_app
with self.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
Helper_App.console_log(f'session: {self.session}')
datastore_user = DataStore_User()
self.user = datastore_user.get_user_session()
self.is_user_logged_in = self.user.is_logged_in
Helper_App.console_log(f'model_view_base init end - model.user: {self.user}')
# Helper_App.console_log(f'model_view_base init end - model.user: {self.user}')
def output_bool(self, boolean):
return str(boolean).lower()
@@ -284,34 +209,6 @@ class Model_View_Base(BaseModel, ABC):
def get_user_session(self):
datastore_user = DataStore_User()
return datastore_user.get_user_session()
"""
def get_is_admin_store_user(self):
datastore_store = DataStore_Store()
user = datastore_store.get_user_session()
if not user.is_logged_in: return False
filters_user = Parameters_User.from_user(user) # get_default(datastore_store)
users, errors = datastore_store.get_many_user(filters_user)
try:
user = users[0]
return av.input_bool(user.can_admin_store, 'can_admin_store', 'Model_View_Base.get_is_admin_store_user')
except:
return False
user = self.get_user_session()
return user.can_admin_store
def get_is_admin_user_user(self):
datastore_store = DataStore_Store()
user = datastore_store.get_user_session()
if not user.is_logged_in: return False
filters_user = Parameters_User.from_user(user) # .get_default(datastore_store)
users, errors = datastore_store.get_many_user(filters_user)
try:
user = users[0]
return av.input_bool(user.can_admin_user, 'can_admin_user', 'Model_View_Base.get_is_admin_user_user')
except:
return False
"""
def get_many_access_level(self, filters=None):
_m = 'Model_View_Store.get_many_access_level'
@@ -358,7 +255,6 @@ class Model_View_Base(BaseModel, ABC):
if preview_str != '':
preview_str += '\n'
obj_json = obj.to_json()
Helper_App.console_log(f'obj_json: {obj_json}')
preview_str += obj_json[obj_json[Base.FLAG_NAME_ATTR_OPTION_TEXT]]
return preview_str
@staticmethod

View File

@@ -0,0 +1,31 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Contact View Model
Description:
Data model for contact view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
from lib import argument_validation as av
# 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_Success(Model_View_Base):
@property
def title(self):
return 'Contact Success'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_CONTACT_SUCCESS, **kwargs):
super().__init__(hash_page_current=hash_page_current, **kwargs)

View File

@@ -1,182 +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
# VARIABLE INSTANTIATION
# METHODS
# IMPORTS
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
from config import app_config
# 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():
print(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
print(f"Success! Here is your starter subscription product id: {starter_subscription.id}")
print(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 == '':
print('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"))))
# template_dir = str(os.path.abspath(os.path.join(
# app_dir, os.getenv("TEMPLATE_DIR"))))
app = Flask(__name__) # , static_folder=static_dir,
# static_url_path="", template_folder=template_dir)
app.config.from_object(app_config)
@app.route('/', methods=['GET'])
def home():
# 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('_home.html', title='Home')
@app.route('/store', methods=['GET'])
def store_home():
return render_template('_store_home.html', title='Store Home')
@app.route('/contact')
def contact():
return render_template('_contact.html', title='Contact Us')
@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')
print(f'checkout session id: {id}')
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='subscription', # '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']
print('event ' + event_type)
if event_type == 'checkout.session.completed':
print('🔔 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

@@ -30,4 +30,4 @@ body {
position: fixed;
top:
}
*/
*/

View File

@@ -4,19 +4,23 @@
}
.contact-form {
max-width: 800px;
max-width: 60vw;
width: fit-content;
margin: 0 auto;
background: #fff;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.contact-form textarea {
max-width: 40vw;
}
.form-grid {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
margin-bottom: 1rem;
}
.form-label {
@@ -36,13 +40,32 @@ textarea.form-input {
min-height: 120px;
}
.marketing-consent {
margin: 2rem 0;
padding-left: 200px;
.marketing-consent input {
display: inline-block;
margin-left: 18vw;
margin-bottom: 1.25rem;
}
.container.recaptcha {
.container.captcha > div:first-child {
margin-left: 15vw;
margin-right: 15vw;
}
.container.captcha > div:first-child > label:first-child {
display: flex;
justify-content: center;
}
.container.captcha > p:last-child{
font-size: 0.9rem;
margin: 1vh 0;
}
.container.captcha .altcha-main {
padding-left: 1rem;
padding-top: 0.75rem;
padding-bottom: 0;
}
.container.captcha .altcha-main > :last-child {
display: none;
}
input[type="submit"] {
@@ -73,7 +96,17 @@ input[type="submit"]:hover {
font-size: 1.1rem;
}
.data-notice ul li {
list-style-position: inside;
}
@media (max-width: 768px) {
.contact-form {
max-width: 80vw;
}
.contact-form textarea {
max-width: 60vw;
}
.form-grid {
grid-template-columns: 1fr;
gap: 0.5rem;

View File

@@ -4,19 +4,23 @@
}
.contact-form {
max-width: 800px;
max-width: 60vw;
width: fit-content;
margin: 0 auto;
background: #fff;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.contact-form textarea {
max-width: 40vw;
}
.form-grid {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
margin-bottom: 1rem;
}
.form-label {
@@ -36,13 +40,32 @@ textarea.form-input {
min-height: 120px;
}
.marketing-consent {
margin: 2rem 0;
padding-left: 200px;
.marketing-consent input {
display: inline-block;
margin-left: 18vw;
margin-bottom: 1.25rem;
}
.container.recaptcha {
.container.captcha > div:first-child {
margin-left: 15vw;
margin-right: 15vw;
}
.container.captcha > div:first-child > label:first-child {
display: flex;
justify-content: center;
}
.container.captcha > p:last-child{
font-size: 0.9rem;
margin: 1vh 0;
}
.container.captcha .altcha-main {
padding-left: 1rem;
padding-top: 0.75rem;
padding-bottom: 0;
}
.container.captcha .altcha-main > :last-child {
display: none;
}
input[type="submit"] {
@@ -73,7 +96,17 @@ input[type="submit"]:hover {
font-size: 1.1rem;
}
.data-notice ul li {
list-style-position: inside;
}
@media (max-width: 768px) {
.contact-form {
max-width: 80vw;
}
.contact-form textarea {
max-width: 60vw;
}
.form-grid {
grid-template-columns: 1fr;
gap: 0.5rem;

View File

@@ -31,6 +31,7 @@ body {
top:
}
*/
.button {
display: inline-block;
padding: 0.75rem 1.5rem;

File diff suppressed because one or more lines are too long

View File

@@ -56,108 +56,4 @@ export default class API {
API.goToUrl(url);
}
// specific api calls
/* Example:
getUsers: () => request('/users'),
getUserById: (id) => request(`/users/${id}`),
createUser: (userData) => request('/users', 'POST', userData),
updateUser: (id, userData) => request(`/users/${id}`, 'PUT', userData),
deleteUser: (id) => request(`/users/${id}`, 'DELETE'),
*/
static async loginUser() {
let callback = {};
callback[flagCallback] = DOM.getHashPageCurrent();
return await API.request(hashPageUserLogin, 'POST', callback);
}
// store
// product categories
static async saveCategories(categories, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagProductCategory] = categories;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreProductCategory, 'POST', dataRequest);
}
// products
static async saveProducts(products, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagProduct] = products;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreProduct, 'POST', dataRequest);
}
// product permutations
static async saveProductPermutations(permutations, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagProductPermutation] = permutations;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreProductPermutation, 'POST', dataRequest);
}
// product variations
static async saveProductVariations(variationTypes, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagProductVariationType] = variationTypes;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreProductVariation, 'POST', dataRequest);
}
// stock items
static async saveStockItems(stockItems, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagStockItem] = stockItems;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreStockItem, 'POST', dataRequest);
}
// suppliers
static async saveSuppliers(suppliers, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagSupplier] = suppliers;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreSupplier, 'POST', dataRequest);
}
// supplier purchase orders
static async saveSupplierPurchaseOrders(supplierPurchaseOrders, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagSupplierPurchaseOrder] = supplierPurchaseOrders;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreSupplierPurchaseOrder, 'POST', dataRequest);
}
// manufacturing purchase orders
static async saveManufacturingPurchaseOrders(manufacturingPurchaseOrders, formFilters, comment) {
let dataRequest = {};
dataRequest[flagFormFilters] = DOM.convertForm2JSON(formFilters);
dataRequest[flagManufacturingPurchaseOrder] = manufacturingPurchaseOrders;
dataRequest[flagComment] = comment;
return await API.request(hashSaveStoreManufacturingPurchaseOrder, 'POST', dataRequest);
}
}
/*
const api = new API();
export default api;
document.addEventListener('DOMContentLoaded', () => {
initializeApp();
setupEventListeners();
initializeComponents();
// Example of using the API
API.getData('/some-endpoint')
.then(data => console.log('Data received:', data))
.catch(error => console.error('Error:', error));
});
*/

View File

@@ -1,13 +1,15 @@
import Utils from '../../lib/utils.js';
function videoPlay(elemVideo) {
if (!_loading) { // elemVideo.paused &&
elemVideo.play();
if (_verbose) { console.log("Playing video element: " + elemVideo.name)};
Utils.consoleLogIfNotProductionEnvironment("Playing video element: " + elemVideo.name);
}
}
function videoPause(elemVideo) {
elemVideo.pause();
if (_verbose) { console.log("Pausing video element: " + elemVideo.name)};
Utils.consoleLogIfNotProductionEnvironment("Pausing video element: " + elemVideo.name);
}

View File

@@ -1,4 +1,5 @@
import Utils from "./lib/utils.js";
import Validation from "./lib/validation.js";
export default class DOM {
@@ -138,18 +139,10 @@ export default class DOM {
debugger;
if (Validation.isEmpty(element)) return null;
return element.getAttribute(attrValueCurrent);
if (!Validation.isEmpty(value) && element.type === "checkbox") {
value = (value === 'true');
}
return value;
}
static getElementAttributeValuePrevious(element) {
if (Validation.isEmpty(element)) return null;
return element.getAttribute(attrValuePrevious);
if (!Validation.isEmpty(value) && element.type === "checkbox") {
value = (value === 'true');
}
return value;
}
/* base_table.handleChangeElementCellTable
static updateAndCheckIsTableElementDirty(element) {
@@ -158,28 +151,17 @@ export default class DOM {
let wasDirtyRow = DOM.hasDirtyChildrenNotDeletedContainer(row);
let isDirty = DOM.updateAndCheckIsElementDirty(element);
let cell = DOM.getCellFromElement(element);
console.log({element, row, cell, isDirty, wasDirty});
Utils.consoleLogIfNotProductionEnvironment({element, row, cell, isDirty, wasDirty});
if (isDirty != wasDirty) {
DOM.handleDirtyElement(cell, isDirty);
let isDirtyRow = DOM.hasDirtyChildrenNotDeletedContainer(row);
console.log({isDirtyRow, wasDirtyRow});
Utils.consoleLogIfNotProductionEnvironment({isDirtyRow, wasDirtyRow});
if (isDirtyRow != wasDirtyRow) {
DOM.handleDirtyElement(row, isDirtyRow);
}
}
}
*/
/*
static updateElement(id, data) {
const element = document.getElementById(id);
if (element) {
element.textContent = data;
}
}
*/
/* non-static method on page object to use
static handleChangeElement(element) {}
*/
static scrollToElement(parent, element) {
// REQUIRED: parent has scroll-bar
parent.scrollTop(parent.scrollTop() + (element.offset().top - parent.offset().top));

View File

@@ -1,4 +1,5 @@
import Utils from '../utils.js';
export default class BusinessObjects {
static getOptionJsonFromObjectJsonAndKeys(objectJson, keyText, keyValue, valueSelected = null) {
@@ -11,16 +12,9 @@ export default class BusinessObjects {
static getOptionJsonFromObjectJson(objectJson, valueSelected = null) {
let keyText = objectJson[flagNameAttrOptionText];
let keyValue = objectJson[flagNameAttrOptionValue];
if (_verbose) { console.log({objectJson, keyText, keyValue}); };
Utils.consoleLogIfNotProductionEnvironment({objectJson, keyText, keyValue});
return BusinessObjects.getOptionJsonFromObjectJsonAndKeys(objectJson, keyText, keyValue, valueSelected);
}
/*
static getOptionJsonFromProductPermutation(permutation) {
return {
text: permutation
};
}
*/
static getObjectText(objectJson) {
return objectJson == null ? '' : objectJson[objectJson[flagNameAttrOptionText]];
}

View File

@@ -16,4 +16,9 @@ export default class Utils {
}
return list;
}
static consoleLogIfNotProductionEnvironment(message) {
if (!environment.is_production) {
console.log(message);
}
}
}

View File

@@ -4,6 +4,7 @@ import Events from "../lib/events.js";
import LocalStorage from "../lib/local_storage.js";
import API from "../api.js";
import DOM from "../dom.js";
import Utils from "../lib/utils.js";
import OverlayConfirm from "../components/common/temporary/overlay_confirm.js";
import OverlayError from "../components/common/temporary/overlay_error.js";
@@ -14,11 +15,10 @@ export default class BasePage {
throw new Error("Router is required");
}
else {
if (_verbose) { console.log("initialising with router: ", router); }
Utils.consoleLogIfNotProductionEnvironment("initialising with router: ", router);
}
this.router = router;
this.title = titlePageCurrent;
// this.hash = hashPageCurrent;
if (this.constructor === BasePage) {
throw new Error("Cannot instantiate abstract class");
}
@@ -38,7 +38,7 @@ export default class BasePage {
}
logInitialisation() {
if (_verbose) { console.log('Initializing ' + this.title + ' page'); }
Utils.consoleLogIfNotProductionEnvironment('Initializing ' + this.title + ' page');
}
hookupCommonElements() {
@@ -58,7 +58,7 @@ export default class BasePage {
hookupLogos() {
this.hookupEventHandler("click", "." + flagImageLogo + "," + "." + flagLogo, (event, element) => {
if (_verbose) { console.log('clicking logo'); }
Utils.consoleLogIfNotProductionEnvironment('clicking logo');
this.router.navigateToHash(hashPageHome);
});
}
@@ -81,15 +81,14 @@ export default class BasePage {
Events.initialiseEventHandler('form.' + flagFilter + ' button.' + flagSave, flagInitialised, (button) => {
button.addEventListener("click", (event) => {
event.stopPropagation();
if (_verbose) { console.log('saving page: ', this.title); }
Utils.consoleLogIfNotProductionEnvironment('saving page: ', this.title);
OverlayConfirm.show();
});
// button.classList.add(flagCollapsed);
});
}
leave() {
if (_verbose) { console.log('Leaving ' + this.title + ' page'); }
Utils.consoleLogIfNotProductionEnvironment('Leaving ' + this.title + ' page');
if (this.constructor === BasePage) {
throw new Error("Must implement leave() method.");
}
@@ -107,11 +106,11 @@ export default class BasePage {
if (show) {
buttonCancel.classList.remove(flagCollapsed);
buttonSave.classList.remove(flagCollapsed);
if (_verbose) { console.log('showing buttons'); }
Utils.consoleLogIfNotProductionEnvironment('showing buttons');
} else {
buttonCancel.classList.add(flagCollapsed);
buttonSave.classList.add(flagCollapsed);
if (_verbose) { console.log('hiding buttons'); }
Utils.consoleLogIfNotProductionEnvironment('hiding buttons');
}
}
@@ -120,4 +119,5 @@ export default class BasePage {
if (isDirty) document.querySelectorAll(idTableMain + ' tbody tr').remove();
return isDirty;
}
}

View File

@@ -122,7 +122,7 @@ export default class TableBasePage extends BasePage {
getAndLoadFilteredTableContentSinglePageApp() {
this.callFilterTableContent()
.then(data => {
if (_verbose) { console.log('Table data received:', data); }
Utils.consoleLogIfNotProductionEnvironment('Table data received:', data);
this.callbackLoadTableContent(data);
})
.catch(error => console.error('Error:', error));
@@ -144,13 +144,13 @@ export default class TableBasePage extends BasePage {
.then(data => {
if (data[flagStatus] == flagSuccess) {
if (_verbose) {
console.log('Records saved!');
console.log('Data received:', data);
Utils.consoleLogIfNotProductionEnvironment('Records saved!');
Utils.consoleLogIfNotProductionEnvironment('Data received:', data);
}
this.callFilterTableContent();
}
else {
if (_verbose) { console.log("error: ", data[flagMessage]); }
Utils.consoleLogIfNotProductionEnvironment("error: ", data[flagMessage]);
OverlayError.show(data[flagMessage]);
}
})
@@ -181,13 +181,13 @@ export default class TableBasePage extends BasePage {
.then(data => {
if (data[flagStatus] == flagSuccess) {
if (_verbose) {
console.log('Records saved!');
console.log('Data received:', data);
Utils.consoleLogIfNotProductionEnvironment('Records saved!');
Utils.consoleLogIfNotProductionEnvironment('Data received:', data);
}
this.callbackLoadTableContent(data);
}
else {
if (_verbose) { console.log("error: ", data[flagMessage]); }
Utils.consoleLogIfNotProductionEnvironment("error: ", data[flagMessage]);
OverlayError.show(data[flagMessage]);
}
})
@@ -236,7 +236,7 @@ export default class TableBasePage extends BasePage {
cacheRowBlank() {
let selectorRowNew = idTableMain + ' tbody tr.' + flagRowNew;
let rowBlankTemp = document.querySelector(selectorRowNew);
if (_verbose) { console.log("row blank temp: ", rowBlankTemp); }
Utils.consoleLogIfNotProductionEnvironment("row blank temp: ", rowBlankTemp);
let countRows = document.querySelectorAll(idTableMain + ' > tbody > tr').length;
_rowBlank = rowBlankTemp.cloneNode(true);
document.querySelectorAll(selectorRowNew).forEach(function(row) {
@@ -257,23 +257,6 @@ export default class TableBasePage extends BasePage {
}
hookupSlidersDisplayOrderTable() {
let selectorDisplayOrder = idTableMain + ' tbody tr td.' + flagDisplayOrder + ' input.' + flagSlider + '.' + flagDisplayOrder;
/*
Events.initialiseEventHandler(selectorDisplayOrder, flagInitialised, (sliderDisplayOrder) => {
/*
sliderDisplayOrder.setAttribute('draggable', true);
sliderDisplayOrder.addEventListener('dragstart', this.handleDragSliderStart.bind(this), false);
sliderDisplayOrder.addEventListener('dragenter', this.handleDragSliderEnter.bind(this), false);
sliderDisplayOrder.addEventListener('dragover', this.handleDragSliderOver.bind(this), false);
sliderDisplayOrder.addEventListener('dragleave', this.handleDragSliderLeave.bind(this), false);
sliderDisplayOrder.addEventListener('drop', this.handleDropSlider.bind(this), false);
sliderDisplayOrder.addEventListener('dragend', this.handleDragSliderEnd.bind(this), false);
*
sliderDisplayOrder.addEventListener('change', (event) => {
console.log("slider change event");
this.handleChangeNestedElementCellTable(sliderDisplayOrder);
});
});
*/
this.hookupChangeHandlerTableCells(selectorDisplayOrder);
}
hookupChangeHandlerTableCells(inputSelector, handler = (event, element) => { this.handleChangeNestedElementCellTable(event, element); }) {
@@ -283,7 +266,6 @@ export default class TableBasePage extends BasePage {
});
handler(null, input);
});
// this.hookupEventHandler("change", inputSelector, handler);
}
/*
handleChangeElementCellTable(event, element) {
@@ -339,15 +321,15 @@ export default class TableBasePage extends BasePage {
let wasDirtyRowTable = DOM.hasDirtyChildrenContainer(rowTable);
let wasDirtyElement = element.classList.contains(flagDirty);
let isDirtyElement = DOM.updateAndCheckIsElementDirty(element);
console.log({isDirtyElement, wasDirtyElement});
Utils.consoleLogIfNotProductionEnvironment({isDirtyElement, wasDirtyElement});
if (isDirtyElement != wasDirtyElement) {
DOM.handleDirtyElement(td, isDirtyElement);
let isNowDirtyRowSubtable = DOM.hasDirtyChildrenContainer(rowSubtable);
console.log({isNowDirtyRowSubtable, wasDirtyRowSubtable});
Utils.consoleLogIfNotProductionEnvironment({isNowDirtyRowSubtable, wasDirtyRowSubtable});
if (isNowDirtyRowSubtable != wasDirtyRowSubtable) {
DOM.handleDirtyElement(rowSubtable, isNowDirtyRowSubtable);
let isNowDirtyRowTable = DOM.hasDirtyChildrenContainer(rowTable);
console.log({isNowDirtyRowTable, wasDirtyRowTable});
Utils.consoleLogIfNotProductionEnvironment({isNowDirtyRowTable, wasDirtyRowTable});
if (isNowDirtyRowTable != wasDirtyRowTable) {
DOM.handleDirtyElement(rowTable, isNowDirtyRowTable);
let rows = this.getTableRecords(true);
@@ -362,7 +344,7 @@ export default class TableBasePage extends BasePage {
let wasDirtyParentRows = this.getAllIsDirtyRowsInParentTree(element);
let wasDirtyElement = element.classList.contains(flagDirty);
let isDirtyElement = DOM.updateAndCheckIsElementDirty(element);
if (_verbose) { console.log({isDirtyElement, wasDirtyElement, wasDirtyParentRows}); }
Utils.consoleLogIfNotProductionEnvironment({isDirtyElement, wasDirtyElement, wasDirtyParentRows});
let td = DOM.getCellFromElement(element);
DOM.setElementAttributeValueCurrent(td, DOM.getElementAttributeValueCurrent(element));
if (isDirtyElement != wasDirtyElement) {
@@ -392,7 +374,7 @@ export default class TableBasePage extends BasePage {
let tr = DOM.getRowFromElement(td);
let isDirtyRow = isDirtyTd || DOM.hasDirtyChildrenContainer(tr);
let wasDirtyRow = wasDirtyParentRows.shift();
if (_verbose) { console.log({isDirtyRow, wasDirtyRow}); }
Utils.consoleLogIfNotProductionEnvironment({isDirtyRow, wasDirtyRow});
if (isDirtyRow != wasDirtyRow) {
DOM.handleDirtyElement(tr, isDirtyRow);
this.updateAndToggleShowButtonsSaveCancel();
@@ -404,64 +386,6 @@ export default class TableBasePage extends BasePage {
}) {
this.hookupEventHandler("change", inputSelector, handler);
}
/* ToDo: Fix this slider drag and drop functionality
handleDragSliderStart(event) {
this.dragSrcEl = event.target;
event.dataTransfer.effectAllowed = flagMove;
/*
console.log("setting outer html: ", this.dragSrcEl.outerHTML);
event.dataTransfer.setData('text/html', this.dragSrcEl.outerHTML);
*
this.dragSrcRow = DOM.getRowFromElement(this.dragSrcEl);
this.dragSrcEl.classList.add(flagDragging);
}
handleDragSliderOver(event) {
if (event.preventDefault) {
event.preventDefault();
}
event.dataTransfer.dropEffect = flagMove;
return false;
}
handleDragSliderEnter(event) {
event.target.closest('tr').classList.add(flagDragOver);
}
handleDragSliderLeave(event) {
event.target.closest('tr').classList.remove(flagDragOver);
}
handleDropSlider(event) {
event.stopPropagation();
let targetRow = DOM.getRowFromElement(event.target);
if (this.dragSourceRow != targetRow) {
targetRow.classList.remove(flagDragOver);
this.dragSrcEl.classList.remove(flagDragging);
let sourceRowClone = this.dragSrcRow.cloneNode(true);
let targetRowClone = targetRow.cloneNode(true);
console.log("sourceRowClone: ", sourceRowClone);
console.log("targetRowClone: ", targetRowClone);
let tbody = targetRow.closest('tbody');
tbody.replaceChild(sourceRowClone, targetRow);
tbody.replaceChild(targetRowClone, this.dragSrcRow);
this.refreshDisplayOrders();
}
return false;
}
handleDragSliderEnd(event) {
let table = TableBasePage.getTableMain();
let rows = table.querySelectorAll('tr');
rows.forEach(row => {
row.classList.remove(flagDragOver);
row.classList.remove(flagDragging);
});
}
refreshDisplayOrders() {
console.log("updating display order values");
let rows = document.querySelectorAll(idTableMain + 'tbody tr.' + flagRow);
rows.forEach((row, indexRow) => {
sliderDisplayOrder = row.querySelector('td.' + flagDisplayOrder + ' .' + flagSlider);
sliderDisplayOrder.setAttribute(attrValueCurrent, indexRow);
});
}
*/
hookupTextareasCodeTable() {
this.hookupChangeHandlerTableCells(idTableMain + ' tbody tr td.' + flagCode + ' textarea');
}
@@ -475,7 +399,7 @@ export default class TableBasePage extends BasePage {
let selectorButton = 'table' + (Validation.isEmpty(flagTable) ? '' : '.' + flagTable) + ' > tbody > tr > td.' + flagActive + ' button';
let selectorButtonDelete = selectorButton + '.' + flagDelete;
let selectorButtonUndelete = selectorButton + ':not(.' + flagDelete + ')';
console.log("hookupFieldsActive: ", selectorButtonDelete, selectorButtonUndelete);
Utils.consoleLogIfNotProductionEnvironment("hookupFieldsActive: ", selectorButtonDelete, selectorButtonUndelete);
this.hookupButtonsRowDelete(selectorButtonDelete, selectorButtonUndelete);
this.hookupButtonsRowUndelete(selectorButtonDelete, selectorButtonUndelete);
this.hookupEventHandler(
@@ -549,7 +473,6 @@ export default class TableBasePage extends BasePage {
}
handleClickTableCellDdlPreview(event, td, optionObjectList, cellSelector, ddlHookup = (cellSelector) => { this.hookupTableCellDdls(cellSelector); }) {
if (td.querySelector('select')) return;
// td.removeEventListener("click", ddlHookup);
let tdNew = td.cloneNode(true);
td.parentNode.replaceChild(tdNew, td);
let idSelected = DOM.getElementAttributeValueCurrent(tdNew);
@@ -558,8 +481,8 @@ export default class TableBasePage extends BasePage {
DOM.setElementValuesCurrentAndPrevious(ddl, idSelected);
let optionJson, option;
if (_verbose) {
console.log("click table cell ddl preview");
console.log({optionObjectList, cellSelector});
Utils.consoleLogIfNotProductionEnvironment("click table cell ddl preview");
Utils.consoleLogIfNotProductionEnvironment({optionObjectList, cellSelector});
}
option = DOM.createOption(null);
ddl.appendChild(option);
@@ -572,34 +495,6 @@ export default class TableBasePage extends BasePage {
let ddlSelector = cellSelector + ' select';
ddlHookup(ddlSelector);
}
/*
handleChangeTableCellDdl(event, ddl) {
let row = DOM.getRowFromElement(ddl);
let td = DOM.getCellFromElement(ddl);
console.log("td: ", td);
let wasDirtyRow = DOM.hasDirtyChildrenContainer(row);
let wasDirtyElement = ddl.classList.contains(flagDirty);
let isDirtyElement = DOM.updateAndCheckIsElementDirty(ddl);
console.log("isDirtyElement: ", isDirtyElement);
console.log("wasDirtyElement: ", wasDirtyElement);
if (isDirtyElement != wasDirtyElement) {
DOM.handleDirtyElement(td, isDirtyElement);
let optionSelected = ddl.options[ddl.selectedIndex];
DOM.setElementAttributeValueCurrent(td, optionSelected.value);
let isNowDirtyRow = DOM.hasDirtyChildrenContainer(row);
console.log("isNowDirtyRow: ", isNowDirtyRow);
console.log("wasDirtyRow: ", wasDirtyRow);
if (isNowDirtyRow != wasDirtyRow) {
DOM.handleDirtyElement(row, isNowDirtyRow);
let rows = this.getTableRecords(true);
let existsDirtyRecord = rows.length > 0;
console.log("dirty records:", rows);
console.log("existsDirtyRecord:", existsDirtyRecord);
this.toggleShowButtonsSaveCancel(existsDirtyRecord);
}
}
}
*/
hookupTableCellDDlPreviewsWhenNotCollapsed(cellSelector, optionList, ddlHookup = (event, element) => { this.hookupTableCellDdls(event, element); }) {
this.hookupEventHandler("click", cellSelector, (event, td) => {
let div = td.querySelector('div');
@@ -607,299 +502,13 @@ export default class TableBasePage extends BasePage {
this.handleClickTableCellDdlPreview(event, td, optionList, cellSelector, (event, element) => { ddlHookup(event, element); });
});
}
hookupProductCategoryDdls(ddlSelector) {
this.hookupChangeHandlerTableCells(ddlSelector, (event, element) => { this.handleChangeProductCategoryDdl(event, element); });
}
handleChangeProductCategoryDdl(event, ddlCategory) {
this.handleChangeNestedElementCellTable(event, ddlCategory);
let idProductCategorySelected = DOM.getElementAttributeValueCurrent(ddlCategory);
let row = DOM.getRowFromElement(ddlCategory);
let tdProduct = row.querySelector('td.' + flagProduct);
tdProduct.dispatchEvent(new Event('click'));
let ddlProduct = row.querySelector('td.' + flagProduct + ' select');
ddlProduct.innerHTML = '';
ddlProduct.appendChild(DOM.createOption(null));
let optionJson, option;
Utils.getListFromDict(products).forEach((product) => {
if (idProductCategorySelected != '0' && product[attrIdProductCategory] != idProductCategorySelected) return;
optionJson = BusinessObjects.getOptionJsonFromObjectJson(product);
option = DOM.createOption(optionJson);
ddlProduct.appendChild(option);
});
this.handleChangeNestedElementCellTable(event, ddlProduct);
}
hookupFieldsProductPermutationVariation() {
this.hookupPreviewsProductPermutationVariation();
this.hookupDdlsProductPermutationVariationType();
this.hookupDdlsProductPermutationVariation();
this.hookupButtonsProductPermutationVariationAddDelete();
}
hookupPreviewsProductPermutationVariation() {
this.hookupEventHandler("click", idTableMain + ' td.' + flagProductVariations, (event, element) => this.handleClickProductPermutationVariationsPreview(event, element));
}
handleClickProductPermutationVariationsPreview(event, element) {
let tblVariations = element.querySelector('table.' + flagProductVariations);
if (!Validation.isEmpty(tblVariations)) return;
this.toggleColumnCollapsed(flagProductVariations, false);
let permutationVariations = this.getElementProductVariations(element);
tblVariations = document.createElement("table");
tblVariations.classList.add(flagProductVariations);
let thead = document.createElement("thead");
let tr = document.createElement("tr");
let thVariationType = document.createElement("th");
thVariationType.classList.add(flagProductVariationType);
thVariationType.textContent = 'Type';
let thNameVariation = document.createElement("th");
thNameVariation.classList.add(flagProductVariation);
thNameVariation.textContent = 'Name';
let buttonAdd = document.createElement("button");
buttonAdd.classList.add(flagAdd);
buttonAdd.textContent = '+';
let thAddDelete = document.createElement("th");
thAddDelete.classList.add(flagAdd);
thAddDelete.appendChild(buttonAdd);
tr.appendChild(thVariationType);
tr.appendChild(thNameVariation);
tr.appendChild(thAddDelete);
thead.appendChild(tr);
tblVariations.appendChild(thead);
let tbody = document.createElement("tbody");
if (!Validation.isEmpty(permutationVariations)) {
permutationVariations.forEach((permutationVariation, index) => {
this.addProductPermutationVariationRow(tbody, permutationVariation);
});
}
tblVariations.appendChild(tbody);
if (_verbose) {
console.log("click product permutation variations preview");
console.log('variations:', permutationVariations);
console.log("tblVariations: ", tblVariations);
}
let cellParent = element.closest(idTableMain + ' tbody tr td.' + flagProductVariations);
cellParent.innerHTML = '';
cellParent.appendChild(tblVariations);
this.hookupFieldsProductPermutationVariation();
}
toggleColumnCollapsed(flagColumn, isCollapsed) {
this.toggleColumnHasClassnameFlag(flagColumn, isCollapsed, flagCollapsed);
}
toggleColumnHeaderCollapsed(flagColumn, isCollapsed) {
this.toggleColumnHasClassnameFlag(flagColumn, isCollapsed, flagCollapsed);
}
getElementProductVariations(element) {
let permutationVariations = element.getAttribute(attrValueCurrent);
let objVariations = [];
let parts, new_variation, new_variation_type;
if (!Validation.isEmpty(permutationVariations)) {
permutationVariations = permutationVariations.split(',');
permutationVariations.forEach((variation) => {
parts = variation.split(':');
if (parts.length == 2) {
if (_verbose) { console.log("parts: ", parts); }
new_variation_type = productVariationTypes[parts[0].trim()];
new_variation = productVariations[parts[1].trim()];
objVariations.push({
[flagProductVariationType]: new_variation_type,
[flagProductVariation]: new_variation,
});
}
else {
if (_verbose) { console.log("error: invalid variation: ", variation); }
}
});
}
return objVariations;
}
static createOptionUnselectedProductVariation() {
return {
[flagProductVariationType]: {
[flagNameAttrOptionText]: [flagName],
[flagNameAttrOptionValue]: [attrIdProductVariationType],
[flagName]: 'Select Variation Type',
[attrIdProductVariationType]: 0,
},
[flagProductVariation]: {
[flagNameAttrOptionText]: [flagName],
[flagNameAttrOptionValue]: [attrIdProductVariation],
[flagName]: 'Select Variation',
[attrIdProductVariation]: 0,
},
};
}
addProductPermutationVariationRow(tbody, permutationVariation) {
if (_verbose) { console.log("permutationVariation: ", permutationVariation); }
let productVariationType, optionProductVariationTypeJson, optionProductVariationType, productVariation, optionProductVariationJson, optionProductVariation;
/*
if (Validation.isEmpty(variations)) {
return;
}
*/
let productVariationKeys = Object.keys(productVariations);
let productVariationTypeKeys = Object.keys(productVariationTypes);
let ddlsProductVariationType = tbody.querySelectorAll('select.' + flagProductVariationType);
let productVariationTypeKeysSelected = new Set();
let valueSelected;
let doFilterProductVariationKeys = permutationVariation[attrIdProductVariationType] != 0;
ddlsProductVariationType.forEach((ddlProductVariationType) => {
valueSelected = DOM.getElementValueCurrent(ddlProductVariationType);
productVariationTypeKeysSelected.add(valueSelected);
});
productVariationTypeKeys = productVariationTypeKeys.filter(typeKey => !productVariationTypeKeysSelected.has(typeKey));
if (productVariationTypeKeys.length == 0) return;
if (doFilterProductVariationKeys) {
productVariationKeys = productVariationKeys.filter(variationKey => !productVariationTypeKeysSelected.has(productVariations[variationKey][attrIdProductVariationType]));
}
let permutationVariationJson = permutationVariation[flagProductVariation];
let permutationVariationTypeJson = permutationVariation[flagProductVariationType];
let tdVariationType = document.createElement("td");
tdVariationType.classList.add(flagProductVariationType);
DOM.setElementAttributesValuesCurrentAndPrevious(tdVariationType, permutationVariationTypeJson[attrIdProductVariationType]);
let ddlVariationType = document.createElement("select");
ddlVariationType.classList.add(flagProductVariationType);
DOM.setElementAttributesValuesCurrentAndPrevious(ddlVariationType, permutationVariationTypeJson[attrIdProductVariationType]);
optionProductVariationType = DOM.createOption(null);
if (_verbose) { console.log("optionProductVariationType: ", optionProductVariationType); }
ddlVariationType.appendChild(optionProductVariationType);
productVariationTypeKeys.forEach((productVariationTypeKey) => {
/*
optionProductVariationType = document.createElement('option');
optionProductVariationType.value = optionVariationType.value;
optionProductVariationType.text = optionVariationType.text;
*/
productVariationType = productVariationTypes[productVariationTypeKey];
optionProductVariationTypeJson = BusinessObjects.getOptionJsonFromObjectJson(productVariationType, permutationVariationTypeJson[attrIdProductVariationType]);
optionProductVariationType = DOM.createOption(optionProductVariationTypeJson);
if (_verbose) { console.log("optionProductVariationType: ", optionProductVariationType); }
ddlVariationType.appendChild(optionProductVariationType);
});
let tdVariation = document.createElement("td");
tdVariation.classList.add(flagProductVariation);
DOM.setElementAttributesValuesCurrentAndPrevious(tdVariation, permutationVariationJson[attrIdProductVariation]);
let ddlVariation = document.createElement("select");
ddlVariation.classList.add(flagProductVariation);
DOM.setElementAttributesValuesCurrentAndPrevious(ddlVariation, permutationVariationJson[attrIdProductVariation]);
optionProductVariation = DOM.createOption(null);
if (_verbose) { console.log("optionProductVariation: ", optionProductVariation); }
ddlVariation.appendChild(optionProductVariation);
productVariationKeys.forEach((productVariationKey) => {
productVariation = productVariations[productVariationKey];
optionProductVariationJson = BusinessObjects.getOptionJsonFromObjectJson(productVariation, permutationVariationJson[attrIdProductVariation]);
optionProductVariation = DOM.createOption(optionProductVariationJson);
if (_verbose) { console.log("optionProductVariation: ", optionProductVariation); }
ddlVariation.appendChild(optionProductVariation);
});
let tdDelete = document.createElement("td");
tdDelete.classList.add(flagDelete);
let buttonDelete = document.createElement("button");
buttonDelete.classList.add(flagActive);
buttonDelete.classList.add(flagDelete);
buttonDelete.textContent = 'x';
DOM.setElementAttributesValuesCurrentAndPrevious(buttonDelete, true);
let tr = document.createElement("tr");
tr.classList.add(flagProductVariation);
tdVariationType.appendChild(ddlVariationType);
tr.appendChild(tdVariationType);
tdVariation.appendChild(ddlVariation);
tr.appendChild(tdVariation);
tdDelete.appendChild(buttonDelete);
tr.appendChild(tdDelete);
tbody.appendChild(tr);
}
hookupDdlsProductPermutationVariationType() {
this.hookupTableCellDdls(
idTableMain + ' td.' + flagProductVariations + ' td.' + flagProductVariationType + ' select'
, (event, ddlVariationType) => {
this.handleChangeProductVariationInput(event, ddlVariationType);
let idVariationTypeSelected = DOM.getElementValueCurrent(ddlVariationType);
let row = DOM.getRowFromElement(ddlVariationType);
let tdVariation = row.querySelector('td.' + flagProductVariation);
tdVariation.dispatchEvent(new Event('click'));
let ddlVariation = row.querySelector('td.' + flagProductVariation + ' select');
ddlVariation.innerHTML = '';
ddlVariation.appendChild(DOM.createOption(null));
let optionJson, option;
let variationType = productVariationTypes[idVariationTypeSelected];
if (variationType == null) variationType = {
[flagProductVariations]: [],
};
variationType[flagProductVariations].forEach((variation) => {
optionJson = BusinessObjects.getOptionJsonFromObjectJson(variation);
option = DOM.createOption(optionJson);
ddlVariation.appendChild(option);
});
this.handleChangeProductVariationInput(event, ddlVariation);
}
);
}
handleChangeProductVariationInput(event, element) {
this.handleChangeNestedElementCellTable(event, element);
this.updateProductPermutationVariations(element);
}
hookupDdlsProductPermutationVariation() {
this.hookupTableCellDdls(idTableMain + ' td.' + flagProductVariations + ' td.' + flagProductVariation + ' select', (event, ddlVariation) => { this.handleChangeProductVariationInput(event, ddlVariation); });
}
hookupButtonsProductPermutationVariationAddDelete() {
let selectorButton = idTableMain + ' td.' + flagProductVariations + ' tr.' + flagProductVariation + ' button';
let selectorButtonDelete = selectorButton + '.' + flagDelete;
let selectorButtonUndelete = selectorButton + '.' + flagAdd;
this.hookupButtonsRowDelete(selectorButtonDelete, selectorButtonUndelete, (event, element) => { this.handleChangeProductVariationInput(event, element); });
this.hookupButtonsRowUndelete(selectorButtonDelete, selectorButtonUndelete, (event, element) => { this.handleChangeProductVariationInput(event, element); });
this.hookupButtonsProductPermutationVariationAdd();
}
hookupButtonsProductPermutationVariationAdd() {
this.hookupEventHandler(
"click"
, idTableMain + ' td.' + flagProductVariations + ' button.' + flagAdd
, (event, element) => { this.handleClickButtonProductPermutationVariationAdd(event, element); }
);
}
handleClickButtonProductPermutationVariationAdd(event, element) {
let variationsCell = element.closest('td.' + flagProductVariations);
let tbody = variationsCell.querySelector('tbody');
let permutationVariation = TableBasePage.createOptionUnselectedProductVariation();
this.addProductPermutationVariationRow(tbody, permutationVariation);
this.hookupFieldsProductPermutationVariation();
}
updateProductPermutationVariations(element) {
let variationsCell = element.closest('td.' + flagProductVariations);
let variationPairsString = this.getProductPermutationVariationsText(variationsCell);
DOM.setElementAttributeValueCurrent(variationsCell, variationPairsString);
this.handleChangeNestedElementCellTable(null, variationsCell);
}
getProductPermutationVariationsText(variationsTd) {
let rows = variationsTd.querySelectorAll(':scope tbody tr:has(button.' + flagDelete + ')');
let variationPairsString = '';
let ddlVariationType, ddlVariation, idVariationType, idVariation;
rows.forEach((row, index) => {
ddlVariationType = row.querySelector(':scope td select.' + flagProductVariationType);
ddlVariation = row.querySelector(':scope td select.' + flagProductVariation);
idVariationType = DOM.getElementValueCurrent(ddlVariationType);
idVariation = DOM.getElementValueCurrent(ddlVariation);
if (variationPairsString != '') variationPairsString += ',';
variationPairsString += idVariationType + ':' + idVariation;
});
return variationPairsString;
}
hookupCurrencyFields() {
this.hookupTableCellDdlPreviews(idTableMain + ' td.' + flagCurrency, Utils.getListFromDict(currencies));
}
createTdActive(isActive) {
let tdActive = document.createElement("td");
tdActive.classList.add(flagActive);
@@ -921,7 +530,6 @@ export default class TableBasePage extends BasePage {
let dataPage = {};
dataPage[flagFormFilters] = DOM.convertForm2JSON(formFilters);
this.setLocalStoragePage(dataPage);
// _rowBlank = null;
}
toggleColumnHasClassnameFlag(columnFlag, isRequiredFlag, classnameFlag) {
@@ -930,12 +538,6 @@ export default class TableBasePage extends BasePage {
let columnThHasFlag = columnTh.classList.contains(classnameFlag);
if (isRequiredFlag == columnThHasFlag) return;
DOM.toggleElementHasClassnameFlag(columnTh, isRequiredFlag, classnameFlag);
/*
let columnTds = table.querySelectorAll('td.' + columnFlag);
columnTds.forEach((columnTd) => {
DOM.toggleElementHasClassnameFlag(columnTd, isRequiredFlag, classnameFlag);
});
*/
}
toggleColumnHeaderHasClassnameFlag(columnFlag, isRequiredFlag, classnameFlag) {
let table = TableBasePage.getTableMain();
@@ -949,26 +551,3 @@ export default class TableBasePage extends BasePage {
this.toggleShowButtonsSaveCancel(existsDirtyRecord);
}
}
/* Example of a subclass of TableBasePage
import { TableBasePage } from "./page_table_base.js";
import API from "../api.js";
import DOM from "../dom.js";
export class PageStoreProductCategories extends TableBasePage {
static hash = hashPageStoreProductCategories;
static attrIdRowObject = attrIdProductCategory;
callSaveTableContent = API.saveCategories;
constructor() {}
initialize() {}
hookupFilters() {}
loadRowTable(rowJson) {}
getJsonRow(row) {}
initialiseRowNew(tbody, row) {}
hookupTableMain() {}
isDirtyRow(row) {}
leave() {}
}
*/

View File

@@ -0,0 +1,16 @@
// internal
import BasePage from "../base.js";
// vendor
import { Altcha } from "../../vendor/altcha.js";
export default class PageContactSuccess extends BasePage {
static hash = hashPageContactSuccess;
constructor(router) {
super(router);
}
initialize() {
this.sharedInitialize();
}
}

View File

@@ -3,19 +3,17 @@
// Core
import PageHome from './pages/core/home.js';
import PageContact from './pages/core/contact.js';
import PageContactSuccess from './pages/core/contact-success.js';
// Legal
import PageAccessibilityReport from './pages/legal/accessibility_report.js';
import PageAccessibilityStatement from './pages/legal/accessibility_statement.js';
import PageLicense from './pages/legal/license.js';
// User
// import PageUserLogin from './pages/user/login.js';
// import PageUserLogout from './pages/user/logout.js';
// import PageUserAccount from './pages/user/account.js';
import PagePrivacyPolicy from './pages/legal/privacy_policy.js';
import PageRetentionSchedule from './pages/legal/retention_schedule.js';
import API from './api.js';
import DOM from './dom.js';
import PagePrivacyPolicy from './pages/legal/privacy_policy.js';
import PageRetentionSchedule from './pages/legal/retention_schedule.js';
import Utils from './lib/utils.js';
export default class Router {
@@ -23,18 +21,14 @@ export default class Router {
// Pages
this.pages = {};
// Core
this.pages[hashPageHome] = { name: 'PageHome', module: PageHome }; // importModule: () => import(/* webpackChunkName: "page_core_home" */ './pages/core/home.js') , pathModule: './pages/core/home.js'
this.pages[hashPageContact] = { name: 'PageContact', module: PageContact }; // pathModule: './pages/core/contact.js' };
this.pages[hashPageHome] = { name: 'PageHome', module: PageHome };
this.pages[hashPageContact] = { name: 'PageContact', module: PageContact };
this.pages[hashPageContactSuccess] = { name: 'PageContact', module: PageContactSuccess };
// Legal
this.pages[hashPageAccessibilityStatement] = { name: 'PageAccessibilityStatement', module: PageAccessibilityStatement }; // pathModule: '../../static/js/pages/legal/accessibility_statement.js' }; // , page: PageAccessibilityStatement
this.pages[hashPageDataRetentionSchedule] = { name: 'PageDataRetentionSchedule', module: PageRetentionSchedule }; // pathModule: './pages/legal/data_retention_schedule.js' };
this.pages[hashPageLicense] = { name: 'PageLicense', module: PageLicense }; // pathModule: './pages/legal/license.js' };
this.pages[hashPagePrivacyPolicy] = { name: 'PagePrivacyPolicy', module: PagePrivacyPolicy }; // pathModule: './pages/legal/privacy_policy.js' }; // importModule: () => {return import(/* webpackChunkName: "page_privacy_policy" */ './pages/legal/privacy_policy.js'); }
// User
// this.pages[hashPageUserLogin] = { name: 'PageUserLogin', module: PageUserLogin }; // pathModule: './pages/user/login.js' };
// this.pages[hashPageUserLogout] = { name: 'PageUserLogout', module: PageUserLogout }; // pathModule: './pages/user/logout.js' };
// this.pages[hashPageUserAccount] = { name: 'PageUserAccount', module: PageUserAccount }; // pathModule: './pages/user/account.js' };
this.pages[hashPageAccessibilityStatement] = { name: 'PageAccessibilityStatement', module: PageAccessibilityStatement };
this.pages[hashPageDataRetentionSchedule] = { name: 'PageDataRetentionSchedule', module: PageRetentionSchedule };
this.pages[hashPageLicense] = { name: 'PageLicense', module: PageLicense };
this.pages[hashPagePrivacyPolicy] = { name: 'PagePrivacyPolicy', module: PagePrivacyPolicy };
// Routes
this.routes = {};
// Core
@@ -45,10 +39,6 @@ export default class Router {
this.routes[hashPageDataRetentionSchedule] = (isPopState = false) => this.navigateToHash(hashPageDataRetentionSchedule, isPopState);
this.routes[hashPageLicense] = (isPopState = false) => this.navigateToHash(hashPageLicense, isPopState);
this.routes[hashPagePrivacyPolicy] = (isPopState = false) => this.navigateToHash(hashPagePrivacyPolicy, isPopState);
// User
// this.routes[hashPageUserLogin] = (isPopState = false) => this.navigateToHash(hashPageUserLogin, isPopState);
// this.routes[hashPageUserLogout] = (isPopState = false) => this.navigateToHash(hashPageUserLogout, isPopState);
// this.routes[hashPageUserAccount] = (isPopState = false) => this.navigateToHash(hashPageUserAccount, isPopState);
this.initialize();
}
loadPage(hashPage, isPopState = false) {
@@ -61,31 +51,17 @@ export default class Router {
let pageJson = this.pages[hashPage];
try {
/*
const module = await pagesContext(pageJson.pathModule);
console.log("module: ", module);
return module[pageJson.name];
*/
// const module = await import(pageJson.pathModule); // pageJson.page;
// const module = () => import(pageJson.pathModule);
const module = pageJson.module; // importModule;
return module; // [pageJson.name];
const module = pageJson.module;
return module;
}
catch (error) {
if (_verbose) { console.log("this.pages: ", this.pages); };
Utils.consoleLogIfNotProductionEnvironment("this.pages: ", this.pages);
console.error('Page not found:', hashPage);
throw error;
}
}
initialize() {
/*
let pages = Router.getPages();
for (const key of Object.keys(pages)) {
let page = pages[key];
this.addRoute(page.hash, page.initialize);
}
*/
window.addEventListener('popstate', this.handlePopState.bind(this)); // page accessed by history navigation
window.addEventListener('popstate', this.handlePopState.bind(this));
}
handlePopState(event) {
this.loadPageCurrent();
@@ -95,26 +71,11 @@ export default class Router {
this.loadPage(hashPageCurrent);
}
navigateToHash(hash, data = null, params = null, isPopState = false) {
// this.beforeLeave();
/*
if (this.routes[hash]) {
this.routes[hash](isPopState);
} else {
console.error(`Hash ${hash} not found`);
}
*/
let url = API.getUrlFromHash(hash, params);
// if (!isPopState)
history.pushState({data: data, params: params}, '', hash);
API.goToUrl(url, data);
}
/* beforeunload listener
async beforeLeave() {
const ClassPageCurrent = await this.getClassPageFromHash(DOM.getHashPageCurrent());
const pageCurrent = new ClassPageCurrent(this);
pageCurrent.leave();
}
*/
navigateToUrl(url, data = null, appendHistory = true) {
// this.beforeLeave();
if (appendHistory) history.pushState(data, '', url);
@@ -128,25 +89,3 @@ export default class Router {
}
export const router = new Router();
/*
router.addRoute('/', () => {
console.log('Home page');
// Load home page content
});
router.addRoute('/about', () => {
console.log('About page');
// Load about page content
});
export function setupNavigationEvents() {
document.querySelectorAll('a[data-nav]').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const url = e.target.getAttribute('href');
router.navigateToUrl(url);
});
});
}
*/

View File

@@ -1,33 +0,0 @@
{% set block_id = 'styles' %}
{% include 'layouts/_shared_store.html' %}
<!-- Include Stylesheet -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/sections/store.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/store/home.css') }}">
<!-- HTML content -->
<div class="model.FLAG_ROW">
<div class="leftcolumn">
{% for cat in model.category_list.categories %}
{% if cat.is_available() %}
{% include 'components/store/_product_category.html' %}
{% endif %}
{% endfor %}
</div>
<div id="{{ model.ID_BASKET_CONTAINER}}" class="rightcolumn">
{% include 'components/store/_basket.html' %}
</div>
</div>
{% set block_id = 'scripts' %}
{% include 'layouts/_shared_store.html' %}
<!-- Include JavaScript -->
<script src="{{ url_for('routes_store.scripts_section_store') }}"></script>
<script src="{{ url_for('static', filename='js/store/home.js') }}"></script>
<script>
{#
var hashPageCurrent = "{{ model.HASH_PAGE_STORE_HOME }}";
#}
</script>

View File

@@ -1,17 +0,0 @@
{% with _is_blank_row = (is_blank_row or model.currencies | length == 0 or currency is not defined or currency is none) %}
{% if not _is_blank_row %}
<div
class="{{ model.FLAG_CURRENCY }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}"
>{{ currency.symbol }}</div>
{% else %}
<div
class="{{ model.FLAG_CURRENCY }}"
{{ model.ATTR_VALUE_CURRENT }}=""
{{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
{% endif %}
{% endwith %}

View File

@@ -1,15 +0,0 @@
{% if not is_blank_row %}
<div
class="{{ model.FLAG_PRODUCT }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ product.id_product }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ product.id_product }}"
>{{ product.name }}</div>
{% else %}
<select class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/common/inputs/_option_blank.html' %}
{% for product in model.category_list_filters.categories[0].products %}
<option value="{{ product.id_product }}">{{ product.name }}</option>
{% endfor %}
</select>
{% endif %}

View File

@@ -1,15 +0,0 @@
{% if not is_blank_row %}
<div
class="{{ model.FLAG_PRODUCT_CATEGORY }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ category.id_category }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ category.id_category }}"
>{{ category.name }}</div>
{% else %}
<select class="{{ model.FLAG_PRODUCT_CATEGORY}}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/common/inputs/_option_blank.html' %}
{% for cat in model.category_list_filters.categories %}
<option value="{{ cat.id_category }}">{{ cat.name }}</option>
{% endfor %}
</select>
{% endif %}

View File

@@ -1,17 +0,0 @@
{% with _is_blank_row = (is_blank_row or units_measurement_time_dict | length == 0 or permutation.id_unit_measurement_interval_expiration_unsealed is none) %}
{% if not _is_blank_row %}
{% set interval_recurrence = units_measurement_time_dict[permutation.id_unit_measurement_interval_expiration_unsealed] %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }} {% if not permutation.does_expire_faster_once_unsealed %}{{ model.FLAG_COLLAPSED }}{% endif %}"
{{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_interval_expiration_unsealed }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_interval_expiration_unsealed }}"
>{{ interval_recurrence.name_singular }}</div>
{% else %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }} {{ model.FLAG_COLLAPSED }}"
{{ model.ATTR_VALUE_CURRENT }}=""
{{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
{% endif %}
{% endwith %}

View File

@@ -1,19 +0,0 @@
{#
<select class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }}" value="{{ permutation.id_unit_measurement_interval_recurrence }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_interval_recurrence }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_interval_recurrence }}"></select>
#}
{% with _is_blank_row = (is_blank_row or units_measurement_time | length == 0 or permutation.id_unit_measurement_interval_recurrence is none) %}
{% if not _is_blank_row %}
{% set interval_recurrence = units_measurement_time[permutation.id_unit_measurement_interval_recurrence] | console_log %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }} {% if not permutation.is_subscription %}{{ model.FLAG_COLLAPSED }}{% endif %}"
{{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_interval_recurrence }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_interval_recurrence }}"
>{{ interval_recurrence.name_singular }}</div>
{% else %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }} {{ model.FLAG_COLLAPSED }}"
{{ model.ATTR_VALUE_CURRENT }}=""
{{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
{% endif %}
{% endwith %}

View File

@@ -1,21 +0,0 @@
{#
<select class="{{ model.FLAG_UNIT_MEASUREMENT_QUANTITY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_quantity }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_quantity }}"></select>
#}
{% with _is_blank_row = (is_blank_row or units_measurement_dict | length == 0 or permutation.id_unit_measurement_quantity is none) %}
{% if not _is_blank_row %}
{% set interval_recurrence = units_measurement_dict[permutation.id_unit_measurement_quantity] | console_log %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_QUANTITY }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_quantity }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_quantity }}"
>{{ interval_recurrence.name_singular }}</div>
{% else %}
<div
class="{{ model.FLAG_UNIT_MEASUREMENT_QUANTITY }}"
{{ model.ATTR_VALUE_CURRENT }}=""
{{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
{% endif %}
{% endwith %}

View File

@@ -1,13 +0,0 @@
{% with _is_blank_row = (is_blank_row or storage_location is not defined or storage_location is none or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{% set name = storage_location.get_full_name() %}
<div
class="{{ model.FLAG_STORAGE_LOCATION }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ name }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ name }}"
>{{ name }}</div>
{% else %}
<div class="{{ model.FLAG_STORAGE_LOCATION }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,17 +0,0 @@
{% with _is_blank_row = (is_blank_row or model.suppliers | length == 0 or supplier is not defined or supplier is none) %}
{% if not _is_blank_row %}
<div
class="{{ model.FLAG_SUPPLIER }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ supplier.id_supplier }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.id_supplier }}"
>{{ supplier.name_company }}</div>
{% else %}
<div
class="{{ model.FLAG_SUPPLIER }}"
{{ model.ATTR_VALUE_CURRENT }}=""
{{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
{% endif %}
{% endwith %}

View File

@@ -1,13 +0,0 @@
{% with _is_blank_row = (is_blank_row or address is not defined or address is none or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{% set json_str = address.to_json_str() %}
<div
class="{{ model.FLAG_ADDRESS }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ json_str }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str }}"
>{{ address.postcode }}</div>
{% else %}
<div class="{{ model.FLAG_ADDRESS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,13 +0,0 @@
{% with _is_blank_row = (is_blank_row or order_items is not defined or order_items is none or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{% set str_items = model.convert_list_objects_to_preview_str(order_items) %}
<div
class="{{ model.FLAG_ITEMS }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ json_str_items }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str_items }}"
>{{ str_items }}</div>
{% else %}
<div class="{{ model.FLAG_ITEMS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,13 +0,0 @@
{% with _is_blank_row = (is_blank_row or order_items is not defined or order_items is none or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{% set str_items = model.convert_list_objects_to_preview_str(order_items) %}
<div
class="{{ model.FLAG_ORDER_ITEMS }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ json_str_items }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str_items }}"
>{{ str_items }}</div>
{% else %}
<div class="{{ model.FLAG_ORDER_ITEMS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,14 +0,0 @@
{% with _is_blank_row = (is_blank_row or variation_tree is not defined or variation_tree is none or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{% set str_ids_variations = variation_tree.to_variation_id_pairs_str() %}
{% set str_variations = variation_tree.to_preview_str() %}
<div
class="{{ model.FLAG_PRODUCT_VARIATIONS }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ str_ids_variations }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ str_ids_variations }}"
>{{ str_variations }}</div>
{% else %}
<div class="{{ model.FLAG_PRODUCT_VARIATIONS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,21 +0,0 @@
{% with _is_blank_row = (is_blank_row or variation_type is not defined or variation_type.variations is none or variation_type.variations == [] or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{# {% set ids_variation = variation_type.get_str_list_ids_variation() %} #}
<div class="{{ model.FLAG_PRODUCT_VARIATIONS }}"
{#
{{ model.ATTR_VALUE_CURRENT }}="{{ ids_variation }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ ids_variation }}"
#}
>
{#
{{ variation_type.get_preview_variations() }}
#}
{% for variation in variation_type.variations %}
{{ variation.name }}<br>
{% endfor %}
</div>
{% else %}
<div class="{{ model.FLAG_PRODUCT_VARIATIONS }}"></div>
{% endif %}
{% endwith %}

View File

@@ -1,15 +0,0 @@
{% with _is_blank_row = (is_blank_row or variation_tree is not defined or is_blank_row is not defined) %}
{% if not _is_blank_row %}
{# % set json_str_variation_types = product.get_json_str_types_variation_trees() % #}
{% set names_variation_type = product.get_variation_types_unique() %}
{% set json_str_variation_types = jsonify(names_variation_type) %}
<div
class="{{ model.FLAG_PRODUCT_VARIATIONS }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ json_str_variation_types }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str_variation_types }}"
>{{ model.join_with_linebreaks(names_variation_type) }}</div>
{% else %}
<div class="{{ model.FLAG_PRODUCT_VARIATIONS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></div>
{% endif %}
{% endwith %}

View File

@@ -1,79 +0,0 @@
<!-- Address form
<div class="card"> -->
<form id="{{ form.output_id() }}" class="{{ model.flag_container }}">
{{ form.hidden_tag() }}
{% if form.form_type_billing_not_delivery %}
<div class="{{ model.flag_container_input }}">
{{ form.identical.label }}
{{ form.identical(checked=True) }}
</div>
{% endif %}
<div class="{{ model.flag_container_input }}">
{{ form.region.label }}
{{ form.region() }}
{% for error in form.region.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.name_full.label }}
{{ form.name_full(size=100) }}
{% for error in form.name_full.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.phone_number.label }}
{{ form.phone_number(size=20) }}
{% for error in form.phone_number.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.postcode.label }}
{{ form.postcode(size=10) }}
{% for error in form.postcode.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.address_1.label }}
{{ form.address_1(size=254) }}
{% for error in form.address_1.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.address_2.label }}
{{ form.address_2(size=254) }}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.city.label }}
{{ form.city(size=100) }}
{% for error in form.city.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.county.label }}
{{ form.county(size=100) }}
{% for error in form.county.errors %}
<p class="error">{{ error }}</p>
{% endfor %}
</div>
<div class="{{ model.flag_container_input }}">
{{ form.submit() }}
</div>
</form>
<!--</div>-->

View File

@@ -1,18 +0,0 @@
<!-- Basket -->
{% set show_delivery_option = False %}
<div id="{{ model.ID_BASKET }}" class="{{ model.FLAG_CARD }} {{ model.FLAG_SCROLLABLE }}">
<div class="container column">
<div class="container row">
<h2>Basket</h2>
<img class="img-icon" src="{{ url_for('static', filename='images/icon_basket.png')}}" alt="Basket icon">
</div>
{% for basket_item in model.basket.items %}
{% include 'components/store/_basket_item.html' %}
{% endfor %}
<h3 id="{{ model.ID_BASKET_TOTAL }}">Total: {{ model.output_basket_total() }}</h3>{% if not model.app.is_included_VAT %}<h4> + VAT </h4>{% endif %}
<p id="{{ model.ID_LABEL_BASKET_EMPTY }}" style="margin: 1vh;">Buy some shit dawg!</p>
<!-- <div id="{{ model.id_basket_notices }}"> include line above
</div> -->
<button id="{{ model.ID_BUTTON_CHECKOUT }}" type="submit">Checkout</button>
</div>
</div>

View File

@@ -1,42 +0,0 @@
<!-- Basket Item -->
<!-- requires:
Basket_Item basket_item
bool show_delivery_option
-->
<div class="container row">
{% set product = basket_item.product %}
{% set permutation = product.get_permutation_selected() %}
<div class="container">
<img class="img-thumbnail" src="{{ product.get_image_from_index(0).url }}" alt="Basket icon"> <!-- model.get_many_product_image_src(product.id_product, '', True, 'THUMBNAIL') -->
</div>
{% set form = product.form_basket_edit %}
<!-- <form {{ model.attr_form_type }}="{{ form.form_type }}" {{ model.attr_id_product }}="{{ product.id }}" class="container column" action="{{ url_for('basket_add') }}" method="POST"> -->
<form {{ model.attr_form_type }}="{{ form.form_type }}" class="{{ model.flag_container }} {{ model.flag_column }}" {{ model.attr_id_product }}="{{ product.id_product}}" {{ model.attr_id_permutation }}="{{ permutation.id_permutation }}"> <!-- id="form_basket_item_id_{{ basket_item.product.id }}" -->
{{ form.hidden_tag() }}
<h2>{{ product.name }}</h2>
{% if permutation.is_available %}
<h3 style="white-space: nowrap;">{{ basket_item.quantity }} x {{ product.output_price(model.app.is_included_VAT) }} = {{ basket_item.output_subtotal() }}</h3>
{% set tmp_quantity = basket_item.quantity %}
{% include 'components/common/inputs/_input_number_plus_minus.html' %}
{% elif permutation.is_unavailable_in_currency_or_region %}
<h3 style="white-space: nowrap;">Product not available in currency and region</h3>
{% else %}
<h3 style="white-space: nowrap;">Product not available</h3>
{% endif %}
<a class="{{ model.FLAG_DELETE }}">Delete</a>
{% if show_delivery_option %}
<div class="{{ model.flag_container_input }}">
{{ product.form_delivery_option.label }}
{{ product.form_delivery_option() }}
</div>
{% endif %}
<script>
if (_verbose) {
console.log('creating basket item for:');
console.log('product id: {{ product.id_product }}');
console.log('permutation id: {{ product.get_id_permutation() }}');
console.log('quantity: {{ basket_item.quantity }}');
}
</script>
</form>
</div>

View File

@@ -1,84 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_MANUFACTURING_PURCHASE_ORDER }}" {{ model.ATTR_ID_MANUFACTURING_PURCHASE_ORDER }}>
<td class="{{ model.FLAG_ORDER_ITEMS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_order_items.html' %}
</td>
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
<td class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL }}">
<input type="number" min="0" step="0.001" class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""/>
<div
class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
<td class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL }}">
<input type="number" min="0" step="0.001" class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""/>
<div
class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_MANUFACTURING_PURCHASE_ORDER }}" {{ model.ATTR_ID_MANUFACTURING_PURCHASE_ORDER }}="{{ order.id_order }}">
{% set order_items = order.items %}
{% set json_str_items = model.jsonify(model.convert_list_objects_to_list_options(order_items)) %}
<td class="{{ model.FLAG_ORDER_ITEMS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ json_str_items }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str_items }}">
{#
{% include 'components/store/_preview_manufacturing_purchase_order_items.html' %}
#}
{% include 'components/store/_preview_order_items.html' %}
</td>
{% set currency = order.currency %}
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.cost_total_local_VAT_excl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.cost_total_local_VAT_excl }}"
></div>
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.cost_total_local_VAT_incl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.cost_total_local_VAT_incl }}"
></div>
</td>
<td class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL }}">
<div
class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.price_total_local_VAT_excl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.price_total_local_VAT_excl }}"
></div>
</td>
<td class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL }}">
<div
class="{{ model.FLAG_PRICE_TOTAL_LOCAL_VAT_INCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.price_total_local_VAT_incl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.price_total_local_VAT_incl }}"
></div>
</td>
{% set active = order.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,60 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_PRODUCT }}" {{ model.ATTR_ID_PRODUCT }}>
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
{#
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }}">
<textarea class="{{ model.FLAG_PRODUCT_VARIATIONS }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
#}
<td class="{{ model.FLAG_HAS_VARIATIONS }}">
<input class="{{ model.FLAG_HAS_VARIATIONS }}" type="checkbox" {{ model.ATTR_VALUE_CURRENT }}="{{ model.FLAG_BOOL_FALSE }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.FLAG_BOOL_FALSE }}">
</td>
<td class="{{ model.FLAG_ACCESS_LEVEL }}"
{{ model.ATTR_ID_ACCESS_LEVEL }}="1" {{ model.FLAG_ACCESS_LEVEL_REQUIRED }}="View"
{{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1"
>
<div class="{{ model.FLAG_ACCESS_LEVEL}}" {{ model.ATTR_ID_ACCESS_LEVEL }}="1">View</div>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_ID_PRODUCT }}="{{ product.id_product }}">
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% set display_order = category.display_order %}
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ product.id_category }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.id_category }}">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}" {{ model.ATTR_VALUE_CURRENT }}="{{ product.name }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.name }}">{{ product.name }}</textarea>
</td>
{#
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }}">
{% include 'components/common/inputs/_textarea_product_variation_types.html' %}
<textarea class="{{ model.FLAG_PRODUCT_VARIATIONS }}" {{ model.ATTR_VALUE_CURRENT }}="{{ product.description }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.description }}">{{ product.description }}</textarea>
</td>
#}
<td class="{{ model.FLAG_HAS_VARIATIONS }}">
<input class="{{ model.FLAG_HAS_VARIATIONS }}" type="checkbox" {% if product.has_variations %}checked{% endif %}
{{ model.ATTR_VALUE_CURRENT }}="{{ product.has_variations | lower }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.has_variations | lower }}">
</td>
<td class="{{ model.FLAG_ACCESS_LEVEL }}"
{{ model.ATTR_ID_ACCESS_LEVEL }}="{{ product.id_access_level_required }}" {{ model.FLAG_ACCESS_LEVEL_REQUIRED }}="{{ product.name_access_level_required }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ product.id_access_level_required }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.id_access_level_required }}"
>
<div class="{{ model.FLAG_ACCESS_LEVEL}}" {{ model.ATTR_ID_ACCESS_LEVEL }}="{{ product.id_access_level_required }}" {{ model.ATTR_VALUE_CURRENT }}="{{ product.id_access_level_required }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ product.id_access_level_required }}">{{ product.name_access_level_required }}</div>
</td>
{% set active = product.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,42 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_ID_PRODUCT_CATEGORY }}>
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_CODE }}">
<textarea class="{{ model.FLAG_CODE }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_DESCRIPTION }}">
<textarea class="{{ model.FLAG_DESCRIPTION }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_ACCESS_LEVEL }}" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
<div class="{{ model.FLAG_ACCESS_LEVEL}}" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">View</div>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_ID_PRODUCT_CATEGORY }}="{{ category.id_category }}">
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% set display_order = category.display_order %}
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_CODE }}">
<textarea class="{{ model.FLAG_CODE }}" {{ model.ATTR_VALUE_CURRENT }}="{{ category.code }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ category.code }}">{{ category.code }}</textarea>
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}" {{ model.ATTR_VALUE_CURRENT }}="{{ category.name }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ category.name }}">{{ category.name }}</textarea>
</td>
<td class="{{ model.FLAG_DESCRIPTION }}">
<textarea class="{{ model.FLAG_DESCRIPTION }}" {{ model.ATTR_VALUE_CURRENT }}="{{ category.description }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ category.description }}">{{ category.description }}</textarea>
</td>
<td class="{{ model.FLAG_ACCESS_LEVEL }}" {{ model.ATTR_VALUE_CURRENT }}="{{ category.id_access_level_required }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ category.id_access_level_required }}">
<div class="{{ model.FLAG_ACCESS_LEVEL}}" {{ model.ATTR_VALUE_CURRENT }}="{{ category.id_access_level_required }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ category.id_access_level_required }}">{{ category.name_access_level_required }}</div>
</td>
{% set active = category.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,183 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_PRODUCT_PERMUTATION }}" {{ model.ATTR_ID_PERMUTATION }}>
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/store/_preview_DDL_product.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }} {{ model.FLAG_COLLAPSED}}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
{% include 'components/store/_preview_product_permutation_variations.html' %}
</td>
<td class="{{ model.FLAG_DESCRIPTION}}">
<textarea class="{{ model.FLAG_DESCRIPTION }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_QUANTITY_STOCK }}">
<input class="{{ model.FLAG_QUANTITY_STOCK }}" type="number" min="0" value="0" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
</td>
<td class="{{ model.FLAG_QUANTITY_MIN }}">
<input class="{{ model.FLAG_QUANTITY_MIN }}" type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
</td>
<td class="{{ model.FLAG_QUANTITY_MAX }}">
<input class="{{ model.FLAG_QUANTITY_MAX }}" type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP }}">
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP }}"
type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
</td>
<td class="{{ model.FLAG_UNIT_MEASUREMENT_QUANTITY }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
{% include 'components/store/_preview_DDL_product_permutation_unit_measurement_quantity.html' %}
</td>
<td class="{{ model.FLAG_IS_SUBSCRIPTION }}">
<input class="{{ model.FLAG_IS_SUBSCRIPTION }}" type="checkbox" {{ model.ATTR_VALUE_CURRENT }}="{{ model.FLAG_BOOL_FALSE }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.FLAG_BOOL_FALSE }}">
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }}">
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }} {{ model.FLAG_COLLAPSED }}"
type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
</td>
<td class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
{% include 'components/store/_preview_DDL_product_permutation_interval_recurrence.html' %}
</td>
<td class="{{ model.FLAG_ID_STRIPE_PRODUCT }}">
<input class="{{ model.FLAG_ID_STRIPE_PRODUCT }}" type="text" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
</td>
<td class="{{ model.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED }}">
<input class="{{ model.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED }}" type="checkbox" {{ model.ATTR_VALUE_CURRENT }}="{{ model.FLAG_BOOL_FALSE }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.FLAG_BOOL_FALSE }}">
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }}">
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }} {{ model.FLAG_COLLAPSED }}"
type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}="1">
</td>
<td class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
{% include 'components/store/_preview_DDL_product_permutation_interval_expiration_unsealed.html' %}
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}">
<!--
<input class="{{ model.FLAG_COST_LOCAL }}" type="number" min="0" step="0.01"
-->
<div class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}>
0
</div>
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}">
<div class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}>
0
</div>
</td>
<td class="{{ model.FLAG_CURRENCY_COST }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}>
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_PROFIT_LOCAL_MIN }}">
<input class="{{ model.FLAG_PROFIT_LOCAL_MIN }}" type="number" min="0" step="0.01"
value="0" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}>
</td>
<td class="{{ model.FLAG_LATENCY_MANUFACTURE }}">
<input class="{{ model.FLAG_LATENCY_MANUFACTURE }}" type="number" min="0" value="1" {{ model.ATTR_VALUE_CURRENT }}="1" {{ model.ATTR_VALUE_PREVIOUS }}>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_PRODUCT_PERMUTATION }}" {{ model.ATTR_ID_PRODUCT_PERMUTATION }}="{{ permutation.id_permutation }}">
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_category }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_category }}">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_product }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_product }}">
{% include 'components/store/_preview_DDL_product.html' %}
</td>
{% set variation_tree = permutation.variation_tree %}
{% set str_ids_variations = variation_tree.to_variation_id_pairs_str() if not (variation_tree is none) else '' %}
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ str_ids_variations }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ str_ids_variations }}">
{% include 'components/store/_preview_product_permutation_variations.html' %}
</td>
<td class="{{ model.FLAG_DESCRIPTION}}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.description }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.description }}">
<textarea class="{{ model.FLAG_DESCRIPTION }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.description }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.description }}">{{ permutation.description }}</textarea>
</td>
<td class="{{ model.FLAG_QUANTITY_STOCK }}">
<input class="{{ model.FLAG_QUANTITY_STOCK }}" type="number" min="0" value="{{ permutation.quantity_stock }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.quantity_stock }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.quantity_stock }}">
</td>
<td class="{{ model.FLAG_QUANTITY_MIN }}">
<input class="{{ model.FLAG_QUANTITY_MIN }}" type="number" min="0" value="{{ permutation.quantity_min }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.quantity_min }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.quantity_min }}">
</td>
<td class="{{ model.FLAG_QUANTITY_MAX }}">
<input class="{{ model.FLAG_QUANTITY_MAX }}" type="number" min="0" value="{{ permutation.quantity_max }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.quantity_max }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.quantity_max }}">
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP }}">
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_PER_QUANTITY_STEP }}" type="number" min="0" value="{{ permutation.count_unit_measurement_per_quantity_step }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.count_unit_measurement_per_quantity_step }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.count_unit_measurement_per_quantity_step }}">
</td>
<td class="{{ model.FLAG_UNIT_MEASUREMENT_QUANTITY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_unit_measurement_quantity }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_unit_measurement_quantity }}">
{% include 'components/store/_preview_DDL_product_permutation_unit_measurement_quantity.html' %}
</td>
<td class="{{ model.FLAG_IS_SUBSCRIPTION }}">
<input type="checkbox" {% if permutation.is_subscription %}checked{% endif %}
class="{{ model.FLAG_IS_SUBSCRIPTION }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ permutation.is_subscription | lower }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.is_subscription | lower }}"
/>
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }}">
{% set value = permutation.count_interval_recurrence if permutation.count_interval_recurrence is not none else 1 %}
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }} {% if not permutation.is_subscription %}{{ model.FLAG_COLLAPSED }}{% endif %}"
type="number" min="0"
value="{% if value is not none %}{{ value }}{% else %}{{ 1 }}{% endif %}"
{{ model.ATTR_VALUE_CURRENT }}="{% if value is not none %}{{ value }}{% else %}{{ 1 }}{% endif %}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ value }}"
/>
</td>
{% set value = permutation.id_unit_measurement_interval_recurrence if permutation.id_unit_measurement_interval_recurrence is not none else '' %}
<td class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_RECURRENCE }}" {{ model.ATTR_VALUE_CURRENT }}="{{ value }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ value }}">
{% include 'components/store/_preview_DDL_product_permutation_interval_recurrence.html' %}
</td>
<td class="{{ model.FLAG_ID_STRIPE_PRODUCT }}">
{% set value = permutation.id_stripe_product if permutation.id_stripe_product is not none else '' %}
<input class="{{ model.FLAG_ID_STRIPE_PRODUCT }}" type="text" value="{{ value }}" {{ model.ATTR_VALUE_CURRENT }}="{{ value }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ value }}">
</td>
<td class="{{ model.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED }}">
<input type="checkbox" {% if permutation.does_expire_faster_once_unsealed %}checked{% endif %}
class="{{ model.FLAG_DOES_EXPIRE_FASTER_ONCE_UNSEALED }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ permutation.does_expire_faster_once_unsealed | lower }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.does_expire_faster_once_unsealed | lower }}"
/>
</td>
<td class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }}">
{% set value = permutation.count_interval_expiration_unsealed if permutation.count_interval_expiration_unsealed is not none else 1 %}
<input class="{{ model.FLAG_COUNT_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }} {% if not permutation.does_expire_faster_once_unsealed %}{{ model.FLAG_COLLAPSED }}{% endif %}"
type="number" min="0"
value="{% if value is not none %}{{ value }}{% else %}{{ 1 }}{% endif %}"
{{ model.ATTR_VALUE_CURRENT }}="{% if value is not none %}{{ value }}{% else %}{{ 1 }}{% endif %}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ value }}"
/>
</td>
{% set value = permutation.id_unit_measurement_interval_expiration_unsealed if permutation.id_unit_measurement_interval_expiration_unsealed is not none else '' %}
<td class="{{ model.FLAG_UNIT_MEASUREMENT_INTERVAL_EXPIRATION_UNSEALED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ value }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ value }}">
{% include 'components/store/_preview_DDL_product_permutation_interval_expiration_unsealed.html' %}
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}">
<div class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.cost_local_VAT_excl }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.cost_local_VAT_excl }}">
{{ permutation.cost_local_VAT_excl }}
</div>
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}">
<div class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.cost_local_VAT_incl }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.cost_local_VAT_incl }}">
{{ permutation.cost_local_VAT_incl }}
</div>
</td>
{% set currency = permutation.currency_cost %}
<td class="{{ model.FLAG_CURRENCY_COST }}" {{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_PROFIT_LOCAL_MIN }}">
<input class="{{ model.FLAG_PROFIT_LOCAL_MIN }}" type="number" min="0" value="{{ permutation.profit_local_min }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.profit_local_min }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.profit_local_min }}">
</td>
<td class="{{ model.FLAG_LATENCY_MANUFACTURE }}">
<input class="{{ model.FLAG_LATENCY_MANUFACTURE }}" type="number" min="0" value="{{ permutation.latency_manufacture }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.latency_manufacture }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.latency_manufacture }}">
</td>
{% set active = permutation.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,51 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_PRODUCT_VARIATION_TYPE }}" {{ model.ATTR_ID_PRODUCT_VARIATION_TYPE }}>
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_CODE }}">
<textarea class="{{ model.FLAG_CODE }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_NAME_PLURAL }}">
<textarea class="{{ model.FLAG_NAME_PLURAL }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_PRODUCT_VARIATIONS}} {{ model.FLAG_COLLAPSED }}">
{% include 'components/store/_preview_product_variation_type_variations.html' %}
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_PRODUCT_VARIATION_TYPE }}" {{ model.ATTR_ID_PRODUCT_VARIATION_TYPE }}="{{ variation_type.id_type }}">
<td class="{{ model.FLAG_DISPLAY_ORDER }}">
{% set display_order = variation_type.display_order %}
{% include 'components/common/buttons/_slider_display_order.html' %}
</td>
<td class="{{ model.FLAG_CODE }}">
<textarea class="{{ model.FLAG_CODE }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ variation_type.code }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ variation_type.code }}"
>{{ variation_type.code }}</textarea>
</td>
<td class="{{ model.FLAG_NAME }}">
<textarea class="{{ model.FLAG_NAME }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ variation_type.name_singular }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ variation_type.name_singular }}"
>{{ variation_type.name_singular }}</textarea>
</td>
<td class="{{ model.FLAG_NAME_PLURAL }}">
<textarea class="{{ model.FLAG_NAME_PLURAL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ variation_type.name_plural }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ variation_type.name_plural }}"
>{{ variation_type.name_plural }}</textarea>
</td>
<td class="{{ model.FLAG_PRODUCT_VARIATIONS}} {{ model.FLAG_COLLAPSED }}">
{% include 'components/store/_preview_product_variation_type_variations.html' %}
</td>
{% set active = variation_type.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,147 +0,0 @@
{% if date_time_now is not defined %}
{% set date_time_now = model.format_date(datetime.now()) %}
{% endif %}
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_STOCK_ITEM }}" {{ model.ATTR_ID_STOCK_ITEM }}>
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_VALUE_CURRENT }}="0" {{ model.ATTR_VALUE_PREVIOUS }}="0">
{% include 'components/store/_preview_DDL_product.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_ID_PRODUCT_PERMUTATION }}="0">
{% include 'components/store/_preview_product_permutation_variations.html' %}
</td>
<td class="{{ model.FLAG_CURRENCY_COST }}">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}">
<input class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}" type="number" min="0" value="" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}>
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}">
<input class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}" type="number" min="0" value="" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}>
</td>
<td class="{{ model.FLAG_DATE_PURCHASED }}">
<input class="{{ model.FLAG_DATE_PURCHASED }}" type="datetime-local" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }} />
</td>
<td class="{{ model.FLAG_DATE_RECEIVED }}">
<input type="datetime-local" class="{{ model.FLAG_DATE_RECEIVED }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }} />
</td>
<td class="{{ model.FLAG_STORAGE_LOCATION }}">
{% include 'components/store/_preview_DDL_stock_item_storage_location.html' %}
</td>
<td class="{{ model.FLAG_IS_SEALED }}">
<input type="checkbox" class="{{ model.FLAG_IS_SEALED }}" checked
{{ model.ATTR_VALUE_CURRENT }}="{{ model.FLAG_BOOL_TRUE }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.FLAG_BOOL_TRUE }}" />
</td>
<td class="{{ model.FLAG_DATE_UNSEALED }}">
<input type="datetime-local" class="{{ model.FLAG_DATE_UNSEALED }} {{ model.FLAG_COLLAPSED }}"
value = "{{ date_time_now }}"
{{ model.ATTR_VALUE_CURRENT }} = "{{ date_time_now }}"
{{ model.ATTR_VALUE_PREVIOUS }}
/>
</td>
<td class="{{ model.FLAG_DATE_EXPIRATION }}">
<input type="datetime-local" class="{{ model.FLAG_DATE_EXPIRATION }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }} />
</td>
<td class="{{ model.FLAG_IS_CONSUMED }}">
<input type="checkbox" class="{{ model.FLAG_IS_CONSUMED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ model.FLAG_BOOL_FALSE }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.FLAG_BOOL_FALSE }}" />
</td>
<td class="{{ model.FLAG_DATE_CONSUMED }}">
<input type="datetime-local" class="{{ model.FLAG_DATE_CONSUMED }} {{ model.FLAG_COLLAPSED }}"
value = "{{ date_time_now }}"
{{ model.ATTR_VALUE_CURRENT }} = "{{ date_time_now }}"
{{ model.ATTR_VALUE_PREVIOUS }}
/>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_STOCK_ITEM }}" {{ model.ATTR_ID_STOCK_ITEM }}="{{ stock_item.id_stock }}">
<td class="{{ model.FLAG_PRODUCT_CATEGORY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_category }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_category }}">
{% include 'components/store/_preview_DDL_product_category.html' %}
</td>
<td class="{{ model.FLAG_PRODUCT }}" {{ model.ATTR_VALUE_CURRENT }}="{{ permutation.id_product }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ permutation.id_product }}">
{% include 'components/store/_preview_DDL_product.html' %}
</td>
{% set variation_tree = permutation.variation_tree %}
{% set str_ids_variations = variation_tree.to_variation_id_pairs_str() if not (variation_tree is none) else '' %}
<td class="{{ model.FLAG_PRODUCT_VARIATIONS }} {{ model.FLAG_COLLAPSED }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ str_ids_variations }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ str_ids_variations }}"
{{ model.ATTR_ID_PRODUCT_PERMUTATION }}="{{ permutation.id_permutation }}"
>
{% include 'components/store/_preview_product_permutation_variations.html' %}
</td>
{% set currency = stock_item.currency_cost %}
<td class="{{ model.FLAG_CURRENCY_COST }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}"
>
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}">
<input class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_EXCL }}" type="number" step="0.001"
value="{{ stock_item.cost_local_VAT_excl }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ stock_item.cost_local_VAT_excl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ stock_item.cost_local_VAT_excl }}"
>
</td>
<td class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}">
<input class="{{ model.FLAG_COST_UNIT_LOCAL_VAT_INCL }}" type="number" step="0.001"
value="{{ stock_item.cost_local_VAT_incl }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ stock_item.cost_local_VAT_incl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ stock_item.cost_local_VAT_incl }}"
>
</td>
<td class="{{ model.FLAG_DATE_PURCHASED }}">
<input type="datetime-local" value="{{ model.format_datetime(stock_item.date_purchased) }}" {{ model.ATTR_VALUE_CURRENT }}="{{ model.format_datetime(stock_item.date_purchased) }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.format_datetime(stock_item.date_purchased) }}" />
</td>
<td class="{{ model.FLAG_DATE_RECEIVED }}">
<input type="datetime-local"
class="{{ model.FLAG_DATE_RECEIVED }} {% if stock_item.date_purchased is none %}{{ model.FLAG_COLLAPSED }}{% endif %}"
value="{{ model.format_datetime(stock_item.date_received) }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ model.format_datetime(stock_item.date_received) }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ model.format_datetime(stock_item.date_received) }}"
/>
</td>
<td class="{{ model.FLAG_STORAGE_LOCATION }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ stock_item.id_location_storage }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ stock_item.id_location_storage }}"
>
{% set storage_location = stock_item.storage_location %}
{% include 'components/store/_preview_DDL_stock_item_storage_location.html' %}
</td>
<td class="{{ model.FLAG_IS_SEALED }}">
<input type="checkbox" {{ "checked" if stock_item.is_sealed else "" }}
{{ model.ATTR_VALUE_CURRENT }}="{{ stock_item.is_sealed | lower }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ stock_item.is_sealed | lower }}"
/>
</td>
<td class="{{ model.FLAG_DATE_UNSEALED }}">
<input type="datetime-local"
class="{{ model.FLAG_DATE_UNSEALED }} {% if stock_item.is_sealed %}{{ model.FLAG_COLLAPSED }}{% endif %}"
value="{{ model.format_datetime(stock_item.date_unsealed) }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ model.format_datetime(stock_item.date_unsealed) }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ model.format_datetime(stock_item.date_unsealed) }}"
/>
</td>
<td class="{{ model.FLAG_DATE_EXPIRATION }}">
<input type="datetime-local" value="{{ model.format_datetime(stock_item.date_expiration) }}" {{ model.ATTR_VALUE_CURRENT }}="{{ model.format_datetime(stock_item.date_expiration) }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ model.format_datetime(stock_item.date_expiration) }}" />
</td>
<td class="{{ model.FLAG_IS_CONSUMED }}">
<input type="checkbox" {{ "checked" if stock_item.is_consumed else "" }} {{ model.ATTR_VALUE_CURRENT }}="{{ stock_item.is_consumed | lower }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ stock_item.is_consumed | lower }}" />
</td>
<td class="{{ model.FLAG_DATE_CONSUMED }}">
<input type="datetime-local"
class="{{ model.FLAG_DATE_CONSUMED }} {% if not stock_item.is_consumed %}{{ model.FLAG_COLLAPSED }}{% endif %}"
value="{{ model.format_datetime(stock_item.date_consumed) }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ model.format_datetime(stock_item.date_consumed) }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ model.format_datetime(stock_item.date_consumed) }}"
/>
</td>
{% set active = stock_item.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,67 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_SUPPLIER }}" {{ model.ATTR_ID_SUPPLIER }}>
<td class="{{ model.FLAG_NAME_COMPANY }}">
<textarea class="{{ model.FLAG_NAME_COMPANY }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_NAME_CONTACT }}">
<textarea class="{{ model.FLAG_NAME_CONTACT }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_DEPARTMENT_CONTACT }}">
<textarea class="{{ model.FLAG_DEPARTMENT_CONTACT }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_ADDRESS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_address.html' %}
</td>
<td class="{{ model.FLAG_PHONE_NUMBER }}">
<textarea class="{{ model.FLAG_PHONE_NUMBER }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_FAX }}">
<textarea class="{{ model.FLAG_FAX }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_EMAIL }}">
<textarea class="{{ model.FLAG_EMAIL }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_WEBSITE }}">
<textarea class="{{ model.FLAG_WEBSITE }}" {{ model.ATTR_VALUE_CURRENT }} {{ model.ATTR_VALUE_PREVIOUS }}></textarea>
</td>
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_SUPPLIER }}" {{ model.ATTR_ID_SUPPLIER }}="{{ supplier.id_supplier }}">
<td class="{{ model.FLAG_NAME_COMPANY }}">
<textarea class="{{ model.FLAG_NAME_COMPANY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.name_company }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.name_company }}">{{ supplier.name_company }}</textarea>
</td>
<td class="{{ model.FLAG_NAME_CONTACT }}">
<textarea class="{{ model.FLAG_NAME_CONTACT }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.name_contact }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.name_contact }}">{{ supplier.name_contact }}</textarea>
</td>
<td class="{{ model.FLAG_DEPARTMENT_CONTACT }}">
<textarea class="{{ model.FLAG_DEPARTMENT_CONTACT }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.department_contact }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.department_contact }}">{{ supplier.department_contact }}</textarea>
</td>
{% set address = supplier.get_address_active() %}
<td class="{{ model.FLAG_ADDRESS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ address.id_address }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ address.id_address }}">
{% include 'components/store/_preview_address.html' %}
</td>
<td class="{{ model.FLAG_PHONE_NUMBER }}">
<textarea class="{{ model.FLAG_PHONE_NUMBER }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.name_company }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.phone_number }}">{{ supplier.phone_number }}</textarea>
</td>
<td class="{{ model.FLAG_FAX }}">
<textarea class="{{ model.FLAG_FAX }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.name_company }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.fax }}">{{ supplier.fax }}</textarea>
</td>
<td class="{{ model.FLAG_EMAIL }}">
<textarea class="{{ model.FLAG_EMAIL }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.name_company }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.email }}">{{ supplier.email }}</textarea>
</td>
<td class="{{ model.FLAG_WEBSITE }}">
<textarea class="{{ model.FLAG_WEBSITE }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.website }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.website }}">{{ supplier.website }}</textarea>
</td>
{% set currency = supplier.currency %}
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
{% set active = supplier.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,66 +0,0 @@
{% if is_blank_row %}
<tr class="{{ model.FLAG_ROW_NEW }} {{ model.FLAG_SUPPLIER_PURCHASE_ORDER }}" {{ model.ATTR_ID_SUPPLIER_PURCHASE_ORDER }}>
<td class="{{ model.FLAG_SUPPLIER }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_DDL_supplier.html' %}
</td>
<td class="{{ model.FLAG_ORDER_ITEMS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_order_items.html' %}
</td>
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}="">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}">
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="" {{ model.ATTR_VALUE_PREVIOUS }}=""
></div>
</td>
{% set active = true %}
{% include 'components/store/_td_active.html' %}
</tr>
{% else %}
<tr class="{{ model.FLAG_SUPPLIER_PURCHASE_ORDER }}" {{ model.ATTR_ID_SUPPLIER_PURCHASE_ORDER }}="{{ order.id_order }}">
{% set supplier = order.supplier %}
<td class="{{ model.FLAG_SUPPLIER }}" {{ model.ATTR_VALUE_CURRENT }}="{{ supplier.id_supplier }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ supplier.id_supplier }}">
{% include 'components/store/_preview_DDL_supplier.html' %}
</td>
{% set order_items = order.items %}
{% set json_str_items = model.jsonify(model.convert_list_objects_to_list_options(order_items)) %}
<td class="{{ model.FLAG_ORDER_ITEMS }} {{ model.FLAG_COLLAPSED }}" {{ model.ATTR_VALUE_CURRENT }}="{{ json_str_items }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ json_str_items }}">
{% include 'components/store/_preview_order_items.html' %}
</td>
{% set currency = order.currency %}
<td class="{{ model.FLAG_CURRENCY }}" {{ model.ATTR_VALUE_CURRENT }}="{{ currency.id_currency }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ currency.id_currency }}">
{% include 'components/store/_preview_DDL_currency.html' %}
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}">
{#
<input type="number" min="0" step="0.001" >
#}
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_EXCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.cost_total_local_VAT_excl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.cost_total_local_VAT_excl }}"
></div>
</td>
<td class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}">
{#
<input type="number" min="0" step="0.001" >
#}
<div
class="{{ model.FLAG_COST_TOTAL_LOCAL_VAT_INCL }}"
{{ model.ATTR_VALUE_CURRENT }}="{{ order.cost_total_local_VAT_incl }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ order.cost_total_local_VAT_incl }}"
></div>
</td>
{% set active = order.active %}
{% include 'components/store/_td_active.html' %}
</tr>
{% endif %}

View File

@@ -1,13 +0,0 @@
{% with _active = (active is not defined or active or active is none) %}
<td class="{{ model.FLAG_ACTIVE }}">
{#
<input class="{{ model.FLAG_ACTIVE }}" type="checkbox" {% if active %}checked{% endif %} {{ model.ATTR_VALUE_CURRENT }}="{{ active | lower }}" {{ model.ATTR_VALUE_PREVIOUS }}="{{ active | lower }}">
#}
<button type="button" class="{{ model.FLAG_ACTIVE }} {% if active %}{{ model.FLAG_DELETE }}{% endif %}"
{{ model.ATTR_VALUE_CURRENT }}="{{ active | lower }}"
{{ model.ATTR_VALUE_PREVIOUS }}="{{ active | lower }}"
>{% if active %}x{% else %}+{% endif %}</button>
</td>
{% endwith %}

View File

@@ -1,31 +0,0 @@
{% if block_id == 'block1' %}
<div class="common-block" id="block1">
<h1>Feckin common block boi</h1>
</div>
{% elif block_id == 'checkout' %}
<!-- Variables from Model_View_Store + model-->
<script>
var hashPageStoreCheckout = "{{ model.HASH_PAGE_STORE_CHECKOUT }}";
var hashPageStoreCheckoutSession = "{{ model.HASH_PAGE_STORE_CHECKOUT_SESSION }}";
var hashStoreBasketInfo = "{{ model.HASH_STORE_BASKET_INFO }}";
var idOverlayInfoBilling = "#{{ model.ID_OVERLAY_INFO_BILLING }}";
var idOverlayInfoDelivery = "#{{ model.ID_OVERLAY_INFO_DELIVERY }}";
var idContainerInfoBilling = "#{{ model.ID_CONTAINER_INFO_BILLING }}";
var idContainerInfoDelivery = "#{{ model.ID_CONTAINER_INFO_DELIVERY }}";
var keyIdCheckout = "{{ model.KEY_ID_CHECKOUT }}";
var keyInfoBilling = "{{ model.KEY_INFO_BILLING }}";
var keyInfoDelivery = "{{ model.KEY_INFO_DELIVERY }}";
var keyInfoIdentical = "{{ model.KEY_INFO_IDENTICAL }}";
var keyInfoType = "{{ model.KEY_INFO_TYPE }}";
var keyIsSubscription = "{{ model.KEY_IS_SUBSCRIPTION }}";
var keyAddress1 = "{{ model.KEY_ADDRESS1 }}";
var keyAddress2 = "{{ model.KEY_ADDRESS2 }}";
var keyCity = "{{ model.KEY_CITY }}";
var keyCounty = "{{ model.KEY_COUNTY }}";
var keyNameFull = "{{ model.KEY_NAME_FULL }}";
var keyPhoneNumber = "{{ model.KEY_PHONE_NUMBER }}";
var keyPostcode = "{{ model.KEY_POSTCODE }}";
var keyRegion = "{{ model.KEY_REGION }}";
var keyUrlCheckout = "{{ model.KEY_URL_CHECKOUT }}";
</script>
{% endif %}

View File

@@ -47,6 +47,11 @@
var attrValueCurrent = "{{ model.ATTR_VALUE_CURRENT }}";
var attrValuePrevious = "{{ model.ATTR_VALUE_PREVIOUS }}";
var attrValueNew = "{{ model.ATTR_VALUE_NEW }}";
var environment = {
"name": "{{ model.app.app_config.FLASK_ENV }}",
"is_production": "{{ model.app.app_config.is_production }}",
"is_development": "{{ model.app.app_config.is_development }}",
};
var flagAccessLevel = "{{ model.FLAG_ACCESS_LEVEL }}";
var flagAccessLevelRequired = "{{ model.FLAG_ACCESS_LEVEL_REQUIRED }}";
var flagActive = "{{ model.FLAG_ACTIVE }}";
@@ -132,23 +137,16 @@
var flagTemporaryElement = "{{ model.FLAG_TEMPORARY_ELEMENT }}";
var flagUser = "{{ model.FLAG_USER }}";
var flagWebsite = "{{ model.FLAG_WEBSITE }}";
var hashALTCHACreateChallenge = "{{ model.HASH_ALTCHA_CREATE_CHALLENGE }}";
var hashApplyFiltersStoreProductPermutation = "{{ model.HASH_APPLY_FILTERS_STORE_PRODUCT_PERMUTATION }}";
var hashGetALTCHAChallenge = "{{ model.HASH_ALTCHA_CREATE_CHALLENGE }}";
var hashPageAccessibilityReport = "{{ model.HASH_PAGE_ACCESSIBILITY_REPORT }}";
var hashPageAccessibilityStatement = "{{ model.HASH_PAGE_ACCESSIBILITY_STATEMENT }}";
var hashPageAdminHome = "{{ model.HASH_PAGE_ADMIN_HOME }}";
var hashPageContact = "{{ model.HASH_PAGE_CONTACT }}";
var hashPageContactSuccess = "{{ model.HASH_PAGE_CONTACT_SUCCESS }}";
var hashPageDataRetentionSchedule = "{{ model.HASH_PAGE_DATA_RETENTION_SCHEDULE }}";
// var hashPageCurrent = "{{ model.hash_page_current }}";
var hashPageErrorNoPermission = "{{ model.HASH_PAGE_ERROR_NO_PERMISSION }}";
var hashPageHome = "{{ model.HASH_PAGE_HOME }}";
var hashPageLicense = "{{ model.HASH_PAGE_LICENSE }}";
var hashPagePrivacyPolicy = "{{ model.HASH_PAGE_PRIVACY_POLICY }}";
var hashPageServices = "{{ model.HASH_PAGE_SERVICES }}";
var hashPageUserAccount = "{{ model.HASH_PAGE_USER_ACCOUNT }}";
var hashPageUserAdmin = "{{ model.HASH_PAGE_USER_ADMIN }}";
var hashPageUserLogin = "{{ model.HASH_PAGE_USER_LOGIN }}";
var hashPageUserLogout = "{{ model.HASH_PAGE_USER_LOGOUT }}";
var idButtonApplyFilters = "#{{ model.ID_BUTTON_APPLY_FILTERS }}";
var idButtonHamburger = "#{{ model.ID_BUTTON_HAMBURGER }}";
var idCSRFToken = "#{{ model.ID_CSRF_TOKEN }}";
@@ -168,10 +166,6 @@
</script>
<!-- Stylesheets
<link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet" type="text/css"/>
<link rel="stylesheet" loading="eager" href="{{ url_for('static', filename='dist/css/main.bundle.css') }}">
-->
<link rel="preload" as="style" href="{{ url_for('static', filename='dist/css/main.bundle.css') }}" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ url_for('static', filename='dist/css/main.bundle.css') }}"></noscript>
@@ -182,7 +176,7 @@
<header>
<div class="container">
<nav class="navbar">
<div class="{{ model.FLAG_LOGO }}" href="/">{{ model.NAME_COMPANY }}</div>
<div class="{{ model.FLAG_LOGO }}" href="{{ model.HASH_PAGE_HOME }}">{{ model.NAME_COMPANY }}</div>
<div class="nav-links">
{% block page_nav_links %}{% endblock %}
</div>
@@ -209,8 +203,8 @@
<div class="footer-section">
<h3>Legal</h3>
<ul>
<li><a href="{{ url_for('routes_legal.privacy_policy') }}">Privacy Policy</a></li>
<li><a href="{{ url_for('routes_legal.accessibility_statement') }}">Accessibility Statement</a></li>
<li><a href="{{ model.HASH_PAGE_PRIVACY_POLICY }}">Privacy Policy</a></li>
<li><a href="{{ model.HASH_PAGE_ACCESSIBILITY_STATEMENT }}">Accessibility Statement</a></li>
</ul>
</div>
@@ -225,24 +219,11 @@
</div>
<div class="footer-bottom">
<p>&copy; {{ current_year }} {{ model.NAME_COMPANY }}. <a href="{{ url_for('routes_legal.license') }}" alt="License" aria-label="License">All rights reserved.</a></p>
<p>&copy; {{ current_year }} {{ model.NAME_COMPANY }}. <a href="{{ model.HASH_PAGE_LICENSE }}" alt="License" aria-label="License">All rights reserved.</a></p>
</div>
</div>
</footer>
<!-- JavaScript -->
<!--<script type="module" src="{{ url_for('static', filename='js/pages/base.js') }}"></script>
<script type="module" src="{{ url_for('static', filename='js/app.js') }}"></script>-->
<script src="{{ url_for('static', filename='dist/js/main.bundle.js') }}"></script>
</body>
</html>
<!--
<script>
$(document).ready(function() {
// alert("naughty boy");
hookupShared();
});
</script>
-->

View File

@@ -2,25 +2,6 @@
{% block page_head %}
<link rel="stylesheet" href="{{ url_for('static', filename='dist/css/core_contact.bundle.css') }}">
{#
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@altcha/browser@latest/dist/index.js" defer></script>
#}
{# with CDN
<script src="https://cdn.jsdelivr.net/npm/@altcha/browser@1.1.0/dist/altcha.min.js"></script>
<style>
.altcha-widget {
margin: 15px 0;
}
</style>
#}
{# with locally stored vendor project - this is imported into contact.js
<script type="module" src="{{ url_for('static', filename='js/vendor/altcha.js')}}"></script>
#}
<style>
.altcha-widget {
margin: 15px 0;
}
</style>
{% endblock %}
{% block page_nav_links %}
@@ -31,36 +12,14 @@
{% endblock %}
{% block page_body %}
{#
<script>
function loadRecaptcha() {
var script = document.createElement('script');
script.src = "https://www.google.com/recaptcha/enterprise.js?render=6Lf8Q8cpAAAAAFAawGu4-ma60bvbEixNVTVvRzKe";
script.async = true;
document.body.appendChild(script);
}
window.addEventListener('load', loadRecaptcha);
</script>
#}
{% set form = model.form_contact %}
<section class="contact-section">
<div class="contact-form">
<h1>Contact Us</h1>
<p>Please fill in the form below and we'll get back to you as soon as possible.</p>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form id="{{ model.ID_CONTACT_FORM }}" method="POST" action="{{ url_for('routes_core.contact') }}">
<form id="{{ model.ID_CONTACT_FORM }}" method="POST" action="{{ model.HASH_POST_CONTACT_FORM }}">
{{ form.csrf_token }}
<div class="form-grid">
@@ -90,27 +49,17 @@
{{ model.form_contact.receive_marketing.label }}
</div>
<div class="{{ model.FLAG_CONTAINER }} {{ model.FLAG_CAPTCHA }}">
{# {{ model.form_contact.recaptcha() }} #}
{#
<altcha-widget
challengeurl="https://eu.altcha.org/api/v1/challenge?apiKey={{ model.app.app_config.ALTCHA_API_KEY }}"
spamfilter
></altcha-widget>
#}
<div>
{{ form.altcha.label }}
{#
{{ form.altcha }}
{{ form.altcha.hidden() }}
#}
<altcha-widget
class="altcha-widget"
challengeurl="{{ url_for('routes_core.create_altcha_challenge') }}"
challengeurl="{{ model.HASH_GET_ALTCHA_CHALLENGE }}"
auto="onload"
id="{{ form.altcha.id }}"
name="{{ form.altcha.name }}"
></altcha-widget>
</div>
<p>This CAPTCHA mechanism is fully GDPR-compliant with no cookies, no fingerprinting, no tracking, and runs in the background so you don't need to do anything!</p>
</div>
<div class="{{ model.FLAG_CONTAINER_INPUT }}">
{{ model.form_contact.submit() }}
@@ -119,74 +68,13 @@
<div class="data-notice">
<h3>How we use your information</h3>
<p>We will use the information you provide in this form to:</p>
<ul>
<li>Respond to your inquiry about our ERP implementation services</li>
<li>Create and send you a proposal if requested</li>
<li>Contact you regarding your interest in our services</li>
</ul>
<p>If you opt in to marketing communications, we will also use your email address to send you updates about our services, ERPNext features, and relevant industry news. You can unsubscribe from these communications at any time.</p>
<p>We retain contact form submissions for customer service purposes and retain marketing consent records as required by law. For details about how long we keep your information, please see our <a href="{{ url_for('routes_legal.retention_schedule') }}">data retention schedule</a>.</p>
<p>For full details about how we handle your personal data, please read our <a href="{{ url_for('routes_legal.privacy_policy') }}">Privacy Policy</a>.</p>
<p>We retain contact form submissions for customer service purposes and retain marketing consent records as required by law. For details about how long we keep your information, please see our <a href="{{ model.HASH_PAGE_DATA_RETENTION_SCHEDULE }}">data retention schedule</a>.</p>
<p>For full details about how we handle your personal data, please read our <a href="{{ model.HASH_PAGE_PRIVACY_POLICY }}">Privacy Policy</a>.</p>
</div>
</div>
</section>
{# included in footer now
<section class="contact-details">
<div class="{{ model.FLAG_CONTAINER }}">
<h2>Our contact details</h2>
<div class="expertise-card">
<ul>
<li>Email: {{ model.get_mail_contact_public() }}</li>
<li>LinkedIn: <a href="https://www.linkedin.com/in/teddyms/">linkedin.com/in/teddyms</a></li>
<li>GitHub: <a href="https://github.com/Teddy-1024/">github.com/Teddy-1024</a></li>
</ul>
</div>
</div>
</section>
#}
{# with CDN
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize ALTCHA widget
ALTCHA.init({
selector: '.altcha-widget',
challenge: {
url: '/get-challenge',
onSuccess: function(result, element) {
// Store the result in the hidden input field
const hiddenInput = element.parentNode.querySelector('input[type="hidden"]');
hiddenInput.value = JSON.stringify(result);
}
}
});
});
</script>
#}
{# with locally stored vendor project - this is now in contact.js
<script type="module">
import { Altcha } from "{{ url_for('static', filename='js/vendor/altcha.js') }}";
window.ALTCHA = { init: (config) => {
document.querySelectorAll(config.selector).forEach(el => {
new Altcha({
target: el,
props: {
challengeurl: config.challenge.url,
auto: 'onload'
}
}).$on('verified', (e) => {
config.challenge.onSuccess(e.detail.payload, el);
});
});
}};
</script>
#}
<script>
var flagALTCHAWidget = "{{ model.FLAG_ALTCHA_WIDGET }}";
var idContactForm = "#{{ model.ID_CONTACT_FORM }}";

View File

@@ -0,0 +1,22 @@
{% extends 'layouts/layout.html' %}
{% block page_head %}
<link rel="stylesheet" href="{{ url_for('static', filename='dist/css/core_contact.bundle.css') }}">
{% endblock %}
{% block page_nav_links %}
{% endblock %}
{% block page_body %}
{% set form = model.form_contact %}
<section class="contact-section">
<div class="contact-form">
<h1>Message Received</h1>
<p>Thanks for contacting us! We've received your message and will respond within 48 hours.</p>
</div>
</section>
<script>
</script>
{% endblock %}

View File

@@ -3,10 +3,6 @@
{% block title %}{{ model.title }}{% endblock %}
{% block page_body %}
<!-- Include Stylesheet
<link rel="stylesheet" href="{{ url_for('static', filename='css/sections/legal.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/pages/legal/accessibility_statement.css') }}">
-->
<link rel="stylesheet" href="{{ url_for('static', filename='dist/css/legal_accessibility_statement.bundle.css') }}">
<!-- HTML content -->
@@ -201,10 +197,9 @@
[Note: publishing the test report is optional but doing so may allow you to make your accessibility statement shorter and more focused.]
https://www.w3.org/WAI/eval/report-tool/#/
-->
<p>You can read the full accessibility test report {{ model.URL_HOST }}{{ url_for('routes_legal.accessibility_report') }}.</p>
<p>You can read the full accessibility test report {{ model.URL_HOST }}{{ model.HASH_PAGE_ACCESSIBILITY_REPORT }}.</p>
</div>
<!-- Include JavaScript -->
<script type="module" src="{{ url_for('static', filename='js/sections/legal.js') }}"></script>
<script type="module" src="{{ url_for('static', filename='js/pages/legal/accessibility_statement.js') }}"></script>
{% endblock %}

View File

@@ -1955,7 +1955,7 @@
<!--
<script>
var hashPageCurrent = "{{ model.HASH_PAGE_RETENTION_SCHEDULE }}";
var hashPageCurrent = "{{ model.HASH_PAGE_DATA_RETENTION_SCHEDULE }}";
$(document).ready(function() {
hookupPageRetentionSchedule();

View File

@@ -1,2 +1,4 @@
Dark mode / light mode
Dark mode / light mode
git commit -m "Feat: \n 1. Contact Us page form submission success page created. \n 2. Contact Us page styling and CAPTCHA text content. \n 3. Removal of ERP, Google CAPTCHA, and ALTCHA API code and left over comments in JavaScript, Python."