Compare commits

..

66 Commits

Author SHA1 Message Date
47c3e98dbf fixed bug api 2025-10-21 22:07:46 -04:00
6a1cd0eab5 updated search 2025-10-12 12:11:20 -04:00
701d9a9cc0 added hot water 2025-10-06 21:14:01 -04:00
2cca684908 config issues prod 2025-09-27 14:25:20 -04:00
d8e5d6eedc reverted search 2025-09-27 11:50:44 -04:00
19f5c70735 fixed service calls 2025-09-27 09:44:21 -04:00
10d82cf81a working auto 2025-09-27 00:13:20 -04:00
deaf2f111a working auto 2025-09-26 20:30:30 -04:00
c9464b1605 Added search 2025-09-24 16:39:16 -04:00
1404a0432a Added call 2025-09-24 16:38:47 -04:00
5d96031992 Updated models 2025-09-23 22:38:25 -04:00
2209145e74 Working API CHARGING! 2025-09-20 15:33:36 -04:00
cfbc12da3b added cleanup 2025-09-19 19:08:25 -04:00
fd906a574b Working payment accopunts 2025-09-19 17:27:44 -04:00
628eb8eaa6 small calc change 2025-09-18 19:31:36 -04:00
8c146d24f6 Good progression 2025-09-18 13:02:14 -04:00
908100514f Working flow authorize 2025-09-16 12:45:32 -04:00
8d9ecf6935 Adding authnet not tested 2025-09-15 15:30:16 -04:00
f0544779bb Updated forms and search 2025-09-14 11:59:53 -04:00
382e12671b Updated charge close to working 2025-09-09 18:26:08 -04:00
14abc3c2b4 work 2025-09-07 18:29:54 -04:00
8753e3e103 Working authorize needs work 2025-09-07 17:53:13 -04:00
a280194079 Added service plan. Password change 2025-09-06 12:28:37 -04:00
cd3f4471cc Fixed edit delivery 2025-09-06 09:44:13 -04:00
66f00aa7b5 Updated time 2025-09-05 11:39:49 -04:00
ca6e7ad778 added text data 2025-09-04 11:22:45 -04:00
20f9a4485e Working log in/route guard 2025-09-04 08:05:01 -04:00
d250e136c3 Major Refactor 2025-09-01 16:42:59 -04:00
9a2f9a6564 Added price for service 2025-08-26 17:19:52 -04:00
067c9055d2 Updated auto desc 2025-08-25 18:28:44 -04:00
652947b30a tons fixes 2025-08-25 17:59:00 -04:00
c526284d98 working calender 2025-08-22 14:48:42 -04:00
51f2e986a9 Working time 2025-08-22 12:58:29 -04:00
3e259a530c Added calender 2025-08-21 17:53:25 -04:00
79aa32e8e4 website online working 2025-08-16 21:51:14 -04:00
b97d729ef1 updated page amount 2025-07-28 12:05:14 -04:00
09fafa59d4 Updated packages. Added new session type 2025-05-09 14:53:07 -04:00
86d6d2dadd added auto info to ticket 2025-02-04 18:22:00 -05:00
98a2c94083 bug fixes. added auto table 2024-12-28 15:54:08 -05:00
e6f85ff014 small fixes 2024-12-23 18:10:18 -05:00
86ec25a499 added maps 2024-11-20 18:01:14 -05:00
5e5b9274e1 added pending waiting 2024-10-30 17:04:58 -04:00
b3f0e85574 updated forms 2024-10-24 11:51:14 -04:00
5649294be0 major update 2024-10-17 17:01:24 -04:00
8cee9dc5bf small bug deciding payment status 2024-10-08 08:25:49 -04:00
d0641950f9 dynamic database 2024-10-07 22:58:17 -04:00
d91460fa82 added promo 2024-10-07 17:35:22 -04:00
fec638a5c8 added checks emergency fixed bugs 2024-09-27 14:13:44 -04:00
0e827053de added checks and finalize ticket 2024-09-26 20:01:13 -04:00
c456ef301c added emergency price 2024-09-26 08:48:37 -04:00
d7c809af82 added description 2024-09-18 12:48:12 -04:00
d77c4e2478 Stats working 2024-08-06 11:08:29 -04:00
93fc535eaf small coding fixes 2024-07-29 13:46:07 -04:00
bc1e38c327 Added automatic stuff 2024-07-24 17:24:00 -04:00
ebeecccd63 updated dynamic auto 2024-07-17 18:05:11 -04:00
12d973d785 Updated 2024-07-16 10:29:54 -04:00
9672b804e6 added config file 2024-07-15 18:29:41 -04:00
2a266eea23 Updated auto 2024-07-02 16:56:18 -04:00
ca3ebf9f9b Updated auto 2024-06-25 17:57:53 -04:00
2433dbb447 Updated 2024-05-16 14:36:14 -04:00
2a4804ecb2 Fixed errors 2024-04-04 16:37:57 -04:00
6fab39bf86 working money and delivery dates 2024-03-29 19:56:59 -04:00
666d0895e4 unsure 2024-03-28 10:36:00 -04:00
52172812cb Changes 2024-03-14 14:11:09 -04:00
ca518af9e8 Small issue 2024-03-12 18:31:08 -04:00
001b8218bd Office driver api get data works 2024-03-12 18:28:22 -04:00
80 changed files with 3968 additions and 1374 deletions

4
.dockerignore Executable file
View File

@@ -0,0 +1,4 @@
local_settings.py
.git
.gitignore
githubtoken.txt

133
.gitignore vendored Executable file
View File

@@ -0,0 +1,133 @@
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.idea
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
*.iml
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
db.sqlite3
# Flask stuff:
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
venvwindows/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
app/package.json
app/package-lock.json
.vscode/
clearnet.ini
rsync_svg.txt
setup/
word_seeds.txt
workspace.code-workspace
app/static/bootstrap
/app/static/images/
/app/static/fonts/
/app/static/css/
/app/static/js/
/app/static/javascriptaddons/
instance/config.py
.idea/
/passwords.py
getnewitems.py
helperfunctions/
test.py
tools/
nginx.txt
app/node_modules/

20
Dockerfile.dev Executable file
View File

@@ -0,0 +1,20 @@
FROM python:3.13.3-bullseye
ENV PYTHONFAULTHANDLER=1
ENV PYTHONUNBUFFERED=1
ENV MODE="DEVELOPMENT"
RUN mkdir -p /app
COPY requirements.txt /app
WORKDIR /app
RUN pip3 install -r requirements.txt
COPY . /app
CMD ["python", "app.py", "--host", "0.0.0.0"]

37
Dockerfile.local Normal file
View File

@@ -0,0 +1,37 @@
FROM python:3.13.3-bullseye
ENV PYTHONFAULTHANDLER=1
ENV PYTHONUNBUFFERED=1
ENV TZ=America/New_York
ENV MODE="LOCAL"
RUN mkdir -p /app
COPY requirements.txt /app
WORKDIR /app
RUN pip3 install -r requirements.txt
RUN pip3 install gunicorn
# Install Nginx
RUN apt-get update && apt-get install -y nginx && rm -rf /var/lib/apt/lists/*
COPY . /app
# Copy Nginx configuration
COPY nginx.conf /etc/nginx/sites-available/default
# Enable the Nginx site
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/
# Copy start script
COPY start.sh /app/start.sh
RUN chmod +x /app/start.sh
EXPOSE 80
CMD ["/app/start.sh"]

23
Dockerfile.prod Normal file
View File

@@ -0,0 +1,23 @@
# Use an official Python runtime as a parent image
FROM python:3.11-slim-bullseye
# Set environment variables
ENV PYTHONUNBUFFERED=1
ENV APP_HOME=/app
WORKDIR $APP_HOME
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn
# Copy the rest of the application code
COPY . .
# Tell Docker that the container listens on port 80
EXPOSE 80
# Run the application using Gunicorn
# This command runs the Flask app. 'app:app' means "in the file named app.py, run the variable named app".
# Adjust if your main file or Flask app variable is named differently.
CMD ["gunicorn", "--bind", "0.0.0.0:80", "app:app"]

110
app/__init__.py Normal file → Executable file
View File

@@ -10,12 +10,10 @@ from flask_login import LoginManager
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from werkzeug.routing import BaseConverter from werkzeug.routing import BaseConverter
from flask_mail import Mail from flask_mail import Mail
from config import load_config
import re
try: ApplicationConfig = load_config()
from local_settings import ApplicationConfig
except Exception as e:
from settings import ApplicationConfig
app = Flask(__name__, app = Flask(__name__,
static_url_path='', static_url_path='',
@@ -27,8 +25,7 @@ app.config.from_object(ApplicationConfig)
session = sessionmaker() session = sessionmaker()
check_enviroment = ApplicationConfig.CURRENT_SETTINGS
print(f"starting server with {check_enviroment} settings")
class RegexConverter(BaseConverter): class RegexConverter(BaseConverter):
@@ -42,10 +39,8 @@ app.jinja_env.autoescape = True
# configuration # configuration
UPLOADED_FILES_DEST_ITEM = ApplicationConfig.UPLOADED_FILES_DEST_ITEM
UPLOADED_FILES_ALLOW = ApplicationConfig.UPLOADED_FILES_ALLOW
CURRENT_SETTINGS = ApplicationConfig.CURRENT_SETTINGS
app.config['CORS_ALLOWED_ORIGINS'] = ApplicationConfig.CORS_ALLOWED_ORIGINS
app.config['UPLOADED_FILES_DEST_ITEM'] = ApplicationConfig.UPLOADED_FILES_DEST_ITEM app.config['UPLOADED_FILES_DEST_ITEM'] = ApplicationConfig.UPLOADED_FILES_DEST_ITEM
app.config['UPLOADED_FILES_ALLOW'] = ApplicationConfig.UPLOADED_FILES_ALLOW app.config['UPLOADED_FILES_ALLOW'] = ApplicationConfig.UPLOADED_FILES_ALLOW
app.config['MAX_CONTENT_LENGTH'] = ApplicationConfig.MAX_CONTENT_LENGTH app.config['MAX_CONTENT_LENGTH'] = ApplicationConfig.MAX_CONTENT_LENGTH
@@ -55,13 +50,13 @@ app.config['SESSION_COOKIE_HTTPONLY'] = ApplicationConfig.SESSION_COOKIE_HTTPONL
app.config['SESSION_COOKIE_SAMESITE'] = ApplicationConfig.SESSION_COOKIE_SAMESITE app.config['SESSION_COOKIE_SAMESITE'] = ApplicationConfig.SESSION_COOKIE_SAMESITE
app.config['SESSION_PERMANENT'] = ApplicationConfig.SESSION_PERMANENT app.config['SESSION_PERMANENT'] = ApplicationConfig.SESSION_PERMANENT
app.config['SESSION_USE_SIGNER'] = ApplicationConfig.SESSION_USE_SIGNER app.config['SESSION_USE_SIGNER'] = ApplicationConfig.SESSION_USE_SIGNER
app.config['ORIGIN_URL'] = ApplicationConfig.ORIGIN_URL
app.config['CURRENT_SETTINGS'] = ApplicationConfig.CURRENT_SETTINGS app.config['CURRENT_SETTINGS'] = ApplicationConfig.CURRENT_SETTINGS
app.config['SECRET_KEY'] = ApplicationConfig.SECRET_KEY app.config['SECRET_KEY'] = ApplicationConfig.SECRET_KEY
session.configure(bind=ApplicationConfig.SQLALCHEMY_DATABASE_URI) session.configure(bind=ApplicationConfig.SQLALCHEMY_DATABASE_URI)
db = SQLAlchemy(app) db = SQLAlchemy(app)
bcrypt = Bcrypt(app) bcrypt = Bcrypt(app)
app.config['SESSION_SQLALCHEMY'] = db
server_session = Session(app) server_session = Session(app)
ma = Marshmallow(app) ma = Marshmallow(app)
mail = Mail(app) mail = Mail(app)
@@ -75,39 +70,41 @@ login_manager.anonymous_user = "Guest"
@login_manager.request_loader @login_manager.request_loader
def load_user_from_request(request): def load_user_from_request(request):
from app.classes.auth import Auth_User from app.classes.auth import Auth_User
# first, try to log in using the api_key url arg
api_key = request.args.get('api_key') # Check for Authorization header first, as it's the standard
auth_header = request.headers.get('Authorization')
if auth_header:
# --- THIS IS THE FIX ---
# Use a case-insensitive regular expression to strip "bearer "
api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
if api_key: if api_key:
user = db.session\ user = db.session.query(Auth_User).filter_by(api_key=api_key).first()
.query(Auth_User)\
.filter_by(api_key=api_key)\
.first()
if user: if user:
return user return user
# next, try to log in using Basic Auth
api_key_auth = request.headers.get('Authorization') # As a fallback, check for api_key in URL args (less secure, but keeps existing logic)
if api_key_auth: api_key_arg = request.args.get('api_key')
api_key = api_key_auth.replace('bearer ', '', 1) if api_key_arg:
if api_key.startswith('"') and api_key.endswith('"'): user = db.session.query(Auth_User).filter_by(api_key=api_key_arg).first()
api_key = api_key[1:-1]
user = db.session\
.query(Auth_User)\
.filter_by(api_key=api_key)\
.first()
if user: if user:
return user return user
# If no valid key is found in header or args, return None
return None return None
# api_main = {
api_main = { # "origins": [ApplicationConfig.ORIGIN_URL],
"origins": [ApplicationConfig.ORIGIN_URL], # "methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"],
"methods": ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"], # "allow_headers": ['Authorization', 'application/json', 'authorization', 'Content-Type',
"allow_headers": ['Authorization', 'application/json', 'authorization', 'Content-Type', # 'Access-Control-Allow-Headers', 'Origin,Accept',
'Access-Control-Allow-Headers', 'Origin,Accept', # 'X-Requested-With', 'Content-Type', 'Access-Control-Request-cMethod',
'X-Requested-With', 'Content-Type', 'Access-Control-Request-Method', # 'Access-Control-Request-Headers']
'Access-Control-Request-Headers'] # }
} cors = CORS(app,
cors = CORS(app, supports_credentials=True, resources={r'/*': api_main}) supports_credentials=True,
resources={r"/*": {"origins": ApplicationConfig.CORS_ALLOWED_ORIGINS}
})
# bind a function after each request, even if an exception is encountered. # bind a function after each request, even if an exception is encountered.
@@ -122,42 +119,42 @@ def teardown_appcontext(error):
@app.errorhandler(500) @app.errorhandler(500)
def internal_error500(): def internal_error500(error):
return jsonify({"error": "Internal Error 500"}), 500 return jsonify({"error": "Internal Error 500"}), 500
@app.errorhandler(502) @app.errorhandler(502)
def internal_error502(): def internal_error502(error):
return jsonify({"error": "Internal Error 502"}), 502 return jsonify({"error": "Internal Error 502"}), 502
@app.errorhandler(404) @app.errorhandler(404)
def internal_error404(): def internal_error404(error):
return jsonify({"error": "Internal Error 400"}), 400 return jsonify({"error": "Internal Error 400"}), 400
@app.errorhandler(401) @app.errorhandler(401)
def internal_error404(): def internal_error401(error):
return jsonify({"error": "Internal Error 401"}), 401 return jsonify({"error": "Internal Error 401"}), 401
@app.errorhandler(400) @app.errorhandler(400)
def internal_error400(): def internal_error400(error):
return jsonify({"error": "Internal Error 400"}), 400 return jsonify({"error": "Internal Error 400"}), 400
@app.errorhandler(413) @app.errorhandler(413)
def to_large_file(): def to_large_file(error):
return jsonify({"error": "File is too large. Use a smaller image/file."}), 413 return jsonify({"error": "File is too large. Use a smaller image/file."}), 413
@app.errorhandler(403) @app.errorhandler(403)
def internal_error403(): def internal_error403(error):
return jsonify({"error": "Internal Error 403"}), 403 return jsonify({"error": "Internal Error 403"}), 403
@app.errorhandler(405) @app.errorhandler(405)
def internal_error(): def internal_error(error):
return jsonify({"error": "Internal Error 405"}), 405 return jsonify({"error": "Internal Error 405"}), 405
@@ -171,15 +168,15 @@ app.register_blueprint(main_blueprint, url_prefix='/main')
from .customer import customer as customer_blueprint from .customer import customer as customer_blueprint
app.register_blueprint(customer_blueprint, url_prefix='/customer') app.register_blueprint(customer_blueprint, url_prefix='/customer')
from .service import service as service_blueprint
app.register_blueprint(service_blueprint, url_prefix='/service')
from .delivery import delivery as delivery_blueprint from .delivery import delivery as delivery_blueprint
app.register_blueprint(delivery_blueprint, url_prefix='/delivery') app.register_blueprint(delivery_blueprint, url_prefix='/delivery')
from .delivery_data import delivery_data as delivery_data_blueprint from .delivery_data import delivery_data as delivery_data_blueprint
app.register_blueprint(delivery_data_blueprint, url_prefix='/deliverydata') app.register_blueprint(delivery_data_blueprint, url_prefix='/deliverydata')
from .delivery_status import deliverystatus as delivery_status_blueprint
app.register_blueprint(delivery_status_blueprint, url_prefix='/deliverystatus')
from .search import search as search_blueprint from .search import search as search_blueprint
app.register_blueprint(search_blueprint, url_prefix='/search') app.register_blueprint(search_blueprint, url_prefix='/search')
@@ -192,6 +189,9 @@ app.register_blueprint(query_blueprint, url_prefix='/query')
from .payment import payment as payment_blueprint from .payment import payment as payment_blueprint
app.register_blueprint(payment_blueprint, url_prefix='/payment') app.register_blueprint(payment_blueprint, url_prefix='/payment')
from .money import money as money_blueprint
app.register_blueprint(money_blueprint, url_prefix='/money')
from .auth import auth as auth_blueprint from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth') app.register_blueprint(auth_blueprint, url_prefix='/auth')
@@ -204,7 +204,21 @@ app.register_blueprint(info_blueprint, url_prefix='/info')
from .stats import stats as stats_blueprint from .stats import stats as stats_blueprint
app.register_blueprint(stats_blueprint, url_prefix='/stats') app.register_blueprint(stats_blueprint, url_prefix='/stats')
from .ticket import ticket as ticket_blueprint
app.register_blueprint(ticket_blueprint, url_prefix='/ticket')
from .promo import promo as promo_blueprint
app.register_blueprint(promo_blueprint, url_prefix='/promo')
from .social import social as social_blueprint
app.register_blueprint(social_blueprint, url_prefix='/social')
from .service import service as service_blueprint
app.register_blueprint(service_blueprint, url_prefix='/service')
with app.app_context(): with app.app_context():
db.configure_mappers() db.configure_mappers()
db.create_all() db.create_all()
db.session.commit() db.session.commit()

0
app/admin/__init__.py Normal file → Executable file
View File

87
app/admin/views.py Normal file → Executable file
View File

@@ -3,11 +3,10 @@ from flask_login import current_user, logout_user, login_user, login_required
from app.admin import admin from app.admin import admin
from app import db from app import db
from datetime import datetime from datetime import datetime
from app.classes.pricing import (Pricing_Service_General, from app.classes.pricing import (
Pricing_Oil_Oil, Pricing_Oil_Oil,
Pricing_Service_General_schema,
Pricing_Oil_Oil_schema) Pricing_Oil_Oil_schema)
from app.classes.admin import Admin_Company, Admin_Company_schema, Call
@admin.route("/oil/create", methods=["POST"]) @admin.route("/oil/create", methods=["POST"])
def create_oil_price(): def create_oil_price():
@@ -18,13 +17,27 @@ def create_oil_price():
price_from_supplier = request.json["price_from_supplier"] price_from_supplier = request.json["price_from_supplier"]
price_for_customer = request.json["price_for_customer"] price_for_customer = request.json["price_for_customer"]
price_for_employee = request.json["price_for_employee"] price_for_employee = request.json["price_for_employee"]
price_same_day = request.json["price_same_day"]
price_prime = request.json["price_prime"]
price_emergency= request.json["price_emergency"]
new_admin_oil_price = Pricing_Oil_Oil( new_admin_oil_price = Pricing_Oil_Oil(
price_from_supplier=price_from_supplier, price_from_supplier=price_from_supplier,
price_for_customer=price_for_customer, price_for_customer=price_for_customer,
price_for_employee=price_for_employee, price_for_employee=price_for_employee,
price_same_day=price_same_day,
price_prime=price_prime,
price_emergency=price_emergency,
date=now, date=now,
) )
# new_admin_oil_price = Pricing_Oil_Oil(
# price_from_supplier=price_from_supplier,
# price_for_customer=price_for_customer,
# price_for_employee=price_for_employee,
# price_same_day=price_same_day,
# price_prime=price_prime,
# date=now,
# )
db.session.add(new_admin_oil_price) db.session.add(new_admin_oil_price)
db.session.commit() db.session.commit()
@@ -35,49 +48,6 @@ def create_oil_price():
}), 200 }), 200
@admin.route("/service/create", methods=["POST"])
def create_service_price():
"""
Changes general labor rates prices
"""
now = datetime.utcnow()
price_service_hour = request.json["price_service_hour"]
price_emergency_service_hourly_rate = request.json["price_emergency_service_hour"]
price_emergency_call = request.json["price_emergency_call"]
price_out_of_oil = request.json["price_out_of_oil"]
price_prime = request.json["price_prime"]
price_service = Pricing_Service_General(
price_service_hour=price_service_hour,
price_out_of_oil=price_out_of_oil,
price_emergency_service_hour=price_emergency_service_hourly_rate,
price_prime=price_prime,
price_emergency_call=price_emergency_call,
date=now,
)
db.session.add(price_service)
db.session.commit()
return jsonify({
"ok": True,
'price': price_service.id,
}), 200
@admin.route("/service/get", methods=["GET"])
def get_service_price():
"""
gets service prices
"""
get_service_prices = (db.session
.query(Pricing_Service_General)
.order_by(Pricing_Service_General.date.desc())
.first())
price_schema = Pricing_Service_General_schema(many=False)
return jsonify(price_schema.dump(get_service_prices))
@admin.route("/oil/get", methods=["GET"]) @admin.route("/oil/get", methods=["GET"])
def get_oil_price(): def get_oil_price():
@@ -90,3 +60,28 @@ def get_oil_price():
.first()) .first())
price_schema = Pricing_Oil_Oil_schema(many=False) price_schema = Pricing_Oil_Oil_schema(many=False)
return jsonify(price_schema.dump(get_oil_prices)) return jsonify(price_schema.dump(get_oil_prices))
@admin.route("/company/<int:company_id>", methods=["GET"])
def get_company(company_id):
get_data_company = (db.session
.query(Admin_Company)
.first())
company_schema = Admin_Company_schema(many=False)
return jsonify(company_schema.dump(get_data_company))
@admin.route("/voip_routing", methods=["GET"])
def get_voip_routing():
"""
Gets the current VOIP routing (latest Call record's current_phone)
"""
latest_call = (db.session
.query(Call)
.order_by(Call.created_at.desc())
.first())
if latest_call:
return jsonify({"current_phone": latest_call.current_phone})
else:
return jsonify({"current_phone": None}), 404

0
app/auth/__init__.py Normal file → Executable file
View File

141
app/auth/views.py Normal file → Executable file
View File

@@ -1,35 +1,34 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user, logout_user, login_user, login_required from flask_login import current_user, logout_user, login_required
from app.auth import auth from app.auth import auth
from app import db, bcrypt from app import db, bcrypt
from datetime import datetime from datetime import datetime
from uuid import uuid4 from uuid import uuid4
from app.classes.auth import Auth_User from app.classes.auth import Auth_User
from app.classes.employee import Employee_Employee
import re
@auth.route("/whoami", methods=["GET"]) @auth.route("/whoami", methods=["GET"])
def check_session(): def check_session():
""" """
Checks auth token to ensure user is authenticated Checks auth token and returns user and associated employee data.
""" """
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({"ok": False, "error": "Authorization header missing"}), 401
api_key = request.headers.get('Authorization') # --- THIS IS THE FIX ---
if not api_key: # Use a case-insensitive regular expression to remove "bearer "
return jsonify({"error": "True"}), 200 # This handles "Bearer ", "bearer ", "BEARER ", etc.
else: api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
api_key = api_key.replace('bearer ', '', 1)
api_key = api_key.replace('"', '') user = db.session.query(Auth_User).filter(Auth_User.api_key == api_key).first()
user_exists = (db.session
.query(Auth_User) if not user:
.filter(Auth_User.api_key == api_key) print("no user found with that api key")
.first()) return jsonify({"ok": False, "error": "Invalid token"}), 401
if not user_exists:
return jsonify({"error": True}), 200 # Now, build the complete response with both user and employee data.
else:
user = db.session\
.query(Auth_User)\
.filter(Auth_User.api_key == api_key)\
.first()
return jsonify({ return jsonify({
"ok": True, "ok": True,
'user': { 'user': {
@@ -40,7 +39,6 @@ def check_session():
'token': user.api_key, 'token': user.api_key,
'confirmed': user.confirmed 'confirmed': user.confirmed
}, },
'token': user.api_key
}), 200 }), 200
@@ -76,45 +74,30 @@ def logout():
@auth.route("/login", methods=["POST"]) @auth.route("/login", methods=["POST"])
def login(): def login():
"""
Main post function to a user
"""
username = request.json["username"] username = request.json["username"]
password = request.json["password"] password = request.json["password"]
user = db.session\ user = db.session.query(Auth_User).filter_by(username=username).first()
.query(Auth_User)\
.filter_by(username=username)\ # Important checks!
.first() is not None
if not user: if not user:
return jsonify({"error": True}), 200 return jsonify({"error": "User not found"}), 401 # Use a more descriptive error and status code
user = db.session\
.query(Auth_User)\
.filter_by(username=username)\
.first()
if not bcrypt.check_password_hash(user.password_hash, password): if not bcrypt.check_password_hash(user.password_hash, password):
return jsonify({"error": "Invalid password"}), 401 # Use a more descriptive error and status code
current_fails = int(user.fails) # Check if user is active
new_fails = current_fails + 1 if user.active != 1:
user.fails = new_fails return jsonify({"error": "Please contact a manager. Login rejected"}), 401
db.session.add(user)
db.session.commit()
return jsonify({"error": True}), 200
user.locked = 0
user.fails = 0
db.session.add(user)
db.session.commit()
# If login is successful, return the correct structure
return jsonify({ return jsonify({
"ok": True, "ok": True,
'user': {'user_id': user.uuid, 'user': {
'user_name': user.username,
'user_id': user.id, 'user_id': user.id,
'user_email': user.email, 'user_email': user.email,
'admin_role': user.admin_role, 'admin_role': user.admin_role,
'token': user.api_key
}, },
'token': user.api_key 'token': user.api_key
}), 200 }), 200
@@ -189,17 +172,21 @@ def register_user():
@auth.route('/change-password', methods=['POST']) @auth.route('/change-password', methods=['POST'])
@login_required
def change_password(): def change_password():
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({"error": "Authorization header missing"}), 401
api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
user = db.session.query(Auth_User).filter(Auth_User.api_key == api_key).first()
if not user:
return jsonify({"error": "Invalid token"}), 401
new_password = request.json["new_password"] new_password = request.json["new_password"]
new_password_confirm = request.json["password_confirm"] new_password_confirm = request.json["password_confirm"]
user = db.session\
.query(Auth_User) \
.filter(Auth_User.id == current_user.id) \
.first()
if str(new_password) != str(new_password_confirm): if str(new_password) != str(new_password_confirm):
return jsonify({"error": "Error: Incorrect Passwords"}), 200 return jsonify({"error": "Error: Incorrect Passwords"}), 200
@@ -211,5 +198,49 @@ def change_password():
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return jsonify({"ok": "success"}), 200 return jsonify({"ok": True}), 200
@auth.route('/admin-change-password', methods=['POST'])
def admin_change_password():
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({"error": "Authorization header missing"}), 401
api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
user = db.session.query(Auth_User).filter(Auth_User.api_key == api_key).first()
if not user:
return jsonify({"error": "Invalid token"}), 401
if user.admin_role != 0:
return jsonify({"error": "Admin access required"}), 403
employee_id = request.json.get("employee_id")
new_password = request.json.get("new_password")
new_password_confirm = request.json.get("password_confirm")
if not employee_id or not new_password or not new_password_confirm:
return jsonify({"error": "Missing required fields"}), 400
if str(new_password) != str(new_password_confirm):
return jsonify({"error": "Passwords do not match"}), 400
from app.classes.employee import Employee_Employee
employee = db.session.query(Employee_Employee).filter(Employee_Employee.id == employee_id).first()
if not employee:
return jsonify({"error": "Employee not found"}), 404
target_user = db.session.query(Auth_User).filter(Auth_User.id == employee.user_id).first()
if not target_user:
return jsonify({"error": "User not found"}), 404
hashed_password = bcrypt.generate_password_hash(new_password).decode('utf-8')
target_user.password_hash = hashed_password
target_user.passwordpinallowed = 0
db.session.add(target_user)
db.session.commit()
return jsonify({"ok": True}), 200

0
app/classes/__init__.py Normal file → Executable file
View File

9
app/classes/admin.py Normal file → Executable file
View File

@@ -4,7 +4,6 @@ from datetime import datetime
class Admin_Company(db.Model): class Admin_Company(db.Model):
__tablename__ = 'admin_company' __tablename__ = 'admin_company'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -24,3 +23,11 @@ class Admin_Company_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Admin_Company model = Admin_Company
# --- ADD THIS ENTIRE NEW MODEL ---
class Call(db.Model):
__tablename__ = "call_call"
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, index=True)
current_phone = db.Column(db.String(500))
created_at = db.Column(db.DateTime, default=datetime.utcnow)

4
app/classes/auth.py Normal file → Executable file
View File

@@ -10,7 +10,6 @@ def get_uuid():
class Auth_User(UserMixin, db.Model): class Auth_User(UserMixin, db.Model):
__tablename__ = 'auth_users' __tablename__ = 'auth_users'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -27,6 +26,7 @@ class Auth_User(UserMixin, db.Model):
admin = db.Column(db.INTEGER) admin = db.Column(db.INTEGER)
admin_role = db.Column(db.INTEGER) admin_role = db.Column(db.INTEGER)
confirmed = db.Column(db.INTEGER) confirmed = db.Column(db.INTEGER)
active = db.Column(db.INTEGER, default=1)
def __init__(self, def __init__(self,
username, username,
@@ -38,6 +38,7 @@ class Auth_User(UserMixin, db.Model):
admin, admin,
admin_role, admin_role,
confirmed, confirmed,
active=1,
): ):
self.username = username self.username = username
self.api_key = api_key self.api_key = api_key
@@ -48,6 +49,7 @@ class Auth_User(UserMixin, db.Model):
self.admin = admin self.admin = admin
self.admin_role = admin_role self.admin_role = admin_role
self.confirmed = confirmed self.confirmed = confirmed
self.active = active
def is_authenticated(self): def is_authenticated(self):
return True return True

82
app/classes/auto.py Normal file → Executable file
View File

@@ -2,21 +2,31 @@
from app import db, ma from app import db, ma
from datetime import datetime from datetime import datetime
class Auto_Update(db.Model):
__tablename__ = 'auto_update'
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
last_updated = db.Column(db.DATE())
class Auto_Temp(db.Model): class Auto_Temp(db.Model):
__tablename__ = 'auto_temp' __tablename__ = 'auto_temp'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
primary_key=True, primary_key=True,
autoincrement=True, autoincrement=True,
unique=False) unique=False)
todays_date = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) todays_date = db.Column(db.DATE())
temp = db.Column(db.DECIMAL(50, 2)) temp = db.Column(db.DECIMAL(6, 2))
temp_max = db.Column(db.DECIMAL(50, 2)) temp_max = db.Column(db.DECIMAL(6, 2))
temp_min = db.Column(db.DECIMAL(50, 2)) temp_min = db.Column(db.DECIMAL(6, 2))
temp_avg = db.Column(db.DECIMAL(50, 2)) temp_avg = db.Column(db.DECIMAL(6, 2))
degree_day = db.Column(db.INTEGER()) degree_day = db.Column(db.INTEGER())
class Auto_Temp_schema(ma.SQLAlchemyAutoSchema): class Auto_Temp_schema(ma.SQLAlchemyAutoSchema):
@@ -27,7 +37,6 @@ class Auto_Temp_schema(ma.SQLAlchemyAutoSchema):
class Auto_Delivery(db.Model): class Auto_Delivery(db.Model):
__tablename__ = 'auto_delivery' __tablename__ = 'auto_delivery'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -35,15 +44,62 @@ class Auto_Delivery(db.Model):
autoincrement=True, autoincrement=True,
unique=False) unique=False)
customer_id = db.Column(db.INTEGER()) customer_id = db.Column(db.INTEGER())
customer_full_name = db.Column(db.DECIMAL(50, 2)) account_number = db.Column(db.VARCHAR(25))
last_fill = db.Column(db.TIMESTAMP()) customer_town = db.Column(db.VARCHAR(140))
last_updated = db.Column(db.TIMESTAMP()) customer_state = db.Column(db.Integer)
estimated_gallons_left = db.Column(db.INTEGER()) customer_address = db.Column(db.VARCHAR(1000))
estimated_gallons_left_prev_day = db.Column(db.INTEGER()) customer_zip = db.Column(db.VARCHAR(25))
customer_full_name = db.Column(db.VARCHAR(250))
last_fill = db.Column(db.DATE())
days_since_last_fill = db.Column(db.Integer)
last_updated = db.Column(db.DATE())
estimated_gallons_left = db.Column(db.DECIMAL(6, 2))
estimated_gallons_left_prev_day = db.Column(db.DECIMAL(6, 2))
tank_height = db.Column(db.VARCHAR(25)) tank_height = db.Column(db.VARCHAR(25))
tank_size = db.Column(db.VARCHAR(25)) tank_size = db.Column(db.VARCHAR(25))
k_factor = db.Column(db.DECIMAL(50, 2)) house_factor = db.Column(db.DECIMAL(5, 2))
hot_water_summer = db.Column(db.Integer)
#0 = waiting
#1 = waiting for delivery
auto_status = db.Column(db.INTEGER())
open_ticket_id = db.Column(db.Integer)
class Auto_Delivery_schema(ma.SQLAlchemyAutoSchema): class Auto_Delivery_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Auto_Delivery model = Auto_Delivery
class Tickets_Auto_Delivery(db.Model):
__tablename__ = 'auto_tickets'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
customer_id = db.Column(db.INTEGER())
account_number = db.Column(db.VARCHAR(25))
customer_town = db.Column(db.VARCHAR(140))
customer_state = db.Column(db.Integer)
customer_address = db.Column(db.VARCHAR(1000))
customer_zip = db.Column(db.VARCHAR(25))
customer_full_name = db.Column(db.VARCHAR(250))
fill_date = db.Column(db.DATE())
oil_prices_id = db.Column(db.INTEGER())
gallons_delivered = db.Column(db.DECIMAL(6, 2))
price_per_gallon = db.Column(db.DECIMAL(6, 2))
total_amount_customer = db.Column(db.DECIMAL(6, 2))
payment_type = db.Column(db.INTEGER, nullable=True)
payment_card_id = db.Column(db.INTEGER, nullable=True)
payment_status = db.Column(db.INTEGER, nullable=True)
class Tickets_Auto_Delivery_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Tickets_Auto_Delivery

9
app/classes/cards.py Normal file → Executable file
View File

@@ -5,7 +5,6 @@ from datetime import datetime
class Card_Card(db.Model): class Card_Card(db.Model):
__tablename__ = 'card_card' __tablename__ = 'card_card'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -17,12 +16,14 @@ class Card_Card(db.Model):
card_number = db.Column(db.VARCHAR(50)) card_number = db.Column(db.VARCHAR(50))
last_four_digits = db.Column(db.INTEGER()) last_four_digits = db.Column(db.INTEGER())
name_on_card = db.Column(db.VARCHAR(500)) name_on_card = db.Column(db.VARCHAR(500))
expiration_month = db.Column(db.INTEGER()) expiration_month = db.Column(db.VARCHAR(20))
expiration_year = db.Column(db.INTEGER()) expiration_year = db.Column(db.VARCHAR(20))
type_of_card = db.Column(db.VARCHAR(500)) type_of_card = db.Column(db.VARCHAR(500))
security_number = db.Column(db.INTEGER()) security_number = db.Column(db.VARCHAR(10))
accepted_or_declined = db.Column(db.INTEGER()) accepted_or_declined = db.Column(db.INTEGER())
main_card = db.Column(db.BOOLEAN()) main_card = db.Column(db.BOOLEAN())
zip_code = db.Column(db.VARCHAR(20))
auth_net_payment_profile_id = db.Column(db.String, nullable=True)
class Card_Card_schema(ma.SQLAlchemyAutoSchema): class Card_Card_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:

5
app/classes/company.py Normal file → Executable file
View File

@@ -4,7 +4,6 @@ from app import db, ma
class Company_Company(db.Model): class Company_Company(db.Model):
__tablename__ = 'company_company' __tablename__ = 'company_company'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -15,8 +14,8 @@ class Company_Company(db.Model):
company_dba_name = db.Column(db.VARCHAR(250)) company_dba_name = db.Column(db.VARCHAR(250))
company_llc_name = db.Column(db.VARCHAR(250)) company_llc_name = db.Column(db.VARCHAR(250))
company_town = db.Column(db.VARCHAR(140)) company_town = db.Column(db.VARCHAR(140))
company_state = db.Column(db.VARCHAR(140)) company_state = db.Column(db.INTEGER)
company_zip = db.Column(db.INTEGER) company_zip = db.Column(db.VARCHAR(25))
class Company_Company_schema(ma.SQLAlchemyAutoSchema): class Company_Company_schema(ma.SQLAlchemyAutoSchema):

66
app/classes/customer.py Normal file → Executable file
View File

@@ -1,31 +1,34 @@
#
from app import db, ma
from app import db, ma, login_manager
from datetime import datetime
class Customer_Customer(db.Model): class Customer_Customer(db.Model):
__tablename__ = 'customer_customer' __tablename__ = 'customer_customer'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
primary_key=True, primary_key=True,
autoincrement=True, autoincrement=True,
unique=False) unique=False)
auth_net_profile_id = db.Column(db.String, unique=True, index=True, nullable=True)
account_number = db.Column(db.VARCHAR(25)) account_number = db.Column(db.VARCHAR(25))
customer_last_name = db.Column(db.VARCHAR(250)) customer_last_name = db.Column(db.VARCHAR(250))
customer_first_name = db.Column(db.VARCHAR(250)) customer_first_name = db.Column(db.VARCHAR(250))
customer_town = db.Column(db.VARCHAR(140)) customer_town = db.Column(db.VARCHAR(140))
customer_state = db.Column(db.INTEGER) customer_state = db.Column(db.INTEGER)
customer_zip = db.Column(db.VARCHAR(25)) customer_zip = db.Column(db.VARCHAR(25))
customer_first_call = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) customer_first_call = db.Column(db.TIMESTAMP())
customer_email = db.Column(db.VARCHAR(500)) customer_email = db.Column(db.VARCHAR(500))
customer_automatic = db.Column(db.INTEGER) customer_automatic = db.Column(db.INTEGER)
customer_phone_number = db.Column(db.VARCHAR(25)) customer_phone_number = db.Column(db.VARCHAR(25))
customer_home_type = db.Column(db.INTEGER) customer_home_type = db.Column(db.INTEGER)
customer_apt = db.Column(db.VARCHAR(140)) customer_apt = db.Column(db.VARCHAR(140))
customer_address = db.Column(db.VARCHAR(1000)) customer_address = db.Column(db.VARCHAR(1000))
company_id = db.Column(db.INTEGER)
customer_latitude = db.Column(db.VARCHAR(250))
customer_longitude = db.Column(db.VARCHAR(250))
correct_address = db.Column(db.BOOLEAN)
class Customer_Customer_schema(ma.SQLAlchemyAutoSchema): class Customer_Customer_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
@@ -34,7 +37,6 @@ class Customer_Customer_schema(ma.SQLAlchemyAutoSchema):
class Customer_Property(db.Model): class Customer_Property(db.Model):
__tablename__ = 'customer_property' __tablename__ = 'customer_property'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -60,7 +62,6 @@ class Customer_Property_schema(ma.SQLAlchemyAutoSchema):
class Customer_Payment_Credit(db.Model): class Customer_Payment_Credit(db.Model):
__tablename__ = 'customer_payment' __tablename__ = 'customer_payment'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -74,9 +75,58 @@ class Customer_Payment_Credit(db.Model):
credit_card_name = db.Column(db.VARCHAR(240)) credit_card_name = db.Column(db.VARCHAR(240))
credit_card_number = db.Column(db.VARCHAR(140)) credit_card_number = db.Column(db.VARCHAR(140))
credit_card_security = db.Column(db.VARCHAR(140)) credit_card_security = db.Column(db.VARCHAR(140))
customer_card_expiration = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) customer_card_expiration = db.Column(db.TIMESTAMP())
class Customer_Payment_Credit_schema(ma.SQLAlchemyAutoSchema): class Customer_Payment_Credit_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Customer_Payment_Credit model = Customer_Payment_Credit
class Customer_Description(db.Model):
__tablename__ = 'customer_description'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
customer_id = db.Column(db.INTEGER)
account_number = db.Column(db.VARCHAR(25))
company_id = db.Column(db.INTEGER)
fill_location = db.Column(db.INTEGER)
description = db.Column(db.VARCHAR(2000))
class Customer_Description_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Customer_Description
class Customer_Property_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Customer_Property
class Customer_Tank_Inspection(db.Model):
__tablename__ = 'customer_tank'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
customer_id = db.Column(db.INTEGER)
last_tank_inspection = db.Column(db.DATE())
tank_status = db.Column(db.BOOLEAN)
outside_or_inside = db.Column(db.BOOLEAN)
tank_size = db.Column(db.INTEGER)
class Customer_Tank_Inspection_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Customer_Tank_Inspection

View File

@@ -0,0 +1,25 @@
#
from app import db, ma
class Customer_Customer_Social(db.Model):
__tablename__ = 'customer_customer_social'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
created = db.Column(db.DATE())
customer_id = db.Column(db.INTEGER)
poster_employee_id = db.Column(db.INTEGER)
comment = db.Column(db.VARCHAR(1000))
class Customer_Customer_Social_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Customer_Customer_Social

70
app/classes/delivery.py Normal file → Executable file
View File

@@ -1,10 +1,10 @@
from app import db, ma from app import db, ma
from datetime import datetime from datetime import datetime
class Delivery_Delivery(db.Model): class Delivery_Delivery(db.Model):
__tablename__ = 'delivery_delivery' __tablename__ = 'delivery_delivery'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -23,81 +23,73 @@ class Delivery_Delivery(db.Model):
# if customer asked for a fill # if customer asked for a fill
customer_asked_for_fill = db.Column(db.INTEGER) customer_asked_for_fill = db.Column(db.INTEGER)
# integer value if delivered, waiting, cancelled etc # integer value if delivered, waiting, cancelled etc
gallons_delivered =db.Column(db.DECIMAL(50, 2)) gallons_delivered =db.Column(db.DECIMAL(6, 2))
# if customer has a full tank # if customer has a full tank
customer_filled = db.Column(db.INTEGER) customer_filled = db.Column(db.INTEGER)
# integer value if delivered, waiting, cancelled etc # integer value if delivered, waiting, cancelled etc
# waiting = 0 # waiting = 0
# delivered = 1 # cancelled = 1
# out for delivery = 2 # out for delivery = 2
# cancelled = 3 # tommorrow = 3
# partial delivery = 4
# issue = 5 # issue = 5
# finalized = 10 # finalized = 10
delivery_status = db.Column(db.INTEGER) delivery_status = db.Column(db.INTEGER)
# when the call to order took place # when the call to order took place
when_ordered = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) when_ordered = db.Column(db.DATE(), default=None)
# when the delivery date happened # when the delivery date happened
when_delivered = db.Column(db.TIMESTAMP(), default=None) when_delivered = db.Column(db.DATE(), default=None)
# when the delivery is expected ie what day # when the delivery is expected ie what day
expected_delivery_date = db.Column(db.DATE(), default=None) expected_delivery_date = db.Column(db.DATE(), default=None)
# automatic delivery # automatic delivery
automatic = db.Column(db.INTEGER) automatic = db.Column(db.INTEGER)
automatic_id = db.Column(db.INTEGER)
# OIL info and id from table # OIL info and id from table
oil_id = db.Column(db.INTEGER) oil_id = db.Column(db.INTEGER)
supplier_price = db.Column(db.DECIMAL(50, 2)) supplier_price = db.Column(db.DECIMAL(6, 2))
customer_price = db.Column(db.DECIMAL(50, 2)) customer_price = db.Column(db.DECIMAL(6, 2))
# weather # weather
customer_temperature = db.Column(db.DECIMAL(50, 2)) customer_temperature = db.Column(db.DECIMAL(6, 2))
# services
dispatcher_notes = db.Column(db.TEXT()) dispatcher_notes = db.Column(db.TEXT())
prime = db.Column(db.INTEGER) prime = db.Column(db.INTEGER)
same_day = db.Column(db.INTEGER) same_day = db.Column(db.INTEGER)
emergency = db.Column(db.INTEGER)
# cash = 0 # cash = 0
# credit = 1 # credit = 1
# credit/cash = 2
# check = 3
# other = 4
payment_type = db.Column(db.INTEGER) payment_type = db.Column(db.INTEGER)
payment_card_id = db.Column(db.INTEGER) payment_card_id = db.Column(db.INTEGER)
cash_recieved = db.Column(db.DECIMAL(6, 2))
driver_employee_id = db.Column(db.VARCHAR(140)) driver_employee_id = db.Column(db.INTEGER)
driver_first_name = db.Column(db.VARCHAR(140)) driver_first_name = db.Column(db.VARCHAR(140))
driver_last_name = db.Column(db.VARCHAR(140)) driver_last_name = db.Column(db.VARCHAR(140))
pre_charge_amount = db.Column(db.DECIMAL(50, 2)) pre_charge_amount = db.Column(db.DECIMAL(6, 2))
total_price = db.Column(db.DECIMAL(50, 2)) total_price = db.Column(db.DECIMAL(6, 2))
final_price = db.Column(db.DECIMAL(50, 2)) final_price = db.Column(db.DECIMAL(6, 2))
check_number = db.Column(db.VARCHAR(20))
promo_id = db.Column(db.INTEGER)
promo_money_discount = db.Column(db.DECIMAL(6, 2))
class Delivery_Delivery_schema(ma.SQLAlchemyAutoSchema): class Delivery_Delivery_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Delivery_Delivery model = Delivery_Delivery
class Delivery_Payment(db.Model):
__tablename__ = 'delivery_payment'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
delivery_id = db.Column(db.INTEGER)
time_added = db.Column(db.TIMESTAMP(), default=datetime.utcnow())
total_amount_oil = db.Column(db.DECIMAL(50, 2))
total_amount_emergency = db.Column(db.DECIMAL(50, 2))
total_amount_prime = db.Column(db.DECIMAL(50, 2))
total_amount_fee = db.Column(db.DECIMAL(50, 2))
total_amount = db.Column(db.DECIMAL(50, 2))
class Delivery_Payment_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Delivery_Payment
class Delivery_Notes_Driver(db.Model): class Delivery_Notes_Driver(db.Model):
__tablename__ = 'delivery_notes' __tablename__ = 'delivery_notes'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,

5
app/classes/employee.py Normal file → Executable file
View File

@@ -4,7 +4,6 @@ from datetime import datetime
class Employee_Employee(db.Model): class Employee_Employee(db.Model):
__tablename__ = 'employee_employee' __tablename__ = 'employee_employee'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -34,7 +33,6 @@ class Employee_Employee_schema(ma.SQLAlchemyAutoSchema):
class Employee_Credentials(db.Model): class Employee_Credentials(db.Model):
__tablename__ = 'employee_credentials' __tablename__ = 'employee_credentials'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -54,7 +52,6 @@ class Employee_Credentials_schema(ma.SQLAlchemyAutoSchema):
class Employee_Vacation(db.Model): class Employee_Vacation(db.Model):
__tablename__ = 'employee_vacation' __tablename__ = 'employee_vacation'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -64,7 +61,7 @@ class Employee_Vacation(db.Model):
employee_id = db.Column(db.INTEGER) employee_id = db.Column(db.INTEGER)
employee_name = db.Column(db.VARCHAR(140)) employee_name = db.Column(db.VARCHAR(140))
employee_total_days_off = db.Column(db.INTEGER) employee_total_days_off = db.Column(db.INTEGER)
employee_days_off_multiplier = db.Column(db.DECIMAL(50, 2)) employee_days_off_multiplier = db.Column(db.DECIMAL(6, 2))
employee_days_off_per_year = db.Column(db.INTEGER) employee_days_off_per_year = db.Column(db.INTEGER)

46
app/classes/money.py Normal file
View File

@@ -0,0 +1,46 @@
from app import db, ma
class Money_delivery(db.Model):
__tablename__ = 'money_delivery'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
delivery_id = db.Column(db.Integer)
time_added = db.Column(db.DATE())
gallons_delivered = db.Column(db.DECIMAL(6, 2))
supplier_price = db.Column(db.DECIMAL(6, 2))
customer_price = db.Column(db.DECIMAL(6, 2))
total_amount_oil = db.Column(db.DECIMAL(6, 2))
total_amount_prime = db.Column(db.DECIMAL(6, 2))
total_amount_same_day = db.Column(db.DECIMAL(6, 2))
total_amount_fee = db.Column(db.DECIMAL(6, 2))
total_amount = db.Column(db.DECIMAL(6, 2))
total_discount_amount = db.Column(db.DECIMAL(6, 2))
total_discount_total = db.Column(db.DECIMAL(6, 2))
taxes_paid = db.Column(db.DECIMAL(6, 2))
total_profit = db.Column(db.DECIMAL(6, 2))
total_profit_oil = db.Column(db.DECIMAL(6, 2))
auto = db.Column(db.BOOLEAN)
class Money_delivery_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Money_delivery
class Pricing_Taxes(db.Model):
__tablename__ = 'pricing_taxes'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
state_id = db.Column(db.Integer)
taxes_oil = db.Column(db.DECIMAL(6, 2))
taxes_other = db.Column(db.DECIMAL(6, 2))

32
app/classes/pricing.py Normal file → Executable file
View File

@@ -2,34 +2,11 @@ from app import db, ma, login_manager
from datetime import datetime from datetime import datetime
class Pricing_Service_General(db.Model):
__tablename__ = 'pricing_service_general'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
price_service_hour = db.Column(db.DECIMAL(50, 2))
price_emergency_service_hour = db.Column(db.DECIMAL(50, 2))
price_emergency_call = db.Column(db.DECIMAL(50, 2))
price_out_of_oil = db.Column(db.DECIMAL(50, 2))
price_prime = db.Column(db.DECIMAL(50, 2))
price_same_day = db.Column(db.DECIMAL(50, 2))
price_cleaning = db.Column(db.DECIMAL(50, 2))
date = db.Column(db.TIMESTAMP(), default=datetime.utcnow())
class Pricing_Service_General_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Pricing_Service_General
class Pricing_Oil_Oil(db.Model): class Pricing_Oil_Oil(db.Model):
__tablename__ = 'pricing_oil_oil' __tablename__ = 'pricing_oil_oil'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -37,9 +14,12 @@ class Pricing_Oil_Oil(db.Model):
autoincrement=True, autoincrement=True,
unique=False) unique=False)
price_from_supplier = db.Column(db.DECIMAL(50, 2)) price_from_supplier = db.Column(db.DECIMAL(6, 2))
price_for_customer = db.Column(db.DECIMAL(50, 2)) price_for_customer = db.Column(db.DECIMAL(6, 2))
price_for_employee = db.Column(db.DECIMAL(50, 2)) price_for_employee = db.Column(db.DECIMAL(6, 2))
price_same_day = db.Column(db.DECIMAL(6, 2))
price_prime = db.Column(db.DECIMAL(6, 2))
price_emergency = db.Column(db.DECIMAL(6, 2))
date = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) date = db.Column(db.TIMESTAMP(), default=datetime.utcnow())

26
app/classes/promo.py Normal file
View File

@@ -0,0 +1,26 @@
from app import db, ma
class Promo_Promo(db.Model):
__tablename__ = 'promo_Promo'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
active = db.Column(db.BOOLEAN())
name_of_promotion = db.Column(db.VARCHAR(1000))
money_off_delivery = db.Column(db.DECIMAL(6, 2))
description = db.Column(db.VARCHAR(1000))
text_on_ticket = db.Column(db.VARCHAR(100))
date_created = db.Column(db.DATE())
class Promo_Promo_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Promo_Promo

23
app/classes/query.py Normal file → Executable file
View File

@@ -4,7 +4,6 @@ from app import db, ma
class Query_EmployeeTypeList(db.Model): class Query_EmployeeTypeList(db.Model):
__tablename__ = 'query_employee_type_list' __tablename__ = 'query_employee_type_list'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER) value = db.Column(db.INTEGER)
@@ -20,13 +19,14 @@ class Query_EmployeeTypeList_Schema(ma.SQLAlchemyAutoSchema):
class Query_StateList(db.Model): class Query_StateList(db.Model):
__tablename__ = 'query_state_list' __tablename__ = 'query_state_list'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER) value = db.Column(db.INTEGER)
text = db.Column(db.VARCHAR(140)) text = db.Column(db.VARCHAR(140))
class Query_StateList_Schema(ma.SQLAlchemyAutoSchema): class Query_StateList_Schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Query_StateList model = Query_StateList
@@ -35,9 +35,24 @@ class Query_StateList_Schema(ma.SQLAlchemyAutoSchema):
value = ma.auto_field() value = ma.auto_field()
class Query_TownList(db.Model):
__tablename__ = 'query_town_list'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER)
text = db.Column(db.VARCHAR(140))
class Query_TownList_Schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Query_TownList
id = ma.auto_field()
text = ma.auto_field()
value = ma.auto_field()
class Query_CustomerTypeList(db.Model): class Query_CustomerTypeList(db.Model):
__tablename__ = 'query_customer_type_list' __tablename__ = 'query_customer_type_list'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER) value = db.Column(db.INTEGER)
@@ -55,7 +70,6 @@ class Query_CustomerTypeList_Schema(ma.SQLAlchemyAutoSchema):
class Query_ServiceTypeList(db.Model): class Query_ServiceTypeList(db.Model):
__tablename__ = 'query_service_type_list' __tablename__ = 'query_service_type_list'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER) value = db.Column(db.INTEGER)
@@ -73,7 +87,6 @@ class Query_ServiceTypeList_Schema(ma.SQLAlchemyAutoSchema):
class Query_DeliveryStatusList(db.Model): class Query_DeliveryStatusList(db.Model):
__tablename__ = 'query_delivery_type_list' __tablename__ = 'query_delivery_type_list'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True, autoincrement=True) id = db.Column(db.Integer, primary_key=True, autoincrement=True)
value = db.Column(db.INTEGER) value = db.Column(db.INTEGER)

View File

@@ -3,9 +3,8 @@ from app import db, ma
from datetime import datetime from datetime import datetime
class Service_Call(db.Model): class Service_Service(db.Model):
__tablename__ = 'service_call' __tablename__ = 'service_service'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -14,52 +13,38 @@ class Service_Call(db.Model):
unique=False) unique=False)
customer_id = db.Column(db.INTEGER) customer_id = db.Column(db.INTEGER)
customer_last_name = db.Column(db.VARCHAR(250)) customer_name = db.Column(db.VARCHAR(1000))
customer_first_name = db.Column(db.VARCHAR(250))
customer_town = db.Column(db.VARCHAR(140))
customer_state = db.Column(db.INTEGER)
customer_zip = db.Column(db.VARCHAR(25))
customer_apt = db.Column(db.VARCHAR(140))
customer_address = db.Column(db.VARCHAR(1000)) customer_address = db.Column(db.VARCHAR(1000))
customer_town = db.Column(db.VARCHAR(140))
customer_state = db.Column(db.VARCHAR(140))
customer_zip = db.Column(db.VARCHAR(10))
# tune-up = 0
# no heat = 1
# fix = 2
# tank = 3
# other = 4
type_service_call = db.Column(db.INTEGER)
when_ordered = db.Column(db.DATETIME())
scheduled_date = db.Column(db.DATETIME())
description = db.Column(db.TEXT())
#0 = closed service_cost = db.Column(db.Numeric(10, 2), nullable=False)
#1 = open
status = db.Column(db.INTEGER)
# 0 = unknown payment_type = db.Column(db.INTEGER, nullable=True)
# 1 = cleaning / tuneup payment_card_id = db.Column(db.INTEGER, nullable=True)
# 2 = problem payment_status = db.Column(db.INTEGER, nullable=True)
# 3 = install
# 3 = callback
service_type = db.Column(db.INTEGER)
# when the call to service took place
when_called = db.Column(db.DATE(), default=datetime.utcnow())
# what day the call will take place
scheduled_date = db.Column(db.DATE(), default=datetime.utcnow())
# what day the call will take place
scheduled_time = db.Column(db.INTEGER)
# when the service took place
when_serviced = db.Column(db.DATE(), default=datetime.utcnow())
# is the call finished or not
# 0 = open
#1 = finished
completed = db.Column(db.INTEGER)
tech_id = db.Column(db.INTEGER)
tech_first_name = db.Column(db.VARCHAR(300))
tech_last_name = db.Column(db.VARCHAR(300))
payment_type = db.Column(db.INTEGER)
payment_card_id = db.Column(db.INTEGER)
class Service_Call_schema(ma.SQLAlchemyAutoSchema): class Service_Service_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Service_Call model = Service_Service
scheduled_date = ma.DateTime(format='%Y-%m-%dT%H:%M:%S')
when_ordered = ma.DateTime(format='%Y-%m-%dT%H:%M:%S')
class Service_Call_Money(db.Model):
__tablename__ = 'service_money' class Service_Parts(db.Model):
__bind_key__ = 'eamco' __tablename__ = 'service_parts'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -67,20 +52,24 @@ class Service_Call_Money(db.Model):
autoincrement=True, autoincrement=True,
unique=False) unique=False)
service_call_id = db.Column(db.INTEGER) customer_id = db.Column(db.INTEGER)
hours = db.Column(db.DECIMAL(50, 2)) oil_filter = db.Column(db.VARCHAR(100))
cost_per_hour = db.Column(db.DECIMAL(50, 2)) oil_filter_2 = db.Column(db.VARCHAR(100))
parts_cost = db.Column(db.DECIMAL(50, 2)) oil_nozzle = db.Column(db.VARCHAR(10))
oil_nozzle_2 = db.Column(db.VARCHAR(10))
hot_water_tank = db.Column(db.INTEGER)
class Service_Call_Money_schema(ma.SQLAlchemyAutoSchema): class Service_Parts_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Service_Call_Money model = Service_Parts
scheduled_date = ma.DateTime(format='%Y-%m-%dT%H:%M:%S')
when_ordered = ma.DateTime(format='%Y-%m-%dT%H:%M:%S')
class Service_Call_Notes_Dispatcher(db.Model):
__tablename__ = 'service_notes_dispatcher' class Service_Plans(db.Model):
__bind_key__ = 'eamco' __tablename__ = 'service_plans'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -88,39 +77,13 @@ class Service_Call_Notes_Dispatcher(db.Model):
autoincrement=True, autoincrement=True,
unique=False) unique=False)
service_call_id = db.Column(db.INTEGER) customer_id = db.Column(db.INTEGER)
dispatcher_notes = db.Column(db.TEXT) contract_plan = db.Column(db.INTEGER, default=0) # 0=no contract, 1=standard, 2=premium
dispatcher_subject = db.Column(db.VARCHAR(1024)) contract_years = db.Column(db.INTEGER, default=1)
time_added = db.Column(db.TIMESTAMP(), default=datetime.utcnow()) contract_start_date = db.Column(db.DATE())
dispatcher_id = db.Column(db.INTEGER)
dispatcher_name = db.Column(db.VARCHAR(140))
class Service_Call_Notes_Dispatcher_schema(ma.SQLAlchemyAutoSchema): class Service_Plans_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Service_Call_Notes_Dispatcher model = Service_Plans
contract_start_date = ma.DateTime(format='%Y-%m-%d')
class Service_Call_Notes_Technician(db.Model):
__tablename__ = 'service_notes_technician'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
service_call_id = db.Column(db.INTEGER)
technician_comments = db.Column(db.TEXT)
time_added = db.Column(db.TIMESTAMP(), default=datetime.utcnow())
technician_id = db.Column(db.INTEGER)
technician_name = db.Column(db.VARCHAR(140))
class Service_Call_Notes_Technician_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Service_Call_Notes_Technician

View File

@@ -0,0 +1,20 @@
from app import db, ma
from datetime import datetime
class Stats_Company(db.Model):
__tablename__ = 'stats_company'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
total_calls = db.Column(db.BigInteger)
class Stats_Company_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Stats_Company

16
app/classes/stats_customer.py Normal file → Executable file
View File

@@ -5,25 +5,27 @@ from datetime import datetime
class Stats_Customer(db.Model): class Stats_Customer(db.Model):
__tablename__ = 'stats_customer' __tablename__ = 'stats_customer'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
primary_key=True, primary_key=True,
autoincrement=True, autoincrement=True,
unique=False) unique=False)
customer_id = db.Column(db.INTEGER)
total_calls = db.Column(db.INTEGER) total_calls = db.Column(db.INTEGER)
service_calls_total = db.Column(db.INTEGER) service_calls_total = db.Column(db.INTEGER)
service_calls_total_spent = db.Column(db.DECIMAL(50, 2)) service_calls_total_spent = db.Column(db.DECIMAL(6, 2))
service_calls_total_profit = db.Column(db.DECIMAL(50, 2)) service_calls_total_profit = db.Column(db.DECIMAL(6, 2))
oil_deliveries = db.Column(db.INTEGER) oil_deliveries = db.Column(db.INTEGER)
oil_total_gallons = db.Column(db.INTEGER) oil_total_gallons = db.Column(db.DECIMAL(6, 2))
oil_total_spent = db.Column(db.DECIMAL(50, 2)) oil_total_spent = db.Column(db.DECIMAL(6, 2))
oil_total_profit = db.Column(db.DECIMAL(50, 2)) oil_total_profit = db.Column(db.DECIMAL(6, 2))
class Stats_Customer_schema(ma.SQLAlchemyAutoSchema): class Stats_Customer_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Stats_Customer model = Stats_Customer

31
app/classes/stats_employee.py Normal file → Executable file
View File

@@ -1,23 +1,22 @@
from app import db, ma, login_manager from app import db, ma
from datetime import datetime from datetime import datetime
class Stats_Employee_Oil(db.Model): class Stats_Employee_Oil(db.Model):
__tablename__ = 'stats_employee_oil' __tablename__ = 'stats_employee_oil'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
primary_key=True, primary_key=True,
autoincrement=True, autoincrement=True,
unique=False) unique=False)
employee_id = db.Column(db.INTEGER)
total_deliveries = db.Column(db.INTEGER) total_deliveries = db.Column(db.INTEGER)
total_gallons_delivered = db.Column(db.INTEGER) total_gallons_delivered = db.Column(db.INTEGER)
total_primes = db.Column(db.INTEGER) total_primes = db.Column(db.INTEGER)
total_gallons_fuel = db.Column(db.INTEGER) oil_total_profit_delivered = db.Column(db.DECIMAL(6, 2))
oil_total_profit_delivered = db.Column(db.DECIMAL(50, 2)) oil_total_money_delivered = db.Column(db.DECIMAL(6, 2))
class Stats_Employee_Oil_schema(ma.SQLAlchemyAutoSchema): class Stats_Employee_Oil_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
@@ -26,9 +25,10 @@ class Stats_Employee_Oil_schema(ma.SQLAlchemyAutoSchema):
class Stats_Employee_Service(db.Model):
__tablename__ = 'stats_employee_service'
__bind_key__ = 'eamco' class Stats_Employee_Office(db.Model):
__tablename__ = 'stats_employee_office'
__table_args__ = {"schema": "public"} __table_args__ = {"schema": "public"}
id = db.Column(db.Integer, id = db.Column(db.Integer,
@@ -36,11 +36,12 @@ class Stats_Employee_Service(db.Model):
autoincrement=True, autoincrement=True,
unique=False) unique=False)
total_service_calls = db.Column(db.INTEGER) employee_id = db.Column(db.INTEGER)
total_service_calls_hours = db.Column(db.INTEGER) total_orders = db.Column(db.INTEGER)
total_gallons_fuel = db.Column(db.INTEGER) total_credit_cards_proccessed = db.Column(db.INTEGER)
total_amount_billed= db.Column(db.DECIMAL(50, 2))
total_profit_made = db.Column(db.DECIMAL(50, 2)) class Stats_Employee_Office_schema(ma.SQLAlchemyAutoSchema):
class Stats_Employee_Service_schema(ma.SQLAlchemyAutoSchema):
class Meta: class Meta:
model = Stats_Employee_Service model = Stats_Employee_Office

View File

@@ -1,27 +0,0 @@
from app import db, ma
from datetime import datetime
class Delivery_Payment(db.Model):
__tablename__ = 'delivery_payment'
__bind_key__ = 'eamco'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer,
primary_key=True,
autoincrement=True,
unique=False)
delivery_id = db.Column(db.INTEGER)
time_added = db.Column(db.TIMESTAMP(), default=datetime.utcnow())
total_amount_oil = db.Column(db.DECIMAL(50, 2))
total_amount_emergency = db.Column(db.DECIMAL(50, 2))
total_amount_prime = db.Column(db.DECIMAL(50, 2))
total_amount_fee = db.Column(db.DECIMAL(50, 2))
total_amount = db.Column(db.DECIMAL(50, 2))
class Delivery_Payment_schema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Delivery_Payment

View File

@@ -0,0 +1,22 @@
from sqlalchemy import Column, Integer, String, Numeric, DateTime
from app import db
import datetime
class Transaction(db.Model):
__tablename__ = "transactions"
id = Column(Integer, primary_key=True, index=True)
preauthorize_amount = Column(Numeric(10, 2), nullable=True) # Amount preauthorized (for auth transactions)
charge_amount = Column(Numeric(10, 2), nullable=True) # Final charge amount (for charge/capture transactions)
transaction_type = Column(Integer) # 0 = charge, 1 = auth, 2 = capture
status = Column(Integer) # 0 = approved, 1 = declined
auth_net_transaction_id = Column(String, unique=True, index=True, nullable=True)
customer_id = Column(Integer)
service_id = Column(Integer, nullable=True) # Reference to Service_Service.id
delivery_id = Column(Integer, nullable=True) # Reference to Delivery_Delivery.id
card_id = Column(Integer, nullable=True) # Reference to credit card used for payment
auto_id = Column(Integer, nullable=True) # Reference to Auto_Delivery or Auto_Ticket.id
payment_gateway = Column(Integer, default=1) # 1 = Authorize.Net, 0 = Other
rejection_reason = Column(String, nullable=True) # Detailed error message when payment is declined
created_at = Column(DateTime, default=datetime.datetime.utcnow)

0
app/common/__init__.py Normal file → Executable file
View File

0
app/common/decorators.py Normal file → Executable file
View File

0
app/customer/__init__.py Normal file → Executable file
View File

478
app/customer/views.py Normal file → Executable file
View File

@@ -1,18 +1,39 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import login_required from flask_login import login_required
from geopy.geocoders import Nominatim
from app.customer import customer from app.customer import customer
from app import db from app import db
from datetime import datetime from datetime import datetime
from app.classes.cards import Card_Card from app.classes.cards import Card_Card
from app.classes.customer import \ from app.classes.customer import \
Customer_Customer, \ Customer_Customer, \
Customer_Customer_schema Customer_Customer_schema,\
Customer_Description, \
Customer_Description_schema,\
Customer_Tank_Inspection_schema,\
Customer_Tank_Inspection
from app.classes.service import Service_Parts
from app.classes.admin import Admin_Company from app.classes.admin import Admin_Company
from app.classes.auto import Auto_Delivery,Auto_Delivery_schema
from app.classes.stats_customer import Stats_Customer
import string import string
import random import random
def generate_random_number_string(length):
# Ensure the length is at least 1
if length < 1:
raise ValueError("Length must be at least 1")
# Generate a random number with the specified length
random_number = ''.join(random.choices(string.digits, k=length))
return random_number
@customer.route("/all", methods=["GET"]) @customer.route("/all", methods=["GET"])
@login_required
def all_customers_around(): def all_customers_around():
customer_list = db.session \ customer_list = db.session \
.query(Customer_Customer) \ .query(Customer_Customer) \
@@ -22,13 +43,13 @@ def all_customers_around():
@customer.route("/all/<int:page>", methods=["GET"]) @customer.route("/all/<int:page>", methods=["GET"])
@login_required
def all_customers(page): def all_customers(page):
""" """
pagination all customers pagination all customers
""" """
per_page_amount = 50 per_page_amount = 100
if page is None: if page is None:
offset_limit = 0 offset_limit = 0
elif page == 1: elif page == 1:
@@ -38,6 +59,7 @@ def all_customers(page):
customer_list = db.session \ customer_list = db.session \
.query(Customer_Customer) \ .query(Customer_Customer) \
.order_by(Customer_Customer.id.desc()) \
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit)
customer_schema = Customer_Customer_schema(many=True) customer_schema = Customer_Customer_schema(many=True)
@@ -47,30 +69,113 @@ def all_customers(page):
@customer.route("/<int:customer_id>", methods=["GET"]) @customer.route("/<int:customer_id>", methods=["GET"])
def get_a_customer(customer_id): def get_a_customer(customer_id):
""" """
Checks auth token to ensure user is authenticated
""" """
get_customer = (db.session get_customer = (db.session
.query(Customer_Customer) .query(Customer_Customer)
.filter(Customer_Customer.id == customer_id) .filter(Customer_Customer.id == customer_id)
.first()) .first())
print(get_customer)
customer_schema = Customer_Customer_schema(many=False) customer_schema = Customer_Customer_schema(many=False)
return jsonify(customer_schema.dump(get_customer)) return jsonify(customer_schema.dump(get_customer))
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size)) @customer.route("/description/<int:customer_id>", methods=["GET"])
def get_a_customer_description(customer_id):
"""
"""
get_customer_description = (db.session
.query(Customer_Description)
.filter(Customer_Description.customer_id == customer_id)
.first())
if get_customer_description is None:
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == customer_id)
.first())
new_description = Customer_Description(
customer_id = customer_id,
account_number = get_customer.account_number,
company_id = get_customer.company_id,
fill_location = None,
description = None,
)
db.session.add(new_description)
db.session.commit()
get_customer_description = (db.session
.query(Customer_Description)
.filter(Customer_Description.customer_id == customer_id)
.first())
customer_schema = Customer_Description_schema(many=False)
return jsonify(customer_schema.dump(get_customer_description))
@customer.route("/tank/<int:customer_id>", methods=["GET"])
def get_a_customer_tank(customer_id):
"""
"""
get_customer_tank = (db.session
.query(Customer_Tank_Inspection)
.filter(Customer_Tank_Inspection.customer_id == customer_id)
.first())
if get_customer_tank is None:
new_tank = Customer_Tank_Inspection(
customer_id = customer_id,
last_tank_inspection = None,
tank_status = False,
outside_or_inside = True,
tank_size = 275,
)
db.session.add(new_tank)
db.session.commit()
get_customer_tank = (db.session
.query(Customer_Tank_Inspection)
.filter(Customer_Tank_Inspection.customer_id == customer_id)
.first())
customer_schema = Customer_Tank_Inspection_schema(many=False)
return jsonify(customer_schema.dump(get_customer_tank))
@customer.route("/create", methods=["POST"]) @customer.route("/create", methods=["POST"])
@login_required
def create_customer(): def create_customer():
""" """
""" """
now = datetime.utcnow() now = datetime.utcnow()
get_company = db.session.query(Admin_Company).filter(Admin_Company.id == 1).first() get_company = (db.session
.query(Admin_Company)
.filter(Admin_Company.id == 1)
.first())
starter_digits = str(get_company.account_prefix) + '-' + id_generator() get_company = (db.session
.query(Admin_Company)
.filter(Admin_Company.id == 1)
.first())
made_account_number = starter_digits
random_string = generate_random_number_string(6)
made_account_number = str(get_company.account_prefix) + '-' + str(random_string)
see_if_exists = (db.session.query(Customer_Customer).filter(Customer_Customer.account_number == made_account_number).first())
if see_if_exists is not None:
random_string = generate_random_number_string(10)
made_account_number = str(get_company.account_prefix) + '-' + str(random_string)
see_if_exists = (db.session.query(Customer_Customer).filter(Customer_Customer.account_number == made_account_number).first())
if see_if_exists is not None:
random_string = generate_random_number_string(10)
made_account_number = str(get_company.account_prefix) + '-' + str(random_string)
see_if_exists = (db.session.query(Customer_Customer).filter(Customer_Customer.account_number == made_account_number).first())
response_customer_last_name = request.json["customer_last_name"] response_customer_last_name = request.json["customer_last_name"]
response_customer_first_name = request.json["customer_first_name"] response_customer_first_name = request.json["customer_first_name"]
@@ -78,20 +183,77 @@ def create_customer():
response_customer_state = request.json["customer_state"] response_customer_state = request.json["customer_state"]
response_customer_zip = request.json["customer_zip"] response_customer_zip = request.json["customer_zip"]
response_customer_email = request.json["customer_email"] response_customer_email = request.json["customer_email"]
response_customer_automatic = request.json["customer_automatic"]
response_customer_home_type = request.json["customer_home_type"] response_customer_home_type = request.json["customer_home_type"]
customer_phone_number = request.json["customer_phone_number"] customer_phone_number = request.json["customer_phone_number"]
customer_address = request.json["customer_address"] customer_address = request.json["customer_address"]
customer_apt = request.json["customer_apt"] customer_apt = request.json["customer_apt"]
if response_customer_automatic is True: customer_description_msg = request.json["customer_description"]
auto_customer = 1
else:
auto_customer = 0
int_customer_home_type = int(response_customer_home_type) int_customer_home_type = int(response_customer_home_type)
response_customer_zip = int(response_customer_zip) response_customer_zip = str(response_customer_zip)
response_customer_state = int(response_customer_state) response_customer_state = int(response_customer_state)
if response_customer_state == 0:
the_state = 'MA'
elif response_customer_state == 1:
the_state = 'RI'
elif response_customer_state == 2:
the_state = 'NH'
else:
the_state = 'MA'
if response_customer_town == 0:
the_town = 'Auburn'
elif response_customer_state == 1:
the_town = 'Charlton'
elif response_customer_state == 2:
the_town = 'Cherry Valley'
elif response_customer_state == 3:
the_town = 'Dudley'
elif response_customer_state == 4:
the_town = 'Grafton'
elif response_customer_state == 5:
the_town = 'Leicester'
elif response_customer_state == 6:
the_town = 'Millbury'
elif response_customer_state == 7:
the_town = 'N Oxford'
elif response_customer_state == 8:
the_town = 'Oxford'
elif response_customer_state == 9:
the_town = 'Rochdale'
elif response_customer_state == 10:
the_town = 'Shrewsbury'
elif response_customer_state == 11:
the_town = 'Southbridge'
elif response_customer_state == 12:
the_town = 'Spencer'
elif response_customer_state == 13:
the_town = 'Sturbridge'
elif response_customer_state == 14:
the_town = 'Webster'
elif response_customer_state == 15:
the_town = 'Worcester'
else:
the_town = 'NA'
geolocator = Nominatim(user_agent="auburnoil")
address_string = customer_address + ' ' + response_customer_town+ ' ' + the_state
try:
location = geolocator.geocode(address_string)
user_lat =location.latitude
user_long = location.longitude
cor_ad = True
except:
user_lat =None
user_long = None
cor_ad = False
new_customer = Customer_Customer( new_customer = Customer_Customer(
account_number=made_account_number, account_number=made_account_number,
customer_last_name=response_customer_last_name, customer_last_name=response_customer_last_name,
@@ -101,15 +263,51 @@ def create_customer():
customer_zip=response_customer_zip, customer_zip=response_customer_zip,
customer_first_call=now, customer_first_call=now,
customer_email=response_customer_email, customer_email=response_customer_email,
customer_automatic=auto_customer, customer_automatic=0,
customer_home_type=int_customer_home_type, customer_home_type=int_customer_home_type,
customer_phone_number=customer_phone_number, customer_phone_number=customer_phone_number,
customer_address=customer_address, customer_address=customer_address,
customer_apt=customer_apt customer_apt=customer_apt,
company_id=1,
customer_latitude=user_lat,
customer_longitude=user_long,
correct_address=cor_ad
) )
db.session.add(new_customer) db.session.add(new_customer)
db.session.flush()
create_stats_customer = Stats_Customer(
customer_id = new_customer.id,
total_calls = 0,
service_calls_total = 0,
service_calls_total_spent = 0,
service_calls_total_profit = 0,
oil_deliveries = 0,
oil_total_gallons = 0,
oil_total_spent = 0,
oil_total_profit = 0,
)
db.session.add(create_stats_customer)
new_description = Customer_Description(
customer_id = new_customer.id,
account_number = made_account_number,
description = customer_description_msg,
fill_location=None,
company_id=1,
)
db.session.add(new_description)
new_tank = Customer_Tank_Inspection(
customer_id = new_customer.id,
last_tank_inspection=None,
tank_status = False,
outside_or_inside = True,
tank_size=275,
)
db.session.add(new_tank)
db.session.commit() db.session.commit()
return jsonify({ return jsonify({
@@ -122,6 +320,7 @@ def create_customer():
}), 200 }), 200
@customer.route("/edit/<int:customer_id>", methods=["PUT"]) @customer.route("/edit/<int:customer_id>", methods=["PUT"])
@login_required @login_required
def edit_customer(customer_id): def edit_customer(customer_id):
@@ -131,6 +330,10 @@ def edit_customer(customer_id):
.query(Customer_Customer) .query(Customer_Customer)
.filter(Customer_Customer.id == customer_id) .filter(Customer_Customer.id == customer_id)
.first()) .first())
get_customer_description = (db.session
.query(Customer_Description)
.filter(Customer_Description.customer_id == customer_id)
.first())
response_customer_last_name = request.json["customer_last_name"] response_customer_last_name = request.json["customer_last_name"]
response_customer_first_name = request.json["customer_first_name"] response_customer_first_name = request.json["customer_first_name"]
response_customer_town = request.json["customer_town"] response_customer_town = request.json["customer_town"]
@@ -138,13 +341,43 @@ def edit_customer(customer_id):
response_customer_zip = request.json["customer_zip"] response_customer_zip = request.json["customer_zip"]
response_customer_phone_number = request.json["customer_phone_number"] response_customer_phone_number = request.json["customer_phone_number"]
response_customer_email = request.json["customer_email"] response_customer_email = request.json["customer_email"]
response_customer_automatic = request.json["customer_automatic"]
response_customer_home_type = request.json["customer_home_type"] response_customer_home_type = request.json["customer_home_type"]
response_customer_address = request.json["customer_address"] response_customer_address = request.json["customer_address"]
response_customer_apt = request.json["customer_apt"]
response_customer_description = request.json["customer_description"]
response_customer_fill_location = request.json["customer_fill_location"]
if get_customer_description is not None:
get_customer_description.description = response_customer_description
get_customer_description.fill_location = response_customer_fill_location
db.session.add(get_customer_description)
if response_customer_state == 0:
the_state = 'MA'
elif response_customer_state == 1:
the_state = 'RI'
elif response_customer_state == 1:
the_state = 'NH'
else:
the_state = 'MA'
geolocator = Nominatim(user_agent="auburnoil")
address_string = response_customer_address + ' ' + response_customer_town+ ' ' + the_state
try:
location = geolocator.geocode(address_string, timeout=10)
get_customer.customer_latitude = location.latitude
get_customer.customer_longitude = location.longitude
cor_ad = True
except:
get_customer.customer_latitude = None
get_customer.customer_longitude = None
cor_ad = False
get_customer.customer_address = response_customer_address get_customer.customer_address = response_customer_address
get_customer.customer_home_type = response_customer_home_type get_customer.customer_home_type = response_customer_home_type
get_customer.customer_automatic = response_customer_automatic
get_customer.customer_phone_number = response_customer_phone_number get_customer.customer_phone_number = response_customer_phone_number
get_customer.customer_last_name = response_customer_last_name get_customer.customer_last_name = response_customer_last_name
get_customer.customer_first_name = response_customer_first_name get_customer.customer_first_name = response_customer_first_name
@@ -152,6 +385,9 @@ def edit_customer(customer_id):
get_customer.customer_state = response_customer_state get_customer.customer_state = response_customer_state
get_customer.customer_zip = response_customer_zip get_customer.customer_zip = response_customer_zip
get_customer.customer_email = response_customer_email get_customer.customer_email = response_customer_email
get_customer.customer_apt = response_customer_apt
get_customer.correct_address = cor_ad
db.session.add(get_customer) db.session.add(get_customer)
db.session.commit() db.session.commit()
@@ -186,7 +422,6 @@ def delete_customer(customer_id):
db.session.delete(get_customer) db.session.delete(get_customer)
db.session.commit() db.session.commit()
print("deleted")
return jsonify({ return jsonify({
"ok": True, "ok": True,
'user': { 'user': {
@@ -196,3 +431,196 @@ def delete_customer(customer_id):
}), 200 }), 200
@customer.route("/count", methods=["GET"])
@login_required
def customer_count():
"""
"""
get_customer = (db.session
.query(Customer_Customer)
.count())
return jsonify({
"ok": True,
'count': get_customer
}), 200
@customer.route("/automatic/status/<int:customer_id>", methods=["GET"])
@login_required
def customer_automatic_status(customer_id):
"""
"""
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == customer_id)
.first())
if get_customer.customer_automatic == 1:
status = 1
if get_customer.customer_automatic == 0:
status = 0
return jsonify({
"ok": True,
'status': status
}), 200
@customer.route("/automatic/deliveries", methods=["GET"])
@login_required
def get_all_automatic_deliveries():
"""
Get all automatic deliveries for the table.
"""
try:
deliveries = Auto_Delivery.query.all()
schema = Auto_Delivery_schema(many=True)
return jsonify(schema.dump(deliveries)), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@customer.route("/automatic/assign/<int:customer_id>", methods=["GET"])
@login_required
def customer_automatic_assignment(customer_id):
"""
"""
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == customer_id)
.first())
get_auto = (db.session
.query(Auto_Delivery)
.filter(Auto_Delivery.customer_id == customer_id)
.first())
get_main_credit_card = (db.session
.query(Card_Card)
.filter(Card_Card.user_id == customer_id)
.filter(Card_Card.main_card == True)
.first())
get_customer_tank = (db.session
.query(Customer_Tank_Inspection)
.filter(Customer_Tank_Inspection.customer_id == customer_id)
.first())
get_service_parts = (db.session
.query(Service_Parts)
.filter(Service_Parts.customer_id == customer_id)
.first())
if get_customer.customer_automatic == 1:
# customer becomes will call
get_customer.customer_automatic = 0
db.session.add(get_customer)
if get_auto is not None:
db.session.delete(get_auto)
status = 3
else:
if get_main_credit_card is None:
status = 2
return jsonify({
"ok": True,
'status': status
}), 200
# customer becomes an automatic
if get_auto is None:
hot_water_value = get_service_parts.hot_water_tank if get_service_parts and get_service_parts.hot_water_tank is not None else 0
create_auto = Auto_Delivery(customer_id=customer_id,
customer_full_name=get_customer.customer_first_name + ' ' + get_customer.customer_last_name,
account_number=get_customer.account_number,
customer_town=get_customer.customer_town,
customer_state=get_customer.customer_state,
customer_zip=get_customer.customer_zip,
customer_address=get_customer.customer_address,
last_fill=None,
last_updated=None,
estimated_gallons_left=0,
estimated_gallons_left_prev_day=0,
tank_height=0,
tank_size=get_customer_tank.tank_size,
house_factor=1,
auto_status=1,
days_since_last_fill=0,
hot_water_summer=hot_water_value
)
db.session.add(create_auto)
get_customer.customer_automatic = 1
db.session.add(get_customer)
status = 1
db.session.commit()
return jsonify({
"ok": True,
'status': status
}), 200
@customer.route("/edit/tank/<int:customer_id>", methods=["PUT"])
@login_required
def edit_customer_tank(customer_id):
"""
Safely edits or creates tank and description details for a customer.
"""
get_customer = db.session.query(Customer_Customer).filter(Customer_Customer.id == customer_id).one_or_none()
if not get_customer:
return jsonify({"ok": False, "error": "Customer not found"}), 404
get_customer_description = db.session.query(Customer_Description).filter(Customer_Description.customer_id == customer_id).first()
if not get_customer_description:
get_customer_description = Customer_Description(customer_id=customer_id)
db.session.add(get_customer_description)
get_customer_tank = db.session.query(Customer_Tank_Inspection).filter(Customer_Tank_Inspection.customer_id == customer_id).first()
if not get_customer_tank:
get_customer_tank = Customer_Tank_Inspection(customer_id=customer_id)
db.session.add(get_customer_tank)
data = request.get_json()
if 'tank_status' in data:
get_customer_tank.tank_status = data["tank_status"]
if 'outside_or_inside' in data:
get_customer_tank.outside_or_inside = data["outside_or_inside"]
response_last_tank_inspection = data.get("last_tank_inspection", None)
response_tank_size = data.get("tank_size", 0)
# --- FIX APPLIED HERE ---
# 1. Get the value from the request. Default to 0 if it's missing.
response_customer_fill_location = data.get("fill_location", 0)
# 2. Add a safety check: if the frontend sent an empty string, convert it to 0.
if response_customer_fill_location == "":
response_customer_fill_location = 0
get_customer_tank.last_tank_inspection = response_last_tank_inspection
get_customer_tank.tank_size = response_tank_size
get_customer_description.fill_location = response_customer_fill_location
if get_customer.customer_automatic == 1:
get_auto_info = db.session.query(Auto_Delivery).filter(Auto_Delivery.customer_id == customer_id).first()
if get_auto_info:
get_auto_info.tank_size = response_tank_size
db.session.add(get_auto_info)
db.session.commit()
return jsonify({"ok": True}), 200

0
app/delivery/__init__.py Normal file → Executable file
View File

745
app/delivery/views.py Normal file → Executable file
View File

@@ -1,71 +1,159 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user from flask_login import current_user
from datetime import date from datetime import date, datetime, timedelta
from app.delivery import delivery from app.delivery import delivery
from app import db from app import db
from datetime import datetime from sqlalchemy import or_
from app.classes.customer import Customer_Customer from app.classes.customer import (Customer_Customer)
from app.classes.delivery import (Delivery_Delivery, from app.classes.delivery import (Delivery_Delivery,
Delivery_Delivery_schema, Delivery_Delivery_schema,
Delivery_Notes_Driver, Delivery_Notes_Driver,
Delivery_Payment,
Delivery_Payment_schema,
) )
from app.classes.employee import Employee_Employee
from app.classes.cards import Card_Card from app.classes.cards import Card_Card
from app.classes.pricing import Pricing_Oil_Oil from app.classes.pricing import Pricing_Oil_Oil
from app.classes.auth import Auth_User from app.classes.auth import Auth_User
from app.classes.pricing import Pricing_Service_General from app.classes.promo import Promo_Promo
from app.classes.stats_customer import Stats_Customer
from app.classes.auto import Tickets_Auto_Delivery, Tickets_Auto_Delivery_schema
# This endpoint is fine, but I've added some comments for clarity.
@delivery.route("/updatestatus", methods=["GET"])
def move_deliveries():
"""
Batch updates delivery statuses based on their expected delivery date relative to today.
- Today's deliveries -> "Out for Delivery" (2)
- Tomorrow's deliveries -> "Scheduled for Tomorrow" (3)
- Future deliveries -> "Waiting" (0)
- Past-due deliveries -> "Pending" (9)
"""
counter = 0
today = date.today()
tomorrow = today + timedelta(days=1)
# Update statuses for deliveries scheduled for today
deliveries_today = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]), # Not cancelled, issue, or finalized
Delivery_Delivery.expected_delivery_date == today,
Delivery_Delivery.delivery_status != 2 # Avoid redundant updates
).all()
for delivery_item in deliveries_today:
delivery_item.delivery_status = 2
db.session.add(delivery_item)
counter += 1
# Update statuses for deliveries scheduled for tomorrow
deliveries_tomorrow = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date == tomorrow,
Delivery_Delivery.delivery_status != 3
).all()
for delivery_item in deliveries_tomorrow:
delivery_item.delivery_status = 3
db.session.add(delivery_item)
counter += 1
# Update statuses for future deliveries (after tomorrow)
deliveries_waiting = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date > tomorrow,
Delivery_Delivery.delivery_status != 0
).all()
for delivery_item in deliveries_waiting:
delivery_item.delivery_status = 0
db.session.add(delivery_item)
counter += 1
# Update statuses for deliveries that are past their expected date
deliveries_pending = db.session.query(Delivery_Delivery).filter(
Delivery_Delivery.delivery_status.notin_([1, 5, 10]),
Delivery_Delivery.expected_delivery_date < today,
Delivery_Delivery.delivery_status != 9
).all()
for delivery_item in deliveries_pending:
delivery_item.delivery_status = 9
db.session.add(delivery_item)
counter += 1
if counter > 0:
db.session.commit()
return jsonify({"ok": True, 'update': True}), 200
return jsonify({"ok": True, 'update': False}), 200
@delivery.route("/<int:delivery_id>", methods=["GET"]) @delivery.route("/<int:delivery_id>", methods=["GET"])
def get_a_delivery(delivery_id): def get_a_delivery(delivery_id):
""" """
Get a single delivery's details.
""" """
get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
if not get_delivery:
return jsonify({"ok": False, "error": "Delivery not found"}), 404
get_delivery = db.session\ # Using the schema is cleaner and less error-prone than building a manual dictionary.
.query(Delivery_Delivery)\ delivery_schema = Delivery_Delivery_schema(many=False)
.filter(Delivery_Delivery.id == delivery_id)\ return jsonify({"ok": True, "delivery": delivery_schema.dump(get_delivery)}), 200
.first()
return jsonify({
"ok": True,
'delivery': {
'id': get_delivery.id,
'customer_id': get_delivery.customer_id,
'delivery_expected_delivery_date': str(get_delivery.expected_delivery_date),
'delivery_asked_for_fill': get_delivery.customer_asked_for_fill,
'delivery_gallons_ordered': get_delivery.gallons_ordered,
'delivery_dispatcher_notes': get_delivery.dispatcher_notes,
'delivery_prime': get_delivery.prime,
'delivery_same_day': get_delivery.same_day,
'when_ordered': get_delivery.when_ordered,
'customer_price': get_delivery.customer_price,
'delivery_status': get_delivery.delivery_status,
'payment_type': get_delivery.payment_type,
'payment_card_id': get_delivery.payment_card_id,
},
}), 200
@delivery.route("/past1/<int:customer_id>", methods=["GET"])
def get_customer_past_delivery1(customer_id):
get_customer_past_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.filter(Delivery_Delivery.delivery_status == 10)
.order_by(Delivery_Delivery.id.desc())
.limit(5))
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_customer_past_delivery))
@delivery.route("/past2/<int:customer_id>", methods=["GET"])
def get_customer_past_delivery2(customer_id):
get_customer_past_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.order_by(Delivery_Delivery.id.desc())
.offset(5)
.limit(5))
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_customer_past_delivery))
@delivery.route("/auto/<int:customer_id>", methods=["GET"])
def get_customer_auto_delivery(customer_id):
get_customer_past_delivery = (db.session
.query(Tickets_Auto_Delivery)
.filter(Tickets_Auto_Delivery.customer_id == customer_id)
.order_by(Tickets_Auto_Delivery.id.desc())
.offset(5)
.limit(5))
delivery_schema = Tickets_Auto_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_customer_past_delivery))
@delivery.route("/order/<int:delivery_id>", methods=["GET"]) @delivery.route("/order/<int:delivery_id>", methods=["GET"])
def get_a_specific_delivery(delivery_id): def get_a_specific_delivery(delivery_id):
"""
Get a single delivery by its ID.
"""
get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
if not get_delivery:
return jsonify({"ok": False, "error": "Delivery not found"}), 404
get_delivery = db.session\
.query(Delivery_Delivery)\
.filter(Delivery_Delivery.id == delivery_id)\
.first()
delivery_schema = Delivery_Delivery_schema(many=False) delivery_schema = Delivery_Delivery_schema(many=False)
return jsonify(delivery_schema.dump(get_delivery)) return jsonify({"ok": True, "delivery": delivery_schema.dump(get_delivery)}), 200
@delivery.route("/order/money/<int:delivery_id>", methods=["GET"])
def get_a_specific_delivery_money(delivery_id):
get_delivery_money = db.session\
.query(Delivery_Payment)\
.filter(Delivery_Payment.id == delivery_id)\
.first()
delivery_schema = Delivery_Payment_schema(many=False)
return jsonify(delivery_schema.dump(get_delivery_money))
@delivery.route("/cash/<int:delivery_id>/<int:type_of_payment>", methods=["PUT"]) @delivery.route("/cash/<int:delivery_id>/<int:type_of_payment>", methods=["PUT"])
@@ -87,24 +175,9 @@ def update_a_delivery_payment(delivery_id, type_of_payment):
return jsonify({"ok": True}), 200 return jsonify({"ok": True}), 200
@delivery.route("/all", methods=["GET"])
def get_deliveries_not_delivered_all():
"""
This will get deliveries not done
"""
deliveries = db.session \ @delivery.route("/all/<int:page>", methods=["GET"])
.query(Delivery_Delivery) \ def get_deliveries_all(page):
.filter(Delivery_Delivery.delivery_status != 1) \
.filter(Delivery_Delivery.delivery_status != 3) \
.all()
customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries))
@delivery.route("/customer/<int:customer_id>/<int:page>", methods=["GET"])
def get_deliveries_from_customer(customer_id, page):
""" """
This will get deliveries not done This will get deliveries not done
""" """
@@ -116,15 +189,44 @@ def get_deliveries_from_customer(customer_id, page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \
.query(Delivery_Delivery) \ deliveries = (db.session
.filter(Delivery_Delivery.customer_id == customer_id) \ .query(Delivery_Delivery)
.limit(per_page_amount).offset(offset_limit) .order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/all/<int:page>", methods=["GET"])
@delivery.route("/customer/<int:customer_id>/<int:page>", methods=["GET"])
def get_deliveries_from_customer(customer_id, page):
"""
This will get deliveries not done
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == customer_id)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries))
@delivery.route("/all/order/<int:page>", methods=["GET"])
def get_deliveries_not_delivered(page): def get_deliveries_not_delivered(page):
""" """
@@ -138,15 +240,17 @@ def get_deliveries_not_delivered(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session\ deliveries = (db.session
.query(Delivery_Delivery)\ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 0)\ .filter(Delivery_Delivery.delivery_status != 10)
.order_by(Delivery_Delivery.when_ordered.desc())\ .order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/waiting/<int:page>", methods=["GET"]) @delivery.route("/waiting/<int:page>", methods=["GET"])
def get_deliveries_waiting(page): def get_deliveries_waiting(page):
""" """
@@ -160,19 +264,24 @@ def get_deliveries_waiting(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 0) \ .filter(or_(Delivery_Delivery.delivery_status == 0,
.order_by(Delivery_Delivery.when_ordered.desc()) \ )
.limit(per_page_amount).offset(offset_limit) )
.filter(Delivery_Delivery.expected_delivery_date != date.today())
.order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/delivered/<int:page>", methods=["GET"])
def get_deliveries_delivered(page):
@delivery.route("/pending/<int:page>", methods=["GET"])
def get_deliveries_pending(page):
""" """
This will get deliveries not done
""" """
per_page_amount = 50 per_page_amount = 50
if page is None: if page is None:
@@ -182,11 +291,11 @@ def get_deliveries_delivered(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 1) \ .filter(Delivery_Delivery.delivery_status == 9)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.delivery_status.asc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@@ -194,7 +303,6 @@ def get_deliveries_delivered(page):
@delivery.route("/outfordelivery/<int:page>", methods=["GET"]) @delivery.route("/outfordelivery/<int:page>", methods=["GET"])
def get_deliveries_outfordelivery(page): def get_deliveries_outfordelivery(page):
""" """
This will get deliveries not done
""" """
per_page_amount = 50 per_page_amount = 50
if page is None: if page is None:
@@ -204,15 +312,43 @@ def get_deliveries_outfordelivery(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 2) \ .filter(Delivery_Delivery.delivery_status == 2)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.delivery_status.asc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/tommorrow/<int:page>", methods=["GET"])
def get_deliveries_tommorrow(page):
"""
This will get deliveries not done
"""
tomm = date.today() + timedelta(days=1)
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
deliveries = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 3)
.order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries))
@delivery.route("/finalized/<int:page>", methods=["GET"]) @delivery.route("/finalized/<int:page>", methods=["GET"])
def get_deliveries_finalized(page): def get_deliveries_finalized(page):
""" """
@@ -226,11 +362,11 @@ def get_deliveries_finalized(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 10) \ .filter(Delivery_Delivery.delivery_status == 10)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.id.desc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@@ -250,16 +386,17 @@ def get_deliveries_cancelled(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 3) \ .filter(Delivery_Delivery.delivery_status == 1)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/partialdelivery/<int:page>", methods=["GET"]) @delivery.route("/partialdelivery/<int:page>", methods=["GET"])
def get_deliveries_partial(page): def get_deliveries_partial(page):
""" """
@@ -273,15 +410,17 @@ def get_deliveries_partial(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 4) \ .filter(Delivery_Delivery.delivery_status == 4)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/issue/<int:page>", methods=["GET"]) @delivery.route("/issue/<int:page>", methods=["GET"])
def get_deliveries_issue(page): def get_deliveries_issue(page):
""" """
@@ -295,142 +434,131 @@ def get_deliveries_issue(page):
else: else:
offset_limit = (per_page_amount * page) - per_page_amount offset_limit = (per_page_amount * page) - per_page_amount
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 5) \ .filter(Delivery_Delivery.delivery_status == 5)
.order_by(Delivery_Delivery.when_ordered.desc()) \ .order_by(Delivery_Delivery.when_ordered.desc())
.limit(per_page_amount).offset(offset_limit) .limit(per_page_amount).offset(offset_limit))
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/time/today", methods=["GET"]) @delivery.route("/time/today", methods=["GET"])
def get_deliveries_today(): def get_deliveries_today():
""" """
This will get today's deliveries This will get today's deliveries
""" """
deliveries = db.session \ deliveries = (db.session
.query(Delivery_Delivery) \ .query(Delivery_Delivery)
.filter(Delivery_Delivery.expected_delivery_date == date.today()) \ .filter(Delivery_Delivery.delivery_status == 2)
.all() .all())
customer_schema = Delivery_Delivery_schema(many=True) customer_schema = Delivery_Delivery_schema(many=True)
return jsonify(customer_schema.dump(deliveries)) return jsonify(customer_schema.dump(deliveries))
@delivery.route("/amount/<int:delivery_id>", methods=["GET"])
def get_deliveries_amount_total(delivery_id):
"""
This will get deliveries totals
"""
delivery_amount = db.session \
.query(Delivery_Payment) \
.filter(Delivery_Payment.delivery_id == delivery_id) \
.all()
delivery_schema = Delivery_Delivery_schema(many=False)
return jsonify(delivery_schema.dump(delivery_amount))
@delivery.route("/edit/<int:delivery_id>", methods=["POST"]) @delivery.route("/edit/<int:delivery_id>", methods=["POST"])
def edit_a_delivery(delivery_id): def edit_a_delivery(delivery_id):
""" """
This will create a delivery using a customer id This will edit a delivery using a delivery id.
""" """
get_delivery = db.session \ data = request.json
.query(Delivery_Delivery) \ get_delivery = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.id == delivery_id).first()
.filter(Delivery_Delivery.id == delivery_id) \
.first()
get_today_price = db.session \
.query(Pricing_Oil_Oil) \
.order_by(Pricing_Oil_Oil.id.desc()) \
.first()
get_customer = db.session \
.query(Customer_Customer) \
.filter(Customer_Customer.id == get_delivery.customer_id) \
.first()
if not get_delivery: if not get_delivery:
return jsonify({"ok": False}), 200 return jsonify({"ok": False, "error": "Delivery not found"}), 404
else:
gallons_ordered = request.json["gallons_ordered"] # --- Fetch related data ---
delivery_status = request.json["delivery_status"] get_customer = db.session.query(Customer_Customer).filter(Customer_Customer.id == get_delivery.customer_id).first()
when_to_deliver = request.json["expected_delivery_date"] if not get_customer:
dispatcher_notes_taken = request.json["dispatcher_notes_taken"] return jsonify({"ok": False, "error": "Associated customer not found"}), 404
customer_wants_fill = request.json["customer_asked_for_fill"] get_today_price = db.session.query(Pricing_Oil_Oil).order_by(Pricing_Oil_Oil.id.desc()).first()
if not get_today_price:
return jsonify({"ok": False, "error": "Pricing information not available"}), 500
card_payment = request.json["credit"] # --- Process Form Input (using .get() for safety) ---
cash_payment = request.json["cash"] get_delivery.gallons_ordered = data.get("gallons_ordered", get_delivery.gallons_ordered)
get_delivery.delivery_status = data.get("delivery_status", get_delivery.delivery_status)
get_delivery.expected_delivery_date = data.get("expected_delivery_date")
get_delivery.when_ordered = data.get("created_delivery_date")
get_delivery.dispatcher_notes = data.get("dispatcher_notes_taken", "")
get_delivery.payment_type = data.get("payment_type", 0)
# Convert booleans to 1 or 0 for the database
if request.json["credit_card_id"]: customer_wants_fill = 1 if data.get("customer_asked_for_fill") else 0
card_payment_id = request.json["credit_card_id"]
else:
card_payment_id = None
if card_payment_id is not None:
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_payment_id)
.filter(Card_Card.user_id == get_customer.id)
.first())
card_id_from_customer = get_card.id
else:
card_id_from_customer = None
if cash_payment is True and card_payment is False:
delivery_payment_method = 0
elif card_payment is True and cash_payment is False:
delivery_payment_method = 1
elif card_payment is True and cash_payment is True:
delivery_payment_method = 2
else:
delivery_payment_method = 3
if customer_wants_fill is True:
customer_wants_fill = 1
else:
customer_wants_fill = 0
same_day_info = request.json["same_day"]
if same_day_info is True:
same_day_info = 1
else:
same_day_info = 0
prime_info = request.json["prime"]
if prime_info is True:
prime_info = 1
else:
prime_info = 0
get_delivery.delivery_status = delivery_status
get_delivery.gallons_ordered = gallons_ordered
get_delivery.customer_asked_for_fill = customer_wants_fill get_delivery.customer_asked_for_fill = customer_wants_fill
get_delivery.expected_delivery_date = when_to_deliver get_delivery.prime = 1 if data.get("prime") else 0
get_delivery.dispatcher_notes = dispatcher_notes_taken get_delivery.same_day = 1 if data.get("same_day") else 0
get_delivery.prime = prime_info get_delivery.emergency = 1 if data.get("emergency") else 0
get_delivery.same_day = same_day_info
get_delivery.gallons_ordered = gallons_ordered
get_delivery.payment_type = delivery_payment_method
get_delivery.payment_card_id = card_id_from_customer
# --- Handle Driver Assignment ---
driver_id = data.get("driver_employee_id")
if driver_id:
get_driver = db.session.query(Employee_Employee).filter(Employee_Employee.id == driver_id).first()
if get_driver:
get_delivery.driver_employee_id = get_driver.id
get_delivery.driver_first_name = get_driver.employee_first_name
get_delivery.driver_last_name = get_driver.employee_last_name
else:
# Optionally, handle the case where the driver ID is invalid
# For now, we'll just not update the driver info.
pass
else:
# No driver provided, set to empty values
get_delivery.driver_employee_id = 0
get_delivery.driver_first_name = ""
get_delivery.driver_last_name = ""
# --- Handle Promotion ---
promo_id = data.get("promo_id")
if promo_id and promo_id != 0:
get_promo = db.session.query(Promo_Promo).filter(Promo_Promo.id == promo_id).first()
if get_promo:
get_delivery.promo_id = get_promo.id
get_delivery.promo_money_discount = get_promo.money_off_delivery
else: # Clear promo if "No Promotion" is selected
get_delivery.promo_id = None
get_delivery.promo_money_discount = None
# --- Handle Credit Card Assignment ---
card_id = data.get("credit_card_id")
# Only assign card if payment type is Card (1) and a valid card_id is provided
if get_delivery.payment_type == 1 and card_id:
get_card = db.session.query(Card_Card).filter(Card_Card.id == card_id, Card_Card.user_id == get_customer.id).first()
if get_card:
get_delivery.payment_card_id = get_card.id
else: # Clear card if payment is not by card
get_delivery.payment_card_id = None
# --- Recalculate Pricing (This logic could be moved to a helper function) ---
gallons_for_calc = 250 if customer_wants_fill else int(get_delivery.gallons_ordered)
base_price = gallons_for_calc * get_today_price.price_for_customer
# Add fees
total_price = base_price
if get_delivery.prime:
total_price += get_today_price.price_prime
if get_delivery.same_day:
total_price += get_today_price.price_same_day
if get_delivery.emergency:
total_price += get_today_price.price_emergency
get_delivery.total_price = base_price # Price before fees
get_delivery.pre_charge_amount = total_price # Price including fees
# --- Commit Changes ---
db.session.add(get_delivery) db.session.add(get_delivery)
db.session.commit() db.session.commit()
return jsonify({ return jsonify({
"ok": True, "ok": True,
'customer': { 'message': f"Delivery {delivery_id} updated successfully.",
'user_id': get_customer.id, 'customer_id': get_customer.id # Keep this if the frontend uses it for navigation
},
}), 200 }), 200
@@ -449,22 +577,77 @@ def create_a_delivery(user_id):
.order_by(Pricing_Oil_Oil.id.desc())\ .order_by(Pricing_Oil_Oil.id.desc())\
.first() .first()
get_service_prices = (db.session
.query(Pricing_Service_General)
.order_by(Pricing_Service_General.id.desc())
.first())
if not get_customer: if not get_customer:
return jsonify({"ok": False}), 200 return jsonify({"ok": False}), 200
else: else:
gallons_ordered = request.json["gallons_ordered"] gallons_ordered = request.json["gallons_ordered"]
customer_wants_fill = request.json["customer_asked_for_fill"] customer_wants_fill = request.json["customer_asked_for_fill"]
when_to_deliver = request.json["expected_delivery_date"] when_to_deliver = request.json["expected_delivery_date"]
dispatcher_notes_taken = request.json["dispatcher_notes_taken"] dispatcher_notes_taken = request.json["dispatcher_notes_taken"]
prime_info = request.json["prime"] prime_info = request.json["prime"]
same_day_info = request.json["same_day"] same_day_info = request.json["same_day"]
emergency_info = request.json["emergency"]
delivery_driver_id = request.json.get("driver_employee_id", 0)
card_payment = request.json["credit"] card_payment = request.json["credit"]
cash_payment = request.json["cash"] cash_payment = request.json["cash"]
check_payment = request.json["check"]
other_payment = request.json["other"]
## promo
try:
if request.json["promo_id"]:
promo_id = request.json["promo_id"]
else:
promo_id = None
except:
promo_id = None
if promo_id is not None:
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
promo_id_get_delivery =get_promo_data.id,
promo_money_discount_get_delivery = get_promo_data.money_off_delivery
else:
promo_id_get_delivery =None
promo_money_discount_get_delivery = None
# 0 = cash only
# 1 = CC - Tiger (Physical)
# 11 = CC - Authorize (API)
# 2 = credit /cash
# 3 = check
# 4 = other
if cash_payment is True:
delivery_payment_method = 0
elif card_payment is True:
delivery_payment_method = 1 # Default to Tiger physical machine
elif check_payment is True:
delivery_payment_method = 3
elif other_payment is True:
delivery_payment_method = 4
else:
delivery_payment_method = 5
if delivery_driver_id:
get_driver = (db.session
.query(Employee_Employee)
.filter(Employee_Employee.id == delivery_driver_id)
.first())
driver_l_name = get_driver.employee_last_name
driver_f_name = get_driver.employee_first_name
driver_emp_id = get_driver.id
else:
driver_l_name = ""
driver_f_name = ""
driver_emp_id = 0
try: try:
if request.json["credit_card_id"]: if request.json["credit_card_id"]:
card_payment_id = request.json["credit_card_id"] card_payment_id = request.json["credit_card_id"]
@@ -483,21 +666,19 @@ def create_a_delivery(user_id):
else: else:
card_id_from_customer = None card_id_from_customer = None
if cash_payment is True and card_payment is False:
delivery_payment_method = 0
elif card_payment is True and cash_payment is False:
delivery_payment_method = 1
elif card_payment is True and cash_payment is True:
delivery_payment_method = 2
else:
delivery_payment_method = 3
if customer_wants_fill is True: if customer_wants_fill is True:
customer_fill_up = 1 customer_fill_up = 1
gallons_ordered = 250 gallons_ordered = 250
else: else:
customer_fill_up = 0 customer_fill_up = 0
gallons_ordered = request.json["gallons_ordered"]
if emergency_info is True:
emergency_asked = 1
else:
emergency_asked = 0
if prime_info is True: if prime_info is True:
prime_asked = 1 prime_asked = 1
@@ -509,26 +690,33 @@ def create_a_delivery(user_id):
else: else:
same_day_asked = 0 same_day_asked = 0
date_object = datetime.strptime(when_to_deliver, '%Y-%m-%d').date()
customer_filled_name = get_customer.customer_last_name + ' ' + get_customer.customer_first_name customer_filled_name = get_customer.customer_first_name + ' ' + get_customer.customer_last_name
now = datetime.utcnow() now = datetime.utcnow()
# Pricing # Pricing
if customer_fill_up == 1: if customer_fill_up == 1:
# Fill
precharge_amount = (250 * get_today_price.price_for_customer) precharge_amount = (250 * get_today_price.price_for_customer)
else: else:
# Gallons
precharge_amount = int(gallons_ordered) * get_today_price.price_for_customer precharge_amount = int(gallons_ordered) * get_today_price.price_for_customer
# if prime/emergency/sameday
if same_day_asked == 1 and prime_asked == 0: if same_day_asked == 1 and prime_asked == 0:
total_precharge_amount = precharge_amount + get_service_prices.price_same_day total_precharge_amount = precharge_amount + get_today_price.price_same_day
elif prime_asked == 1 and same_day_asked == 0: elif prime_asked == 1 and same_day_asked == 0:
total_precharge_amount = precharge_amount + get_service_prices.price_prime total_precharge_amount = precharge_amount + get_today_price.price_prime
elif emergency_asked == 1:
total_precharge_amount = precharge_amount + get_today_price.price_emergency
else: else:
total_precharge_amount = precharge_amount + get_service_prices.price_prime + get_service_prices.price_same_day total_precharge_amount = precharge_amount + get_today_price.price_prime + get_today_price.price_same_day
new_delivery = Delivery_Delivery( new_delivery = Delivery_Delivery(
@@ -545,8 +733,9 @@ def create_a_delivery(user_id):
delivery_status=0, delivery_status=0,
when_ordered=now, when_ordered=now,
when_delivered=None, when_delivered=None,
expected_delivery_date=date_object, expected_delivery_date=when_to_deliver,
automatic=get_customer.customer_automatic, automatic=get_customer.customer_automatic,
automatic_id=None,
oil_id=get_today_price.id, oil_id=get_today_price.id,
supplier_price=get_today_price.price_from_supplier, supplier_price=get_today_price.price_from_supplier,
customer_price=get_today_price.price_for_customer, customer_price=get_today_price.price_for_customer,
@@ -554,14 +743,31 @@ def create_a_delivery(user_id):
dispatcher_notes=dispatcher_notes_taken, dispatcher_notes=dispatcher_notes_taken,
prime=prime_asked, prime=prime_asked,
same_day=same_day_asked, same_day=same_day_asked,
emergency=emergency_asked,
payment_type=delivery_payment_method, payment_type=delivery_payment_method,
payment_card_id=card_id_from_customer, payment_card_id=card_id_from_customer,
pre_charge_amount=total_precharge_amount, pre_charge_amount=total_precharge_amount,
total_price=precharge_amount, total_price=precharge_amount,
final_price=0, final_price=0,
driver_last_name=driver_l_name,
driver_first_name=driver_f_name,
driver_employee_id=driver_emp_id,
promo_id=promo_id_get_delivery,
promo_money_discount=promo_money_discount_get_delivery
) )
get_stats_customer = (db.session
.query(Stats_Customer)
.filter(Stats_Customer.customer_id == get_customer.id)
.first())
new_calls = int(get_stats_customer.total_calls) + 1
get_stats_customer.total_calls = new_calls
db.session.add(get_stats_customer)
db.session.add(new_delivery) db.session.add(new_delivery)
db.session.commit() db.session.commit()
@@ -582,7 +788,7 @@ def cancel_a_delivery(delivery_id):
.filter(Delivery_Delivery.id == delivery_id)\ .filter(Delivery_Delivery.id == delivery_id)\
.first() .first()
get_delivery.delivery_status = 3 get_delivery.delivery_status = 1
db.session.add(get_delivery) db.session.add(get_delivery)
db.session.commit() db.session.commit()
@@ -647,15 +853,15 @@ def delivery_note_driver(delivery_id):
driver_notes = request.json["driver_notes"] driver_notes = request.json["driver_notes"]
now = datetime.utcnow() now = datetime.utcnow()
user = db.session\ user = (db.session
.query(Auth_User)\ .query(Auth_User)
.filter(Auth_User.id == current_user.id)\ .filter(Auth_User.id == current_user.id)
.first() .first())
get_delivery = db.session\ get_delivery = (db.session
.query(Delivery_Delivery)\ .query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)\ .filter(Delivery_Delivery.id == delivery_id)
.first() .first())
create_new_note = Delivery_Notes_Driver( create_new_note = Delivery_Notes_Driver(
delivery_id=get_delivery.id, delivery_id=get_delivery.id,
@@ -672,7 +878,7 @@ def delivery_note_driver(delivery_id):
@delivery.route("/delete/<int:delivery_id>", methods=["DELETE"]) @delivery.route("/delete/<int:delivery_id>", methods=["DELETE"])
def service_delete_call(delivery_id): def delete_call(delivery_id):
""" """
delete a delivery call delete a delivery call
""" """
@@ -684,3 +890,76 @@ def service_delete_call(delivery_id):
db.session.commit() db.session.commit()
return jsonify({"ok": True}), 200 return jsonify({"ok": True}), 200
@delivery.route("/total/<int:delivery_id>", methods=["GET"])
def calculate_total(delivery_id):
"""
This will get deliveries not done
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
# Use gallons_delivered if available and > 0, otherwise use gallons_ordered
gallons = get_delivery.gallons_delivered if get_delivery.gallons_delivered and get_delivery.gallons_delivered > 0 else get_delivery.gallons_ordered
if get_delivery.promo_id:
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == get_delivery.promo_id)
.first())
discount_gallon_price = get_delivery.customer_price - get_promo_data.money_off_delivery
total_amount_after_discount = (gallons * discount_gallon_price)
discount = (gallons * get_delivery.customer_price) - total_amount_after_discount
else:
discount = 0
total_amount_after_discount = gallons * get_delivery.customer_price
get_price_query = (db.session
.query(Pricing_Oil_Oil)
.order_by(Pricing_Oil_Oil.date.desc())
.first())
if get_delivery.prime == 1:
priceprime = float(get_price_query.price_prime)
else:
priceprime = 0
if get_delivery.emergency == 1:
priceemergency = float(get_price_query.price_emergency)
else:
priceemergency = 0
if get_delivery.same_day == 1:
pricesameday = float(get_price_query.price_same_day)
else:
pricesameday = 0
# Recalculate base price using actual gallons
base_price = gallons * get_delivery.customer_price
total = float(base_price) + float(priceprime) + float(pricesameday) + float(priceemergency)
priceprime = round(priceprime, 2)
pricesameday = round(pricesameday, 2)
priceemergency = round(priceemergency, 2)
total_amount_after_discount = round(total_amount_after_discount, 2)
discount = round(discount, 2)
total = round(total, 2)
return jsonify({
"ok": True,
'priceprime': priceprime,
'pricesameday': pricesameday,
'priceemergency': priceemergency,
'total_amount':total,
'discount': discount,
'total_amount_after_discount':total_amount_after_discount,
}), 200

0
app/delivery_data/__init__.py Normal file → Executable file
View File

207
app/delivery_data/views.py Normal file → Executable file
View File

@@ -1,123 +1,160 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user from datetime import datetime
from datetime import date from decimal import Decimal
from app.delivery_data import delivery_data from app.delivery_data import delivery_data
from app import db from app import db
from datetime import datetime from app.classes.customer import Customer_Customer, Customer_Description
from app.classes.customer import Customer_Customer from app.classes.delivery import Delivery_Delivery
from app.classes.delivery import (Delivery_Delivery, from app.classes.employee import Employee_Employee
Delivery_Delivery_schema,
Delivery_Notes_Driver,
Delivery_Payment,
Delivery_Payment_schema,
)
from app.classes.cards import Card_Card from app.classes.cards import Card_Card
from app.classes.pricing import Pricing_Oil_Oil from app.classes.stats_employee import Stats_Employee_Oil
from app.classes.auth import Auth_User from app.classes.auto import Auto_Delivery
from app.classes.pricing import Pricing_Service_General from app.classes.stats_customer import Stats_Customer
@delivery_data.route("/pending", methods=["GET"])
def pending_delivery():
"""
Get deliveries that have been delivered
"""
delivery_ticket = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status!=0)
.all())
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(delivery_ticket))
@delivery_data.route("/finalize/<int:delivery_id>", methods=["PUT"]) @delivery_data.route("/finalize/<int:delivery_id>", methods=["PUT"])
def finalize_delivery(delivery_id): def office_finalize_delivery(delivery_id):
""" """
Get deliveries that have been delivered This will make a delivery finalized from the driver
""" """
""" """
Finalizes a delivery from office Finalizes a delivery from office
""" """
now = datetime.utcnow()
get_delivery = db.session \ get_delivery = db.session \
.query(Delivery_Delivery) \ .query(Delivery_Delivery) \
.filter(Delivery_Delivery.id == delivery_id) \ .filter(Delivery_Delivery.id == delivery_id) \
.first() .first()
get_today_price = db.session \
.query(Pricing_Oil_Oil) \
.order_by(Pricing_Oil_Oil.id.desc()) \
.first()
get_customer = db.session \ get_customer = db.session \
.query(Customer_Customer) \ .query(Customer_Customer) \
.filter(Customer_Customer.id == get_delivery.customer_id) \ .filter(Customer_Customer.id == get_delivery.customer_id) \
.first() .first()
get_customer_description = db.session \
.query(Customer_Description) \
.filter(Customer_Description.customer_id == get_delivery.customer_id) \
.first()
if get_customer_description is None:
new_customer_desc = Customer_Description(
customer_id = get_customer.id,
account_number =get_customer.account_number,
company_id = get_customer.company_id,
fill_location = None,
description = None,
)
db.session.add(new_customer_desc)
db.session.flush()
get_customer_description = db.session \
.query(Customer_Description) \
.filter(Customer_Description.customer_id == get_delivery.customer_id) \
.first()
#TODO hardcode for now
delivery_driver_id = 2
get_driver = (db.session
.query(Employee_Employee)
.filter(Employee_Employee.id == delivery_driver_id)
.first())
get_stats_employee = (db.session
.query(Stats_Employee_Oil)
.filter(Stats_Employee_Oil.employee_id == get_delivery.driver_employee_id)
.first())
get_stats_customer = (db.session
.query(Stats_Customer)
.filter(Stats_Customer.customer_id == get_customer.id)
.first())
if get_stats_customer is None:
create_stats_customer = Stats_Customer(
customer_id = get_customer.id,
total_calls = 1,
service_calls_total = 0,
service_calls_total_spent = 0,
service_calls_total_profit = 0,
oil_deliveries = 1,
oil_total_gallons = 0,
oil_total_spent = 0,
oil_total_profit = 0,
)
db.session.add(create_stats_customer)
db.session.flush()
get_stats_customer = (db.session
.query(Stats_Customer)
.filter(Stats_Customer.customer_id == get_customer.id)
.first())
if get_stats_employee is None:
create_stats = Stats_Employee_Oil(
employee_id = get_delivery.driver_employee_id,
total_deliveries = 0,
total_gallons_delivered = 0,
total_primes = 0,
oil_total_profit_delivered = 0,
)
db.session.add(create_stats)
db.session.flush()
get_stats_employee = (db.session
.query(Stats_Employee_Oil)
.filter(Stats_Employee_Oil.employee_id == get_delivery.driver_employee_id)
.first())
if request.json["cash_recieved"]:
cash_amount = request.json["cash_recieved"]
else:
cash_amount = None
gallons_delivered = request.json["gallons_delivered"] gallons_delivered = request.json["gallons_delivered"]
card_payment = request.json["credit"] check_number = request.json["check_number"]
cash_payment = request.json["cash"] fill_location = request.json["fill_location"]
if request.json["credit_card_id"]:
card_payment_id = request.json["credit_card_id"]
else:
card_payment_id = None
if card_payment_id is not None: # update driver
get_card = (db.session get_delivery.driver_last_name = get_driver.employee_last_name
.query(Card_Card) get_delivery.driver_first_name = get_driver.employee_first_name
.filter(Card_Card.id == card_payment_id) get_delivery.driver_employee_id = get_driver.id
.filter(Card_Card.user_id == get_customer.id)
.first())
card_id_from_customer = get_card.id
else:
card_id_from_customer = None
if cash_payment is True and card_payment is False:
delivery_payment_method = 0
elif card_payment is True and cash_payment is False:
delivery_payment_method = 1
elif card_payment is True and cash_payment is True:
delivery_payment_method = 2
else:
delivery_payment_method = 3
same_day_info = request.json["same_day"]
if same_day_info is True:
same_day_info = 1
else:
same_day_info = 0
prime_info = request.json["prime"]
if prime_info is True:
prime_info = 1
else:
prime_info = 0
# update delivery
get_delivery.when_delivered = now
get_delivery.gallons_delivered = gallons_delivered get_delivery.gallons_delivered = gallons_delivered
get_delivery.prime = prime_info get_delivery.cash_recieved = cash_amount
get_delivery.same_day = same_day_info get_delivery.check_number = check_number
get_delivery.payment_type = delivery_payment_method
get_delivery.payment_card_id = card_id_from_customer
get_delivery.delivery_status = 10 get_delivery.delivery_status = 10
# update stats employee
current_deliveres = get_stats_employee.total_deliveries + 1
get_stats_employee.total_deliveries = current_deliveres
current_gallons_delivered = Decimal(get_stats_employee.total_gallons_delivered) + Decimal(gallons_delivered)
get_stats_employee.total_gallons_delivered = current_gallons_delivered
# update stats customer
current_deliveres = int(get_stats_customer.oil_deliveries) + 1
get_stats_customer.oil_deliveries = current_deliveres
new_gallons = Decimal(get_stats_customer.oil_total_gallons) + Decimal(gallons_delivered)
get_stats_customer.oil_total_gallons = new_gallons
# update fill location
get_customer_description.fill_location = fill_location
db.session.add(get_customer_description)
db.session.add(get_stats_customer)
db.session.add(get_stats_employee)
db.session.add(get_delivery) db.session.add(get_delivery)
db.session.commit() db.session.commit()
return jsonify({ return jsonify({
"ok": True, "ok": True,
'delivery': { 'delivery': {
'id': get_delivery.id, 'id': get_delivery.id,
}, },
}), 200 }), 200

View File

@@ -0,0 +1,7 @@
# coding=utf-8
from flask import Blueprint
deliverystatus = Blueprint('delivery_status', __name__)
from . import views

67
app/delivery_status/views.py Executable file
View File

@@ -0,0 +1,67 @@
from flask import jsonify
from datetime import date, timedelta
from app.delivery_status import deliverystatus
from app import db
from app.classes.delivery import (Delivery_Delivery,
Delivery_Delivery_schema,
)
from app.classes.service import Service_Service
from app.classes.auto import Auto_Delivery
from app.classes.transactions import Transaction
from datetime import date, timedelta, datetime
from zoneinfo import ZoneInfo
# --- NEW EFFICIENT ENDPOINT ---
@deliverystatus.route("/stats/sidebar-counts", methods=["GET"])
def get_sidebar_counts():
"""
Efficiently gets all counts needed for the navigation sidebar in a single request.
This combines logic from all the individual /count/* endpoints.
"""
try:
eastern = ZoneInfo("America/New_York")
now_local = datetime.now(eastern).replace(tzinfo=None) # naive local time
today_date = datetime.now(eastern).date() # local date
# Replicate the logic from each of your /count/* endpoints
today_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 2).count()
tomorrow_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 3).count()
waiting_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 0).count()
pending_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 9).count()
automatic_count = db.session.query(Auto_Delivery).filter(Auto_Delivery.estimated_gallons_left <= 80).count()
start_of_today = datetime.combine(today_date, datetime.min.time())
start_of_tomorrow = datetime.combine(today_date + timedelta(days=1), datetime.min.time())
today_service_count = db.session.query(Service_Service).filter(
Service_Service.scheduled_date >= start_of_today,
Service_Service.scheduled_date < start_of_tomorrow
).count()
# Upcoming service calls start from tomorrow, not today
upcoming_service_count = db.session.query(Service_Service).filter(Service_Service.scheduled_date >= start_of_tomorrow).count()
transaction_count = db.session.query(Transaction).filter(Transaction.transaction_type == 0).count()
return jsonify({
"ok": True,
"counts": {
"today": today_count,
"tomorrow": tomorrow_count,
"waiting": waiting_count,
"pending": pending_count,
"automatic": automatic_count,
"upcoming_service": upcoming_service_count,
"today_service": today_service_count,
"transaction": transaction_count,
}
}), 200
except Exception as e:
# Basic error handling
return jsonify({"ok": False, "error": str(e)}), 500

0
app/employees/__init__.py Normal file → Executable file
View File

63
app/employees/views.py Normal file → Executable file
View File

@@ -1,20 +1,39 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user
from sqlalchemy import or_ from sqlalchemy import or_
from datetime import date, timedelta
from flask_login import login_required from flask_login import login_required
from app.employees import employees from app.employees import employees
from app import db from app import db
from datetime import datetime
from app.classes.employee import Employee_Employee, Employee_Employee_schema from app.classes.employee import Employee_Employee, Employee_Employee_schema
from app.classes.auth import Auth_User
from app.classes.stats_employee import Stats_Employee_Oil, Stats_Employee_Office
@employees.route("/<int:userid>", methods=["GET"]) @employees.route("/<int:userid>", methods=["GET"])
@login_required @login_required
def get_specific_employee(userid): def get_specific_employee(userid):
employee = db.session \ employee = db.session \
.query(Employee_Employee) \ .query(Employee_Employee) \
.filter(Employee_Employee.id == userid) \ .filter(Employee_Employee.user_id == userid) \
.first()
# Get active status from Auth_User
user = db.session.query(Auth_User).filter(Auth_User.id == userid).first()
active_status = user.active if user else 1
employee_schema = Employee_Employee_schema(many=False)
employee_data = employee_schema.dump(employee)
employee_data['active'] = active_status
return jsonify(employee_data)
@employees.route("/byid/<int:employee_id>", methods=["GET"])
@login_required
def get_employee_by_id(employee_id):
employee = db.session \
.query(Employee_Employee) \
.filter(Employee_Employee.id == employee_id) \
.first() .first()
employee_schema = Employee_Employee_schema(many=False) employee_schema = Employee_Employee_schema(many=False)
return jsonify(employee_schema.dump(employee)) return jsonify(employee_schema.dump(employee))
@@ -27,7 +46,6 @@ def get_specific_employee_user_id(userid):
.query(Employee_Employee) \ .query(Employee_Employee) \
.filter(Employee_Employee.user_id == userid) \ .filter(Employee_Employee.user_id == userid) \
.first() .first()
# print(employee.id)
employee_schema = Employee_Employee_schema(many=False) employee_schema = Employee_Employee_schema(many=False)
return jsonify(employee_schema.dump(employee)) return jsonify(employee_schema.dump(employee))
@@ -69,9 +87,11 @@ def all_employees():
def all_employees_drivers(): def all_employees_drivers():
employee_list = db.session \ employee_list = db.session \
.query(Employee_Employee) \ .query(Employee_Employee) \
.filter(Employee_Employee.employee_type == 4) \ .filter(or_(Employee_Employee.employee_type == 4,
Employee_Employee.employee_type == 8)) \
.all() .all()
customer_schema = Employee_Employee_schema(mwany=True)
customer_schema = Employee_Employee_schema(many=True)
return jsonify(customer_schema.dump(employee_list)) return jsonify(customer_schema.dump(employee_list))
@@ -130,8 +150,25 @@ def employee_create():
employee_phone_number=e_phone_number, employee_phone_number=e_phone_number,
) )
db.session.add(new_employee) db.session.add(new_employee)
db.session.commit() db.session.flush()
new_stats_office = Stats_Employee_Office(
employee_id = new_employee.id,
total_orders = 0,
total_credit_cards_proccessed = 0,
)
db.session.add(new_stats_office)
new_stats_oil = Stats_Employee_Oil(
employee_id = new_employee.id,
total_deliveries = 0,
total_gallons_delivered = 0,
total_primes = 0,
oil_total_profit_delivered = 0,
)
db.session.add(new_stats_oil)
return jsonify({"ok": True, return jsonify({"ok": True,
'user_id': new_employee.id, 'user_id': new_employee.id,
@@ -152,7 +189,7 @@ def employee_edit(employee_id):
e_type = request.json["employee_type"] e_type = request.json["employee_type"]
e_start_date = request.json["employee_start_date"] e_start_date = request.json["employee_start_date"]
e_end_date = request.json["employee_end_date"] e_end_date = request.json["employee_end_date"]
print(request.json["employee_end_date"]) e_active = request.json.get("active", 1)
get_employee = db.session \ get_employee = db.session \
.query(Employee_Employee) \ .query(Employee_Employee) \
@@ -170,6 +207,12 @@ def employee_edit(employee_id):
if e_end_date != 'None': if e_end_date != 'None':
get_employee.employee_end_date = e_end_date get_employee.employee_end_date = e_end_date
# Update active status in Auth_User
user = db.session.query(Auth_User).filter(Auth_User.id == get_employee.user_id).first()
if user:
user.active = int(e_active)
db.session.add(user)
db.session.add(get_employee) db.session.add(get_employee)
db.session.commit() db.session.commit()

0
app/info/__init__.py Normal file → Executable file
View File

60
app/info/views.py Normal file → Executable file
View File

@@ -1,37 +1,65 @@
from flask import jsonify from flask import jsonify
from decimal import Decimal
from app.info import info from app.info import info
from app import db from app import db
from app.classes.pricing import Pricing_Oil_Oil, Pricing_Service_General from app.classes.pricing import Pricing_Oil_Oil, Pricing_Oil_Oil_schema
from app.classes.admin import Admin_Company from app.classes.admin import Admin_Company
from app.classes.delivery import Delivery_Delivery
from app.classes.service import Service_Service
@info.route("/price/oil/tiers", methods=["GET"])
def get_pricing_tiers():
get_price_query = (db.session
.query(Pricing_Oil_Oil)
.order_by(Pricing_Oil_Oil.date.desc())
.first())
if not get_price_query:
return jsonify({"error": "No pricing data available"}), 404
# Get the single price per gallon from the database, e.g., Decimal('2.92')
price_per_gallon = get_price_query.price_for_customer
# Define the specific gallon amounts you want to display totals for
gallon_tiers = [100, 125, 150, 175, 200, 220]
# Calculate the total price for each gallon amount by multiplication
pricing_totals = {
gallons: price_per_gallon * gallons
for gallons in gallon_tiers
}
# Return the dictionary of totals
return jsonify(pricing_totals)
@info.route("/price/oil", methods=["GET"]) @info.route("/price/oil", methods=["GET"])
def get_oil_price(): def get_oil_price_today():
get_price_query = (db.session get_price_query = (db.session
.query(Pricing_Oil_Oil) .query(Pricing_Oil_Oil)
.order_by(Pricing_Oil_Oil.date.desc()) .order_by(Pricing_Oil_Oil.date.desc())
.first()) .first())
return jsonify({"ok": True, return jsonify({"ok": True,
'price': get_price_query.price_for_customer, 'price_from_supplier': get_price_query.price_from_supplier,
'price_for_customer': get_price_query.price_for_customer,
'price_for_employee': get_price_query.price_for_employee,
'price_same_day': get_price_query.price_same_day,
'price_prime': get_price_query.price_prime,
'price_emergency': get_price_query.price_emergency,
}), 200 }), 200
@info.route("/price/service", methods=["GET"]) @info.route("/price/oil/table", methods=["GET"])
def get_service_price(): def get_pricing():
get_price_query = (db.session get_price_query = (db.session
.query(Pricing_Service_General) .query(Pricing_Oil_Oil)
.order_by(Pricing_Service_General.date.desc()) .order_by(Pricing_Oil_Oil.date.desc())
.first()) .first())
delivery_schema = Pricing_Oil_Oil_schema(many=False)
return jsonify(delivery_schema.dump(get_price_query))
return jsonify({"ok": True,
'same_day': get_price_query.price_same_day,
'price_hourly': get_price_query.price_service_hour,
'emergency_fee': get_price_query.price_emergency_call,
'emergency_rate': get_price_query.price_emergency_service_hour,
'prime': get_price_query.price_prime,
'cleaning': get_price_query.price_cleaning,
'out_of_oil': get_price_query.price_out_of_oil,
}), 200
@info.route("/company", methods=["GET"]) @info.route("/company", methods=["GET"])

0
app/main/__init__.py Normal file → Executable file
View File

6
app/main/views.py Normal file → Executable file
View File

@@ -1,6 +1,10 @@
from flask import jsonify, Response from flask import jsonify, Response, url_for
from app import app from app import app
@app.route("/favicon.ico")
def favicon():
return url_for('static', filename='data:,')
@app.route('/robots.txt') @app.route('/robots.txt')
@app.route('/sitemap.xml') @app.route('/sitemap.xml')

8
app/money/__init__.py Normal file
View File

@@ -0,0 +1,8 @@
# coding=utf-8
from flask import Blueprint
money = Blueprint('money', __name__)
from . import views

83
app/money/views.py Normal file
View File

@@ -0,0 +1,83 @@
from flask import jsonify
from app.money import money
from app import db
import datetime
from datetime import date
from app.classes.money import Money_delivery, Money_delivery_schema
from app.classes.delivery import Delivery_Delivery, Delivery_Delivery_schema
def get_monday_date(date_object):
"""Gets the date of the Monday for the given date."""
# Get the day of the week as an integer (0 = Monday, 6 = Sunday)
day_of_week = date_object.weekday()
# Calculate the number of days to subtract to get to Monday
days_to_monday = day_of_week - 0 # Monday is 0
# Subtract the days from the given date to get Monday's date
monday_date = date_object - datetime.timedelta(days=days_to_monday)
return monday_date
@money.route("/profit/week", methods=["GET"])
def total_profit_week():
# Get today's date
total_profit = 0
total_deliveries = 0
today = datetime.date.today()
# Get the date of the Monday for today
monday = get_monday_date(today)
get_total = (db.session
.query(Money_delivery)
.filter(Money_delivery.time_added >= monday)
.filter(Money_delivery.time_added <= today)
.all())
for f in get_total:
total_profit = total_profit + f.total_profit
total_deliveries = total_deliveries + 1
return jsonify({"ok": True,
'total_profit': total_profit,
'total_deliveries': total_deliveries
}), 200
@money.route("/profit/year", methods=["GET"])
def total_profit_year():
# Get today's date
total_profit = 0
first_of_year = date(date.today().year, 1, 1)
last_of_year = date(date.today().year, 12, 31)
# Get the date of the Monday for today
get_total = (db.session
.query(Money_delivery)
.filter(Money_delivery.time_added >= first_of_year)
.filter(Money_delivery.time_added <= last_of_year)
.all())
for f in get_total:
total_profit = total_profit + f.total_profit
return jsonify({"ok": True,
'total': total_profit
}), 200
@money.route("/<int:delivery_id>", methods=["GET"])
def get_money_delivery(delivery_id):
"""
"""
profit = (db.session
.query(Money_delivery)
.filter(Money_delivery.delivery_id == delivery_id)
.first())
money_schema = Money_delivery_schema(many=False)
return jsonify(money_schema.dump(profit))

0
app/payment/__init__.py Normal file → Executable file
View File

580
app/payment/views.py Normal file → Executable file
View File

@@ -3,32 +3,51 @@ from app.payment import payment
from app import db from app import db
from app.classes.customer import Customer_Customer from app.classes.customer import Customer_Customer
from app.classes.cards import Card_Card, Card_Card_schema from app.classes.cards import Card_Card, Card_Card_schema
from flask_login import current_user from app.classes.transactions import Transaction
from app.classes.delivery import Delivery_Delivery
from app.classes.service import Service_Service, Service_Service_schema
def set_card_main(user_id):
def set_card_main(user_id, card_id):
""" """
updates a card of a user updates a card of a user
""" """
get_card_count = db.session \ get_card_count = (
.query(Card_Card) \ db.session
.filter(Card_Card.user_id == user_id) \ .query(Card_Card)
.filter(Card_Card.user_id == user_id)
.count() .count()
print(get_card_count) )
if get_card_count > 0:
print("true")
get_old_card = db.session \ get_card = (
.query(Card_Card) \ db.session
.filter(Card_Card.main_card == True) \ .query(Card_Card)
.filter(Card_Card.user_id == user_id) \ .filter(Card_Card.user_id == user_id)
.filter(Card_Card.id == card_id)
.first() .first()
print(get_old_card.id) )
if get_card_count > 0:
get_old_card = (
db.session
.query(Card_Card)
.filter(Card_Card.main_card == True)
.filter(Card_Card.user_id == user_id)
.first()
)
get_old_card.main_card = False get_old_card.main_card = False
get_card.main_card = True
db.session.add(get_old_card) db.session.add(get_old_card)
db.session.commit()
else:
get_card.main_card = True
db.session.add(get_card)
db.session.commit() db.session.commit()
@@ -37,10 +56,10 @@ def get_user_cards(user_id):
""" """
gets all cards of a user gets all cards of a user
""" """
get_u_cards = db.session \ get_u_cards = (db.session
.query(Card_Card) \ .query(Card_Card)
.filter(Card_Card.user_id == user_id) \ .filter(Card_Card.user_id == user_id)
.all() .all())
card_schema = Card_Card_schema(many=True) card_schema = Card_Card_schema(many=True)
return jsonify(card_schema.dump(get_u_cards)) return jsonify(card_schema.dump(get_u_cards))
@@ -52,10 +71,10 @@ def get_user_cards_count(user_id):
gets all cards of a user gets all cards of a user
""" """
get_u_cards = db.session \ get_u_cards = (db.session
.query(Card_Card) \ .query(Card_Card)
.filter(Card_Card.user_id == user_id) \ .filter(Card_Card.user_id == user_id)
.count() .count())
return jsonify({ return jsonify({
"ok": True, "ok": True,
@@ -69,56 +88,15 @@ def get_user_specific_card(card_id):
gets a specific card of a user gets a specific card of a user
""" """
get_user_card = db.session \ get_user_card = (db.session
.query(Card_Card) \ .query(Card_Card)
.filter(Card_Card.id == card_id) \ .filter(Card_Card.id == card_id)
.first() .first())
card_schema = Card_Card_schema(many=False) card_schema = Card_Card_schema(many=False)
return jsonify(card_schema.dump(get_user_card)) return jsonify(card_schema.dump(get_user_card))
@payment.route("/card/create/<int:user_id>", methods=["POST"])
def create_user_card(user_id):
"""
adds a card of a user
"""
get_customer = db.session \
.query(Customer_Customer) \
.filter(Customer_Customer.id == user_id) \
.first()
name_on_card = request.json["card_name"]
expiration_month = request.json["expiration_month"]
expiration_year = request.json["expiration_year"]
type_of_card = request.json["type_of_card"]
security_number = request.json["security_number"]
main_card = request.json["main_card"]
card_number = request.json["card_number"]
last_four = card_number[-4]
create_new_card = Card_Card(
user_id=get_customer.id,
card_number=card_number,
last_four_digits=last_four,
name_on_card=name_on_card,
expiration_month=expiration_month,
expiration_year=expiration_year,
type_of_card=type_of_card,
security_number=security_number,
accepted_or_declined=None,
main_card=main_card,
)
print(main_card)
if main_card is True:
set_card_main(user_id=get_customer.id)
db.session.add(create_new_card)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/main/<int:card_id>/<int:user_id>", methods=["PUT"]) @payment.route("/card/main/<int:card_id>/<int:user_id>", methods=["PUT"])
def set_main_card(user_id, card_id): def set_main_card(user_id, card_id):
@@ -126,17 +104,18 @@ def set_main_card(user_id, card_id):
updates a card of a user updates a card of a user
""" """
get_new_main_card = db.session \ get_new_main_card = (db.session
.query(Card_Card) \ .query(Card_Card)
.filter(Card_Card.user_id == user_id) \ .filter(Card_Card.user_id == user_id)
.filter(Card_Card.id == card_id) \ .filter(Card_Card.id == card_id)
.first() .first())
get_other_card = (db.session
.query(Card_Card)
.filter(Card_Card.main_card == True)
.filter(Card_Card.user_id == user_id)
.first())
get_other_card = db.session \
.query(Card_Card) \
.filter(Card_Card.main_card == True) \
.filter(Card_Card.user_id == user_id) \
.first()
if get_other_card is not None: if get_other_card is not None:
get_other_card.main_card = False get_other_card.main_card = False
db.session.add(get_other_card) db.session.add(get_other_card)
@@ -148,45 +127,6 @@ def set_main_card(user_id, card_id):
return jsonify({"ok": True}), 200 return jsonify({"ok": True}), 200
@payment.route("/card/edit/<int:card_id>", methods=["PUT"])
def update_user_card(card_id):
"""
edits a card
"""
get_card = db.session \
.query(Card_Card) \
.filter(Card_Card.id == card_id) \
.first()
get_customer = db.session \
.query(Customer_Customer) \
.filter(Customer_Customer.id == get_card.user_id) \
.first()
name_on_card = request.json["card_name"]
expiration_month = request.json["expiration_month"]
expiration_year = request.json["expiration_year"]
type_of_card = request.json["type_of_card"]
security_number = request.json["security_number"]
card_number = request.json["card_number"]
main_card = request.json["main_card"]
get_card.user_id = get_customer.id
get_card.card_number = card_number
get_card.name_on_card = name_on_card
get_card.expiration_month = expiration_month
get_card.expiration_year = expiration_year
get_card.type_of_card = type_of_card
get_card.security_number = security_number
get_card.main_card = main_card
if main_card is True:
set_card_main(user_id=get_customer.id, card_id=get_card.id)
db.session.add(get_card)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/remove/<int:card_id>", methods=["DELETE"]) @payment.route("/card/remove/<int:card_id>", methods=["DELETE"])
@@ -195,12 +135,414 @@ def remove_user_card(card_id):
removes a card removes a card
""" """
get_card = db.session \ get_card = (db.session
.query(Card_Card) \ .query(Card_Card)
.filter(Card_Card.id == card_id) \ .filter(Card_Card.id == card_id)
.first() .first())
db.session.delete(get_card) db.session.delete(get_card)
db.session.commit() db.session.commit()
return jsonify({"ok": True}), 200 return jsonify({"ok": True}), 200
# In your Flask payment routes file (e.g., app/routes/payment.py)
# ... (your existing imports: jsonify, request, db, Customer_Customer, Card_Card) ...
@payment.route("/card/create/<int:user_id>", methods=["POST"])
def create_user_card(user_id):
"""
Adds a card for a user to the local database. This is its only job.
"""
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == user_id)
.first())
if not get_customer:
return jsonify({"ok": False, "error": "Customer not found"}), 404
data = request.get_json()
name_on_card = data.get("name_on_card")
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
card_number = data.get("card_number")
last_four = card_number[-4:] if card_number else ""
try:
create_new_card = Card_Card(
user_id=get_customer.id,
card_number=card_number,
last_four_digits=last_four,
name_on_card=name_on_card,
expiration_month=expiration_month,
expiration_year=expiration_year,
type_of_card=type_of_card,
security_number=security_number,
accepted_or_declined=None, # This is correct, as we don't know the status yet
main_card=main_card,
zip_code=zip_code
)
db.session.add(create_new_card)
db.session.flush()
if main_card:
# Assuming set_card_main is another function you have
set_card_main(user_id=get_customer.id, card_id=create_new_card.id)
db.session.commit()
print(f"SUCCESS: Card saved locally for user {user_id} with new ID {create_new_card.id}")
except Exception as e:
db.session.rollback()
print(f"DATABASE ERROR: Could not save card for user {user_id}. Error: {e}")
return jsonify({"ok": False, "error": "Failed to save card information."}), 500
# Return a success response with the card_id
return jsonify({"ok": True, "card_id": create_new_card.id}), 200
@payment.route("/card/update_payment_profile/<int:card_id>", methods=["PUT"])
def update_card_payment_profile(card_id):
"""
Updates the auth_net_payment_profile_id for a card
"""
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_id)
.first())
if not get_card:
return jsonify({"ok": False, "error": "Card not found"}), 404
data = request.get_json()
auth_net_payment_profile_id = data.get("auth_net_payment_profile_id")
get_card.auth_net_payment_profile_id = auth_net_payment_profile_id
db.session.add(get_card)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/edit/<int:card_id>", methods=["PUT"])
def update_user_card(card_id):
"""
edits a card
"""
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_id)
.first())
if not get_card:
return jsonify({"ok": False, "error": "Card not found"}), 404
data = request.get_json()
# FIX: Use .get() for safety and get the correct key 'name_on_card'
name_on_card = data.get("name_on_card") # <-- This now matches the frontend
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
card_number = data.get("card_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
auth_net_payment_profile_id = data.get("auth_net_payment_profile_id")
get_card.card_number = card_number
get_card.name_on_card = name_on_card
get_card.expiration_month = expiration_month
get_card.expiration_year = expiration_year
get_card.type_of_card = type_of_card
get_card.security_number = security_number
get_card.main_card = main_card
get_card.zip_code = zip_code
get_card.auth_net_payment_profile_id = auth_net_payment_profile_id
# FIX: Correctly slice the last four digits on edit
if card_number and card_number[-4:].isdigit():
get_card.last_four_digits = int(card_number[-4:])
if main_card:
set_card_main(user_id=get_card.user_id, card_id=get_card.id)
db.session.add(get_card)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/transactions/authorize/<int:page>", methods=["GET"])
def get_authorize_transactions(page):
"""
Gets transactions with transaction_type = 0 (charge), for the authorize page
"""
try:
per_page = 50
offset = (page - 1) * per_page
query = (
db.session
.query(Transaction, Customer_Customer)
.join(Customer_Customer, Transaction.customer_id == Customer_Customer.id)
.order_by(Transaction.created_at.desc())
.offset(offset)
.limit(per_page)
)
results = query.all()
transactions_data = []
for transaction, customer in results:
transactions_data.append({
"id": transaction.id,
"preauthorize_amount": transaction.preauthorize_amount,
"charge_amount": transaction.charge_amount,
"transaction_type": transaction.transaction_type,
"status": transaction.status,
"customer_name": f"{customer.customer_first_name} {customer.customer_last_name}",
"created_at": transaction.created_at.isoformat(),
"auth_net_transaction_id": transaction.auth_net_transaction_id,
"rejection_reason": transaction.rejection_reason,
"delivery_id": transaction.delivery_id,
"service_id": transaction.service_id,
"auto_id": transaction.auto_id,
})
return jsonify(transactions_data), 200
except Exception as e:
return jsonify({"ok": False, "error": str(e)}), 500
@payment.route("/authorize/cleanup/<int:customer_id>", methods=["POST"])
def cleanup_authorize_profile(customer_id):
"""
Clean up Authorize.Net profile data in local database when API check fails.
Sets customer auth_net_profile_id to null and clears all card payment profile IDs.
"""
try:
# Get customer and set auth_net_profile_id to null
customer = db.session.query(Customer_Customer).filter(Customer_Customer.id == customer_id).first()
if not customer:
return jsonify({"ok": False, "error": "Customer not found"}), 404
customer.auth_net_profile_id = None
# Get all cards for this customer and set their auth_net_payment_profile_id to null
cards = db.session.query(Card_Card).filter(Card_Card.user_id == customer_id).all()
for card in cards:
card.auth_net_payment_profile_id = None
# Commit all changes
db.session.commit()
return jsonify({"ok": True, "message": f"Cleaned up Authorize.Net data for customer {customer_id}"}), 200
except Exception as e:
db.session.rollback()
return jsonify({"ok": False, "error": f"Failed to cleanup profile: {str(e)}"}), 500
@payment.route("/authorize/<int:delivery_id>", methods=["PUT"])
def update_delivery_payment_authorize(delivery_id):
"""
Update a delivery's payment_type to 11 (CC - Authorize API) after successful preauthorization
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
if not get_delivery:
return jsonify({"ok": False, "error": "Delivery not found"}), 404
get_delivery.payment_type = 11
db.session.add(get_delivery)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/transaction/delivery/<int:delivery_id>", methods=["GET"])
def get_transaction_by_delivery(delivery_id):
"""
Get a single transaction by delivery_id for Authorize.net payments
"""
transaction = (db.session
.query(Transaction)
.filter(Transaction.delivery_id == delivery_id)
.first())
if not transaction:
return jsonify({"ok": False, "error": "Transaction not found"}), 404
# Convert to dict-like format for frontend
return jsonify({
"ok": True,
"transaction": {
"id": transaction.id,
"preauthorize_amount": float(transaction.preauthorize_amount or 0),
"charge_amount": float(transaction.charge_amount or 0),
"transaction_type": transaction.transaction_type,
"status": transaction.status,
"auth_net_transaction_id": transaction.auth_net_transaction_id,
"delivery_id": transaction.delivery_id,
"customer_id": transaction.customer_id,
"service_id": transaction.service_id,
"card_id": transaction.card_id,
"auto_id": transaction.auto_id,
"created_at": transaction.created_at.isoformat() if transaction.created_at else None
}
})
@payment.route("/transactions/customer/<int:customer_id>/<int:page>", methods=["GET"])
def get_customer_transactions(customer_id, page):
"""
Gets transactions for a specific customer
"""
try:
per_page = 50
offset = (page - 1) * per_page
query = (
db.session
.query(Transaction)
.filter(Transaction.customer_id == customer_id)
.order_by(Transaction.created_at.desc())
.offset(offset)
.limit(per_page)
)
results = query.all()
transactions_data = []
for transaction in results:
transactions_data.append({
"id": transaction.id,
"preauthorize_amount": transaction.preauthorize_amount,
"charge_amount": transaction.charge_amount,
"transaction_type": transaction.transaction_type,
"status": transaction.status,
"created_at": transaction.created_at.isoformat(),
"auth_net_transaction_id": transaction.auth_net_transaction_id,
"rejection_reason": transaction.rejection_reason,
"delivery_id": transaction.delivery_id,
"service_id": transaction.service_id,
"auto_id": transaction.auto_id,
})
return jsonify(transactions_data), 200
except Exception as e:
return jsonify({"ok": False, "error": str(e)}), 500
@payment.route("/service/payment/<int:service_id>/<int:payment_type>", methods=["PUT"])
def process_service_payment_tiger(service_id, payment_type):
service = db.session.query(Service_Service).filter(Service_Service.id == service_id).first()
if not service:
return jsonify({"ok": False, "error": "Service not found"}), 404
# Set payment columns as specified
service.payment_type = payment_type # 1 for Tiger
service.payment_status = 2 # As specified
# payment_card_id retains the selected card's ID if set in the service record
try:
db.session.commit()
return jsonify({"ok": True}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@payment.route("/authorize/service/<int:service_id>", methods=["PUT"])
def update_service_payment_authorize(service_id):
service = db.session.query(Service_Service).filter(Service_Service.id == service_id).first()
if not service:
return jsonify({"error": "Service not found"}), 404
data = request.get_json()
card_id = data.get('card_id')
status = data.get('status', 0)
service.payment_type = 11 # CC - Authorize
service.payment_status = status
if card_id:
service.payment_card_id = card_id
try:
db.session.commit()
return jsonify({"ok": True}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@payment.route("/capture/service/<int:service_id>", methods=["PUT"])
def update_service_payment_capture(service_id):
service = db.session.query(Service_Service).filter(Service_Service.id == service_id).first()
if not service:
return jsonify({"error": "Service not found"}), 404
data = request.get_json()
card_id = data.get('card_id')
status = data.get('status', 3) # Default to 3 for capture
if service.payment_type is None or service.payment_type != 11:
service.payment_type = 11 # CC - Authorize
service.payment_status = status # 3 for capture
if card_id:
service.payment_card_id = card_id
try:
db.session.commit()
return jsonify({"ok": True}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@payment.route("/transactions/service/<int:service_id>", methods=["GET"])
def get_service_transactions(service_id):
"""
Gets all transactions for a specific service ID
"""
try:
query = (
db.session
.query(Transaction)
.filter(Transaction.service_id == service_id)
.order_by(Transaction.created_at.desc())
)
transactions = query.all()
transactions_data = []
for transaction in transactions:
transactions_data.append({
"id": transaction.id,
"preauthorize_amount": float(transaction.preauthorize_amount or 0),
"charge_amount": float(transaction.charge_amount or 0),
"transaction_type": transaction.transaction_type,
"status": transaction.status,
"created_at": transaction.created_at.isoformat() if transaction.created_at else None,
"auth_net_transaction_id": transaction.auth_net_transaction_id,
"rejection_reason": transaction.rejection_reason,
"delivery_id": transaction.delivery_id,
"service_id": transaction.service_id,
"card_id": transaction.card_id,
})
return jsonify(transactions_data), 200
except Exception as e:
print(f"Error fetching transactions for service {service_id}: {str(e)}")
return jsonify({"ok": False, "error": str(e)}), 500

7
app/promo/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
# coding=utf-8
from flask import Blueprint
promo = Blueprint('promo', __name__)
from . import views

182
app/promo/views.py Normal file
View File

@@ -0,0 +1,182 @@
from flask import request, jsonify
import decimal
from datetime import datetime
from app.promo import promo
from app import db
from app.classes.promo import (
Promo_Promo,
Promo_Promo_schema)
from app.classes.delivery import (Delivery_Delivery,
Delivery_Delivery_schema,
Delivery_Notes_Driver,
)
def convert_to_decimal(text):
try:
number = float(text)
return round(number, 2)
except ValueError:
return "0"
@promo.route("/<int:promo_id>", methods=["GET"])
def get_promo(promo_id):
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
query_schema = Promo_Promo_schema(many=False)
return jsonify(query_schema.dump(get_promo_data))
@promo.route("/promoprice/<int:delivery_id>", methods=["GET"])
def get_promo_price(delivery_id):
"""
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.id == delivery_id)
.first())
price = get_delivery.customer_price - get_delivery.promo_money_discount
price = convert_to_decimal(price)
return jsonify({
"ok": True,
"price": price,
}), 200
@promo.route("/all", methods=["GET"])
def get_all_promo():
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.all())
query_schema = Promo_Promo_schema(many=True)
return jsonify(query_schema.dump(get_promo_data))
@promo.route("/delete/<int:promo_id>", methods=["DELETE"])
def delete_a_promo(promo_id):
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
db.session.delete(get_promo_data)
db.session.commit()
return jsonify({
"ok": True,
}), 200
@promo.route("/create", methods=["POST"])
def create_promo():
"""
"""
date_created = datetime.utcnow()
name_of_promotion = request.json["name_of_promotion"]
money_off_delivery = request.json["money_off_delivery"]
description = request.json["description"]
text_on_ticket = request.json["text_on_ticket"]
# see if it exists
amount_off = convert_to_decimal(money_off_delivery)
new_promo = Promo_Promo(
name_of_promotion = name_of_promotion,
money_off_delivery = amount_off,
description = description,
date_created = date_created,
text_on_ticket=text_on_ticket
)
db.session.add(new_promo)
db.session.commit()
return jsonify({
"ok": True,
'promo_id':new_promo.id,
}), 200
@promo.route("/edit/<int:promo_id>", methods=["PUT"])
def edit_promo(promo_id):
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
text_on_ticket = request.json["text_on_ticket"]
name_of_promotion = request.json["name_of_promotion"]
money_off_delivery = request.json["money_off_delivery"]
description = request.json["description"]
amount_off = convert_to_decimal(money_off_delivery)
get_promo_data.text_on_ticket = text_on_ticket
get_promo_data.description = description
get_promo_data.name_of_promotion = name_of_promotion
get_promo_data.money_off_delivery = amount_off
db.session.add(get_promo_data)
db.session.commit()
return jsonify({
"ok": True,
'promo_id':get_promo_data.id,
}), 200
@promo.route("/on/<int:promo_id>", methods=["PATCH"])
def turn_on_promo(promo_id):
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
get_promo_data.active = True
db.session.add(get_promo_data)
db.session.commit()
return jsonify({
"ok": True,
'promo_id':get_promo_data.id,
}), 200
@promo.route("/off/<int:promo_id>", methods=["PATCH"])
def turn_off_promo(promo_id):
"""
"""
get_promo_data = (db.session
.query(Promo_Promo)
.filter(Promo_Promo.id == promo_id)
.first())
get_promo_data.active = False
db.session.add(get_promo_data)
db.session.commit()
return jsonify({
"ok": True,
'promo_id':get_promo_data.id,
}), 200

0
app/query/__init__.py Normal file → Executable file
View File

50
app/query/views.py Normal file → Executable file
View File

@@ -1,20 +1,28 @@
from flask import request, jsonify from flask import jsonify
from flask_login import current_user
from datetime import date, timedelta
from app.query import query from app.query import query
from app import db from app import db
from datetime import datetime from app.classes.admin import Admin_Company
from app.classes.query import Query_StateList, \ from app.classes.query import (Query_StateList,
Query_DeliveryStatusList, \ Query_DeliveryStatusList,
Query_DeliveryStatusList_Schema, \ Query_DeliveryStatusList_Schema,
Query_StateList_Schema, \ Query_StateList_Schema,
Query_CustomerTypeList, \ Query_CustomerTypeList,
Query_CustomerTypeList_Schema,\ Query_CustomerTypeList_Schema,
Query_EmployeeTypeList, \ Query_EmployeeTypeList,
Query_EmployeeTypeList_Schema,\ Query_EmployeeTypeList_Schema)
Query_ServiceTypeList,\
Query_ServiceTypeList_Schema
@query.route("/company/<int:company_id>", methods=["GET"])
def get_company(company_id):
"""
This will get the company from env variable
"""
query_data = (db.session
.query(Admin_Company)
.filter(Admin_Company.id == company_id)
.first())
delivery_schema = Query_DeliveryStatusList_Schema(many=False)
return jsonify(delivery_schema.dump(query_data))
@query.route("/states", methods=["GET"]) @query.route("/states", methods=["GET"])
def get_state_list(): def get_state_list():
@@ -43,17 +51,6 @@ def get_customer_type_list():
return jsonify(customer_schema.dump(query_data)) return jsonify(customer_schema.dump(query_data))
@query.route("/servicetype", methods=["GET"])
def get_service_type_list():
"""
This will get types of service
"""
query_data = db.session \
.query(Query_ServiceTypeList) \
.all()
customer_schema = Query_ServiceTypeList_Schema(many=True)
return jsonify(customer_schema.dump(query_data))
@query.route("/employeetype", methods=["GET"]) @query.route("/employeetype", methods=["GET"])
@@ -80,3 +77,6 @@ def get_delivery_status_list():
.all() .all()
delivery_schema = Query_DeliveryStatusList_Schema(many=True) delivery_schema = Query_DeliveryStatusList_Schema(many=True)
return jsonify(delivery_schema.dump(query_data)) return jsonify(delivery_schema.dump(query_data))

0
app/reports/__init__.py Normal file → Executable file
View File

36
app/reports/views.py Normal file → Executable file
View File

@@ -1,16 +1,10 @@
from flask import request, jsonify from flask import jsonify
from flask_login import current_user
from sqlalchemy.sql import func from sqlalchemy.sql import func
from datetime import date, timedelta
from app.reports import reports from app.reports import reports
from app import db from app import db
from datetime import datetime
from app.classes.auth import Auth_User
from app.classes.customer import Customer_Customer from app.classes.customer import Customer_Customer
from app.classes.service import Service_Call, Service_Call_schema
from app.classes.employee import Employee_Employee
from app.classes.service import Service_Call_Notes_Dispatcher, Service_Call_Notes_Technician
from app.classes.delivery import Delivery_Delivery from app.classes.delivery import Delivery_Delivery
@@ -21,4 +15,28 @@ def oil_total_gallons():
.group_by(Delivery_Delivery.id)\ .group_by(Delivery_Delivery.id)\
.all() .all()
return jsonify({"ok": True }), 200 return jsonify({"ok": True, "oil": total_oil }), 200
@reports.route("/customers/list", methods=["GET"])
def customer_list():
"""
Retrieve a list of customers with selected fields for printing.
Returns account number, first name, last name, address, town, and phone number.
Ordered by last name from A to Z.
"""
customers = db.session.query(Customer_Customer).order_by(Customer_Customer.customer_last_name.asc()).all()
customer_data = [
{
"account_number": customer.account_number,
"first_name": customer.customer_first_name,
"last_name": customer.customer_last_name,
"address": customer.customer_address,
"town": customer.customer_town,
"phone_number": customer.customer_phone_number
}
for customer in customers
]
response = jsonify({"ok": True, "customers": customer_data})
return response, 200

0
app/search/__init__.py Normal file → Executable file
View File

29
app/search/views.py Normal file → Executable file
View File

@@ -1,16 +1,9 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user
from sqlalchemy.sql import func
from datetime import date, timedelta
from app.search import search from app.search import search
from app import db from app import db
from datetime import datetime from sqlalchemy import or_
from app.classes.auth import Auth_User
from app.classes.customer import Customer_Customer, Customer_Customer_schema from app.classes.customer import Customer_Customer, Customer_Customer_schema
from app.classes.service import Service_Call, Service_Call_schema
from app.classes.employee import Employee_Employee
from app.classes.service import Service_Call_Notes_Dispatcher, Service_Call_Notes_Technician
from app.classes.delivery import Delivery_Delivery, Delivery_Delivery_schema from app.classes.delivery import Delivery_Delivery, Delivery_Delivery_schema
@@ -26,14 +19,15 @@ def search_customers():
search = search.replace("!", "") search = search.replace("!", "")
search = search.replace("#", "") search = search.replace("#", "")
search = search.replace("@", "") search = search.replace("@", "")
search = search.replace("$", "")
# search by last name # search by last name
if search_type == '@': if search_type == '@':
search = search[1:] search = search[1:]
customer_list = (db.session customer_list = (db.session
.query(Customer_Customer) .query(Customer_Customer)
.filter(Customer_Customer.customer_first_name.ilike(search)) .filter(Customer_Customer.customer_last_name.ilike(search))
.all()) .all())
# Customer Address
elif search_type == '!': elif search_type == '!':
search = search[::1] search = search[::1]
@@ -48,11 +42,22 @@ def search_customers():
.query(Customer_Customer) .query(Customer_Customer)
.filter(Customer_Customer.customer_phone_number.ilike(search)) .filter(Customer_Customer.customer_phone_number.ilike(search))
.all()) .all())
# Account Number
elif search_type == '$':
search = search[::1]
customer_list = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.account_number.ilike(search))
.order_by(Customer_Customer.account_number.asc())
.all())
else: else:
customer_list = (db.session customer_list = (db.session
.query(Customer_Customer) .query(Customer_Customer)
.filter(Customer_Customer.customer_last_name.ilike(search)) .filter(or_(Customer_Customer.customer_last_name.ilike(search),
Customer_Customer.customer_first_name.ilike(search),
Customer_Customer.customer_address.ilike(search)))
.all()) .all())

View File

@@ -1,397 +1,415 @@
from flask import request, jsonify from flask import request, jsonify
from flask_login import current_user, login_required
from app.service import service from app.service import service
from app import db from app import db
from datetime import datetime from datetime import datetime, date, timedelta
from app.classes.customer import (Customer_Customer)
from app.classes.auth import Auth_User from app.classes.service import (Service_Service,
from app.classes.customer import Customer_Customer Service_Service_schema, Service_Parts, Service_Parts_schema,
from app.classes.service import (Service_Call, Service_Plans, Service_Plans_schema
Service_Call_schema,
Service_Call_Notes_Dispatcher,
Service_Call_Notes_Technician,
Service_Call_Notes_Dispatcher_schema,
) )
from app.classes.cards import Card_Card from app.classes.auto import Auto_Delivery
from app.classes.employee import Employee_Employee
@service.route("/<string:service_id>", methods=["GET"])
@login_required
def get_specific_service_call(service_id):
service_call = db.session \
.query(Service_Call) \
.filter(Service_Call.id == service_id) \
.first()
service_schema = Service_Call_schema(many=False)
return jsonify(service_schema.dump(service_call))
@service.route("/paymenttype/<int:service_id>/<int:type_of_payment>", methods=["PUT"])
def update_a_service_payment(service_id, type_of_payment):
"""
This update a delivery for example if user updates to a fill
"""
service_call = (db.session
.query(Service_Call)
.filter(Service_Call.id == service_id)
.first())
service_call.payment_type = type_of_payment
db.session.add(service_call)
db.session.commit()
return jsonify({"ok": True}), 200
@service.route("/call/notes/<string:service_id>", methods=["GET"])
@login_required
def get_service_notes_call(service_id):
service_call = db.session \
.query(Service_Call_Notes_Dispatcher) \
.filter(Service_Call_Notes_Dispatcher.service_call_id == service_id) \
.first()
service_schema = Service_Call_Notes_Dispatcher_schema(many=False)
return jsonify(service_schema.dump(service_call))
@service.route("/all", methods=["GET"]) @service.route("/all", methods=["GET"])
def service_view_all(): def get_all_service_calls():
"""
Get all service calls
"""
service_calls = db.session \
.query(Service_Call) \
.filter(Service_Call.completed == 0) \
.order_by(Service_Call.when_called.desc()) \
.all()
customer_schema = Service_Call_schema(many=True)
return jsonify(customer_schema.dump(service_calls))
@service.route("/all/<int:page>", methods=["GET"])
def service_view(page):
"""
Get all service calls
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
service_calls = db.session \
.query(Service_Call) \
.filter(Service_Call.completed == 0) \
.order_by(Service_Call.when_called.desc()) \
.limit(per_page_amount).offset(offset_limit)
customer_schema = Service_Call_schema(many=True)
return jsonify(customer_schema.dump(service_calls))
@service.route("/customer/<int:customer_id>/<int:page>", methods=["GET"])
def service_customer_view(customer_id, page):
"""
Get all service calls
"""
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
service_calls = db.session \
.query(Service_Call) \
.filter(Service_Call.customer_id == customer_id) \
.order_by(Service_Call.id.desc()) \
.limit(per_page_amount).offset(offset_limit)
customer_schema = Service_Call_schema(many=True)
return jsonify(customer_schema.dump(service_calls))
@service.route("/create/<int:user_id>", methods=["POST"])
def service_create_call(user_id):
"""
create a service call
"""
now = datetime.utcnow()
get_customer = db.session \
.query(Customer_Customer) \
.filter(Customer_Customer.id == user_id) \
.first()
print(request.json)
get_service_type = request.json["type_of_service"]
service_note = request.json["dispatcher_notes_taken"]
service_subject = request.json["dispatcher_subject_taken"]
scheduled_date_date = request.json["date_scheduled"]
dispatcher_id = request.json["dispatcher_id"]
card_payment = request.json["credit"]
cash_payment = request.json["cash"]
try: try:
if request.json["credit_card_id"]: all_services = Service_Service.query.all()
card_payment_id = request.json["credit_card_id"] color_map = {
else: 0: {"backgroundColor": "blue", "textColor": "white"}, 1: {"backgroundColor": "red", "textColor": "white"},
card_payment_id = None 2: {"backgroundColor": "green", "textColor": "white"}, 3: {"backgroundColor": "yellow", "textColor": "black"},
except: 4: {"backgroundColor": "black", "textColor": "white"}
card_payment_id = None }
service_type_map = {0: 'Tune-up', 1: 'No Heat', 2: 'Fix', 3: 'Tank Install', 4: 'Other'}
if cash_payment is True and card_payment is False: calendar_events = []
delivery_payment_method = 0 for service_record in all_services:
elif card_payment is True and cash_payment is False: service_type_text = service_type_map.get(service_record.type_service_call, 'Service')
delivery_payment_method = 1 event_title = f"{service_type_text}: {service_record.customer_name}"
event_colors = color_map.get(service_record.type_service_call, {"backgroundColor": "gray", "textColor": "white"})
elif card_payment is True and cash_payment is True: # Use the schema to safely get the date string
delivery_payment_method = 2 serialized_record = Service_Service_schema().dump(service_record)
else: start_date = serialized_record.get('scheduled_date')
delivery_payment_method = 3
if card_payment_id is not None: event_data = {
get_card = (db.session "id": service_record.id,
.query(Card_Card) "title": event_title,
.filter(Card_Card.id == card_payment_id) "start": start_date,
.filter(Card_Card.user_id == get_customer.id) "end": None,
.first()) "extendedProps": {
card_id_from_customer = get_card.id "customer_id": service_record.customer_id,
else: "description": service_record.description,
card_id_from_customer = None "type_service_call": service_record.type_service_call,
"service_cost": str(service_record.service_cost) if service_record.service_cost is not None else None
},
"backgroundColor": event_colors.get("backgroundColor"),
"textColor": event_colors.get("textColor"),
"borderColor": event_colors.get("backgroundColor")
}
calendar_events.append(event_data)
date_object = datetime.strptime(scheduled_date_date, '%Y-%m-%d').date() return jsonify(calendar_events), 200
except Exception as e:
# Add error logging to see what's happening
print(f"An error occurred in /service/all: {e}")
return jsonify(error=str(e)), 500
get_tech = (db.session
.query(Employee_Employee)
.first())
create_a_call = Service_Call( # --- THIS IS THE FIX ---
customer_id=get_customer.id, # The logic from /all has been copied here to ensure a consistent data structure.
customer_last_name=get_customer.customer_last_name, @service.route("/upcoming", methods=["GET"])
customer_first_name=get_customer.customer_first_name, def get_upcoming_service_calls():
customer_town=get_customer.customer_town, """
customer_state=get_customer.customer_state, Fetches a list of all future service calls from today onwards.
customer_zip=get_customer.customer_zip, """
customer_apt=get_customer.customer_apt, now = datetime.now()
customer_address=get_customer.customer_address, upcoming_services = (
status=0, Service_Service.query
service_type=get_service_type, .filter(Service_Service.scheduled_date >= now)
when_called=now, .order_by(Service_Service.scheduled_date.asc())
scheduled_date=date_object, .limit(100)
when_serviced=None, .all()
tech_id=get_tech.id,
tech_first_name=get_tech.employee_first_name,
tech_last_name=get_tech.employee_last_name,
completed=0,
payment_type=delivery_payment_method,
payment_card_id=card_id_from_customer,
) )
db.session.add(create_a_call) service_schema = Service_Service_schema(many=True)
db.session.flush() result = service_schema.dump(upcoming_services)
create_new_note = Service_Call_Notes_Dispatcher(
service_call_id=create_a_call.id, return jsonify(result), 200
dispatcher_subject=service_subject,
dispatcher_notes=service_note,
time_added=now, @service.route("/past", methods=["GET"])
dispatcher_id=dispatcher_id, def get_past_service_calls():
dispatcher_name=None, """
Fetches a list of all past service calls before today.
"""
past_services = (
Service_Service.query
.filter(Service_Service.scheduled_date < datetime.combine(date.today(), datetime.min.time()))
.order_by(Service_Service.scheduled_date.asc())
.limit(100)
.all()
) )
db.session.add(create_new_note) service_schema = Service_Service_schema(many=True)
result = service_schema.dump(past_services)
return jsonify(result), 200
@service.route("/today", methods=["GET"])
def get_today_service_calls():
"""
Fetches a list of all service calls for today.
"""
start_of_today = datetime.combine(date.today(), datetime.min.time())
start_of_tomorrow = datetime.combine(date.today() + timedelta(days=1), datetime.min.time())
today_services = (
Service_Service.query
.filter(Service_Service.scheduled_date >= start_of_today)
.filter(Service_Service.scheduled_date < start_of_tomorrow)
.order_by(Service_Service.scheduled_date.asc())
.limit(100)
.all()
)
service_schema = Service_Service_schema(many=True)
result = service_schema.dump(today_services)
return jsonify(result), 200
@service.route("/upcoming/count", methods=["GET"])
def get_upcoming_service_calls_count():
now = datetime.now()
try:
count = (db.session.query(Service_Service).filter(Service_Service.scheduled_date >= now).count())
return jsonify({"count": count}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@service.route("/for-customer/<int:customer_id>", methods=["GET"])
def get_service_calls_for_customer(customer_id):
service_records = (Service_Service.query.filter_by(customer_id=customer_id).order_by(Service_Service.scheduled_date.desc()).all())
service_schema = Service_Service_schema(many=True)
result = service_schema.dump(service_records)
return jsonify(result), 200
@service.route("/create", methods=["POST"])
def create_service_call():
data = request.get_json()
if not data: return jsonify({"error": "No data provided"}), 400
cus_id=data.get('customer_id')
get_customer = (db.session.query(Customer_Customer).filter(Customer_Customer.id == cus_id).first())
if not get_customer: return jsonify({"error": f"Customer with id {cus_id} not found."}), 404
scheduled_datetime_str = data.get('expected_delivery_date')
scheduled_datetime_obj = datetime.fromisoformat(scheduled_datetime_str)
new_service_call = Service_Service(
customer_id=get_customer.id, customer_name=get_customer.customer_first_name + ' ' + get_customer.customer_last_name,
customer_address=get_customer.customer_address, customer_town=get_customer.customer_town,
customer_state=get_customer.customer_state, customer_zip=get_customer.customer_zip,
type_service_call=data.get('type_service_call'), when_ordered=datetime.utcnow(),
scheduled_date=scheduled_datetime_obj, description=data.get('description'), service_cost=None,
)
db.session.add(new_service_call)
db.session.commit()
return jsonify({ "ok": True, "id": new_service_call.id }), 201
@service.route("/update-cost/<int:id>", methods=["PUT"])
def update_service_cost(id):
"""
Dedicated endpoint to update only the service cost for a service call.
This is used after payment capture/charge to update the actual amount.
"""
try:
# Find the service
service_record = Service_Service.query.get_or_404(id)
# Get request data - only service_cost
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
# Extract and validate the service_cost
new_cost = data.get('service_cost')
if new_cost is None:
return jsonify({"error": "service_cost is required"}), 400
# Convert to float for validation
try:
new_cost_float = float(new_cost)
except (ValueError, TypeError):
return jsonify({"error": "service_cost must be a valid number"}), 400
# Update the service_cost
service_record.service_cost = new_cost_float
# Commit the transaction
db.session.commit() db.session.commit()
db.session.commit() # Return success response
return jsonify({
return jsonify({"ok": True, "ok": True,
'user_id': get_customer.id, "service_id": id,
'service_id': create_a_call.id, "service_cost_updated": new_cost_float,
"message": f"Service {id} cost updated to ${new_cost_float}"
}), 200 }), 200
@service.route("/delete/<int:service_id>", methods=["DELETE"]) except Exception as e:
def service_delete_call(service_id): db.session.rollback()
""" print(f"Error updating service cost for service {id}: {str(e)}")
delete a service call return jsonify({"error": str(e)}), 500
"""
get_call_to_delete = (db.session @service.route("/update/<int:id>", methods=["PUT"])
.query(Service_Call) def update_service_call(id):
.filter(Service_Call.id == service_id) service_record = Service_Service.query.get_or_404(id)
.first()) data = request.get_json()
db.session.delete(get_call_to_delete) if not data: return jsonify({"error": "No data provided"}), 400
scheduled_datetime_str = data.get('scheduled_date')
if scheduled_datetime_str:
service_record.scheduled_date = datetime.fromisoformat(scheduled_datetime_str)
service_record.type_service_call = data.get('type_service_call', service_record.type_service_call)
service_record.description = data.get('description', service_record.description)
service_record.service_cost = data.get('service_cost', service_record.service_cost)
try:
db.session.commit() db.session.commit()
service_schema = Service_Service_schema(many=False)
return jsonify({"ok": True}), 200 return jsonify({"ok": True, "service": service_schema.dump(service_record)}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
# Service Plans CRUD endpoints
@service.route("/edit/<int:service_id>", methods=["PUT"]) @service.route("/plans/active", methods=["GET"])
def service_edit_call(service_id): def get_active_service_plans():
""" """
Update a service call Get all active service plans (contract_plan > 0)
""" """
try:
plans = Service_Plans.query.filter(Service_Plans.contract_plan > 0).all()
plans_schema = Service_Plans_schema(many=True)
result = plans_schema.dump(plans)
get_service_type = request.json["type_of_service"] # Add customer info to each plan
scheduled_date_date = request.json["date_scheduled"] for plan in result:
dispatcher_subject_taken = request.json["dispatcher_subject_taken"] customer = Customer_Customer.query.get(plan['customer_id'])
dispatcher_notes_taken = request.json["dispatcher_notes_taken"] if customer:
tech_id = request.json["tech_id"] plan['customer_name'] = f"{customer.customer_first_name} {customer.customer_last_name}"
plan['customer_address'] = customer.customer_address
plan['customer_town'] = customer.customer_town
card_payment = request.json["credit"] return jsonify(result), 200
cash_payment = request.json["cash"] except Exception as e:
return jsonify({"error": str(e)}), 500
if request.json["credit_card_id"]:
card_payment_id = request.json["credit_card_id"] @service.route("/plans/customer/<int:customer_id>", methods=["GET"])
def get_customer_service_plan(customer_id):
"""
Get service plan for a specific customer
"""
try:
plan = Service_Plans.query.filter_by(customer_id=customer_id).first()
if plan:
plan_schema = Service_Plans_schema()
return jsonify(plan_schema.dump(plan)), 200
else: else:
card_payment_id = None return jsonify(None), 200
except Exception as e:
get_tech = db.session \ return jsonify({"error": str(e)}), 500
.query(Employee_Employee) \
.filter(Employee_Employee.id == tech_id) \
.first()
get_service_call_call = db.session \
.query(Service_Call) \
.filter(Service_Call.id == service_id) \
.first()
if card_payment_id is not None:
get_customer = db.session \
.query(Customer_Customer) \
.filter(Customer_Customer.id == get_service_call_call.customer_id) \
.first()
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_payment_id)
.filter(Card_Card.user_id == get_customer.id)
.first())
card_id_from_customer = get_card.id
else:
card_id_from_customer = None
if cash_payment is True and card_payment is False:
delivery_payment_method = 0
elif card_payment is True and cash_payment is False:
delivery_payment_method = 1
elif card_payment is True and cash_payment is True:
delivery_payment_method = 2
else:
delivery_payment_method = 3
get_service_call_notes = (db.session
.query(Service_Call_Notes_Dispatcher)
.filter(Service_Call_Notes_Dispatcher.service_call_id == get_service_call_call.id)
.first())
get_service_call_notes.dispatcher_subject = dispatcher_subject_taken
get_service_call_notes.dispatcher_notes = dispatcher_notes_taken
get_service_call_call.service_type = get_service_type
get_service_call_call.scheduled_date = scheduled_date_date
get_service_call_call.tech_id = get_tech.id
get_service_call_call.payment_type = delivery_payment_method
get_service_call_call.payment_card_id = card_id_from_customer
db.session.add(get_service_call_call)
db.session.commit()
return jsonify({"ok": True}), 200
@service.route("/update/type/<int:service_id>", methods=["PUT"]) @service.route("/plans/create", methods=["POST"])
def service_update_service_type(service_id): def create_service_plan():
""" """
Update a service call diagnosis Create a new service plan for a customer
""" """
get_service_type = request.json["service_type"] data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
get_service_call = db.session \ try:
.query(Service_Call) \ new_plan = Service_Plans(
.filter(Service_Call.service_id == service_id) \ customer_id=data['customer_id'],
.first() contract_plan=data['contract_plan'],
contract_years=data['contract_years'],
get_service_call.service_type = get_service_type contract_start_date=datetime.fromisoformat(data['contract_start_date'])
db.session.add(get_service_call)
db.session.commit()
return jsonify({"ok": True}), 200
@service.route("/note/dispatcher/<int:service_id>", methods=["PUT"])
def service_create_note_dispatcher(service_id):
"""
Initial dispatcher note about the call
"""
service_note = request.json["dispatcher_text"]
now = datetime.utcnow()
user = db.session \
.query(Auth_User) \
.filter(Auth_User.id == current_user.id) \
.first()
get_service_call_order = db.session \
.query(Service_Call) \
.filter(Service_Call.service_id == service_id) \
.first()
create_new_note = Service_Call_Notes_Dispatcher(
service_call_id=get_service_call_order.id,
dispatcher_notes=service_note,
time_added=now,
dispatcher_id=user.id,
dispatcher_name=user.username,
) )
db.session.add(new_plan)
db.session.add(create_new_note)
db.session.commit() db.session.commit()
return jsonify({"ok": True}), 200 plan_schema = Service_Plans_schema()
return jsonify({"ok": True, "plan": plan_schema.dump(new_plan)}), 201
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@service.route("/note/technician/<int:service_id>", methods=["PUT"]) @service.route("/plans/update/<int:customer_id>", methods=["PUT"])
def service_create_note_technician(service_id): def update_service_plan(customer_id):
""" """
Technician can create notes on the call Update existing service plan for a customer
""" """
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
service_technician_notes = request.json["technician_comments"] try:
now = datetime.utcnow() plan = Service_Plans.query.filter_by(customer_id=customer_id).first()
if not plan:
# Create new plan if it doesn't exist
plan = Service_Plans(customer_id=customer_id)
db.session.add(plan)
user = db.session \ plan.contract_plan = data.get('contract_plan', plan.contract_plan)
.query(Auth_User) \ plan.contract_years = data.get('contract_years', plan.contract_years)
.filter(Auth_User.id == current_user.id) \ if data.get('contract_start_date'):
.first() plan.contract_start_date = datetime.fromisoformat(data['contract_start_date'])
get_service_call = db.session \
.query(Service_Call) \
.filter(Service_Call.service_id == service_id) \
.first()
create_new_note = Service_Call_Notes_Technician(
service_call_id=get_service_call.id,
technician_comments=service_technician_notes,
time_added=now,
technician_id=user.id,
technician_name=user.username,
)
db.session.add(create_new_note)
db.session.commit() db.session.commit()
plan_schema = Service_Plans_schema()
return jsonify({"ok": True, "plan": plan_schema.dump(plan)}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@service.route("/plans/delete/<int:customer_id>", methods=["DELETE"])
def delete_service_plan(customer_id):
"""
Delete service plan for a customer
"""
try:
plan = Service_Plans.query.filter_by(customer_id=customer_id).first()
if not plan:
return jsonify({"error": "Service plan not found"}), 404
db.session.delete(plan)
db.session.commit()
return jsonify({"ok": True, "message": "Service plan deleted successfully"}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@service.route("/<int:id>", methods=["GET"])
def get_service_by_id(id):
service_record = Service_Service.query.get_or_404(id)
service_schema = Service_Service_schema()
return jsonify({"ok": True, "service": service_schema.dump(service_record)}), 200
@service.route("/delete/<int:id>", methods=["DELETE"])
def delete_service_call(id):
service_record = Service_Service.query.get_or_404(id)
try:
db.session.delete(service_record)
db.session.commit()
return jsonify({"ok": True, "message": "Service deleted successfully"}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@service.route("/parts/customer/<int:customer_id>", methods=["GET"])
def get_service_parts(customer_id):
parts = Service_Parts.query.filter_by(customer_id=customer_id).first()
if parts:
parts_schema = Service_Parts_schema()
return jsonify(parts_schema.dump(parts)), 200
else:
return jsonify({
"customer_id": customer_id, "oil_filter": "", "oil_filter_2": "",
"oil_nozzle": "", "oil_nozzle_2": "", "hot_water_tank": 0
}), 200
@service.route("/parts/update/<int:customer_id>", methods=["POST"])
def update_service_parts(customer_id):
try:
data = request.get_json()
if not data:
return jsonify({"error": "No data provided"}), 400
get_customer = db.session.query(Customer_Customer).filter(Customer_Customer.id == customer_id).first()
parts = Service_Parts.query.filter_by(customer_id=customer_id).first()
if not parts:
parts = Service_Parts(customer_id=customer_id)
db.session.add(parts)
parts.oil_filter = data.get('oil_filter', parts.oil_filter)
parts.oil_filter_2 = data.get('oil_filter_2', parts.oil_filter_2)
parts.oil_nozzle = data.get('oil_nozzle', parts.oil_nozzle)
parts.oil_nozzle_2 = data.get('oil_nozzle_2', parts.oil_nozzle_2)
parts.hot_water_tank = data.get('hot_water_tank', parts.hot_water_tank if parts.hot_water_tank is not None else 0)
# Sync to Auto_Delivery if customer is automatic
if get_customer and get_customer.customer_automatic == 1:
get_auto = db.session.query(Auto_Delivery).filter(Auto_Delivery.customer_id == customer_id).first()
if get_auto:
get_auto.hot_water_summer = parts.hot_water_tank
db.session.add(get_auto)
db.session.commit()
return jsonify({"ok": True, "message": "Service parts updated successfully"}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500
@service.route("/payment/<int:service_id>/<int:payment_type>", methods=["PUT"])
def process_service_payment(service_id, payment_type):
service = db.session.query(Service_Service).filter(Service_Service.id == service_id).first()
if not service:
return jsonify({"ok": False, "error": "Service not found"}), 404
# Set payment columns as specified
service.payment_type = payment_type # e.g., 1 for Tiger
service.payment_status = 2 # As specified
# payment_card_id retains the selected card's ID if set in the service record
try:
db.session.commit()
return jsonify({"ok": True}), 200 return jsonify({"ok": True}), 200
except Exception as e:
db.session.rollback()
return jsonify({"error": str(e)}), 500

7
app/social/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
# coding=utf-8
from flask import Blueprint
social = Blueprint('social', __name__)
from . import views

77
app/social/views.py Normal file
View File

@@ -0,0 +1,77 @@
from flask import jsonify, request
import datetime
from app.social import social
from app import db
from app.classes.customer_social import (Customer_Customer_Social_schema,
Customer_Customer_Social)
@social.route("/posts/<int:customer_id>/<int:page>", methods=["GET"])
def get_customer_posts(customer_id, page):
per_page_amount = 50
if page is None:
offset_limit = 0
elif page == 1:
offset_limit = 0
else:
offset_limit = (per_page_amount * page) - per_page_amount
customer_posts = (db.session
.query(Customer_Customer_Social)
.filter(Customer_Customer_Social.customer_id == customer_id)
.order_by(Customer_Customer_Social.id.desc())
.limit(per_page_amount).offset(offset_limit))
customer_social_schema = Customer_Customer_Social_schema(many=True)
return jsonify(customer_social_schema.dump(customer_posts))
@social.route("/create/<int:customer_id>", methods=["POST"])
def create_post(customer_id):
comment = request.json["comment"]
poster_employee_id = request.json["poster_employee_id"]
create_post = Customer_Customer_Social(
created = datetime.datetime.utcnow(),
customer_id = customer_id,
poster_employee_id = poster_employee_id,
comment = comment
)
db.session.add(create_post)
db.session.commit()
return jsonify({ "ok": True,}), 200
@social.route("/posts/<int:post_id>", methods=["PATCH"])
def edit_post(post_id):
customer_post = (db.session
.query(Customer_Customer_Social)
.filter(Customer_Customer_Social.id == post_id)
.first())
comment = request.json["comment"]
customer_post.comment = comment
db.session.add(customer_post)
db.session.commit()
return jsonify({ "ok": True,}), 200
@social.route("/delete/<int:post_id>", methods=["DELETE"])
def delete_post(post_id):
customer_post = (db.session
.query(Customer_Customer_Social)
.filter(Customer_Customer_Social.id == post_id)
.first())
db.session.delete(customer_post)
db.session.commit()
return jsonify({ "ok": True,}), 200

0
app/stats/__init__.py Normal file → Executable file
View File

213
app/stats/views.py Normal file → Executable file
View File

@@ -1,31 +1,218 @@
from flask import jsonify from flask import jsonify
from sqlalchemy import func
from datetime import date from datetime import date
from app.stats import stats from app.stats import stats
import datetime
from app import db from app import db
from app.classes.delivery import Delivery_Delivery from app.classes.delivery import Delivery_Delivery
from app.classes.service import Service_Call from app.classes.stats_company import Stats_Company, Stats_Company_schema
from app.classes.stats_customer import Stats_Customer, Stats_Customer_schema
def get_monday_date(date_object):
"""Gets the date of the Monday for the given date."""
# Get the day of the week as an integer (0 = Monday, 6 = Sunday)
day_of_week = date_object.weekday()
# Calculate the number of days to subtract to get to Monday
days_to_monday = day_of_week - 0 # Monday is 0
# Subtract the days from the given date to get Monday's date
monday_date = date_object - datetime.timedelta(days=days_to_monday)
return monday_date
@stats.route("/calls/add", methods=["PUT"])
def total_calls_post():
total_calls_today = (db.session
.query(Stats_Company)
.filter(Stats_Company.expected_delivery_date == date.today())
.first())
current_call_count = total_calls_today.total_calls
new_call = current_call_count + 1
total_calls_today.total_calls = new_call
db.session.add(total_calls_today)
db.session.commit()
return jsonify({"ok": True,}), 200
@stats.route("/calls/count/today", methods=["GET"])
def total_calls_today():
total_calls_today = (db.session
.query(Stats_Company)
.filter(Stats_Company.expected_delivery_date == date.today())
.count())
return jsonify({"ok": True,
'data': total_calls_today,
}), 200
@stats.route("/gallons/total/<int:driver_id>", methods=["GET"])
def total_gallons_delivered_driver(driver_id):
gallons_list = []
total_gallons = db.session\
.query(Delivery_Delivery)\
.filter(Delivery_Delivery.driver_employee_id == driver_id)\
.all()
for f in total_gallons:
gallons_list.append(f.gallons_delivered)
sum_of_gallons = (sum(gallons_list))
return jsonify({"ok": True,
'data': sum_of_gallons,
}), 200
@stats.route("/delivery/total/<int:driver_id>", methods=["GET"])
def total_deliveries_driver(driver_id):
total_stops = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.driver_employee_id == driver_id)
.count())
return jsonify({"ok": True,
'data': total_stops,
}), 200
@stats.route("/primes/total/<int:driver_id>", methods=["GET"])
def total_primes_driver(driver_id):
total_stops = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.driver_employee_id == driver_id)
.filter(Delivery_Delivery.prime == 1)
.count())
return jsonify({"ok": True,
'data': total_stops,
}), 200
@stats.route("/delivery/count/today", methods=["GET"]) @stats.route("/delivery/count/today", methods=["GET"])
def get_delivery_today(): def total_deliveries_today():
today_deliveries = (db.session total_stops = (db.session
.query(Delivery_Delivery) .query(Delivery_Delivery)
.order_by(func.date(Delivery_Delivery.expected_delivery_date) == date.today()) .filter(Delivery_Delivery.expected_delivery_date == date.today())
.count()) .count())
return jsonify({"ok": True, return jsonify({"ok": True,
'data': today_deliveries, 'data': total_stops,
}), 200 }), 200
@stats.route("/service/count/today", methods=["GET"]) @stats.route("/delivery/count/delivered/today", methods=["GET"])
def get_service_today(): def total_deliveries_today_finished():
today_calls = (db.session total_stops = (db.session
.query(Service_Call) .query(Delivery_Delivery)
.order_by(func.date(Service_Call.scheduled_date) == date.today()) .filter(Delivery_Delivery.expected_delivery_date == date.today())
.filter((Delivery_Delivery.delivery_status == 10))
.count()) .count())
return jsonify({"ok": True, return jsonify({"ok": True,
'data': today_calls, 'data': total_stops,
}), 200 }), 200
@stats.route("/user/<int:user_id>", methods=["GET"])
def get_user_stats(user_id):
"""
gets stats of user
"""
get_user = db.session \
.query(Stats_Customer) \
.filter(Stats_Customer.customer_id == user_id) \
.first()
if get_user is None:
new_stats = Stats_Customer(
customer_id = user_id,
total_calls = 1,
service_calls_total = 0,
service_calls_total_spent = 0,
service_calls_total_profit = 0,
oil_deliveries = 0,
oil_total_gallons = 0,
oil_total_spent = 0,
oil_total_profit = 0,
)
db.session.add(new_stats)
db.session.commit()
get_user = db.session \
.query(Stats_Customer) \
.filter(Stats_Customer.customer_id == user_id) \
.first()
user_schema = Stats_Customer_schema(many=False)
return jsonify(user_schema.dump(get_user))
@stats.route("/user/lastdelivery/<int:user_id>", methods=["GET"])
def get_user_last_delivery(user_id):
"""
gets users last delivery. used on profile page
"""
get_delivery= db.session \
.query(Delivery_Delivery) \
.filter(Delivery_Delivery.customer_id == user_id) \
.filter(Delivery_Delivery.delivery_status == 10) \
.order_by(Delivery_Delivery.id.desc())\
.first()
if get_delivery:
date_delivered = get_delivery.when_delivered
else:
date_delivered = "no deliveries on record"
return jsonify({"ok": True,
'date': str(date_delivered),
}), 200
@stats.route("/gallons/week", methods=["GET"])
def total_gallons_delivered_this_week():
# Get today's date
total_gallons = 0
today = datetime.date.today()
# Get the date of the Monday for today
monday = get_monday_date(today)
get_total = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.when_delivered >= monday)
.filter(Delivery_Delivery.when_delivered <= today)
.all())
for f in get_total:
total_gallons = total_gallons + f.gallons_delivered
return jsonify({"ok": True,
'total': total_gallons,
}), 200
@stats.route("/gallons/check/total/<int:user_id>", methods=["GET"])
def calculate_gallons_user(user_id):
# Get today's date
total_gallons = 0
# Get the date of the Monday for today
get_total = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.customer_id == user_id)
.all())
get_user = db.session \
.query(Stats_Customer) \
.filter(Stats_Customer.customer_id == user_id) \
.first()
for f in get_total:
total_gallons = total_gallons + f.gallons_delivered
get_user.oil_total_gallons = total_gallons
db.session.add(get_user)
db.session.commit()
return jsonify({"ok": True,
}), 200

7
app/ticket/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
# coding=utf-8
from flask import Blueprint
ticket = Blueprint('ticket', __name__)
from . import views

14
app/ticket/views.py Normal file
View File

@@ -0,0 +1,14 @@
from flask import jsonify
from app.ticket import ticket
from app import db
from app.classes.delivery import Delivery_Delivery
@ticket.route("/<int:ticket_id>", methods=["GET"])
def get_ticket_printer_letter(ticket_id):
return jsonify({"ok": True,
}), 200

24
config.py Normal file
View File

@@ -0,0 +1,24 @@
import os
def load_config(mode=os.environ.get('MODE')):
try:
print(f"mode is {mode}")
if mode == 'PRODUCTION':
from settings_prod import ApplicationConfig
return ApplicationConfig
elif mode == 'DEVELOPMENT':
from settings_dev import ApplicationConfig
return ApplicationConfig
elif mode == 'LOCAL':
from settings_local import ApplicationConfig
return ApplicationConfig
else:
from settings_prod import ApplicationConfig
return ApplicationConfig
except ImportError:
from settings_local import ApplicationConfig
return ApplicationConfig

View File

@@ -0,0 +1 @@
# 0 = charge, 1 = auth, 3 = capture

8
info_delivery_status.txt Normal file
View File

@@ -0,0 +1,8 @@
0: Waiting
1: Cancelled
2: Today
3: Tomorrow_Delivery
4: Partial_Delivery
5: Issue_Delivery
9: Pending
10: Finalized

7
info_home_type.txt Normal file
View File

@@ -0,0 +1,7 @@
0: Residential
1: apartment
2: condo
3: commercial
4: business
5: construction
6: container

13
info_paymant_type.txt Normal file
View File

@@ -0,0 +1,13 @@
0: Cash
1: CC
2: Cash/CC
3: Check
4: Other
0: Cash
1: CC - Tiger
11: CC - Authorize
2: Cash/CC
3: Check
4: Other

7
info_state_list.txt Normal file
View File

@@ -0,0 +1,7 @@
0: MA
1: RI
2: NH
3: ME
4: VT
5: CT
6: NY

17
nginx.conf Normal file
View File

@@ -0,0 +1,17 @@
server {
listen 80;
server_name _;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /app;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -1,19 +1,35 @@
flask==2.3.3 bcrypt==4.3.0
flask_sqlalchemy blinker==1.9.0
flask_session==0.5.0 cachelib==0.13.0
flask-login==0.6.3 click==8.1.8
flask-moment Flask==3.1.0
flask-paranoid Flask-Bcrypt==1.0.1
flask-bcrypt flask-cors==5.0.1
flask-cors Flask-Login==0.6.3
flask_marshmallow Flask-Mail==0.10.0
gunicorn flask-marshmallow==1.3.0
python-dateutil Flask-Moment==1.0.6
python-dotenv Flask-Paranoid==0.3.0
marshmallow-sqlalchemy Flask-Session==0.8.0
psycopg2-binary Flask-SQLAlchemy==3.1.1
redis Flask-WTF==1.2.2
sqlalchemy geographiclib==2.0
flask_wtf geopy==2.4.1
flask_mail greenlet==3.2.2
Werkzeug==2.3.8 gunicorn==23.0.0
itsdangerous==2.2.0
Jinja2==3.1.6
MarkupSafe==3.0.2
marshmallow==4.0.0
marshmallow-sqlalchemy==1.4.2
msgspec==0.19.0
packaging==25.0
psycopg2-binary==2.9.10
python-dateutil==2.9.0.post0
python-dotenv==1.1.0
redis==6.0.0
six==1.17.0
SQLAlchemy==2.0.40
typing_extensions==4.13.2
Werkzeug==3.1.3
WTForms==3.2.1

20
requirements.txt.bak Executable file
View File

@@ -0,0 +1,20 @@
flask==2.3.3
flask_sqlalchemy
flask_session==0.5.0
flask-login==0.6.3
flask-moment
flask-paranoid
flask-bcrypt
flask-cors
flask_marshmallow
gunicorn
python-dateutil
python-dotenv
marshmallow-sqlalchemy
psycopg2-binary
redis
sqlalchemy
flask_wtf
flask_mail
Werkzeug==2.3.8
geopy

11
runProduction.py Executable file
View File

@@ -0,0 +1,11 @@
# coding=utf-8
from app import app
PORT = 6000
HOST = '0.0.0.0'
if __name__ == '__main__':
app.run(
host=HOST,
port=PORT,
threaded=True
)

55
settings_dev.py Normal file
View File

@@ -0,0 +1,55 @@
class ApplicationConfig:
"""
Basic Configuration for a generic User
"""
CURRENT_SETTINGS = 'LOCAL'
# databases info
POSTGRES_USERNAME = 'postgres'
POSTGRES_PW = 'password'
POSTGRES_SERVER = '192.168.1.204:5432'
POSTGRES_DBNAME00 = 'eamco'
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}/{}".format(POSTGRES_USERNAME,
POSTGRES_PW,
POSTGRES_SERVER,
POSTGRES_DBNAME00
)
SQLALCHEMY_BINDS = {'eamco': SQLALCHEMY_DATABASE_URI}
# sqlalchemy config
SQLALCHEMY_TRACK_MODIFICATIONS = False
TRAP_HTTP_EXCEPTIONS = True
PROPAGATE_EXCEPTIONS = True
DEBUG = True
UPLOADED_FILES_DEST_ITEM = '/data/item'
# file uploads
UPLOADED_FILES_ALLOW = ['png', 'jpeg', 'jpg', 'png', 'gif']
MAX_CONTENT_LENGTH = 5 * 2500 * 2500
ALLOWED_EXTENSIONS = ['png', 'jpeg', 'jpg', 'png', 'gif']
# secret keys
SECRET_KEY = "youwillneverguessthiskeycia"
# sessions
# Available SESSION_TYPE options: 'redis', 'sqlalchemy', 'mongodb', 'filesystem', 'memcached'
SESSION_TYPE = "sqlalchemy"
SESSION_COOKIE_NAME = "eamco_session"
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Strict"
SESSION_PERMANENT = False
SESSION_USE_SIGNER = True
# CORS
CORS_ALLOWED_ORIGINS = [
"*"
]
CORS_SEND_WILDCARD = False
CORS_SUPPORT_CREDENTIALS = True
CORS_EXPOSE_HEADERS = None
CORS_ALLOW_HEADERS = "*"

58
settings_local.py Executable file
View File

@@ -0,0 +1,58 @@
class ApplicationConfig:
"""
Basic Configuration for a generic User
"""
CURRENT_SETTINGS = 'LOCAL'
# databases info
POSTGRES_USERNAME = 'postgres'
POSTGRES_PW = 'password'
POSTGRES_SERVER = '192.168.1.204:5432'
POSTGRES_DBNAME00 = 'auburnoil'
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}/{}".format(POSTGRES_USERNAME,
POSTGRES_PW,
POSTGRES_SERVER,
POSTGRES_DBNAME00
)
SQLALCHEMY_BINDS = {'auburnoil': SQLALCHEMY_DATABASE_URI}
# sqlalchemy config
SQLALCHEMY_TRACK_MODIFICATIONS = False
TRAP_HTTP_EXCEPTIONS = True
PROPAGATE_EXCEPTIONS = True
DEBUG = True
UPLOADED_FILES_DEST_ITEM = '/data/item'
# file uploads
UPLOADED_FILES_ALLOW = ['png', 'jpeg', 'jpg', 'png', 'gif']
MAX_CONTENT_LENGTH = 5 * 2500 * 2500
ALLOWED_EXTENSIONS = ['png', 'jpeg', 'jpg', 'png', 'gif']
# secret keys
SECRET_KEY = "youwillneverguessthiskeycia"
# sessions
# Available SESSION_TYPE options: 'redis', 'sqlalchemy', 'mongodb', 'filesystem', 'memcached'
SESSION_TYPE = "sqlalchemy"
SESSION_COOKIE_NAME = "eamco_session"
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Strict"
SESSION_PERMANENT = False
SESSION_USE_SIGNER = True
# CORS
CORS_SEND_WILDCARD = False
CORS_SUPPORT_CREDENTIALS = True
CORS_EXPOSE_HEADERS = None
CORS_ALLOW_HEADERS = "*"
CORS_ALLOWED_ORIGINS = [
'http://192.168.1.204:9610',
'http://192.168.1.204:9611',
'http://192.168.1.204:9612',
'http://192.168.1.204:9613',
'http://192.168.1.204:9614',
]

56
settings_prod.py Normal file
View File

@@ -0,0 +1,56 @@
class ApplicationConfig:
"""
Basic Configuration for a generic User
"""
CURRENT_SETTINGS = 'PRODUCTION'
# databases info
POSTGRES_USERNAME = 'postgres'
POSTGRES_PW = 'password'
POSTGRES_SERVER = '192.168.1.204:5432'
POSTGRES_DBNAME00 = 'auburnoil'
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}/{}".format(POSTGRES_USERNAME,
POSTGRES_PW,
POSTGRES_SERVER,
POSTGRES_DBNAME00
)
SQLALCHEMY_BINDS = {'auburnoil': SQLALCHEMY_DATABASE_URI}
# sqlalchemy config
SQLALCHEMY_TRACK_MODIFICATIONS = False
TRAP_HTTP_EXCEPTIONS = True
PROPAGATE_EXCEPTIONS = True
DEBUG = False
UPLOADED_FILES_DEST_ITEM = '/data/item'
# file uploads
UPLOADED_FILES_ALLOW = ['png', 'jpeg', 'jpg', 'png', 'gif']
MAX_CONTENT_LENGTH = 5 * 2500 * 2500
ALLOWED_EXTENSIONS = ['png', 'jpeg', 'jpg', 'png', 'gif']
# secret keys
SECRET_KEY = "34dsfkjh43123cxzfvqwer23432dsf233214efdasf2134321"
# sessions
SESSION_TYPE = "sqlalchemy"
SESSION_COOKIE_NAME = "eamco_session"
SESSION_COOKIE_SECURE = False
SESSION_COOKIE_HTTPONLY = True
REMEMBER_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Strict"
SESSION_PERMANENT = False
SESSION_USE_SIGNER = True
# CORS
CORS_SEND_WILDCARD = False
CORS_SUPPORT_CREDENTIALS = True
CORS_EXPOSE_HEADERS = None
CORS_ALLOW_HEADERS = "*"
CORS_ALLOWED_ORIGINS = [
'https://oil.edwineames.com',
'https://edwineames.com'
]

8
start.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -e
# Start Gunicorn
gunicorn --bind 127.0.0.1:8000 --workers 4 --timeout 120 app:app &
# Start Nginx
nginx -g 'daemon off;'

13
update_requirements.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Script to update pip packages and regenerate requirements.txt
# Extract package names from requirements.txt, removing version specifiers
packages=$(cat requirements.txt | sed 's/==.*//' | sed 's/<.*//' | sed 's/>.*//' | tr '\n' ' ')
# Upgrade all packages
pip3 install --upgrade $packages
# Regenerate requirements.txt with the new versions
pip3 freeze > requirements.txt
echo "Requirements updated successfully."